Storage implementations for AT Protocol OAuth applications. Provides a simple key-value storage interface with implementations for in-memory and SQLite backends.
1/**
2 * Pre-built adapters for common SQLite drivers.
3 * Each adapter converts a specific driver's API to the SQLiteAdapter interface.
4 */
5
6import type { SQLiteAdapter } from "./types.ts";
7
8/**
9 * Driver interface for Val.Town sqlite and libSQL/Turso client.
10 * Both use the same execute({ sql, args }) pattern.
11 */
12export interface ExecutableDriver {
13 execute(
14 query: { sql: string; args: unknown[] },
15 ): Promise<{ rows: unknown[][] }>;
16}
17
18/**
19 * Driver interface for @db/sqlite (Deno native) and similar prepare-based drivers.
20 */
21export interface PrepareDriver {
22 prepare(sql: string): {
23 all<T = Record<string, unknown>>(...params: unknown[]): T[];
24 };
25}
26
27/**
28 * Adapter for SQLite drivers using the execute({ sql, args }) pattern.
29 * Works with Val.Town sqlite, libSQL/Turso, and similar drivers.
30 *
31 * @example Val.Town
32 * ```typescript
33 * import { sqlite } from "https://esm.town/v/std/sqlite";
34 * import { SQLiteStorage, sqliteAdapter } from "@tijs/atproto-storage";
35 *
36 * const storage = new SQLiteStorage(sqliteAdapter(sqlite));
37 * ```
38 *
39 * @example libSQL/Turso
40 * ```typescript
41 * import { createClient } from "@libsql/client";
42 * import { SQLiteStorage, sqliteAdapter } from "@tijs/atproto-storage";
43 *
44 * const client = createClient({ url: "libsql://..." });
45 * const storage = new SQLiteStorage(sqliteAdapter(client));
46 * ```
47 */
48export function sqliteAdapter(driver: ExecutableDriver): SQLiteAdapter {
49 return {
50 execute: async (sql: string, params: unknown[]): Promise<unknown[][]> => {
51 const result = await driver.execute({ sql, args: params });
52 return result.rows;
53 },
54 };
55}
56
57/**
58 * Adapter for @db/sqlite (Deno native SQLite).
59 * Converts the synchronous prepare/all pattern to the async adapter interface.
60 *
61 * @example
62 * ```typescript
63 * import { Database } from "jsr:@db/sqlite";
64 * import { SQLiteStorage, denoSqliteAdapter } from "@tijs/atproto-storage";
65 *
66 * const db = new Database("storage.db");
67 * const storage = new SQLiteStorage(denoSqliteAdapter(db));
68 * ```
69 */
70export function denoSqliteAdapter(db: PrepareDriver): SQLiteAdapter {
71 return {
72 execute: (sql: string, params: unknown[]): Promise<unknown[][]> => {
73 const stmt = db.prepare(sql);
74 const rows = stmt.all(...params);
75 // Convert object rows to array rows (column order from Object.values)
76 return Promise.resolve(
77 rows.map((row) => Object.values(row as Record<string, unknown>)),
78 );
79 },
80 };
81}
82
83/**
84 * Adapter for better-sqlite3 (Node.js).
85 * Same pattern as Deno native but for Node environment.
86 *
87 * @example
88 * ```typescript
89 * import Database from "better-sqlite3";
90 * import { SQLiteStorage, betterSqlite3Adapter } from "@tijs/atproto-storage";
91 *
92 * const db = new Database("storage.db");
93 * const storage = new SQLiteStorage(betterSqlite3Adapter(db));
94 * ```
95 */
96export function betterSqlite3Adapter(db: PrepareDriver): SQLiteAdapter {
97 return {
98 execute: (sql: string, params: unknown[]): Promise<unknown[][]> => {
99 const stmt = db.prepare(sql);
100 const rows = stmt.all(...params);
101 return Promise.resolve(
102 rows.map((row) => Object.values(row as Record<string, unknown>)),
103 );
104 },
105 };
106}