@supabase/lite 0.0.1

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.
Files changed (44) hide show
  1. package/dist/Connection-BJclIu8v.d.ts +529 -0
  2. package/dist/Connection-BsiQMo4A.d.ts +562 -0
  3. package/dist/Connection-rAPmec1m.d.ts +710 -0
  4. package/dist/cli/index.d.ts +1 -0
  5. package/dist/cli/index.js +29518 -0
  6. package/dist/cli/lib.d.ts +158 -0
  7. package/dist/cli/lib.js +6343 -0
  8. package/dist/db/browser/index.d.ts +550 -0
  9. package/dist/db/browser/index.js +15682 -0
  10. package/dist/db/bun/index.d.ts +548 -0
  11. package/dist/db/bun/index.js +15670 -0
  12. package/dist/db/fallback.d.ts +182 -0
  13. package/dist/db/fallback.js +15566 -0
  14. package/dist/db/node/index.d.ts +547 -0
  15. package/dist/db/node/index.js +15663 -0
  16. package/dist/db/postgres/BasePostgresConnection-B7zHDAib.d.ts +24 -0
  17. package/dist/db/postgres/BasePostgresConnection-COHRCvc-.d.ts +24 -0
  18. package/dist/db/postgres/BasePostgresConnection-Di94o0ON.d.ts +24 -0
  19. package/dist/db/postgres/PostgresConnection.d.ts +16 -0
  20. package/dist/db/postgres/PostgresConnection.js +611 -0
  21. package/dist/db/postgres/index.js +802 -0
  22. package/dist/db/postgres/pglite/PgliteConnection.d.ts +38 -0
  23. package/dist/db/postgres/pglite/PgliteConnection.js +890 -0
  24. package/dist/db/sqlite/index.js +14724 -0
  25. package/dist/db/workerd/index.d.ts +570 -0
  26. package/dist/db/workerd/index.js +15788 -0
  27. package/dist/index-4cbfQyFv.d.ts +763 -0
  28. package/dist/index-xv_pDjEt.d.ts +769 -0
  29. package/dist/index.d.ts +2498 -0
  30. package/dist/index.js +32303 -0
  31. package/dist/static/.vite/manifest.json +11 -0
  32. package/dist/static/assets/main-BsWx0q9l.js +40913 -0
  33. package/dist/static/assets/main-DexXgo9R.css +4002 -0
  34. package/dist/static/favicon.ico +0 -0
  35. package/dist/static/fonts/CustomFont-Black.woff2 +0 -0
  36. package/dist/static/fonts/CustomFont-BlackItalic.woff2 +0 -0
  37. package/dist/static/fonts/CustomFont-Bold.woff2 +0 -0
  38. package/dist/static/fonts/CustomFont-BoldItalic.woff2 +0 -0
  39. package/dist/static/fonts/CustomFont-Book.woff2 +0 -0
  40. package/dist/static/fonts/CustomFont-BookItalic.woff2 +0 -0
  41. package/dist/static/fonts/CustomFont-Medium.woff2 +0 -0
  42. package/dist/vite/index.d.ts +3017 -0
  43. package/dist/vite/index.js +1923 -0
  44. package/package.json +195 -0
