@flightdev/db 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,136 @@
1
+ /**
2
+ * @flightdev/db/supabase - Supabase Database Driver
3
+ *
4
+ * Uses @supabase/supabase-js for Supabase PostgreSQL.
5
+ * Includes RLS (Row Level Security) support out of the box.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { supabase } from '@flightdev/db/supabase';
10
+ * import { createDb } from '@flightdev/db';
11
+ *
12
+ * const db = createDb(supabase({
13
+ * url: process.env.SUPABASE_URL,
14
+ * key: process.env.SUPABASE_ANON_KEY, // or SERVICE_ROLE_KEY for admin
15
+ * }));
16
+ *
17
+ * // Use Supabase's query builder
18
+ * const { data, error } = await db.from('users').select('*');
19
+ * ```
20
+ */
21
+
22
+ import type { DatabaseDriver, QueryResult, ExecuteResult, Transaction } from './index.js';
23
+
24
+ export interface SupabaseConfig {
25
+ /** Supabase project URL */
26
+ url: string;
27
+ /** Supabase API key (anon or service_role) */
28
+ key: string;
29
+ /** Optional schema (default: public) */
30
+ schema?: string;
31
+ }
32
+
33
+ /**
34
+ * Create a Supabase driver
35
+ */
36
+ export function supabase(config: SupabaseConfig): DatabaseDriver & {
37
+ /** Access Supabase client directly for advanced features */
38
+ readonly client: any;
39
+ /** Query builder (Supabase style) */
40
+ from: (table: string) => any;
41
+ } {
42
+ let client: any = null;
43
+
44
+ async function getClient() {
45
+ if (!client) {
46
+ const { createClient } = await import('@supabase/supabase-js');
47
+ client = createClient(config.url, config.key, {
48
+ db: { schema: config.schema ?? 'public' },
49
+ });
50
+ }
51
+ return client;
52
+ }
53
+
54
+ const driver = {
55
+ name: 'supabase',
56
+
57
+ get client() {
58
+ return client;
59
+ },
60
+
61
+ async from(table: string) {
62
+ const c = await getClient();
63
+ return c.from(table);
64
+ },
65
+
66
+ async query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResult<T>> {
67
+ const c = await getClient();
68
+
69
+ // Use Supabase's RPC for raw SQL
70
+ // Note: This requires creating an RPC function in Supabase
71
+ // For simpler cases, use the query builder via .from()
72
+ const { data, error } = await c.rpc('flight_raw_query', {
73
+ query_text: sql,
74
+ query_params: params ?? [],
75
+ });
76
+
77
+ if (error) {
78
+ throw new Error(`Supabase query error: ${error.message}`);
79
+ }
80
+
81
+ return {
82
+ rows: (data ?? []) as T[],
83
+ rowCount: data?.length ?? 0,
84
+ raw: { data, error },
85
+ };
86
+ },
87
+
88
+ async execute(sql: string, params?: unknown[]): Promise<ExecuteResult> {
89
+ const c = await getClient();
90
+
91
+ const { data, error } = await c.rpc('flight_raw_execute', {
92
+ query_text: sql,
93
+ query_params: params ?? [],
94
+ });
95
+
96
+ if (error) {
97
+ throw new Error(`Supabase execute error: ${error.message}`);
98
+ }
99
+
100
+ return {
101
+ rowsAffected: data?.affected_rows ?? 0,
102
+ insertId: data?.insert_id,
103
+ raw: { data, error },
104
+ };
105
+ },
106
+
107
+ async transaction<T>(_fn: (tx: Transaction) => Promise<T>): Promise<T> {
108
+ // Supabase doesn't support client-side transactions
109
+ // Use RPC functions with SECURITY DEFINER for transactional logic
110
+ throw new Error(
111
+ 'Supabase does not support client-side transactions. ' +
112
+ 'Use RPC functions with SECURITY DEFINER for transactional operations.'
113
+ );
114
+ },
115
+
116
+ async close(): Promise<void> {
117
+ // Supabase client doesn't need explicit closing
118
+ client = null;
119
+ },
120
+
121
+ async ping(): Promise<boolean> {
122
+ try {
123
+ const c = await getClient();
124
+ const { error } = await c.from('_flight_ping').select('1').limit(1);
125
+ // Table might not exist, but connection works if no network error
126
+ return !error || error.code === 'PGRST116'; // 42P01 = table not found
127
+ } catch {
128
+ return false;
129
+ }
130
+ },
131
+ };
132
+
133
+ return driver;
134
+ }
135
+
136
+ export default supabase;
package/src/turso.ts ADDED
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @flightdev/db/turso - Turso/SQLite Driver
3
+ *
4
+ * Uses @libsql/client for Turso cloud and local SQLite.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { turso } from '@flightdev/db/turso';
9
+ * import { createDb } from '@flightdev/db';
10
+ *
11
+ * // Turso Cloud
12
+ * const db = createDb(turso({
13
+ * url: 'libsql://your-db.turso.io',
14
+ * authToken: process.env.TURSO_AUTH_TOKEN,
15
+ * }));
16
+ *
17
+ * // Local SQLite
18
+ * const localDb = createDb(turso({
19
+ * url: 'file:local.db',
20
+ * }));
21
+ * ```
22
+ */
23
+
24
+ import type { DatabaseDriver, QueryResult, ExecuteResult, Transaction } from './index.js';
25
+
26
+ export interface TursoConfig {
27
+ /** Database URL (libsql://... or file:...) */
28
+ url: string;
29
+ /** Auth token for Turso cloud */
30
+ authToken?: string;
31
+ /** Sync URL for embedded replicas */
32
+ syncUrl?: string;
33
+ }
34
+
35
+ /**
36
+ * Create a Turso/SQLite driver
37
+ */
38
+ export function turso(config: TursoConfig): DatabaseDriver {
39
+ let client: any = null;
40
+
41
+ async function getClient() {
42
+ if (!client) {
43
+ const { createClient } = await import('@libsql/client');
44
+ client = createClient({
45
+ url: config.url,
46
+ authToken: config.authToken,
47
+ syncUrl: config.syncUrl,
48
+ });
49
+ }
50
+ return client;
51
+ }
52
+
53
+ return {
54
+ name: 'turso',
55
+
56
+ async query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResult<T>> {
57
+ const c = await getClient();
58
+ const result = await c.execute({ sql, args: params ?? [] });
59
+
60
+ // Convert libsql rows to plain objects
61
+ const rows = result.rows.map((row: any) => {
62
+ const obj: Record<string, unknown> = {};
63
+ for (const [key, value] of Object.entries(row)) {
64
+ obj[key] = value;
65
+ }
66
+ return obj;
67
+ }) as T[];
68
+
69
+ return {
70
+ rows,
71
+ rowCount: result.rows.length,
72
+ raw: result,
73
+ };
74
+ },
75
+
76
+ async execute(sql: string, params?: unknown[]): Promise<ExecuteResult> {
77
+ const c = await getClient();
78
+ const result = await c.execute({ sql, args: params ?? [] });
79
+ return {
80
+ rowsAffected: result.rowsAffected,
81
+ insertId: result.lastInsertRowid ? Number(result.lastInsertRowid) : undefined,
82
+ raw: result,
83
+ };
84
+ },
85
+
86
+ async transaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {
87
+ const c = await getClient();
88
+ const libsqlTx = await c.transaction('write');
89
+
90
+ try {
91
+ const tx: Transaction = {
92
+ async query<R = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResult<R>> {
93
+ const result = await libsqlTx.execute({ sql, args: params ?? [] });
94
+ const rows = result.rows.map((row: any) => {
95
+ const obj: Record<string, unknown> = {};
96
+ for (const [key, value] of Object.entries(row)) {
97
+ obj[key] = value;
98
+ }
99
+ return obj;
100
+ }) as R[];
101
+ return { rows, rowCount: result.rows.length, raw: result };
102
+ },
103
+
104
+ async execute(sql: string, params?: unknown[]): Promise<ExecuteResult> {
105
+ const result = await libsqlTx.execute({ sql, args: params ?? [] });
106
+ return {
107
+ rowsAffected: result.rowsAffected,
108
+ insertId: result.lastInsertRowid ? Number(result.lastInsertRowid) : undefined,
109
+ raw: result,
110
+ };
111
+ },
112
+
113
+ async commit() {
114
+ await libsqlTx.commit();
115
+ },
116
+
117
+ async rollback() {
118
+ await libsqlTx.rollback();
119
+ },
120
+ };
121
+
122
+ const result = await fn(tx);
123
+ await libsqlTx.commit();
124
+ return result;
125
+ } catch (error) {
126
+ await libsqlTx.rollback();
127
+ throw error;
128
+ }
129
+ },
130
+
131
+ async close(): Promise<void> {
132
+ if (client) {
133
+ client.close();
134
+ client = null;
135
+ }
136
+ },
137
+
138
+ async ping(): Promise<boolean> {
139
+ try {
140
+ const c = await getClient();
141
+ await c.execute('SELECT 1');
142
+ return true;
143
+ } catch {
144
+ return false;
145
+ }
146
+ },
147
+ };
148
+ }
149
+
150
+ export default turso;
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src",
6
+ "noEmit": false,
7
+ "noUnusedLocals": false,
8
+ "noUnusedParameters": false
9
+ },
10
+ "include": [
11
+ "src/**/*"
12
+ ],
13
+ "exclude": [
14
+ "node_modules",
15
+ "dist"
16
+ ]
17
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,26 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ export default defineConfig({
4
+ entry: [
5
+ 'src/index.ts',
6
+ 'src/postgres.ts',
7
+ 'src/supabase.ts',
8
+ 'src/turso.ts',
9
+ 'src/neon.ts',
10
+ 'src/d1.ts',
11
+ ],
12
+ format: ['esm'],
13
+ dts: {
14
+ compilerOptions: {
15
+ skipLibCheck: true,
16
+ },
17
+ },
18
+ clean: true,
19
+ target: 'node20',
20
+ external: [
21
+ 'pg',
22
+ '@supabase/supabase-js',
23
+ '@libsql/client',
24
+ '@neondatabase/serverless',
25
+ ],
26
+ });