@rebasepro/server-postgres 0.9.1-canary.ff338b5 → 0.10.1-canary.14e53ae
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.
- package/README.md +21 -0
- package/dist/PostgresBackendDriver.d.ts +18 -0
- package/dist/PostgresBootstrapper.d.ts +11 -1
- package/dist/auth/services.d.ts +93 -54
- package/dist/backup/backup-logic.d.ts +23 -0
- package/dist/backup/backup-service.d.ts +44 -2
- package/dist/backup/pg-tools.d.ts +41 -1
- package/dist/chunk-DSJWtz9O.js +40 -0
- package/dist/cli-helpers.d.ts +33 -1
- package/dist/ensure-collection-tables-CNlIONzj.js +304 -0
- package/dist/ensure-collection-tables-CNlIONzj.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +2090 -4741
- package/dist/index.es.js.map +1 -1
- package/dist/schema/auth-bootstrap-sql.d.ts +1 -1
- package/dist/schema/auth-schema.d.ts +194 -24
- package/dist/schema/destructive-sql.d.ts +49 -0
- package/dist/schema/ensure-collection-tables.d.ts +79 -0
- package/dist/schema/generate-postgres-ddl-logic.d.ts +4 -1
- package/dist/schema/introspect-db-logic.d.ts +0 -5
- package/dist/schema/introspect-db-naming.d.ts +10 -0
- package/dist/security/policy-drift.d.ts +24 -0
- package/dist/security/rls-enforcement.d.ts +2 -2
- package/dist/services/cdc/CdcListener.d.ts +7 -14
- package/dist/services/channel-bus/ChannelBus.d.ts +29 -0
- package/dist/services/channel-bus/PostgresChannelBus.d.ts +111 -0
- package/dist/services/channel-bus/index.d.ts +55 -0
- package/dist/services/channel-history.d.ts +129 -0
- package/dist/services/channel-presence.d.ts +66 -0
- package/dist/services/pg-notify-listener.d.ts +47 -0
- package/dist/services/realtimeService.d.ts +183 -8
- package/dist/src-B0v4IKaI.js +329 -0
- package/dist/src-B0v4IKaI.js.map +1 -0
- package/dist/src-DmsRg8MR.js +4056 -0
- package/dist/src-DmsRg8MR.js.map +1 -0
- package/package.json +7 -31
- package/src/PostgresBackendDriver.ts +56 -5
- package/src/PostgresBootstrapper.ts +87 -1
- package/src/auth/ensure-tables.ts +187 -19
- package/src/auth/services.ts +309 -170
- package/src/backup/backup-cli.ts +60 -1
- package/src/backup/backup-cron.ts +24 -1
- package/src/backup/backup-logic.ts +62 -0
- package/src/backup/backup-service.ts +132 -13
- package/src/backup/pg-tools.ts +70 -2
- package/src/cli-helpers.ts +82 -27
- package/src/cli.ts +152 -6
- package/src/index.ts +4 -0
- package/src/schema/auth-bootstrap-sql.ts +7 -1
- package/src/schema/auth-schema.ts +53 -15
- package/src/schema/destructive-sql.ts +94 -0
- package/src/schema/ensure-collection-tables.test.ts +156 -0
- package/src/schema/ensure-collection-tables.ts +297 -0
- package/src/schema/generate-postgres-ddl-logic.ts +3 -3
- package/src/schema/introspect-db-inference.ts +1 -1
- package/src/schema/introspect-db-logic.ts +1 -10
- package/src/schema/introspect-db-naming.ts +15 -0
- package/src/schema/introspect-runtime.ts +1 -1
- package/src/security/policy-drift.test.ts +46 -0
- package/src/security/policy-drift.ts +70 -4
- package/src/security/rls-enforcement.ts +11 -5
- package/src/services/cdc/CdcListener.ts +27 -91
- package/src/services/channel-bus/ChannelBus.ts +44 -0
- package/src/services/channel-bus/PostgresChannelBus.ts +299 -0
- package/src/services/channel-bus/index.ts +123 -0
- package/src/services/channel-history.ts +378 -0
- package/src/services/channel-presence.ts +148 -0
- package/src/services/pg-notify-listener.ts +137 -0
- package/src/services/realtimeService.ts +581 -24
- package/src/websocket.ts +30 -11
|
@@ -21,4 +21,4 @@
|
|
|
21
21
|
* `DROP SCHEMA "rebase" CASCADE` on the next diff. The `auth` schema is safe
|
|
22
22
|
* because the generated schema.sql declares it.
|
|
23
23
|
*/
|
|
24
|
-
export declare const AUTH_BOOTSTRAP_SQL = "-- Auth schema + RLS helper functions (required by the policies below)\nCREATE SCHEMA IF NOT EXISTS auth;\n\nCREATE OR REPLACE FUNCTION auth.uid() RETURNS text AS $$\n SELECT NULLIF(current_setting('app.user_id', true), '');\n$$ LANGUAGE sql STABLE;\n\nCREATE OR REPLACE FUNCTION auth.jwt() RETURNS jsonb AS $$\n SELECT COALESCE(\n NULLIF(current_setting('app.jwt', true), ''),\n '{}'\n )::jsonb;\n$$ LANGUAGE sql STABLE;\n\nCREATE OR REPLACE FUNCTION auth.roles() RETURNS text AS $$\n SELECT COALESCE(NULLIF(current_setting('app.user_roles', true), ''), '');\n$$ LANGUAGE sql STABLE;\n";
|
|
24
|
+
export declare const AUTH_BOOTSTRAP_SQL = "-- Auth schema + RLS helper functions (required by the policies below)\nCREATE SCHEMA IF NOT EXISTS auth;\n\n-- Falls back to the pre-rename `app.user_id` so a database that has taken the\n-- new schema but is still served by an older backend keeps resolving the\n-- principal. Drop the COALESCE once no such deployment remains.\nCREATE OR REPLACE FUNCTION auth.uid() RETURNS text AS $$\n SELECT COALESCE(\n NULLIF(current_setting('app.uid', true), ''),\n NULLIF(current_setting('app.user_id', true), '')\n );\n$$ LANGUAGE sql STABLE;\n\nCREATE OR REPLACE FUNCTION auth.jwt() RETURNS jsonb AS $$\n SELECT COALESCE(\n NULLIF(current_setting('app.jwt', true), ''),\n '{}'\n )::jsonb;\n$$ LANGUAGE sql STABLE;\n\nCREATE OR REPLACE FUNCTION auth.roles() RETURNS text AS $$\n SELECT COALESCE(NULLIF(current_setting('app.user_roles', true), ''), '');\n$$ LANGUAGE sql STABLE;\n";
|
|
@@ -232,6 +232,23 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
232
232
|
}, {}, {
|
|
233
233
|
$type: Record<string, unknown>;
|
|
234
234
|
}>;
|
|
235
|
+
tokensValidAfter: import("drizzle-orm/pg-core").PgColumn<{
|
|
236
|
+
name: "tokens_valid_after";
|
|
237
|
+
tableName: "users";
|
|
238
|
+
dataType: "date";
|
|
239
|
+
columnType: "PgTimestamp";
|
|
240
|
+
data: Date;
|
|
241
|
+
driverParam: string;
|
|
242
|
+
notNull: false;
|
|
243
|
+
hasDefault: false;
|
|
244
|
+
isPrimaryKey: false;
|
|
245
|
+
isAutoincrement: false;
|
|
246
|
+
hasRuntimeDefault: false;
|
|
247
|
+
enumValues: undefined;
|
|
248
|
+
baseColumn: never;
|
|
249
|
+
identity: undefined;
|
|
250
|
+
generated: undefined;
|
|
251
|
+
}, {}, {}>;
|
|
235
252
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
236
253
|
name: "created_at";
|
|
237
254
|
tableName: "users";
|
|
@@ -290,8 +307,8 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
290
307
|
identity: undefined;
|
|
291
308
|
generated: undefined;
|
|
292
309
|
}, {}, {}>;
|
|
293
|
-
|
|
294
|
-
name: "
|
|
310
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
311
|
+
name: "uid";
|
|
295
312
|
tableName: "refresh_tokens";
|
|
296
313
|
dataType: "string";
|
|
297
314
|
columnType: "PgUUID";
|
|
@@ -307,6 +324,23 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
307
324
|
identity: undefined;
|
|
308
325
|
generated: undefined;
|
|
309
326
|
}, {}, {}>;
|
|
327
|
+
sessionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
328
|
+
name: "session_id";
|
|
329
|
+
tableName: "refresh_tokens";
|
|
330
|
+
dataType: "string";
|
|
331
|
+
columnType: "PgUUID";
|
|
332
|
+
data: string;
|
|
333
|
+
driverParam: string;
|
|
334
|
+
notNull: true;
|
|
335
|
+
hasDefault: true;
|
|
336
|
+
isPrimaryKey: false;
|
|
337
|
+
isAutoincrement: false;
|
|
338
|
+
hasRuntimeDefault: false;
|
|
339
|
+
enumValues: undefined;
|
|
340
|
+
baseColumn: never;
|
|
341
|
+
identity: undefined;
|
|
342
|
+
generated: undefined;
|
|
343
|
+
}, {}, {}>;
|
|
310
344
|
tokenHash: import("drizzle-orm/pg-core").PgColumn<{
|
|
311
345
|
name: "token_hash";
|
|
312
346
|
tableName: "refresh_tokens";
|
|
@@ -343,6 +377,57 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
343
377
|
identity: undefined;
|
|
344
378
|
generated: undefined;
|
|
345
379
|
}, {}, {}>;
|
|
380
|
+
revoked: import("drizzle-orm/pg-core").PgColumn<{
|
|
381
|
+
name: "revoked";
|
|
382
|
+
tableName: "refresh_tokens";
|
|
383
|
+
dataType: "boolean";
|
|
384
|
+
columnType: "PgBoolean";
|
|
385
|
+
data: boolean;
|
|
386
|
+
driverParam: boolean;
|
|
387
|
+
notNull: true;
|
|
388
|
+
hasDefault: true;
|
|
389
|
+
isPrimaryKey: false;
|
|
390
|
+
isAutoincrement: false;
|
|
391
|
+
hasRuntimeDefault: false;
|
|
392
|
+
enumValues: undefined;
|
|
393
|
+
baseColumn: never;
|
|
394
|
+
identity: undefined;
|
|
395
|
+
generated: undefined;
|
|
396
|
+
}, {}, {}>;
|
|
397
|
+
rotatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
398
|
+
name: "rotated_at";
|
|
399
|
+
tableName: "refresh_tokens";
|
|
400
|
+
dataType: "date";
|
|
401
|
+
columnType: "PgTimestamp";
|
|
402
|
+
data: Date;
|
|
403
|
+
driverParam: string;
|
|
404
|
+
notNull: false;
|
|
405
|
+
hasDefault: false;
|
|
406
|
+
isPrimaryKey: false;
|
|
407
|
+
isAutoincrement: false;
|
|
408
|
+
hasRuntimeDefault: false;
|
|
409
|
+
enumValues: undefined;
|
|
410
|
+
baseColumn: never;
|
|
411
|
+
identity: undefined;
|
|
412
|
+
generated: undefined;
|
|
413
|
+
}, {}, {}>;
|
|
414
|
+
sessionStartedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
415
|
+
name: "session_started_at";
|
|
416
|
+
tableName: "refresh_tokens";
|
|
417
|
+
dataType: "date";
|
|
418
|
+
columnType: "PgTimestamp";
|
|
419
|
+
data: Date;
|
|
420
|
+
driverParam: string;
|
|
421
|
+
notNull: true;
|
|
422
|
+
hasDefault: true;
|
|
423
|
+
isPrimaryKey: false;
|
|
424
|
+
isAutoincrement: false;
|
|
425
|
+
hasRuntimeDefault: false;
|
|
426
|
+
enumValues: undefined;
|
|
427
|
+
baseColumn: never;
|
|
428
|
+
identity: undefined;
|
|
429
|
+
generated: undefined;
|
|
430
|
+
}, {}, {}>;
|
|
346
431
|
userAgent: import("drizzle-orm/pg-core").PgColumn<{
|
|
347
432
|
name: "user_agent";
|
|
348
433
|
tableName: "refresh_tokens";
|
|
@@ -422,8 +507,8 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
422
507
|
identity: undefined;
|
|
423
508
|
generated: undefined;
|
|
424
509
|
}, {}, {}>;
|
|
425
|
-
|
|
426
|
-
name: "
|
|
510
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
511
|
+
name: "uid";
|
|
427
512
|
tableName: "password_reset_tokens";
|
|
428
513
|
dataType: "string";
|
|
429
514
|
columnType: "PgUUID";
|
|
@@ -593,8 +678,8 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
593
678
|
identity: undefined;
|
|
594
679
|
generated: undefined;
|
|
595
680
|
}, {}, {}>;
|
|
596
|
-
|
|
597
|
-
name: "
|
|
681
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
682
|
+
name: "uid";
|
|
598
683
|
tableName: "user_identities";
|
|
599
684
|
dataType: "string";
|
|
600
685
|
columnType: "PgUUID";
|
|
@@ -723,8 +808,8 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
723
808
|
identity: undefined;
|
|
724
809
|
generated: undefined;
|
|
725
810
|
}, {}, {}>;
|
|
726
|
-
|
|
727
|
-
name: "
|
|
811
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
812
|
+
name: "uid";
|
|
728
813
|
tableName: "mfa_factors";
|
|
729
814
|
dataType: "string";
|
|
730
815
|
columnType: "PgUUID";
|
|
@@ -983,8 +1068,8 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
983
1068
|
identity: undefined;
|
|
984
1069
|
generated: undefined;
|
|
985
1070
|
}, {}, {}>;
|
|
986
|
-
|
|
987
|
-
name: "
|
|
1071
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
1072
|
+
name: "uid";
|
|
988
1073
|
tableName: "recovery_codes";
|
|
989
1074
|
dataType: "string";
|
|
990
1075
|
columnType: "PgUUID";
|
|
@@ -1077,8 +1162,8 @@ export declare function createAuthSchema(usersSchemaName?: string): {
|
|
|
1077
1162
|
identity: undefined;
|
|
1078
1163
|
generated: undefined;
|
|
1079
1164
|
}, {}, {}>;
|
|
1080
|
-
|
|
1081
|
-
name: "
|
|
1165
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
1166
|
+
name: "uid";
|
|
1082
1167
|
tableName: "magic_link_tokens";
|
|
1083
1168
|
dataType: "string";
|
|
1084
1169
|
columnType: "PgUUID";
|
|
@@ -1398,6 +1483,23 @@ export declare const users: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1398
1483
|
}, {}, {
|
|
1399
1484
|
$type: Record<string, unknown>;
|
|
1400
1485
|
}>;
|
|
1486
|
+
tokensValidAfter: import("drizzle-orm/pg-core").PgColumn<{
|
|
1487
|
+
name: "tokens_valid_after";
|
|
1488
|
+
tableName: "users";
|
|
1489
|
+
dataType: "date";
|
|
1490
|
+
columnType: "PgTimestamp";
|
|
1491
|
+
data: Date;
|
|
1492
|
+
driverParam: string;
|
|
1493
|
+
notNull: false;
|
|
1494
|
+
hasDefault: false;
|
|
1495
|
+
isPrimaryKey: false;
|
|
1496
|
+
isAutoincrement: false;
|
|
1497
|
+
hasRuntimeDefault: false;
|
|
1498
|
+
enumValues: undefined;
|
|
1499
|
+
baseColumn: never;
|
|
1500
|
+
identity: undefined;
|
|
1501
|
+
generated: undefined;
|
|
1502
|
+
}, {}, {}>;
|
|
1401
1503
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1402
1504
|
name: "created_at";
|
|
1403
1505
|
tableName: "users";
|
|
@@ -1456,8 +1558,8 @@ export declare const refreshTokens: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
1456
1558
|
identity: undefined;
|
|
1457
1559
|
generated: undefined;
|
|
1458
1560
|
}, {}, {}>;
|
|
1459
|
-
|
|
1460
|
-
name: "
|
|
1561
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
1562
|
+
name: "uid";
|
|
1461
1563
|
tableName: "refresh_tokens";
|
|
1462
1564
|
dataType: "string";
|
|
1463
1565
|
columnType: "PgUUID";
|
|
@@ -1473,6 +1575,23 @@ export declare const refreshTokens: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
1473
1575
|
identity: undefined;
|
|
1474
1576
|
generated: undefined;
|
|
1475
1577
|
}, {}, {}>;
|
|
1578
|
+
sessionId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1579
|
+
name: "session_id";
|
|
1580
|
+
tableName: "refresh_tokens";
|
|
1581
|
+
dataType: "string";
|
|
1582
|
+
columnType: "PgUUID";
|
|
1583
|
+
data: string;
|
|
1584
|
+
driverParam: string;
|
|
1585
|
+
notNull: true;
|
|
1586
|
+
hasDefault: true;
|
|
1587
|
+
isPrimaryKey: false;
|
|
1588
|
+
isAutoincrement: false;
|
|
1589
|
+
hasRuntimeDefault: false;
|
|
1590
|
+
enumValues: undefined;
|
|
1591
|
+
baseColumn: never;
|
|
1592
|
+
identity: undefined;
|
|
1593
|
+
generated: undefined;
|
|
1594
|
+
}, {}, {}>;
|
|
1476
1595
|
tokenHash: import("drizzle-orm/pg-core").PgColumn<{
|
|
1477
1596
|
name: "token_hash";
|
|
1478
1597
|
tableName: "refresh_tokens";
|
|
@@ -1509,6 +1628,57 @@ export declare const refreshTokens: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
1509
1628
|
identity: undefined;
|
|
1510
1629
|
generated: undefined;
|
|
1511
1630
|
}, {}, {}>;
|
|
1631
|
+
revoked: import("drizzle-orm/pg-core").PgColumn<{
|
|
1632
|
+
name: "revoked";
|
|
1633
|
+
tableName: "refresh_tokens";
|
|
1634
|
+
dataType: "boolean";
|
|
1635
|
+
columnType: "PgBoolean";
|
|
1636
|
+
data: boolean;
|
|
1637
|
+
driverParam: boolean;
|
|
1638
|
+
notNull: true;
|
|
1639
|
+
hasDefault: true;
|
|
1640
|
+
isPrimaryKey: false;
|
|
1641
|
+
isAutoincrement: false;
|
|
1642
|
+
hasRuntimeDefault: false;
|
|
1643
|
+
enumValues: undefined;
|
|
1644
|
+
baseColumn: never;
|
|
1645
|
+
identity: undefined;
|
|
1646
|
+
generated: undefined;
|
|
1647
|
+
}, {}, {}>;
|
|
1648
|
+
rotatedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1649
|
+
name: "rotated_at";
|
|
1650
|
+
tableName: "refresh_tokens";
|
|
1651
|
+
dataType: "date";
|
|
1652
|
+
columnType: "PgTimestamp";
|
|
1653
|
+
data: Date;
|
|
1654
|
+
driverParam: string;
|
|
1655
|
+
notNull: false;
|
|
1656
|
+
hasDefault: false;
|
|
1657
|
+
isPrimaryKey: false;
|
|
1658
|
+
isAutoincrement: false;
|
|
1659
|
+
hasRuntimeDefault: false;
|
|
1660
|
+
enumValues: undefined;
|
|
1661
|
+
baseColumn: never;
|
|
1662
|
+
identity: undefined;
|
|
1663
|
+
generated: undefined;
|
|
1664
|
+
}, {}, {}>;
|
|
1665
|
+
sessionStartedAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1666
|
+
name: "session_started_at";
|
|
1667
|
+
tableName: "refresh_tokens";
|
|
1668
|
+
dataType: "date";
|
|
1669
|
+
columnType: "PgTimestamp";
|
|
1670
|
+
data: Date;
|
|
1671
|
+
driverParam: string;
|
|
1672
|
+
notNull: true;
|
|
1673
|
+
hasDefault: true;
|
|
1674
|
+
isPrimaryKey: false;
|
|
1675
|
+
isAutoincrement: false;
|
|
1676
|
+
hasRuntimeDefault: false;
|
|
1677
|
+
enumValues: undefined;
|
|
1678
|
+
baseColumn: never;
|
|
1679
|
+
identity: undefined;
|
|
1680
|
+
generated: undefined;
|
|
1681
|
+
}, {}, {}>;
|
|
1512
1682
|
userAgent: import("drizzle-orm/pg-core").PgColumn<{
|
|
1513
1683
|
name: "user_agent";
|
|
1514
1684
|
tableName: "refresh_tokens";
|
|
@@ -1588,8 +1758,8 @@ export declare const passwordResetTokens: import("drizzle-orm/pg-core").PgTableW
|
|
|
1588
1758
|
identity: undefined;
|
|
1589
1759
|
generated: undefined;
|
|
1590
1760
|
}, {}, {}>;
|
|
1591
|
-
|
|
1592
|
-
name: "
|
|
1761
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
1762
|
+
name: "uid";
|
|
1593
1763
|
tableName: "password_reset_tokens";
|
|
1594
1764
|
dataType: "string";
|
|
1595
1765
|
columnType: "PgUUID";
|
|
@@ -1759,8 +1929,8 @@ export declare const userIdentities: import("drizzle-orm/pg-core").PgTableWithCo
|
|
|
1759
1929
|
identity: undefined;
|
|
1760
1930
|
generated: undefined;
|
|
1761
1931
|
}, {}, {}>;
|
|
1762
|
-
|
|
1763
|
-
name: "
|
|
1932
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
1933
|
+
name: "uid";
|
|
1764
1934
|
tableName: "user_identities";
|
|
1765
1935
|
dataType: "string";
|
|
1766
1936
|
columnType: "PgUUID";
|
|
@@ -1889,8 +2059,8 @@ export declare const mfaFactors: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
1889
2059
|
identity: undefined;
|
|
1890
2060
|
generated: undefined;
|
|
1891
2061
|
}, {}, {}>;
|
|
1892
|
-
|
|
1893
|
-
name: "
|
|
2062
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
2063
|
+
name: "uid";
|
|
1894
2064
|
tableName: "mfa_factors";
|
|
1895
2065
|
dataType: "string";
|
|
1896
2066
|
columnType: "PgUUID";
|
|
@@ -2149,8 +2319,8 @@ export declare const recoveryCodes: import("drizzle-orm/pg-core").PgTableWithCol
|
|
|
2149
2319
|
identity: undefined;
|
|
2150
2320
|
generated: undefined;
|
|
2151
2321
|
}, {}, {}>;
|
|
2152
|
-
|
|
2153
|
-
name: "
|
|
2322
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
2323
|
+
name: "uid";
|
|
2154
2324
|
tableName: "recovery_codes";
|
|
2155
2325
|
dataType: "string";
|
|
2156
2326
|
columnType: "PgUUID";
|
|
@@ -2243,8 +2413,8 @@ export declare const magicLinkTokens: import("drizzle-orm/pg-core").PgTableWithC
|
|
|
2243
2413
|
identity: undefined;
|
|
2244
2414
|
generated: undefined;
|
|
2245
2415
|
}, {}, {}>;
|
|
2246
|
-
|
|
2247
|
-
name: "
|
|
2416
|
+
uid: import("drizzle-orm/pg-core").PgColumn<{
|
|
2417
|
+
name: "uid";
|
|
2248
2418
|
tableName: "magic_link_tokens";
|
|
2249
2419
|
dataType: "string";
|
|
2250
2420
|
columnType: "PgUUID";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure helpers that classify an Atlas declarative-apply plan as destructive
|
|
3
|
+
* or not, and decide what `rebase db push` should do about it.
|
|
4
|
+
*
|
|
5
|
+
* `db push` runs `atlas schema apply` to make the live database match the
|
|
6
|
+
* generated `schema.sql`. Removing a collection field compiles to
|
|
7
|
+
* `DROP COLUMN`; renaming compiles to drop-then-add — either destroys data.
|
|
8
|
+
* We first run the apply with `--dry-run` to obtain the planned SQL, scan it
|
|
9
|
+
* here, and refuse to auto-approve anything destructive without an explicit
|
|
10
|
+
* opt-in.
|
|
11
|
+
*
|
|
12
|
+
* Everything in this file is side-effect free so it can be unit-tested
|
|
13
|
+
* without Atlas or a database.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Split a SQL script into individual statements, dropping blank lines and
|
|
17
|
+
* `--` comment lines. Deliberately simple: Atlas emits one plain statement
|
|
18
|
+
* per `;`, without string literals that contain semicolons in a schema DDL
|
|
19
|
+
* plan, so a naive split is safe and keeps this dependency-free.
|
|
20
|
+
*/
|
|
21
|
+
export declare function splitSqlStatements(sql: string): string[];
|
|
22
|
+
export interface DestructiveStatement {
|
|
23
|
+
/** The offending statement (trimmed, without the trailing `;`). */
|
|
24
|
+
statement: string;
|
|
25
|
+
/** Which destructive operation it was flagged for, e.g. "DROP COLUMN". */
|
|
26
|
+
kind: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Scan an Atlas plan (the SQL printed by `schema apply --dry-run`) and return
|
|
30
|
+
* the statements that would destroy data. An empty array means the plan is
|
|
31
|
+
* safe to auto-approve.
|
|
32
|
+
*/
|
|
33
|
+
export declare function detectDestructiveStatements(planSql: string): DestructiveStatement[];
|
|
34
|
+
export type PushDecision = "apply" | "confirm" | "refuse";
|
|
35
|
+
/**
|
|
36
|
+
* Decide how `db push` should proceed given the plan's destructiveness and
|
|
37
|
+
* the invocation context.
|
|
38
|
+
*
|
|
39
|
+
* - No destructive statements → `apply` (safe to auto-approve).
|
|
40
|
+
* - Destructive + `--allow-destructive` → `apply` (operator opted in).
|
|
41
|
+
* - Destructive + interactive TTY → `confirm` (prompt before applying).
|
|
42
|
+
* - Destructive + non-interactive → `refuse` (never silently drop data in
|
|
43
|
+
* CI / scripts / agents).
|
|
44
|
+
*/
|
|
45
|
+
export declare function decidePushSafety(opts: {
|
|
46
|
+
destructiveCount: number;
|
|
47
|
+
allowDestructive: boolean;
|
|
48
|
+
interactive: boolean;
|
|
49
|
+
}): PushDecision;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bringing a database up to date with a bundle's collections, additively.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this exists
|
|
5
|
+
*
|
|
6
|
+
* A managed runtime boots someone else's compiled project against a database it
|
|
7
|
+
* has never seen. Auth tables are ensured at boot already, but collection tables
|
|
8
|
+
* were not created by anything: the platform ran the app and every `/api/data/*`
|
|
9
|
+
* request answered 500 on a missing relation. `rebase db push` cannot help — it
|
|
10
|
+
* is an Atlas-driven CLI command, and the runtime image ships no CLI.
|
|
11
|
+
*
|
|
12
|
+
* ## Why additive-only, forever
|
|
13
|
+
*
|
|
14
|
+
* This runs unattended, against a database with customers' data in it, with no
|
|
15
|
+
* human reading a diff. So it may only ever do things that cannot lose data:
|
|
16
|
+
* create a missing table, add a missing column, create a missing enum type.
|
|
17
|
+
*
|
|
18
|
+
* It will **never** drop a table or a column, narrow a type, or alter a
|
|
19
|
+
* constraint. A removed field leaves its column behind; a renamed field looks
|
|
20
|
+
* like an addition and the old column stays. That is the correct trade for an
|
|
21
|
+
* automated path — the alternative is an unattended process that can silently
|
|
22
|
+
* destroy a column, which is precisely the failure `db push` was hardened
|
|
23
|
+
* against. Destructive changes stay a deliberate, human-reviewed migration.
|
|
24
|
+
*
|
|
25
|
+
* Because of that, this is safe to run on every boot, and re-running it is a
|
|
26
|
+
* no-op.
|
|
27
|
+
*/
|
|
28
|
+
import { type CollectionConfig } from "@rebasepro/types";
|
|
29
|
+
/**
|
|
30
|
+
* The subset of a database handle this needs: run a statement, get rows back.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately parameterless. Everything here is DDL or catalogue reads keyed by
|
|
33
|
+
* schema name, and schema names are identifiers — they cannot be bound as
|
|
34
|
+
* parameters anyway. They are validated against {@link SAFE_IDENTIFIER} before
|
|
35
|
+
* they reach a statement, so a config that somehow carried a quote is refused
|
|
36
|
+
* rather than concatenated.
|
|
37
|
+
*/
|
|
38
|
+
export interface Queryable {
|
|
39
|
+
query<T = unknown>(sql: string): Promise<{
|
|
40
|
+
rows: T[];
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
/** What the database currently has, as the planner needs it. */
|
|
44
|
+
export interface ExistingSchema {
|
|
45
|
+
/** `schema.table` → set of column names. */
|
|
46
|
+
tables: Map<string, Set<string>>;
|
|
47
|
+
/** `schema.typename` of every enum type that already exists. */
|
|
48
|
+
enums: Set<string>;
|
|
49
|
+
}
|
|
50
|
+
export interface EnsureAction {
|
|
51
|
+
kind: "create-enum" | "create-table" | "add-column";
|
|
52
|
+
/** Qualified target, for logging: `public.posts` or `public.posts.title`. */
|
|
53
|
+
target: string;
|
|
54
|
+
sql: string;
|
|
55
|
+
}
|
|
56
|
+
export interface EnsurePlan {
|
|
57
|
+
actions: EnsureAction[];
|
|
58
|
+
/** Every statement, in dependency order. Empty when the schema is current. */
|
|
59
|
+
statements: string[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Decide what to add. Pure — the caller supplies what exists and runs the result.
|
|
63
|
+
*
|
|
64
|
+
* Ordering matters and is deliberate: enum types before the tables and columns
|
|
65
|
+
* that reference them, tables before the columns added to other tables (a new
|
|
66
|
+
* table may be the target of a relation), and nothing is emitted twice.
|
|
67
|
+
*/
|
|
68
|
+
export declare function planCollectionSchemaEnsure(collections: CollectionConfig[], existing: ExistingSchema): EnsurePlan;
|
|
69
|
+
/** Read what the database has, for the schemas the collections live in. */
|
|
70
|
+
export declare function readExistingSchema(client: Queryable, schemas: string[]): Promise<ExistingSchema>;
|
|
71
|
+
/**
|
|
72
|
+
* Bring the database up to date. Returns what it did.
|
|
73
|
+
*
|
|
74
|
+
* Each statement runs on its own rather than in one transaction: they are all
|
|
75
|
+
* independently safe and idempotent, and a single failure (an enum label that
|
|
76
|
+
* cannot be added, say) should not roll back the tables that were created fine.
|
|
77
|
+
* The error is surfaced with the statement that caused it.
|
|
78
|
+
*/
|
|
79
|
+
export declare function ensureCollectionTables(client: Queryable, collections: CollectionConfig[], log?: (message: string) => void): Promise<EnsurePlan>;
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import { CollectionConfig } from "@rebasepro/types";
|
|
1
|
+
import { CollectionConfig, Property } from "@rebasepro/types";
|
|
2
|
+
export declare const resolveColumnName: (propName: string, prop?: Property | null) => string;
|
|
3
|
+
export declare const isIdProperty: (propName: string, prop: Property, collection: CollectionConfig) => boolean;
|
|
4
|
+
export declare const getSqlColumnType: (propName: string, prop: Property, collection: CollectionConfig, collections: CollectionConfig[]) => string;
|
|
2
5
|
export declare const generatePostgresDdl: (collections: CollectionConfig[], options?: {
|
|
3
6
|
includePolicies?: boolean;
|
|
4
7
|
}) => Promise<string>;
|
|
@@ -32,11 +32,6 @@ export interface TableMeta {
|
|
|
32
32
|
fks: ForeignKeyRow[];
|
|
33
33
|
}
|
|
34
34
|
export declare function singularize(word: string): string;
|
|
35
|
-
/**
|
|
36
|
-
* Convert a snake_case name to a human-readable Title Case label.
|
|
37
|
-
* e.g. "created_at" -> "Created At", "customer_id" -> "Customer Id"
|
|
38
|
-
*/
|
|
39
|
-
export declare function humanize(snakeName: string): string;
|
|
40
35
|
/**
|
|
41
36
|
* Convert a snake_case table name to a camelCase + "Collection" variable name.
|
|
42
37
|
* e.g. "company_token" -> "companyTokenCollection"
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Naming helpers shared by the introspection modules. These live apart from
|
|
3
|
+
* `introspect-db-logic.ts` because the inference pass needs them too, and
|
|
4
|
+
* importing them from there would close a cycle back through this module.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Convert a snake_case name to a human-readable Title Case label.
|
|
8
|
+
* e.g. "created_at" -> "Created At", "customer_id" -> "Customer Id"
|
|
9
|
+
*/
|
|
10
|
+
export declare function humanize(snakeName: string): string;
|
|
@@ -26,6 +26,14 @@ export interface PolicyRef {
|
|
|
26
26
|
hasUsing: boolean;
|
|
27
27
|
/** Whether a WITH CHECK clause is present at all (not what it says). */
|
|
28
28
|
hasWithCheck: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* The live clause text, when read from `pg_policies`. Present only for live
|
|
31
|
+
* policies (the expected side is parsed from DDL and does not carry it).
|
|
32
|
+
* Used solely for the insecure-tautology scan, not for divergence — Postgres
|
|
33
|
+
* rewrites this text, so it is not safe to diff against expected.
|
|
34
|
+
*/
|
|
35
|
+
qual?: string | null;
|
|
36
|
+
withCheck?: string | null;
|
|
29
37
|
}
|
|
30
38
|
export interface PolicyDrift {
|
|
31
39
|
/** Described by the collections, absent from the database. */
|
|
@@ -38,6 +46,22 @@ export interface PolicyDrift {
|
|
|
38
46
|
actual: PolicyRef;
|
|
39
47
|
differences: string[];
|
|
40
48
|
}[];
|
|
49
|
+
/**
|
|
50
|
+
* A live policy whose expression is the known-permissive tautology
|
|
51
|
+
* `auth.uid() IS NOT NULL` — true for anonymous visitors too, because the
|
|
52
|
+
* user path coerces a blank id to the `'anonymous'` sentinel. This is what
|
|
53
|
+
* `policy.authenticated()` used to compile to, so a database pushed before
|
|
54
|
+
* that fix carries it, and neither the name, roles, command nor clause
|
|
55
|
+
* *presence* differs from the corrected policy — the only thing that changed
|
|
56
|
+
* is the expression text, which this checker otherwise (correctly) ignores.
|
|
57
|
+
* So it is the one drift that hides from every other check here.
|
|
58
|
+
*
|
|
59
|
+
* @see reason a sentence naming the clause and what to do.
|
|
60
|
+
*/
|
|
61
|
+
insecure: {
|
|
62
|
+
policy: PolicyRef;
|
|
63
|
+
reason: string;
|
|
64
|
+
}[];
|
|
41
65
|
}
|
|
42
66
|
export interface Queryable {
|
|
43
67
|
query<R>(text: string, values?: unknown[]): Promise<{
|
|
@@ -51,7 +51,7 @@ export interface ConnectionPosture {
|
|
|
51
51
|
privileged: boolean;
|
|
52
52
|
}
|
|
53
53
|
export interface AuthContext {
|
|
54
|
-
|
|
54
|
+
uid: string;
|
|
55
55
|
/** Raw roles as carried on the user (strings or `{ id }` objects). */
|
|
56
56
|
roles: unknown[];
|
|
57
57
|
}
|
|
@@ -93,7 +93,7 @@ export declare function ensureAppRole(run: RawSqlRunner, schemas: string[]): Pro
|
|
|
93
93
|
* SECURITY: this function is only ever called on the **user** path (the server
|
|
94
94
|
* context uses the base/owner driver and never calls it). The default policies
|
|
95
95
|
* treat `auth.uid() IS NULL` as the trusted server context, and `auth.uid()`
|
|
96
|
-
* is `NULLIF(current_setting('app.
|
|
96
|
+
* is `NULLIF(current_setting('app.uid'), '')` — so an EMPTY user id would
|
|
97
97
|
* be read as NULL and silently escalate a user request to server privileges.
|
|
98
98
|
* Coerce empty/blank ids to `ANONYMOUS_USER_ID` here, at the single chokepoint,
|
|
99
99
|
* rather than trusting every caller (e.g. realtime subscription auth) to do it.
|
|
@@ -23,19 +23,14 @@ export declare function parseCdcPayload(payload: string): CdcChangeEvent | null;
|
|
|
23
23
|
/**
|
|
24
24
|
* Dedicated Postgres LISTEN client for database-level CDC.
|
|
25
25
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* A {@link PgNotifyListener} — a connection outside the Drizzle pool that stays
|
|
27
|
+
* open and repairs itself — plus the parsing that turns a `rebase_cdc` payload
|
|
28
|
+
* into a change event. Each backend instance runs one, so every instance
|
|
29
|
+
* observes every committed change regardless of which instance (or external
|
|
30
|
+
* process) made the write.
|
|
31
31
|
*/
|
|
32
32
|
export declare class CdcListener {
|
|
33
|
-
private readonly
|
|
34
|
-
private readonly onEvent;
|
|
35
|
-
private client?;
|
|
36
|
-
private running;
|
|
37
|
-
private reconnectTimer?;
|
|
38
|
-
private static readonly RECONNECT_DELAY_MS;
|
|
33
|
+
private readonly listener;
|
|
39
34
|
constructor(connectionString: string, onEvent: (event: CdcChangeEvent) => void | Promise<void>);
|
|
40
35
|
/**
|
|
41
36
|
* Connect and begin listening. Idempotent.
|
|
@@ -44,11 +39,9 @@ export declare class CdcListener {
|
|
|
44
39
|
* established (or `LISTEN` is refused), this rejects so callers — notably
|
|
45
40
|
* `REALTIME_CDC=auto` — can detect an unusable connection and fall back to
|
|
46
41
|
* app-level realtime. Once the initial connection succeeds, later drops
|
|
47
|
-
* self-heal
|
|
42
|
+
* self-heal in the background.
|
|
48
43
|
*/
|
|
49
44
|
start(): Promise<void>;
|
|
50
45
|
/** Stop listening and release the connection. */
|
|
51
46
|
stop(): Promise<void>;
|
|
52
|
-
private connect;
|
|
53
|
-
private scheduleReconnect;
|
|
54
47
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime pieces of the channel bus that are not the contract itself.
|
|
3
|
+
*
|
|
4
|
+
* The interface, the frame shape and the implementer's contract live in
|
|
5
|
+
* `@rebasepro/types` (`types/channel_bus.ts`), so a transport shipped as its own
|
|
6
|
+
* package — a Redis one, say — depends on the contract and not on this database
|
|
7
|
+
* adapter. They are re-exported here for convenience: code already importing
|
|
8
|
+
* from the adapter should not have to know where the types are declared.
|
|
9
|
+
*/
|
|
10
|
+
import type { ChannelBusFrame } from "@rebasepro/types";
|
|
11
|
+
export type { ChannelBus, ChannelBusFrame, ChannelBusHandler, ChannelBusConfig, ChannelBusSetting } from "@rebasepro/types";
|
|
12
|
+
export { isChannelBusInstance } from "@rebasepro/types";
|
|
13
|
+
/**
|
|
14
|
+
* The default: no cross-instance delivery at all.
|
|
15
|
+
*
|
|
16
|
+
* This is what every deployment ran before the bus existed, and what a
|
|
17
|
+
* single-instance deployment should keep running — `publish` resolves without
|
|
18
|
+
* touching the network, so the broadcast path is the same handful of `ws.send`
|
|
19
|
+
* calls it always was.
|
|
20
|
+
*/
|
|
21
|
+
export declare class MemoryChannelBus {
|
|
22
|
+
readonly kind: "memory";
|
|
23
|
+
readonly maxFrameBytes: number;
|
|
24
|
+
start(): Promise<void>;
|
|
25
|
+
publish(): Promise<void>;
|
|
26
|
+
stop(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/** Encoded size of a frame, for a transport's size check. */
|
|
29
|
+
export declare function frameByteLength(frame: ChannelBusFrame): number;
|