@@ -0,0 +1,611 @@
1
+ import postgres from 'postgres';
2
+ import { PostgresJSDialect } from 'kysely-postgres-js';
3
+ import { Kysely, sql } from 'kysely';
4
+ import { Connection, invariant, RelationNotFoundError } from '@supabase/lite';
5
+
6
+ try {
7
+ /**
8
+ * Adding this to avoid warnings from node:sqlite being experimental
9
+ */
10
+ const { emitWarning } = process;
11
+ process.emitWarning = (warning, ...args) => {
12
+ if (warning.includes("SQLite is an experimental feature")) return;
13
+ return emitWarning(warning, ...args);
14
+ };
15
+ } catch {}
16
+ var ForceRollback = class extends Error {
17
+ constructor(result) {
18
+ super("Force rollback");
19
+ this.result = result;
20
+ }
21
+ };
22
+ var BasePostgresConnection = class extends Connection {
23
+ dialect = "postgres";
24
+ constructor(options) {
25
+ super({
26
+ ...options,
27
+ // @todo: this should be handled by the auth schema
28
+ baseSchema: `
29
+ ${options.baseSchema ?? ""}
30
+
31
+ CREATE SCHEMA IF NOT EXISTS auth;
32
+
33
+ -- Function to get current user ID (for RLS policies)
34
+ CREATE OR REPLACE FUNCTION auth.uid() RETURNS UUID AS $$
35
+ SELECT NULLIF(current_setting('request.jwt.claim.sub', true), '')::uuid;
36
+ $$ LANGUAGE SQL STABLE;
37
+
38
+ -- Function to get current user role (for RLS policies)
39
+ CREATE OR REPLACE FUNCTION auth.role() RETURNS TEXT AS $$
40
+ SELECT NULLIF(current_setting('request.jwt.claim.role', true), '');
41
+ $$ LANGUAGE SQL STABLE;
42
+
43
+ -- Function to get current user email (for RLS policies)
44
+ CREATE OR REPLACE FUNCTION auth.email() RETURNS TEXT AS $$
45
+ SELECT NULLIF(current_setting('request.jwt.claim.email', true), '');
46
+ $$ LANGUAGE SQL STABLE;
47
+
48
+ -- Function to get JWT claims (for RLS policies)
49
+ CREATE OR REPLACE FUNCTION auth.jwt() RETURNS JSONB AS $$
50
+ SELECT COALESCE(
51
+ NULLIF(current_setting('request.jwt.claims', true), ''),
52
+ '{}'
53
+ )::jsonb;
54
+ $$ LANGUAGE SQL STABLE;
55
+ `
56
+ });
57
+ }
58
+ async introspect(options) {
59
+ invariant(typeof options === "object" || options === void 0, "options must be an object");
60
+ const useCache = options?.useCache ?? false;
61
+ const cached = await this.readCachedIntrospection({ useCache });
62
+ if (cached) return cached;
63
+ try {
64
+ const tblResult = await sql`
65
+ SELECT
66
+ tbls.table_name AS "name",
67
+ tbls.table_schema AS "schema",
68
+ tbls.table_type AS "type",
69
+ COALESCE(s.n_live_tup, 0) AS "rows"
70
+ FROM information_schema.tables tbls
71
+ LEFT JOIN pg_stat_user_tables s
72
+ ON tbls.table_schema = s.schemaname AND tbls.table_name = s.relname
73
+ WHERE
74
+ tbls.table_schema NOT IN ('information_schema', 'pg_catalog')
75
+ AND tbls.table_type IN ('BASE TABLE', 'FOREIGN TABLE')
76
+ ORDER BY tbls.table_schema, tbls.table_name
77
+ `.execute(this.kysely);
78
+ const tables = tblResult.rows.map((r) => ({
79
+ name: r.name,
80
+ schema: r.schema,
81
+ type: "table",
82
+ rows: Number(r.rows),
83
+ sql: "",
84
+ engine: "",
85
+ collation: ""
86
+ }));
87
+ const colResult = await sql`
88
+ SELECT
89
+ cols.table_name AS "table",
90
+ cols.table_schema AS "schema",
91
+ cols.column_name AS "name",
92
+ cols.ordinal_position AS "ordinal_position",
93
+ CASE
94
+ WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%' THEN
95
+ CASE
96
+ WHEN LOWER(cols.data_type) = 'smallint' THEN 'smallserial'
97
+ WHEN LOWER(cols.data_type) = 'integer' THEN 'serial'
98
+ WHEN LOWER(cols.data_type) = 'bigint' THEN 'bigserial'
99
+ ELSE LOWER(cols.data_type)
100
+ END
101
+ WHEN cols.data_type = 'ARRAY' THEN format_type(pt.typelem, NULL)
102
+ WHEN LOWER(cols.data_type) = 'user-defined' THEN format_type(pt.oid, NULL)
103
+ ELSE LOWER(cols.data_type)
104
+ END AS "type",
105
+ CASE WHEN cols.is_nullable = 'YES' THEN true ELSE false END AS "nullable",
106
+ CASE
107
+ WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%'
108
+ THEN NULL
109
+ ELSE cols.column_default
110
+ END AS "default_value",
111
+ cols.character_maximum_length::text AS "character_maximum_length", CASE
112
+ WHEN cols.data_type IN ('numeric', 'decimal')
113
+ THEN
114
+ json_build_object(
115
+ 'precision',
116
+ cols.numeric_precision,
117
+ 'scale',
118
+ cols.numeric_scale)
119
+ ELSE NULL
120
+ END AS "precision",
121
+ CASE
122
+ WHEN cols.is_identity = 'YES' THEN true
123
+ WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%'
124
+ THEN true
125
+ ELSE false
126
+ END AS "is_identity",
127
+ COALESCE(cols.collation_name, '') AS "collation",
128
+ cols.udt_name AS "udt_name",
129
+ CASE WHEN cols.is_generated = 'NEVER' THEN false ELSE true END AS "is_generated"
130
+ FROM information_schema.columns cols
131
+ LEFT JOIN pg_catalog.pg_class c ON c.relname = cols.table_name
132
+ JOIN pg_catalog.pg_namespace n
133
+ ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
134
+ LEFT JOIN pg_catalog.pg_attribute attr
135
+ ON attr.attrelid = c.oid AND attr.attname = cols.column_name
136
+ LEFT JOIN pg_catalog.pg_type pt ON pt.oid = attr.atttypid
137
+ WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
138
+ ORDER BY cols.table_schema, cols.table_name, cols.ordinal_position
139
+ `.execute(this.kysely);
140
+ const columns = colResult.rows.map((r) => ({
141
+ table: r.table,
142
+ schema: r.schema,
143
+ name: r.name,
144
+ type: r.type,
145
+ nullable: r.nullable,
146
+ default_value: r.default_value,
147
+ is_primary_key: false,
148
+ // filled below
149
+ ordinal_position: Number(r.ordinal_position),
150
+ character_maximum_length: r.character_maximum_length ?? null,
151
+ precision: r.precision ?? null,
152
+ is_identity: r.is_identity,
153
+ collation: r.collation,
154
+ pg_type: r.udt_name ?? void 0,
155
+ is_generated: r.is_generated ?? false
156
+ }));
157
+ const fkResult = await sql`
158
+ SELECT
159
+ ns.nspname AS "schema",
160
+ cl.relname AS "table",
161
+ att.attname AS "column",
162
+ nr.nspname AS "ref_schema",
163
+ ref_cl.relname AS "ref_table",
164
+ ref_att.attname AS "ref_column",
165
+ con.conname AS "foreign_key_name",
166
+ pg_get_constraintdef(con.oid) AS "fk_def"
167
+ FROM pg_constraint con
168
+ JOIN pg_class cl ON cl.oid = con.conrelid
169
+ JOIN pg_namespace ns ON ns.oid = cl.relnamespace
170
+ JOIN pg_class ref_cl ON ref_cl.oid = con.confrelid
171
+ JOIN pg_namespace nr ON nr.oid = ref_cl.relnamespace
172
+ CROSS JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS cols(conkey_num, confkey_num, ord)
173
+ JOIN pg_attribute att
174
+ ON att.attrelid = con.conrelid AND att.attnum = cols.conkey_num
175
+ JOIN pg_attribute ref_att ON ref_att.attrelid = con.confrelid AND ref_att.attnum = cols.confkey_num
176
+ WHERE
177
+ con.contype = 'f'
178
+ AND ns.nspname NOT IN (
179
+ 'information_schema'
180
+ , 'pg_catalog')
181
+ ORDER BY ns.nspname, cl.relname, con.conname, cols.ord
182
+ `.execute(this.kysely);
183
+ const fks = fkResult.rows.map((r) => ({
184
+ table: r.table,
185
+ column: r.column,
186
+ schema: r.schema,
187
+ ref_table: r.ref_table,
188
+ ref_column: r.ref_column,
189
+ foreign_key_name: r.foreign_key_name,
190
+ fk_def: r.fk_def,
191
+ on_update: "",
192
+ on_delete: ""
193
+ }));
194
+ const pkResult = await sql`
195
+ SELECT
196
+ ns.nspname AS "schema",
197
+ cl.relname AS "table",
198
+ array_agg(att.attname ORDER BY cols.ord) AS "columns"
199
+ FROM pg_constraint con
200
+ JOIN pg_class cl ON cl.oid = con.conrelid
201
+ JOIN pg_namespace ns ON ns.oid = cl.relnamespace
202
+ CROSS JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS cols(attnum, ord)
203
+ JOIN pg_attribute att
204
+ ON att.attrelid = con.conrelid AND att.attnum = cols.attnum
205
+ WHERE
206
+ con.contype = 'p'
207
+ AND ns.nspname NOT IN (
208
+ 'information_schema'
209
+ , 'pg_catalog')
210
+ GROUP BY ns.nspname, cl.relname, con.oid
211
+ `.execute(this.kysely);
212
+ const pks = pkResult.rows.map((r) => ({
213
+ table: r.table,
214
+ columns: r.columns,
215
+ schema: r.schema,
216
+ field_count: r.columns.length
217
+ }));
218
+ const pkSet = new Set(
219
+ pks.flatMap((pk) => pk.columns.map((col) => `${pk.schema}.${pk.table}.${col}`))
220
+ );
221
+ for (const col of columns) {
222
+ col.is_primary_key = pkSet.has(`${col.schema}.${col.table}.${col.name}`);
223
+ }
224
+ const idxResult = await sql`
225
+ SELECT
226
+ tnsp.nspname AS "schema",
227
+ cl.relname AS "table",
228
+ ic.relname AS "name",
229
+ idx.indisunique AS "unique",
230
+ array_agg(att.attname ORDER BY a.ord) AS "columns"
231
+ FROM pg_index idx
232
+ JOIN pg_class cl ON cl.oid = idx.indrelid
233
+ JOIN pg_namespace tnsp ON cl.relnamespace = tnsp.oid
234
+ JOIN pg_class ic ON ic.oid = idx.indexrelid
235
+ CROSS JOIN LATERAL unnest(idx.indkey) WITH ORDINALITY AS a(attnum, ord)
236
+ JOIN pg_attribute att
237
+ ON att.attrelid = cl.oid AND att.attnum = a.attnum
238
+ WHERE
239
+ idx.indisunique = true
240
+ AND NOT idx.indisprimary
241
+ AND tnsp.nspname NOT IN (
242
+ 'information_schema'
243
+ , 'pg_catalog')
244
+ GROUP BY tnsp.nspname, cl.relname, ic.relname, idx.indisunique
245
+ `.execute(this.kysely);
246
+ const indexes = idxResult.rows.map((r) => ({
247
+ table: r.table,
248
+ name: r.name,
249
+ unique: r.unique,
250
+ columns: r.columns,
251
+ schema: r.schema
252
+ }));
253
+ const viewResult = await sql`
254
+ SELECT
255
+ views.schemaname AS "schema",
256
+ views.viewname AS "name",
257
+ views.definition AS "sql"
258
+ FROM pg_views views
259
+ WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
260
+ ORDER BY views.schemaname, views.viewname
261
+ `.execute(this.kysely);
262
+ const views = viewResult.rows.map((r) => ({
263
+ name: r.name,
264
+ schema: r.schema,
265
+ sql: r.sql ?? ""
266
+ }));
267
+ const chkResult = await sql`
268
+ SELECT
269
+ n.nspname AS "schema",
270
+ cl.relname AS "table",
271
+ substring(pg_get_constraintdef(c.oid) FROM 'CHECK \\((.*)\\)') AS "expression"
272
+ FROM pg_constraint c
273
+ JOIN pg_class cl ON cl.oid = c.conrelid
274
+ JOIN pg_namespace n ON n.oid = cl.relnamespace
275
+ WHERE
276
+ c.contype = 'c'
277
+ AND n.nspname NOT IN ('information_schema', 'pg_catalog')
278
+ ORDER BY n.nspname, cl.relname
279
+ `.execute(this.kysely);
280
+ const checkConstraints = chkResult.rows.map((r) => ({
281
+ schema: r.schema,
282
+ table: r.table,
283
+ expression: r.expression ?? ""
284
+ }));
285
+ const uqResult = await sql`
286
+ SELECT
287
+ n.nspname AS "schema",
288
+ cl.relname AS "table",
289
+ c.conname AS "name",
290
+ array_agg(att.attname ORDER BY cols.ord) AS "columns"
291
+ FROM pg_constraint c
292
+ JOIN pg_class cl ON cl.oid = c.conrelid
293
+ JOIN pg_namespace n ON n.oid = cl.relnamespace
294
+ CROSS JOIN LATERAL unnest(c.conkey) WITH ORDINALITY AS cols(attnum, ord)
295
+ JOIN pg_attribute att ON att.attrelid = c.conrelid AND att.attnum = cols.attnum
296
+ WHERE
297
+ c.contype = 'u'
298
+ AND n.nspname NOT IN ('information_schema', 'pg_catalog')
299
+ GROUP BY n.nspname, cl.relname, c.conname, c.oid
300
+ `.execute(this.kysely);
301
+ const uniqueConstraints = uqResult.rows.map((r) => ({
302
+ schema: r.schema,
303
+ table: r.table,
304
+ name: r.name,
305
+ columns: r.columns
306
+ }));
307
+ const commentResult = await sql`
308
+ SELECT
309
+ n.nspname AS "schema",
310
+ cl.relname AS "table",
311
+ att.attname AS "column",
312
+ d.description AS "text"
313
+ FROM pg_description d
314
+ JOIN pg_class cl ON cl.oid = d.objoid
315
+ JOIN pg_namespace n ON n.oid = cl.relnamespace
316
+ LEFT JOIN pg_attribute att
317
+ ON att.attrelid = cl.oid AND att.attnum = d.objsubid AND d.objsubid > 0
318
+ WHERE
319
+ n.nspname NOT IN ('information_schema', 'pg_catalog')
320
+ AND cl.relkind IN ('r', 'v', 'm', 'f', 'p')
321
+ AND d.description IS NOT NULL
322
+ `.execute(this.kysely);
323
+ const comments = commentResult.rows.map((r) => ({
324
+ schema: r.schema,
325
+ table: r.table,
326
+ column: r.column ?? void 0,
327
+ text: r.text
328
+ }));
329
+ const enumResult = await sql`
330
+ SELECT
331
+ n.nspname AS "schema",
332
+ t.typname AS "type",
333
+ 'enum' AS "kind",
334
+ array_agg(e.enumlabel ORDER BY e.enumsortorder) AS "values"
335
+ FROM pg_type t
336
+ JOIN pg_enum e ON t.oid = e.enumtypid
337
+ JOIN pg_namespace n ON n.oid = t.typnamespace
338
+ WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
339
+ GROUP BY n.nspname, t.typname
340
+ `.execute(this.kysely);
341
+ const compositeResult = await sql`
342
+ SELECT
343
+ n.nspname AS "schema",
344
+ t.typname AS "type",
345
+ 'composite' AS "kind",
346
+ json_agg(json_build_object(
347
+ 'name',
348
+ a.attname,
349
+ 'type',
350
+ format_type(a.atttypid, a.atttypmod)) ORDER BY a.attnum
351
+ ) AS "fields"
352
+ FROM pg_type t
353
+ JOIN pg_namespace n ON n.oid = t.typnamespace
354
+ JOIN pg_class c ON c.oid = t.typrelid
355
+ JOIN pg_attribute a ON a.attrelid = c.oid
356
+ WHERE
357
+ t.typtype = 'c'
358
+ AND c.relkind = 'c'
359
+ AND a.attnum > 0
360
+ AND NOT a.attisdropped
361
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema')
362
+ GROUP BY n.nspname, t.typname
363
+ `.execute(this.kysely);
364
+ const customTypes = [
365
+ ...enumResult.rows.map((r) => ({
366
+ schema: r.schema,
367
+ type: r.type,
368
+ kind: "enum",
369
+ values: r.values
370
+ })),
371
+ ...compositeResult.rows.map((r) => ({
372
+ schema: r.schema,
373
+ type: r.type,
374
+ kind: "composite",
375
+ fields: r.fields
376
+ }))
377
+ ];
378
+ const dbResult = await sql`SELECT current_database() AS "name"`.execute(this.kysely);
379
+ const dbName = dbResult.rows[0]?.name ?? "postgres";
380
+ const result = {
381
+ tables,
382
+ columns,
383
+ indexes,
384
+ foreign_keys: fks,
385
+ primary_keys: pks,
386
+ views,
387
+ triggers: [],
388
+ check_constraints: checkConstraints,
389
+ unique_constraints: uniqueConstraints,
390
+ comments,
391
+ custom_types: customTypes,
392
+ database_name: dbName,
393
+ version: ""
394
+ };
395
+ await this.writeCachedIntrospection(result, { useDriver: useCache });
396
+ return result;
397
+ } catch (e) {
398
+ console.error("Introspection failed:", e);
399
+ return {
400
+ tables: [],
401
+ columns: [],
402
+ indexes: [],
403
+ foreign_keys: [],
404
+ primary_keys: [],
405
+ views: [],
406
+ triggers: [],
407
+ check_constraints: [],
408
+ unique_constraints: [],
409
+ comments: [],
410
+ custom_types: [],
411
+ database_name: "postgres",
412
+ version: ""
413
+ };
414
+ }
415
+ }
416
+ rlsState = "unknown";
417
+ /**
418
+ * Detect if any table has RLS enabled, and if so ensure anon/authenticated
419
+ * roles exist with default privileges. Runs once per connection.
420
+ */
421
+ async ensureRlsContext() {
422
+ if (this.rlsState !== "unknown") return this.rlsState === "active";
423
+ const result = await sql`
424
+ SELECT count(*) ::text as count FROM pg_class WHERE relrowsecurity = true
425
+ `.execute(this.kysely);
426
+ const hasRls = Number(result.rows[0]?.count ?? 0) > 0;
427
+ if (!hasRls) {
428
+ this.rlsState = "inactive";
429
+ return false;
430
+ }
431
+ this.rlsState = "active";
432
+ await sql`
433
+ DO $$ BEGIN
434
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'anon') THEN
435
+ CREATE ROLE anon NOLOGIN;
436
+ END IF;
437
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'authenticated') THEN
438
+ CREATE ROLE authenticated NOLOGIN;
439
+ END IF;
440
+ END $$
441
+ `.execute(this.kysely);
442
+ const schemas = await sql`
443
+ SELECT DISTINCT n.nspname FROM pg_class c
444
+ JOIN pg_namespace n ON n.oid = c.relnamespace
445
+ WHERE c.relrowsecurity = true
446
+ `.execute(this.kysely);
447
+ for (const { nspname } of schemas.rows) {
448
+ await sql`GRANT USAGE ON SCHEMA ${sql.ref(nspname)} TO anon, authenticated`.execute(
449
+ this.kysely
450
+ );
451
+ await sql.raw(`GRANT ALL ON ALL TABLES IN SCHEMA "${nspname}" TO anon, authenticated`).execute(this.kysely);
452
+ await sql.raw(`GRANT ALL ON ALL SEQUENCES IN SCHEMA "${nspname}" TO anon, authenticated`).execute(this.kysely);
453
+ await sql.raw(
454
+ `ALTER DEFAULT PRIVILEGES IN SCHEMA "${nspname}" GRANT ALL ON TABLES TO anon, authenticated`
455
+ ).execute(this.kysely);
456
+ await sql.raw(
457
+ `ALTER DEFAULT PRIVILEGES IN SCHEMA "${nspname}" GRANT ALL ON SEQUENCES TO anon, authenticated`
458
+ ).execute(this.kysely);
459
+ }
460
+ return true;
461
+ }
462
+ async withContext(vars, fn, opts) {
463
+ const forceRollback = opts?.forceRollback === true;
464
+ if (!forceRollback && !vars) return fn(this.kysely);
465
+ const rlsActive = vars ? await this.ensureRlsContext() : false;
466
+ if (!forceRollback && !rlsActive) return fn(this.kysely);
467
+ try {
468
+ return await this.kysely.transaction().execute(async (trx) => {
469
+ if (vars && rlsActive) {
470
+ const auth = vars.auth;
471
+ const role = auth?.role || "anon";
472
+ const uid = String(auth?.uid ?? "");
473
+ const jwt = auth?.jwt;
474
+ await sql`SELECT set_config('role', ${role}, true)`.execute(trx);
475
+ await sql`SELECT set_config('request.jwt.claim.sub', ${uid}, true)`.execute(trx);
476
+ await sql`SELECT set_config('request.jwt.claim.role', ${role}, true)`.execute(trx);
477
+ if (jwt && typeof jwt === "object") {
478
+ await sql`SELECT set_config('request.jwt.claims', ${JSON.stringify(jwt)}, true)`.execute(
479
+ trx
480
+ );
481
+ }
482
+ }
483
+ const result = await fn(trx);
484
+ if (forceRollback) throw new ForceRollback(result);
485
+ return result;
486
+ });
487
+ } catch (error) {
488
+ if (error instanceof ForceRollback) return error.result;
489
+ throw error;
490
+ }
491
+ }
492
+ async onPostgrestAST(ast) {
493
+ if (!ast.from) return ast;
494
+ const schema = ast.schema ?? "public";
495
+ const relation = ast.from;
496
+ const introspection = await this.introspect({ useCache: true });
497
+ if (!introspection.tables.some((t) => t.name === relation && t.schema === schema) && !introspection.views.some((v) => v.name === relation && v.schema === schema)) {
498
+ throw new RelationNotFoundError(schema, relation);
499
+ }
500
+ return ast;
501
+ }
502
+ async transaction(statements, opts) {
503
+ await this.exec("BEGIN");
504
+ try {
505
+ for (const statement of statements) {
506
+ await this.exec(statement);
507
+ }
508
+ await this.exec("COMMIT");
509
+ } catch (error) {
510
+ await this.exec("ROLLBACK");
511
+ throw error;
512
+ } finally {
513
+ if (opts?.intent === "migration") {
514
+ await this.clearSchemaCache();
515
+ }
516
+ }
517
+ }
518
+ };
519
+
520
+ // src/db/postgres/PostgresConnection.ts
521
+ var PostgresConnection = class extends BasePostgresConnection {
522
+ driver;
523
+ dialect = "postgres";
524
+ constructor(config) {
525
+ super(config);
526
+ this.driver = postgres(config.url ?? "", {
527
+ types: {
528
+ // Parse bigint/int8 (OID 20) as JS number instead of string
529
+ bigint: {
530
+ to: 20,
531
+ from: [20],
532
+ serialize: (x) => x.toString(),
533
+ parse: (x) => {
534
+ const n = Number(x);
535
+ if (Number.isSafeInteger(n)) return n;
536
+ return BigInt(x);
537
+ }
538
+ },
539
+ // json (OID 114) and jsonb (OID 3802): postgres.js leaves both as text by default.
540
+ // PostgREST returns them as parsed JSON, including json_agg() in embed aggregates.
541
+ json: {
542
+ to: 114,
543
+ from: [114],
544
+ serialize: (x) => JSON.stringify(x),
545
+ parse: (x) => JSON.parse(x)
546
+ },
547
+ jsonb: {
548
+ to: 3802,
549
+ from: [3802],
550
+ serialize: (x) => JSON.stringify(x),
551
+ parse: (x) => JSON.parse(x)
552
+ },
553
+ // numeric (OID 1700): postgres.js returns as string for precision safety.
554
+ // PostgREST returns as JSON number — coerce when round-trip is exact, else keep string.
555
+ numeric: {
556
+ to: 1700,
557
+ from: [1700],
558
+ serialize: (x) => String(x),
559
+ parse: (x) => {
560
+ const n = Number(x);
561
+ if (Number.isFinite(n) && String(n) === x) return n;
562
+ return x;
563
+ }
564
+ },
565
+ // Date/time OIDs: keep as raw strings to mirror SQLite, which stores ISO text.
566
+ // Auth/storage code already uses Date.parse() on these, and the spec timestamp
567
+ // sentinel only matches strings.
568
+ date: {
569
+ to: 1082,
570
+ from: [1082],
571
+ serialize: (x) => x,
572
+ parse: (x) => x
573
+ },
574
+ time: {
575
+ to: 1083,
576
+ from: [1083],
577
+ serialize: (x) => x,
578
+ parse: (x) => x
579
+ },
580
+ timestamp: {
581
+ to: 1114,
582
+ from: [1114],
583
+ serialize: (x) => x,
584
+ parse: (x) => x
585
+ },
586
+ timestamptz: {
587
+ to: 1184,
588
+ from: [1184],
589
+ serialize: (x) => x,
590
+ parse: (x) => x
591
+ },
592
+ timetz: {
593
+ to: 1266,
594
+ from: [1266],
595
+ serialize: (x) => x,
596
+ parse: (x) => x
597
+ }
598
+ }
599
+ });
600
+ const dialect = new PostgresJSDialect({ postgres: this.driver });
601
+ this.kysely = new Kysely({ dialect });
602
+ }
603
+ async close() {
604
+ await this.driver.end();
605
+ }
606
+ };
607
+ function createPostgresConnection(config) {
608
+ return new PostgresConnection(config);
609
+ }
610
+
611
+ export { PostgresConnection, createPostgresConnection };