@supabase/lite 0.0.1-next.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 (37) hide show
  1. package/dist/Connection-rAPmec1m.d.ts +710 -0
  2. package/dist/cli/index.d.ts +1 -0
  3. package/dist/cli/index.js +30950 -0
  4. package/dist/cli/lib.d.ts +158 -0
  5. package/dist/cli/lib.js +18598 -0
  6. package/dist/db/browser/index.d.ts +550 -0
  7. package/dist/db/browser/index.js +16112 -0
  8. package/dist/db/bun/index.d.ts +548 -0
  9. package/dist/db/bun/index.js +16100 -0
  10. package/dist/db/fallback.d.ts +182 -0
  11. package/dist/db/fallback.js +15996 -0
  12. package/dist/db/node/index.d.ts +547 -0
  13. package/dist/db/node/index.js +16093 -0
  14. package/dist/db/postgres/BasePostgresConnection-B7zHDAib.d.ts +24 -0
  15. package/dist/db/postgres/PostgresConnection.d.ts +16 -0
  16. package/dist/db/postgres/PostgresConnection.js +611 -0
  17. package/dist/db/postgres/pglite/PgliteConnection.d.ts +38 -0
  18. package/dist/db/postgres/pglite/PgliteConnection.js +890 -0
  19. package/dist/db/workerd/index.d.ts +570 -0
  20. package/dist/db/workerd/index.js +16218 -0
  21. package/dist/index-xv_pDjEt.d.ts +769 -0
  22. package/dist/index.d.ts +2498 -0
  23. package/dist/index.js +32305 -0
  24. package/dist/static/.vite/manifest.json +11 -0
  25. package/dist/static/assets/main-BsWx0q9l.js +40913 -0
  26. package/dist/static/assets/main-DexXgo9R.css +4002 -0
  27. package/dist/static/favicon.ico +0 -0
  28. package/dist/static/fonts/CustomFont-Black.woff2 +0 -0
  29. package/dist/static/fonts/CustomFont-BlackItalic.woff2 +0 -0
  30. package/dist/static/fonts/CustomFont-Bold.woff2 +0 -0
  31. package/dist/static/fonts/CustomFont-BoldItalic.woff2 +0 -0
  32. package/dist/static/fonts/CustomFont-Book.woff2 +0 -0
  33. package/dist/static/fonts/CustomFont-BookItalic.woff2 +0 -0
  34. package/dist/static/fonts/CustomFont-Medium.woff2 +0 -0
  35. package/dist/vite/index.d.ts +3017 -0
  36. package/dist/vite/index.js +1923 -0
  37. package/package.json +195 -0
@@ -0,0 +1,890 @@
1
+ import { PGlite } from '@electric-sql/pglite';
2
+ export { PGlite } from '@electric-sql/pglite';
3
+ import { Kysely, sql, PostgresQueryCompiler, PostgresIntrospector, PostgresAdapter, CompiledQuery } from 'kysely';
4
+ import { Connection, invariant, RelationNotFoundError, DataLossError } from '@supabase/lite';
5
+ import { isn } from '@electric-sql/pglite/contrib/isn';
6
+ import { ltree } from '@electric-sql/pglite/contrib/ltree';
7
+ import { file_fdw } from '@electric-sql/pglite/contrib/file_fdw';
8
+ import { pgcrypto } from '@electric-sql/pglite/contrib/pgcrypto';
9
+
10
+ try {
11
+ /**
12
+ * Adding this to avoid warnings from node:sqlite being experimental
13
+ */
14
+ const { emitWarning } = process;
15
+ process.emitWarning = (warning, ...args) => {
16
+ if (warning.includes("SQLite is an experimental feature")) return;
17
+ return emitWarning(warning, ...args);
18
+ };
19
+ } catch {}
20
+ async function ensureDataDirExist(dataDir) {
21
+ let path;
22
+ if (typeof dataDir === "string") {
23
+ path = dataDir;
24
+ }
25
+ if (typeof dataDir === "object" && dataDir.dataDir) {
26
+ path = dataDir.dataDir;
27
+ }
28
+ if (path) {
29
+ const { mkdir } = await import('node:fs');
30
+ mkdir(path, { recursive: true }, (err) => {
31
+ if (err) {
32
+ throw err;
33
+ }
34
+ });
35
+ }
36
+ }
37
+ var PGliteDriver = class {
38
+ #client;
39
+ #ownsClient;
40
+ constructor(client, ownsClient) {
41
+ this.#client = client;
42
+ this.#ownsClient = ownsClient;
43
+ }
44
+ async acquireConnection() {
45
+ return new PGliteConnection(this.#client);
46
+ }
47
+ async beginTransaction(connection, _settings) {
48
+ await connection.executeQuery(CompiledQuery.raw("BEGIN"));
49
+ }
50
+ async commitTransaction(connection) {
51
+ await connection.executeQuery(CompiledQuery.raw("COMMIT"));
52
+ }
53
+ async rollbackTransaction(connection) {
54
+ await connection.executeQuery(CompiledQuery.raw("ROLLBACK"));
55
+ }
56
+ async destroy() {
57
+ if (this.#ownsClient) {
58
+ await this.#client.close();
59
+ }
60
+ }
61
+ async init() {
62
+ }
63
+ async releaseConnection(_connection) {
64
+ }
65
+ };
66
+ var PGliteConnection = class {
67
+ #client;
68
+ constructor(client) {
69
+ this.#client = client;
70
+ }
71
+ async executeQuery(compiledQuery) {
72
+ const result = await this.#client.query(compiledQuery.sql, [...compiledQuery.parameters]);
73
+ const affected = result.affectedRows;
74
+ return {
75
+ rows: result.rows,
76
+ numAffectedRows: typeof affected === "number" ? BigInt(affected) : void 0
77
+ };
78
+ }
79
+ // biome-ignore lint/correctness/useYield: .
80
+ async *streamQuery() {
81
+ throw new Error("PGlite does not support streaming.");
82
+ }
83
+ };
84
+ var KyselyPglite = class {
85
+ client;
86
+ #ownsClient = true;
87
+ constructor(dataDirOrOptionsOrPGlite, opts) {
88
+ void ensureDataDirExist(dataDirOrOptionsOrPGlite);
89
+ let options = { ...opts };
90
+ if (typeof dataDirOrOptionsOrPGlite === "object" && dataDirOrOptionsOrPGlite instanceof PGlite) {
91
+ this.client = dataDirOrOptionsOrPGlite;
92
+ this.#ownsClient = false;
93
+ return;
94
+ }
95
+ if (typeof dataDirOrOptionsOrPGlite === "string") {
96
+ options = {
97
+ dataDir: dataDirOrOptionsOrPGlite,
98
+ ...options
99
+ };
100
+ } else {
101
+ options = dataDirOrOptionsOrPGlite ?? {};
102
+ }
103
+ this.client = new PGlite(options);
104
+ }
105
+ dialect = {
106
+ createAdapter: () => new PostgresAdapter(),
107
+ createDriver: () => new PGliteDriver(this.client, this.#ownsClient),
108
+ createIntrospector: (db) => new PostgresIntrospector(db),
109
+ createQueryCompiler: () => new PostgresQueryCompiler()
110
+ };
111
+ };
112
+ var ForceRollback = class extends Error {
113
+ constructor(result) {
114
+ super("Force rollback");
115
+ this.result = result;
116
+ }
117
+ };
118
+ var BasePostgresConnection = class extends Connection {
119
+ dialect = "postgres";
120
+ constructor(options) {
121
+ super({
122
+ ...options,
123
+ // @todo: this should be handled by the auth schema
124
+ baseSchema: `
125
+ ${options.baseSchema ?? ""}
126
+
127
+ CREATE SCHEMA IF NOT EXISTS auth;
128
+
129
+ -- Function to get current user ID (for RLS policies)
130
+ CREATE OR REPLACE FUNCTION auth.uid() RETURNS UUID AS $$
131
+ SELECT NULLIF(current_setting('request.jwt.claim.sub', true), '')::uuid;
132
+ $$ LANGUAGE SQL STABLE;
133
+
134
+ -- Function to get current user role (for RLS policies)
135
+ CREATE OR REPLACE FUNCTION auth.role() RETURNS TEXT AS $$
136
+ SELECT NULLIF(current_setting('request.jwt.claim.role', true), '');
137
+ $$ LANGUAGE SQL STABLE;
138
+
139
+ -- Function to get current user email (for RLS policies)
140
+ CREATE OR REPLACE FUNCTION auth.email() RETURNS TEXT AS $$
141
+ SELECT NULLIF(current_setting('request.jwt.claim.email', true), '');
142
+ $$ LANGUAGE SQL STABLE;
143
+
144
+ -- Function to get JWT claims (for RLS policies)
145
+ CREATE OR REPLACE FUNCTION auth.jwt() RETURNS JSONB AS $$
146
+ SELECT COALESCE(
147
+ NULLIF(current_setting('request.jwt.claims', true), ''),
148
+ '{}'
149
+ )::jsonb;
150
+ $$ LANGUAGE SQL STABLE;
151
+ `
152
+ });
153
+ }
154
+ async introspect(options) {
155
+ invariant(typeof options === "object" || options === void 0, "options must be an object");
156
+ const useCache = options?.useCache ?? false;
157
+ const cached = await this.readCachedIntrospection({ useCache });
158
+ if (cached) return cached;
159
+ try {
160
+ const tblResult = await sql`
161
+ SELECT
162
+ tbls.table_name AS "name",
163
+ tbls.table_schema AS "schema",
164
+ tbls.table_type AS "type",
165
+ COALESCE(s.n_live_tup, 0) AS "rows"
166
+ FROM information_schema.tables tbls
167
+ LEFT JOIN pg_stat_user_tables s
168
+ ON tbls.table_schema = s.schemaname AND tbls.table_name = s.relname
169
+ WHERE
170
+ tbls.table_schema NOT IN ('information_schema', 'pg_catalog')
171
+ AND tbls.table_type IN ('BASE TABLE', 'FOREIGN TABLE')
172
+ ORDER BY tbls.table_schema, tbls.table_name
173
+ `.execute(this.kysely);
174
+ const tables = tblResult.rows.map((r) => ({
175
+ name: r.name,
176
+ schema: r.schema,
177
+ type: "table",
178
+ rows: Number(r.rows),
179
+ sql: "",
180
+ engine: "",
181
+ collation: ""
182
+ }));
183
+ const colResult = await sql`
184
+ SELECT
185
+ cols.table_name AS "table",
186
+ cols.table_schema AS "schema",
187
+ cols.column_name AS "name",
188
+ cols.ordinal_position AS "ordinal_position",
189
+ CASE
190
+ WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%' THEN
191
+ CASE
192
+ WHEN LOWER(cols.data_type) = 'smallint' THEN 'smallserial'
193
+ WHEN LOWER(cols.data_type) = 'integer' THEN 'serial'
194
+ WHEN LOWER(cols.data_type) = 'bigint' THEN 'bigserial'
195
+ ELSE LOWER(cols.data_type)
196
+ END
197
+ WHEN cols.data_type = 'ARRAY' THEN format_type(pt.typelem, NULL)
198
+ WHEN LOWER(cols.data_type) = 'user-defined' THEN format_type(pt.oid, NULL)
199
+ ELSE LOWER(cols.data_type)
200
+ END AS "type",
201
+ CASE WHEN cols.is_nullable = 'YES' THEN true ELSE false END AS "nullable",
202
+ CASE
203
+ WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%'
204
+ THEN NULL
205
+ ELSE cols.column_default
206
+ END AS "default_value",
207
+ cols.character_maximum_length::text AS "character_maximum_length", CASE
208
+ WHEN cols.data_type IN ('numeric', 'decimal')
209
+ THEN
210
+ json_build_object(
211
+ 'precision',
212
+ cols.numeric_precision,
213
+ 'scale',
214
+ cols.numeric_scale)
215
+ ELSE NULL
216
+ END AS "precision",
217
+ CASE
218
+ WHEN cols.is_identity = 'YES' THEN true
219
+ WHEN cols.column_default IS NOT NULL AND cols.column_default LIKE 'nextval(%'
220
+ THEN true
221
+ ELSE false
222
+ END AS "is_identity",
223
+ COALESCE(cols.collation_name, '') AS "collation",
224
+ cols.udt_name AS "udt_name",
225
+ CASE WHEN cols.is_generated = 'NEVER' THEN false ELSE true END AS "is_generated"
226
+ FROM information_schema.columns cols
227
+ LEFT JOIN pg_catalog.pg_class c ON c.relname = cols.table_name
228
+ JOIN pg_catalog.pg_namespace n
229
+ ON n.oid = c.relnamespace AND n.nspname = cols.table_schema
230
+ LEFT JOIN pg_catalog.pg_attribute attr
231
+ ON attr.attrelid = c.oid AND attr.attname = cols.column_name
232
+ LEFT JOIN pg_catalog.pg_type pt ON pt.oid = attr.atttypid
233
+ WHERE cols.table_schema NOT IN ('information_schema', 'pg_catalog')
234
+ ORDER BY cols.table_schema, cols.table_name, cols.ordinal_position
235
+ `.execute(this.kysely);
236
+ const columns = colResult.rows.map((r) => ({
237
+ table: r.table,
238
+ schema: r.schema,
239
+ name: r.name,
240
+ type: r.type,
241
+ nullable: r.nullable,
242
+ default_value: r.default_value,
243
+ is_primary_key: false,
244
+ // filled below
245
+ ordinal_position: Number(r.ordinal_position),
246
+ character_maximum_length: r.character_maximum_length ?? null,
247
+ precision: r.precision ?? null,
248
+ is_identity: r.is_identity,
249
+ collation: r.collation,
250
+ pg_type: r.udt_name ?? void 0,
251
+ is_generated: r.is_generated ?? false
252
+ }));
253
+ const fkResult = await sql`
254
+ SELECT
255
+ ns.nspname AS "schema",
256
+ cl.relname AS "table",
257
+ att.attname AS "column",
258
+ nr.nspname AS "ref_schema",
259
+ ref_cl.relname AS "ref_table",
260
+ ref_att.attname AS "ref_column",
261
+ con.conname AS "foreign_key_name",
262
+ pg_get_constraintdef(con.oid) AS "fk_def"
263
+ FROM pg_constraint con
264
+ JOIN pg_class cl ON cl.oid = con.conrelid
265
+ JOIN pg_namespace ns ON ns.oid = cl.relnamespace
266
+ JOIN pg_class ref_cl ON ref_cl.oid = con.confrelid
267
+ JOIN pg_namespace nr ON nr.oid = ref_cl.relnamespace
268
+ CROSS JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS cols(conkey_num, confkey_num, ord)
269
+ JOIN pg_attribute att
270
+ ON att.attrelid = con.conrelid AND att.attnum = cols.conkey_num
271
+ JOIN pg_attribute ref_att ON ref_att.attrelid = con.confrelid AND ref_att.attnum = cols.confkey_num
272
+ WHERE
273
+ con.contype = 'f'
274
+ AND ns.nspname NOT IN (
275
+ 'information_schema'
276
+ , 'pg_catalog')
277
+ ORDER BY ns.nspname, cl.relname, con.conname, cols.ord
278
+ `.execute(this.kysely);
279
+ const fks = fkResult.rows.map((r) => ({
280
+ table: r.table,
281
+ column: r.column,
282
+ schema: r.schema,
283
+ ref_table: r.ref_table,
284
+ ref_column: r.ref_column,
285
+ foreign_key_name: r.foreign_key_name,
286
+ fk_def: r.fk_def,
287
+ on_update: "",
288
+ on_delete: ""
289
+ }));
290
+ const pkResult = await sql`
291
+ SELECT
292
+ ns.nspname AS "schema",
293
+ cl.relname AS "table",
294
+ array_agg(att.attname ORDER BY cols.ord) AS "columns"
295
+ FROM pg_constraint con
296
+ JOIN pg_class cl ON cl.oid = con.conrelid
297
+ JOIN pg_namespace ns ON ns.oid = cl.relnamespace
298
+ CROSS JOIN LATERAL unnest(con.conkey) WITH ORDINALITY AS cols(attnum, ord)
299
+ JOIN pg_attribute att
300
+ ON att.attrelid = con.conrelid AND att.attnum = cols.attnum
301
+ WHERE
302
+ con.contype = 'p'
303
+ AND ns.nspname NOT IN (
304
+ 'information_schema'
305
+ , 'pg_catalog')
306
+ GROUP BY ns.nspname, cl.relname, con.oid
307
+ `.execute(this.kysely);
308
+ const pks = pkResult.rows.map((r) => ({
309
+ table: r.table,
310
+ columns: r.columns,
311
+ schema: r.schema,
312
+ field_count: r.columns.length
313
+ }));
314
+ const pkSet = new Set(
315
+ pks.flatMap((pk) => pk.columns.map((col) => `${pk.schema}.${pk.table}.${col}`))
316
+ );
317
+ for (const col of columns) {
318
+ col.is_primary_key = pkSet.has(`${col.schema}.${col.table}.${col.name}`);
319
+ }
320
+ const idxResult = await sql`
321
+ SELECT
322
+ tnsp.nspname AS "schema",
323
+ cl.relname AS "table",
324
+ ic.relname AS "name",
325
+ idx.indisunique AS "unique",
326
+ array_agg(att.attname ORDER BY a.ord) AS "columns"
327
+ FROM pg_index idx
328
+ JOIN pg_class cl ON cl.oid = idx.indrelid
329
+ JOIN pg_namespace tnsp ON cl.relnamespace = tnsp.oid
330
+ JOIN pg_class ic ON ic.oid = idx.indexrelid
331
+ CROSS JOIN LATERAL unnest(idx.indkey) WITH ORDINALITY AS a(attnum, ord)
332
+ JOIN pg_attribute att
333
+ ON att.attrelid = cl.oid AND att.attnum = a.attnum
334
+ WHERE
335
+ idx.indisunique = true
336
+ AND NOT idx.indisprimary
337
+ AND tnsp.nspname NOT IN (
338
+ 'information_schema'
339
+ , 'pg_catalog')
340
+ GROUP BY tnsp.nspname, cl.relname, ic.relname, idx.indisunique
341
+ `.execute(this.kysely);
342
+ const indexes = idxResult.rows.map((r) => ({
343
+ table: r.table,
344
+ name: r.name,
345
+ unique: r.unique,
346
+ columns: r.columns,
347
+ schema: r.schema
348
+ }));
349
+ const viewResult = await sql`
350
+ SELECT
351
+ views.schemaname AS "schema",
352
+ views.viewname AS "name",
353
+ views.definition AS "sql"
354
+ FROM pg_views views
355
+ WHERE views.schemaname NOT IN ('information_schema', 'pg_catalog')
356
+ ORDER BY views.schemaname, views.viewname
357
+ `.execute(this.kysely);
358
+ const views = viewResult.rows.map((r) => ({
359
+ name: r.name,
360
+ schema: r.schema,
361
+ sql: r.sql ?? ""
362
+ }));
363
+ const chkResult = await sql`
364
+ SELECT
365
+ n.nspname AS "schema",
366
+ cl.relname AS "table",
367
+ substring(pg_get_constraintdef(c.oid) FROM 'CHECK \\((.*)\\)') AS "expression"
368
+ FROM pg_constraint c
369
+ JOIN pg_class cl ON cl.oid = c.conrelid
370
+ JOIN pg_namespace n ON n.oid = cl.relnamespace
371
+ WHERE
372
+ c.contype = 'c'
373
+ AND n.nspname NOT IN ('information_schema', 'pg_catalog')
374
+ ORDER BY n.nspname, cl.relname
375
+ `.execute(this.kysely);
376
+ const checkConstraints = chkResult.rows.map((r) => ({
377
+ schema: r.schema,
378
+ table: r.table,
379
+ expression: r.expression ?? ""
380
+ }));
381
+ const uqResult = await sql`
382
+ SELECT
383
+ n.nspname AS "schema",
384
+ cl.relname AS "table",
385
+ c.conname AS "name",
386
+ array_agg(att.attname ORDER BY cols.ord) AS "columns"
387
+ FROM pg_constraint c
388
+ JOIN pg_class cl ON cl.oid = c.conrelid
389
+ JOIN pg_namespace n ON n.oid = cl.relnamespace
390
+ CROSS JOIN LATERAL unnest(c.conkey) WITH ORDINALITY AS cols(attnum, ord)
391
+ JOIN pg_attribute att ON att.attrelid = c.conrelid AND att.attnum = cols.attnum
392
+ WHERE
393
+ c.contype = 'u'
394
+ AND n.nspname NOT IN ('information_schema', 'pg_catalog')
395
+ GROUP BY n.nspname, cl.relname, c.conname, c.oid
396
+ `.execute(this.kysely);
397
+ const uniqueConstraints = uqResult.rows.map((r) => ({
398
+ schema: r.schema,
399
+ table: r.table,
400
+ name: r.name,
401
+ columns: r.columns
402
+ }));
403
+ const commentResult = await sql`
404
+ SELECT
405
+ n.nspname AS "schema",
406
+ cl.relname AS "table",
407
+ att.attname AS "column",
408
+ d.description AS "text"
409
+ FROM pg_description d
410
+ JOIN pg_class cl ON cl.oid = d.objoid
411
+ JOIN pg_namespace n ON n.oid = cl.relnamespace
412
+ LEFT JOIN pg_attribute att
413
+ ON att.attrelid = cl.oid AND att.attnum = d.objsubid AND d.objsubid > 0
414
+ WHERE
415
+ n.nspname NOT IN ('information_schema', 'pg_catalog')
416
+ AND cl.relkind IN ('r', 'v', 'm', 'f', 'p')
417
+ AND d.description IS NOT NULL
418
+ `.execute(this.kysely);
419
+ const comments = commentResult.rows.map((r) => ({
420
+ schema: r.schema,
421
+ table: r.table,
422
+ column: r.column ?? void 0,
423
+ text: r.text
424
+ }));
425
+ const enumResult = await sql`
426
+ SELECT
427
+ n.nspname AS "schema",
428
+ t.typname AS "type",
429
+ 'enum' AS "kind",
430
+ array_agg(e.enumlabel ORDER BY e.enumsortorder) AS "values"
431
+ FROM pg_type t
432
+ JOIN pg_enum e ON t.oid = e.enumtypid
433
+ JOIN pg_namespace n ON n.oid = t.typnamespace
434
+ WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
435
+ GROUP BY n.nspname, t.typname
436
+ `.execute(this.kysely);
437
+ const compositeResult = await sql`
438
+ SELECT
439
+ n.nspname AS "schema",
440
+ t.typname AS "type",
441
+ 'composite' AS "kind",
442
+ json_agg(json_build_object(
443
+ 'name',
444
+ a.attname,
445
+ 'type',
446
+ format_type(a.atttypid, a.atttypmod)) ORDER BY a.attnum
447
+ ) AS "fields"
448
+ FROM pg_type t
449
+ JOIN pg_namespace n ON n.oid = t.typnamespace
450
+ JOIN pg_class c ON c.oid = t.typrelid
451
+ JOIN pg_attribute a ON a.attrelid = c.oid
452
+ WHERE
453
+ t.typtype = 'c'
454
+ AND c.relkind = 'c'
455
+ AND a.attnum > 0
456
+ AND NOT a.attisdropped
457
+ AND n.nspname NOT IN ('pg_catalog', 'information_schema')
458
+ GROUP BY n.nspname, t.typname
459
+ `.execute(this.kysely);
460
+ const customTypes = [
461
+ ...enumResult.rows.map((r) => ({
462
+ schema: r.schema,
463
+ type: r.type,
464
+ kind: "enum",
465
+ values: r.values
466
+ })),
467
+ ...compositeResult.rows.map((r) => ({
468
+ schema: r.schema,
469
+ type: r.type,
470
+ kind: "composite",
471
+ fields: r.fields
472
+ }))
473
+ ];
474
+ const dbResult = await sql`SELECT current_database() AS "name"`.execute(this.kysely);
475
+ const dbName = dbResult.rows[0]?.name ?? "postgres";
476
+ const result = {
477
+ tables,
478
+ columns,
479
+ indexes,
480
+ foreign_keys: fks,
481
+ primary_keys: pks,
482
+ views,
483
+ triggers: [],
484
+ check_constraints: checkConstraints,
485
+ unique_constraints: uniqueConstraints,
486
+ comments,
487
+ custom_types: customTypes,
488
+ database_name: dbName,
489
+ version: ""
490
+ };
491
+ await this.writeCachedIntrospection(result, { useDriver: useCache });
492
+ return result;
493
+ } catch (e) {
494
+ console.error("Introspection failed:", e);
495
+ return {
496
+ tables: [],
497
+ columns: [],
498
+ indexes: [],
499
+ foreign_keys: [],
500
+ primary_keys: [],
501
+ views: [],
502
+ triggers: [],
503
+ check_constraints: [],
504
+ unique_constraints: [],
505
+ comments: [],
506
+ custom_types: [],
507
+ database_name: "postgres",
508
+ version: ""
509
+ };
510
+ }
511
+ }
512
+ rlsState = "unknown";
513
+ /**
514
+ * Detect if any table has RLS enabled, and if so ensure anon/authenticated
515
+ * roles exist with default privileges. Runs once per connection.
516
+ */
517
+ async ensureRlsContext() {
518
+ if (this.rlsState !== "unknown") return this.rlsState === "active";
519
+ const result = await sql`
520
+ SELECT count(*) ::text as count FROM pg_class WHERE relrowsecurity = true
521
+ `.execute(this.kysely);
522
+ const hasRls = Number(result.rows[0]?.count ?? 0) > 0;
523
+ if (!hasRls) {
524
+ this.rlsState = "inactive";
525
+ return false;
526
+ }
527
+ this.rlsState = "active";
528
+ await sql`
529
+ DO $$ BEGIN
530
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'anon') THEN
531
+ CREATE ROLE anon NOLOGIN;
532
+ END IF;
533
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'authenticated') THEN
534
+ CREATE ROLE authenticated NOLOGIN;
535
+ END IF;
536
+ END $$
537
+ `.execute(this.kysely);
538
+ const schemas = await sql`
539
+ SELECT DISTINCT n.nspname FROM pg_class c
540
+ JOIN pg_namespace n ON n.oid = c.relnamespace
541
+ WHERE c.relrowsecurity = true
542
+ `.execute(this.kysely);
543
+ for (const { nspname } of schemas.rows) {
544
+ await sql`GRANT USAGE ON SCHEMA ${sql.ref(nspname)} TO anon, authenticated`.execute(
545
+ this.kysely
546
+ );
547
+ await sql.raw(`GRANT ALL ON ALL TABLES IN SCHEMA "${nspname}" TO anon, authenticated`).execute(this.kysely);
548
+ await sql.raw(`GRANT ALL ON ALL SEQUENCES IN SCHEMA "${nspname}" TO anon, authenticated`).execute(this.kysely);
549
+ await sql.raw(
550
+ `ALTER DEFAULT PRIVILEGES IN SCHEMA "${nspname}" GRANT ALL ON TABLES TO anon, authenticated`
551
+ ).execute(this.kysely);
552
+ await sql.raw(
553
+ `ALTER DEFAULT PRIVILEGES IN SCHEMA "${nspname}" GRANT ALL ON SEQUENCES TO anon, authenticated`
554
+ ).execute(this.kysely);
555
+ }
556
+ return true;
557
+ }
558
+ async withContext(vars, fn, opts) {
559
+ const forceRollback = opts?.forceRollback === true;
560
+ if (!forceRollback && !vars) return fn(this.kysely);
561
+ const rlsActive = vars ? await this.ensureRlsContext() : false;
562
+ if (!forceRollback && !rlsActive) return fn(this.kysely);
563
+ try {
564
+ return await this.kysely.transaction().execute(async (trx) => {
565
+ if (vars && rlsActive) {
566
+ const auth = vars.auth;
567
+ const role = auth?.role || "anon";
568
+ const uid = String(auth?.uid ?? "");
569
+ const jwt = auth?.jwt;
570
+ await sql`SELECT set_config('role', ${role}, true)`.execute(trx);
571
+ await sql`SELECT set_config('request.jwt.claim.sub', ${uid}, true)`.execute(trx);
572
+ await sql`SELECT set_config('request.jwt.claim.role', ${role}, true)`.execute(trx);
573
+ if (jwt && typeof jwt === "object") {
574
+ await sql`SELECT set_config('request.jwt.claims', ${JSON.stringify(jwt)}, true)`.execute(
575
+ trx
576
+ );
577
+ }
578
+ }
579
+ const result = await fn(trx);
580
+ if (forceRollback) throw new ForceRollback(result);
581
+ return result;
582
+ });
583
+ } catch (error) {
584
+ if (error instanceof ForceRollback) return error.result;
585
+ throw error;
586
+ }
587
+ }
588
+ async onPostgrestAST(ast) {
589
+ if (!ast.from) return ast;
590
+ const schema = ast.schema ?? "public";
591
+ const relation = ast.from;
592
+ const introspection = await this.introspect({ useCache: true });
593
+ if (!introspection.tables.some((t) => t.name === relation && t.schema === schema) && !introspection.views.some((v) => v.name === relation && v.schema === schema)) {
594
+ throw new RelationNotFoundError(schema, relation);
595
+ }
596
+ return ast;
597
+ }
598
+ async transaction(statements, opts) {
599
+ await this.exec("BEGIN");
600
+ try {
601
+ for (const statement of statements) {
602
+ await this.exec(statement);
603
+ }
604
+ await this.exec("COMMIT");
605
+ } catch (error) {
606
+ await this.exec("ROLLBACK");
607
+ throw error;
608
+ } finally {
609
+ if (opts?.intent === "migration") {
610
+ await this.clearSchemaCache();
611
+ }
612
+ }
613
+ }
614
+ };
615
+
616
+ // src/db/postgres/migration/pgdelta-pglite.ts
617
+ var parseArray = (value, parseElement = (entry) => entry) => {
618
+ if (!value || value === "{}") return [];
619
+ const inner = value.slice(1, -1);
620
+ if (inner === "") return [];
621
+ const result = [];
622
+ let current = "";
623
+ let inQuotes = false;
624
+ let depth = 0;
625
+ for (let i = 0; i < inner.length; i++) {
626
+ const char = inner[i];
627
+ if (char === '"' && inner[i - 1] !== "\\") {
628
+ inQuotes = !inQuotes;
629
+ current += char;
630
+ } else if (char === "{" && !inQuotes) {
631
+ depth++;
632
+ current += char;
633
+ } else if (char === "}" && !inQuotes) {
634
+ depth--;
635
+ current += char;
636
+ } else if (char === "," && !inQuotes && depth === 0) {
637
+ result.push(parseElement(current));
638
+ current = "";
639
+ } else {
640
+ current += char;
641
+ }
642
+ }
643
+ if (current !== "") {
644
+ result.push(parseElement(current));
645
+ }
646
+ return result;
647
+ };
648
+ var parseStringElement = (value) => {
649
+ if (value === "NULL") return null;
650
+ if (value.startsWith('"') && value.endsWith('"')) {
651
+ return value.slice(1, -1).replace(/\\(.)/g, "$1");
652
+ }
653
+ return value;
654
+ };
655
+ var parseIntElement = (value) => {
656
+ if (value === "NULL") return null;
657
+ return Number.parseInt(value, 10);
658
+ };
659
+ function isSqlTagLike(value) {
660
+ return typeof value === "object" && value !== null && "text" in value && "values" in value;
661
+ }
662
+ function normalizeValue(value, dataTypeID) {
663
+ if (value === null || value === void 0) return value;
664
+ switch (dataTypeID) {
665
+ case 20:
666
+ return typeof value === "bigint" ? value : BigInt(value);
667
+ case 22:
668
+ if (Array.isArray(value)) return value;
669
+ if (typeof value === "string") {
670
+ return value.split(" ").map(Number).filter((entry) => !Number.isNaN(entry));
671
+ }
672
+ return value;
673
+ case 1002:
674
+ case 1009:
675
+ case 1015:
676
+ return typeof value === "string" ? parseArray(value, parseStringElement) : value;
677
+ case 1005:
678
+ case 1007:
679
+ case 1016:
680
+ return typeof value === "string" ? parseArray(value, parseIntElement) : value;
681
+ default:
682
+ return value;
683
+ }
684
+ }
685
+ function pgDeltaQueryable(db) {
686
+ return {
687
+ async query(input, values) {
688
+ const sql3 = isSqlTagLike(input) ? input.text : input;
689
+ const parameters = isSqlTagLike(input) ? input.values : values ?? [];
690
+ const result = await db.driver.query(sql3, [...parameters]);
691
+ const rows = result.rows.map(
692
+ (row) => Object.fromEntries(
693
+ result.fields.map((field) => [
694
+ field.name,
695
+ normalizeValue(row[field.name], field.dataTypeID)
696
+ ])
697
+ )
698
+ );
699
+ return {
700
+ rows
701
+ };
702
+ }
703
+ };
704
+ }
705
+
706
+ // src/db/postgres/migration/PostgresMigrator.ts
707
+ async function loadPgDelta() {
708
+ try {
709
+ return await import('@supabase/pg-delta');
710
+ } catch (error) {
711
+ throw new Error(
712
+ "Postgres/PGlite schema diffing now requires a Bun/Node-compatible runtime with pg-delta available.",
713
+ { cause: error }
714
+ );
715
+ }
716
+ }
717
+ function parseRiskWarning(input) {
718
+ const dropTableMatch = /^drop table (.+)$/.exec(input);
719
+ if (dropTableMatch) {
720
+ return {
721
+ table: dropTableMatch[1],
722
+ reason: "drop table"
723
+ };
724
+ }
725
+ const dropColumnMatch = /^drop column (.+) on (.+)$/.exec(input);
726
+ if (dropColumnMatch) {
727
+ return {
728
+ table: dropColumnMatch[2],
729
+ reason: `drop column ${dropColumnMatch[1]}`
730
+ };
731
+ }
732
+ const dropSequenceMatch = /^drop sequence (.+)$/.exec(input);
733
+ if (dropSequenceMatch) {
734
+ return {
735
+ table: dropSequenceMatch[1],
736
+ reason: "drop sequence"
737
+ };
738
+ }
739
+ return {
740
+ table: "unknown",
741
+ reason: input
742
+ };
743
+ }
744
+ var PostgresMigrator = class {
745
+ constructor(from, desiredSchema) {
746
+ this.from = from;
747
+ this.desiredSchema = desiredSchema;
748
+ }
749
+ // Desired schema is immutable for a migrator, so cache its extracted catalog.
750
+ // This avoids rebuilding a scratch PGlite database on repeated diff() calls.
751
+ #desiredCatalog;
752
+ async #getDesiredCatalog(extractCatalog) {
753
+ if (this.#desiredCatalog) {
754
+ return this.#desiredCatalog;
755
+ }
756
+ const toConnection = new this.from.constructor();
757
+ try {
758
+ await toConnection.exec(this.desiredSchema);
759
+ this.#desiredCatalog = await extractCatalog(pgDeltaQueryable(toConnection));
760
+ return this.#desiredCatalog;
761
+ } finally {
762
+ await toConnection.close();
763
+ }
764
+ }
765
+ async diff() {
766
+ const { createPlan, extractCatalog } = await loadPgDelta();
767
+ const from = pgDeltaQueryable(this.from);
768
+ const toCatalog = await this.#getDesiredCatalog(extractCatalog);
769
+ const fromCatalog = await extractCatalog(from);
770
+ const result = await createPlan(fromCatalog, toCatalog);
771
+ if (!result) {
772
+ return {
773
+ diff: "",
774
+ plan: {
775
+ steps: [],
776
+ warnings: [],
777
+ unsafe: false
778
+ }
779
+ };
780
+ }
781
+ const warnings = result.plan.risk?.level === "data_loss" ? result.plan.risk.statements.map(parseRiskWarning) : [];
782
+ return {
783
+ diff: result.plan.statements.join("\n\n"),
784
+ plan: {
785
+ steps: result.plan.statements.map((sql3) => ({ sql: sql3 })),
786
+ warnings,
787
+ unsafe: result.plan.risk?.level === "data_loss"
788
+ }
789
+ };
790
+ }
791
+ async migratePlan(planResult, opts) {
792
+ if (planResult.steps.length === 0) {
793
+ return;
794
+ }
795
+ if (planResult.unsafe && !opts?.force) {
796
+ throw new DataLossError(planResult.warnings ?? []);
797
+ }
798
+ await this.from.transaction(
799
+ planResult.steps.map((step) => step.sql),
800
+ { intent: "migration" }
801
+ );
802
+ }
803
+ async migrate(opts) {
804
+ const result = await this.diff();
805
+ await this.migratePlan(result.plan, opts);
806
+ return result;
807
+ }
808
+ safeSortPlanSteps(steps) {
809
+ return steps;
810
+ }
811
+ };
812
+ var PgliteConnection = class extends BasePostgresConnection {
813
+ driver;
814
+ dialect = "postgres";
815
+ #closed = false;
816
+ constructor(config = {}) {
817
+ const client = new PGlite({
818
+ ...config.pgliteOptions,
819
+ dataDir: config.url ?? void 0,
820
+ extensions: {
821
+ ...config.pgliteOptions?.extensions,
822
+ isn,
823
+ ltree,
824
+ file_fdw,
825
+ pgcrypto
826
+ },
827
+ parsers: {
828
+ // bigint/int8 (OID 20) → JS number when safe, else BigInt. Mirrors PostgresConnection.
829
+ 20: (x) => {
830
+ const n = Number(x);
831
+ return Number.isSafeInteger(n) ? n : BigInt(x);
832
+ },
833
+ // json (OID 114) and jsonb (OID 3802): pglite leaves both as text by default.
834
+ // PostgREST returns them as parsed JSON, including json_agg() in embed aggregates.
835
+ 114: (x) => JSON.parse(x),
836
+ 3802: (x) => JSON.parse(x),
837
+ // numeric (OID 1700): coerce when round-trip exact, else keep string for precision safety.
838
+ 1700: (x) => {
839
+ const n = Number(x);
840
+ if (Number.isFinite(n) && String(n) === x) return n;
841
+ return x;
842
+ },
843
+ // Date/time OIDs (1082 date, 1083 time, 1114 timestamp, 1184 timestamptz,
844
+ // 1266 timetz): keep as raw strings to mirror SQLite. Auth/storage code uses
845
+ // Date.parse() on these and the spec timestamp sentinel only matches strings.
846
+ 1082: (x) => x,
847
+ 1083: (x) => x,
848
+ 1114: (x) => x,
849
+ 1184: (x) => x,
850
+ 1266: (x) => x,
851
+ ...config.pgliteOptions?.parsers
852
+ }
853
+ });
854
+ const { dialect } = new KyselyPglite(client);
855
+ super(config);
856
+ this.driver = client;
857
+ this.kysely = new Kysely({ dialect });
858
+ }
859
+ async exec(query, ...parameters) {
860
+ try {
861
+ if (parameters.length > 0) {
862
+ const { rows } = await sql(query, ...parameters ?? []).execute(this.kysely);
863
+ return { rows };
864
+ }
865
+ const result = await this.driver.exec(query);
866
+ return {
867
+ rows: result[0].rows
868
+ };
869
+ } catch (error) {
870
+ console.error(error);
871
+ throw new Error(`Failed to execute query: ${query}`);
872
+ }
873
+ }
874
+ async close() {
875
+ if (this.#closed) return;
876
+ this.#closed = true;
877
+ await this.clearSchemaCache();
878
+ await this.kysely.destroy();
879
+ await this.driver.close();
880
+ }
881
+ createMigrator(desiredSchema) {
882
+ const migrationSchema = [this.config.baseSchema, desiredSchema].filter(Boolean).join("\n\n");
883
+ return new PostgresMigrator(this, migrationSchema);
884
+ }
885
+ };
886
+ async function createPgliteConnection(config = {}) {
887
+ return new PgliteConnection(config);
888
+ }
889
+
890
+ export { PgliteConnection, createPgliteConnection };