@rebasepro/server-postgresql 0.7.0 → 0.8.0

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 (56) hide show
  1. package/build-errors.txt +37 -0
  2. package/dist/PostgresAdapter.d.ts +6 -0
  3. package/dist/PostgresBackendDriver.d.ts +118 -0
  4. package/dist/PostgresBootstrapper.d.ts +46 -0
  5. package/dist/auth/ensure-tables.d.ts +10 -0
  6. package/dist/auth/services.d.ts +251 -0
  7. package/dist/cli-errors.d.ts +29 -0
  8. package/dist/cli-helpers.d.ts +7 -0
  9. package/dist/cli.d.ts +1 -0
  10. package/dist/collections/PostgresCollectionRegistry.d.ts +47 -0
  11. package/dist/connection.d.ts +65 -0
  12. package/dist/data-transformer.d.ts +55 -0
  13. package/dist/databasePoolManager.d.ts +20 -0
  14. package/dist/history/HistoryService.d.ts +71 -0
  15. package/dist/history/ensure-history-table.d.ts +7 -0
  16. package/dist/index.d.ts +14 -0
  17. package/dist/index.es.js +13560 -0
  18. package/dist/index.es.js.map +1 -0
  19. package/dist/interfaces.d.ts +18 -0
  20. package/dist/schema/auth-default-policies.d.ts +12 -0
  21. package/dist/schema/auth-schema.d.ts +2376 -0
  22. package/dist/schema/doctor-cli.d.ts +2 -0
  23. package/dist/schema/doctor.d.ts +52 -0
  24. package/dist/schema/generate-drizzle-schema-logic.d.ts +2 -0
  25. package/dist/schema/generate-drizzle-schema.d.ts +1 -0
  26. package/dist/schema/generate-postgres-ddl-logic.d.ts +5 -0
  27. package/dist/schema/generate-postgres-ddl.d.ts +1 -0
  28. package/dist/schema/introspect-db-inference.d.ts +5 -0
  29. package/dist/schema/introspect-db-logic.d.ts +118 -0
  30. package/dist/schema/introspect-db.d.ts +1 -0
  31. package/dist/schema/test-schema.d.ts +24 -0
  32. package/dist/services/BranchService.d.ts +47 -0
  33. package/dist/services/EntityFetchService.d.ts +214 -0
  34. package/dist/services/EntityPersistService.d.ts +40 -0
  35. package/dist/services/RelationService.d.ts +98 -0
  36. package/dist/services/entity-helpers.d.ts +38 -0
  37. package/dist/services/entityService.d.ts +110 -0
  38. package/dist/services/index.d.ts +4 -0
  39. package/dist/services/realtimeService.d.ts +220 -0
  40. package/dist/types.d.ts +3 -0
  41. package/dist/utils/drizzle-conditions.d.ts +138 -0
  42. package/dist/utils/pg-array-null-patch.d.ts +16 -0
  43. package/dist/utils/pg-error-utils.d.ts +65 -0
  44. package/dist/utils/table-classification.d.ts +8 -0
  45. package/dist/websocket.d.ts +11 -0
  46. package/package.json +17 -17
  47. package/src/PostgresBackendDriver.ts +135 -24
  48. package/src/cli.ts +73 -1
  49. package/src/collections/PostgresCollectionRegistry.ts +6 -6
  50. package/src/data-transformer.ts +2 -2
  51. package/src/schema/auth-default-policies.ts +13 -6
  52. package/src/schema/generate-drizzle-schema-logic.ts +16 -79
  53. package/src/schema/generate-postgres-ddl-logic.ts +14 -51
  54. package/src/services/realtimeService.ts +26 -2
  55. package/test/entity-callbacks-redaction.test.ts +86 -0
  56. package/test/postgresDataDriver.test.ts +2 -1
@@ -133,6 +133,7 @@ export class PostgresBackendDriver implements DataDriver {
133
133
  if (!collection && !path) return {
134
134
  collection: undefined,
135
135
  callbacks: undefined,
136
+ globalCallbacks: undefined,
136
137
  propertyCallbacks: undefined
137
138
  };
138
139
  const registryCollection = this.registry?.getCollectionByPath(path);
@@ -144,6 +145,7 @@ export class PostgresBackendDriver implements DataDriver {
144
145
  : collection as EntityCollection<M>;
145
146
 
146
147
  const callbacks = resolvedCollection?.callbacks;
148
+ const globalCallbacks = this.registry?.getGlobalCallbacks();
147
149
  const properties = resolvedCollection?.properties;
148
150
  let propertyCallbacks;
149
151
  if (properties) {
@@ -152,6 +154,7 @@ export class PostgresBackendDriver implements DataDriver {
152
154
  return {
153
155
  collection: resolvedCollection,
154
156
  callbacks,
157
+ globalCallbacks,
155
158
  propertyCallbacks
156
159
  };
157
160
  }
@@ -184,13 +187,24 @@ export class PostgresBackendDriver implements DataDriver {
184
187
  const {
185
188
  collection: resolvedCollection,
186
189
  callbacks,
190
+ globalCallbacks,
187
191
  propertyCallbacks
188
192
  } = this.resolveCollectionCallbacks(collection, path);
189
193
 
190
- if (callbacks?.afterRead || propertyCallbacks?.afterRead) {
194
+ if (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead) {
191
195
  const contextForCallback = this.buildCallContext();
192
196
  return Promise.all(entities.map(async (entity) => {
193
197
  let fetched = entity;
198
+ // 1. Global callbacks first
199
+ if (globalCallbacks?.afterRead) {
200
+ fetched = await globalCallbacks.afterRead({
201
+ collection: resolvedCollection as unknown as EntityCollection,
202
+ path,
203
+ entity: fetched as unknown as Entity<Record<string, unknown>>,
204
+ context: contextForCallback
205
+ }) as unknown as Entity<M>;
206
+ }
207
+ // 2. Collection callbacks second
194
208
  if (callbacks?.afterRead) {
195
209
  fetched = await callbacks.afterRead({
196
210
  collection: resolvedCollection as EntityCollection<M>,
@@ -199,13 +213,14 @@ export class PostgresBackendDriver implements DataDriver {
199
213
  context: contextForCallback
200
214
  }) ?? fetched;
201
215
  }
216
+ // 3. Property callbacks third
202
217
  if (propertyCallbacks?.afterRead) {
203
218
  fetched = await propertyCallbacks.afterRead({
204
- collection: resolvedCollection as EntityCollection<M>,
219
+ collection: resolvedCollection as unknown as EntityCollection,
205
220
  path,
206
- entity: fetched,
221
+ entity: fetched as unknown as Entity<Record<string, unknown>>,
207
222
  context: contextForCallback
208
- }) as Entity<M> ?? fetched;
223
+ }) as unknown as Entity<M>;
209
224
  }
210
225
  return fetched;
211
226
  }));
@@ -293,26 +308,38 @@ export class PostgresBackendDriver implements DataDriver {
293
308
  const {
294
309
  collection: resolvedCollection,
295
310
  callbacks,
311
+ globalCallbacks,
296
312
  propertyCallbacks
297
313
  } = this.resolveCollectionCallbacks(collection, path);
298
314
 
299
- if (entity && (callbacks?.afterRead || propertyCallbacks?.afterRead)) {
315
+ if (entity && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {
300
316
  const contextForCallback = this.buildCallContext();
317
+ // 1. Global callbacks first
318
+ if (globalCallbacks?.afterRead) {
319
+ entity = await globalCallbacks.afterRead({
320
+ collection: resolvedCollection as unknown as EntityCollection,
321
+ path,
322
+ entity: entity as unknown as Entity<Record<string, unknown>>,
323
+ context: contextForCallback
324
+ }) as unknown as Entity<M>;
325
+ }
326
+ // 2. Collection callbacks second
301
327
  if (callbacks?.afterRead) {
302
328
  entity = await callbacks.afterRead({
303
329
  collection: resolvedCollection as EntityCollection<M>,
304
330
  path,
305
- entity,
331
+ entity: entity!,
306
332
  context: contextForCallback
307
- }) ?? entity;
333
+ });
308
334
  }
335
+ // 3. Property callbacks third
309
336
  if (propertyCallbacks?.afterRead) {
310
337
  entity = await propertyCallbacks.afterRead({
311
- collection: resolvedCollection as EntityCollection<M>,
338
+ collection: resolvedCollection as unknown as EntityCollection,
312
339
  path,
313
- entity,
340
+ entity: entity as unknown as Entity<Record<string, unknown>>,
314
341
  context: contextForCallback
315
- }) as Entity<M> ?? entity;
342
+ }) as unknown as Entity<M>;
316
343
  }
317
344
  }
318
345
 
@@ -375,6 +402,7 @@ export class PostgresBackendDriver implements DataDriver {
375
402
  const {
376
403
  collection: resolvedCollection,
377
404
  callbacks,
405
+ globalCallbacks,
378
406
  propertyCallbacks
379
407
  } = this.resolveCollectionCallbacks(collection, path);
380
408
 
@@ -390,7 +418,22 @@ export class PostgresBackendDriver implements DataDriver {
390
418
  }
391
419
  }
392
420
 
393
- if (callbacks?.beforeSave || propertyCallbacks?.beforeSave) {
421
+ if (globalCallbacks?.beforeSave || callbacks?.beforeSave || propertyCallbacks?.beforeSave) {
422
+ // 1. Global callbacks first
423
+ if (globalCallbacks?.beforeSave) {
424
+ const result = await globalCallbacks.beforeSave({
425
+ collection: resolvedCollection as unknown as EntityCollection,
426
+ path,
427
+ entityId,
428
+ values: updatedValues,
429
+ previousValues: previousValuesForHistory,
430
+ status,
431
+ context: contextForCallback
432
+ });
433
+ if (result) updatedValues = mergeDeep(updatedValues, result);
434
+ }
435
+
436
+ // 2. Collection callbacks second
394
437
  if (callbacks?.beforeSave) {
395
438
  const result = await callbacks.beforeSave({
396
439
  collection: resolvedCollection as EntityCollection<M>,
@@ -404,9 +447,10 @@ export class PostgresBackendDriver implements DataDriver {
404
447
  if (result) updatedValues = mergeDeep(updatedValues, result);
405
448
  }
406
449
 
450
+ // 3. Property callbacks third
407
451
  if (propertyCallbacks?.beforeSave) {
408
452
  const result = await propertyCallbacks.beforeSave({
409
- collection: resolvedCollection as EntityCollection<M>,
453
+ collection: resolvedCollection as unknown as EntityCollection,
410
454
  path,
411
455
  entityId,
412
456
  values: updatedValues,
@@ -438,7 +482,17 @@ export class PostgresBackendDriver implements DataDriver {
438
482
  resolvedCollection?.databaseId
439
483
  );
440
484
 
441
- if (savedEntity && (callbacks?.afterRead || propertyCallbacks?.afterRead)) {
485
+ if (savedEntity && (globalCallbacks?.afterRead || callbacks?.afterRead || propertyCallbacks?.afterRead)) {
486
+ // 1. Global callbacks first
487
+ if (globalCallbacks?.afterRead) {
488
+ savedEntity = await globalCallbacks.afterRead({
489
+ collection: resolvedCollection as unknown as EntityCollection,
490
+ path,
491
+ entity: savedEntity as unknown as Entity<Record<string, unknown>>,
492
+ context: contextForCallback
493
+ }) as unknown as Entity<M>;
494
+ }
495
+ // 2. Collection callbacks second
442
496
  if (callbacks?.afterRead) {
443
497
  savedEntity = await callbacks.afterRead({
444
498
  collection: resolvedCollection as EntityCollection<M>,
@@ -447,17 +501,31 @@ export class PostgresBackendDriver implements DataDriver {
447
501
  context: contextForCallback
448
502
  }) ?? savedEntity;
449
503
  }
504
+ // 3. Property callbacks third
450
505
  if (propertyCallbacks?.afterRead) {
451
506
  savedEntity = await propertyCallbacks.afterRead({
452
- collection: resolvedCollection as EntityCollection<M>,
507
+ collection: resolvedCollection as unknown as EntityCollection,
453
508
  path,
454
- entity: savedEntity,
509
+ entity: savedEntity as unknown as Entity<Record<string, unknown>>,
455
510
  context: contextForCallback
456
- }) as Entity<M> ?? savedEntity;
511
+ }) as unknown as Entity<M>;
457
512
  }
458
513
  }
459
514
 
460
- if (callbacks?.afterSave || propertyCallbacks?.afterSave) {
515
+ if (globalCallbacks?.afterSave || callbacks?.afterSave || propertyCallbacks?.afterSave) {
516
+ // 1. Global callbacks first
517
+ if (globalCallbacks?.afterSave) {
518
+ await globalCallbacks.afterSave({
519
+ collection: resolvedCollection as unknown as EntityCollection,
520
+ path,
521
+ entityId: savedEntity.id,
522
+ values: savedEntity.values,
523
+ previousValues: previousValuesForHistory,
524
+ status,
525
+ context: contextForCallback
526
+ });
527
+ }
528
+ // 2. Collection callbacks second
461
529
  if (callbacks?.afterSave) {
462
530
  await callbacks.afterSave({
463
531
  collection: resolvedCollection as EntityCollection<M>,
@@ -469,9 +537,10 @@ export class PostgresBackendDriver implements DataDriver {
469
537
  context: contextForCallback
470
538
  });
471
539
  }
540
+ // 3. Property callbacks third
472
541
  if (propertyCallbacks?.afterSave) {
473
542
  await propertyCallbacks.afterSave({
474
- collection: resolvedCollection as EntityCollection<M>,
543
+ collection: resolvedCollection as unknown as EntityCollection,
475
544
  path,
476
545
  entityId: savedEntity.id,
477
546
  values: savedEntity.values,
@@ -513,7 +582,20 @@ export class PostgresBackendDriver implements DataDriver {
513
582
 
514
583
  return savedEntity;
515
584
  } catch (error) {
516
- if (callbacks?.afterSaveError || propertyCallbacks?.afterSaveError) {
585
+ if (globalCallbacks?.afterSaveError || callbacks?.afterSaveError || propertyCallbacks?.afterSaveError) {
586
+ // 1. Global callbacks first
587
+ if (globalCallbacks?.afterSaveError) {
588
+ await globalCallbacks.afterSaveError({
589
+ collection: resolvedCollection as unknown as EntityCollection,
590
+ path,
591
+ entityId: entityId || "unknown",
592
+ values: updatedValues,
593
+ previousValues: undefined,
594
+ status,
595
+ context: contextForCallback
596
+ });
597
+ }
598
+ // 2. Collection callbacks second
517
599
  if (callbacks?.afterSaveError) {
518
600
  await callbacks.afterSaveError({
519
601
  collection: resolvedCollection as EntityCollection<M>,
@@ -525,9 +607,10 @@ export class PostgresBackendDriver implements DataDriver {
525
607
  context: contextForCallback
526
608
  });
527
609
  }
610
+ // 3. Property callbacks third
528
611
  if (propertyCallbacks?.afterSaveError) {
529
612
  await propertyCallbacks.afterSaveError({
530
- collection: resolvedCollection as EntityCollection<M>,
613
+ collection: resolvedCollection as unknown as EntityCollection,
531
614
  path,
532
615
  entityId: entityId || "unknown",
533
616
  values: updatedValues,
@@ -550,13 +633,28 @@ export class PostgresBackendDriver implements DataDriver {
550
633
  const {
551
634
  collection: resolvedCollection,
552
635
  callbacks,
636
+ globalCallbacks,
553
637
  propertyCallbacks
554
638
  } = this.resolveCollectionCallbacks(collection, entity.path);
555
639
 
556
640
  const contextForCallback = this.buildCallContext();
557
641
 
558
- if (callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {
642
+ if (globalCallbacks?.beforeDelete || callbacks?.beforeDelete || propertyCallbacks?.beforeDelete) {
559
643
  let preventDefault = false;
644
+ // 1. Global callbacks first
645
+ if (globalCallbacks?.beforeDelete) {
646
+ const result = await globalCallbacks.beforeDelete({
647
+ collection: resolvedCollection as unknown as EntityCollection,
648
+ path: entity.path,
649
+ entityId: entity.id,
650
+ entity,
651
+ context: contextForCallback
652
+ });
653
+ if (result === false) {
654
+ preventDefault = true;
655
+ }
656
+ }
657
+ // 2. Collection callbacks second
560
658
  if (callbacks?.beforeDelete) {
561
659
  const result = await callbacks.beforeDelete({
562
660
  collection: resolvedCollection as EntityCollection<M>,
@@ -569,9 +667,10 @@ export class PostgresBackendDriver implements DataDriver {
569
667
  preventDefault = true;
570
668
  }
571
669
  }
670
+ // 3. Property callbacks third
572
671
  if (propertyCallbacks?.beforeDelete) {
573
672
  const result = await propertyCallbacks.beforeDelete({
574
- collection: resolvedCollection as EntityCollection<M>,
673
+ collection: resolvedCollection as unknown as EntityCollection,
575
674
  path: entity.path,
576
675
  entityId: entity.id,
577
676
  entity,
@@ -592,7 +691,18 @@ export class PostgresBackendDriver implements DataDriver {
592
691
  entity.databaseId || resolvedCollection?.databaseId
593
692
  );
594
693
 
595
- if (callbacks?.afterDelete || propertyCallbacks?.afterDelete) {
694
+ if (globalCallbacks?.afterDelete || callbacks?.afterDelete || propertyCallbacks?.afterDelete) {
695
+ // 1. Global callbacks first
696
+ if (globalCallbacks?.afterDelete) {
697
+ await globalCallbacks.afterDelete({
698
+ collection: resolvedCollection as unknown as EntityCollection,
699
+ path: entity.path,
700
+ entityId: entity.id,
701
+ entity,
702
+ context: contextForCallback
703
+ });
704
+ }
705
+ // 2. Collection callbacks second
596
706
  if (callbacks?.afterDelete) {
597
707
  await callbacks.afterDelete({
598
708
  collection: resolvedCollection as EntityCollection<M>,
@@ -602,9 +712,10 @@ export class PostgresBackendDriver implements DataDriver {
602
712
  context: contextForCallback
603
713
  });
604
714
  }
715
+ // 3. Property callbacks third
605
716
  if (propertyCallbacks?.afterDelete) {
606
717
  await propertyCallbacks.afterDelete({
607
- collection: resolvedCollection as EntityCollection<M>,
718
+ collection: resolvedCollection as unknown as EntityCollection,
608
719
  path: entity.path,
609
720
  entityId: entity.id,
610
721
  entity,
package/src/cli.ts CHANGED
@@ -18,7 +18,36 @@ const __cliDirname = path.dirname(fileURLToPath(import.meta.url));
18
18
 
19
19
 
20
20
 
21
+ async function loadEnv(): Promise<void> {
22
+ try {
23
+ const dotenv = await import("dotenv");
24
+ const envPaths = [
25
+ process.env.DOTENV_CONFIG_PATH,
26
+ path.resolve(process.cwd(), ".env"),
27
+ path.resolve(process.cwd(), "../.env"),
28
+ path.resolve(process.cwd(), "../../.env")
29
+ ].filter(Boolean) as string[];
30
+
31
+ for (const p of envPaths) {
32
+ if (fs.existsSync(p)) {
33
+ const parsed = dotenv.config({ path: p });
34
+ if (parsed.parsed) {
35
+ for (const [key, val] of Object.entries(parsed.parsed)) {
36
+ if (process.env[key] === undefined) {
37
+ process.env[key] = val;
38
+ }
39
+ }
40
+ break;
41
+ }
42
+ }
43
+ }
44
+ } catch {
45
+ // ignore
46
+ }
47
+ }
48
+
21
49
  export async function runPluginCommand(args: string[]) {
50
+ await loadEnv();
22
51
  const domain = args[0]; // "db" or "schema"
23
52
  const subcommand = args[1];
24
53
 
@@ -126,16 +155,23 @@ async function dbCommand(subcommand: string, rawArgs: string[]): Promise<void> {
126
155
  logger.info("");
127
156
  logger.info(chalk.gray(" Step 2/3: Pushing schema to database with Atlas..."));
128
157
  logger.info("");
158
+ const databaseUrl = process.env.DATABASE_URL;
159
+ if (databaseUrl) {
160
+ await ensureAuthSchemaAndFunctions(databaseUrl);
161
+ }
129
162
  await runAtlas("schema", ["apply", "--to", "file://drizzle/schema.sql", "--auto-approve"], collectionsPath);
130
163
  logger.info("");
131
164
 
132
- const databaseUrl = process.env.DATABASE_URL;
133
165
  if (databaseUrl) {
134
166
  await applyPolicies(databaseUrl);
135
167
  } else {
136
168
  logger.warn(chalk.yellow(" ⚠️ DATABASE_URL not found in environment, skipping RLS policies application."));
137
169
  }
138
170
  } else if (subcommand === "migrate") {
171
+ const databaseUrl = process.env.DATABASE_URL;
172
+ if (databaseUrl) {
173
+ await ensureAuthSchemaAndFunctions(databaseUrl);
174
+ }
139
175
  const extraArgs = argsList._.filter(arg => arg !== "migrate");
140
176
  await runAtlas("migrate", ["apply", "--dir", "file://drizzle/migrations", ...extraArgs], collectionsPath);
141
177
  }
@@ -146,6 +182,39 @@ async function dbCommand(subcommand: string, rawArgs: string[]): Promise<void> {
146
182
  }
147
183
  }
148
184
 
185
+ async function ensureAuthSchemaAndFunctions(databaseUrl: string): Promise<void> {
186
+ try {
187
+ const { Client } = await import("pg");
188
+ const client = new Client({ connectionString: databaseUrl });
189
+ await client.connect();
190
+ try {
191
+ await client.query(`
192
+ CREATE SCHEMA IF NOT EXISTS auth;
193
+ CREATE SCHEMA IF NOT EXISTS rebase;
194
+
195
+ CREATE OR REPLACE FUNCTION auth.uid() RETURNS text AS $$
196
+ SELECT NULLIF(current_setting('app.user_id', true), '');
197
+ $$ LANGUAGE sql STABLE;
198
+
199
+ CREATE OR REPLACE FUNCTION auth.jwt() RETURNS jsonb AS $$
200
+ SELECT COALESCE(
201
+ NULLIF(current_setting('app.jwt', true), ''),
202
+ '{}'
203
+ )::jsonb;
204
+ $$ LANGUAGE sql STABLE;
205
+
206
+ CREATE OR REPLACE FUNCTION auth.roles() RETURNS text AS $$
207
+ SELECT COALESCE(NULLIF(current_setting('app.user_roles', true), ''), '');
208
+ $$ LANGUAGE sql STABLE;
209
+ `);
210
+ } finally {
211
+ await client.end();
212
+ }
213
+ } catch (err) {
214
+ logger.warn(chalk.yellow(` ⚠️ Failed to bootstrap auth schema and helper functions: ${err instanceof Error ? err.message : String(err)}`));
215
+ }
216
+ }
217
+
149
218
  async function applyPolicies(databaseUrl: string): Promise<void> {
150
219
  try {
151
220
  const policiesPath = path.resolve(process.cwd(), "drizzle", "policies.sql");
@@ -426,6 +495,9 @@ async function runAtlas(domain: "schema" | "migrate", args: string[], collection
426
495
  atlasArgs.push("--dev-url", devDatabaseUrl);
427
496
  } else if (args.includes("apply") || args.includes("status")) {
428
497
  atlasArgs.push("--url", databaseUrl, "--revisions-schema", "rebase");
498
+ if (args.includes("apply")) {
499
+ atlasArgs.push("--allow-dirty");
500
+ }
429
501
  }
430
502
  }
431
503
 
@@ -1,5 +1,5 @@
1
1
  import { CollectionRegistry } from "@rebasepro/common";
2
- import { type CollectionWithRelations, type EntityCollection, type Relation, getDataSourceCapabilities } from "@rebasepro/types";
2
+ import { type EntityCollection, type Relation, getDataSourceCapabilities } from "@rebasepro/types";
3
3
  import { PgEnum, PgTable } from "drizzle-orm/pg-core";
4
4
  import { Relations } from "drizzle-orm";
5
5
  import { CollectionRegistryInterface } from "../interfaces";
@@ -40,11 +40,11 @@ export class PostgresCollectionRegistry extends CollectionRegistry implements Co
40
40
  }
41
41
 
42
42
  /**
43
- * Finds collections assigned to a specific driver that do not have a registered table.
43
+ * Finds collections assigned to a specific data source that do not have a registered table.
44
44
  */
45
- getCollectionsWithoutTables(driverId = "(default)"): EntityCollection[] {
45
+ getCollectionsWithoutTables(dataSourceKey = "(default)"): EntityCollection[] {
46
46
  const collections = this.getCollections().filter(
47
- c => c.driver === driverId || (!c.driver && driverId === "(default)")
47
+ c => c.dataSource === dataSourceKey || (!c.dataSource && dataSourceKey === "(default)")
48
48
  );
49
49
  return collections.filter(c => !this.tables.has(getTableName(c)));
50
50
  }
@@ -95,8 +95,8 @@ export class PostgresCollectionRegistry extends CollectionRegistry implements Co
95
95
  */
96
96
  getRelationKeysForCollection(collectionPath: string): string[] {
97
97
  const collection = this.getCollectionByPath(collectionPath);
98
- if (!collection || !getDataSourceCapabilities(collection.driver).supportsRelations || !(collection as CollectionWithRelations).relations) return [];
99
- return (collection as CollectionWithRelations).relations!.map((r: Relation) => r.relationName || r.localKey || "").filter(Boolean);
98
+ if (!collection || !getDataSourceCapabilities(collection.engine).supportsRelations || !collection.relations) return [];
99
+ return collection.relations!.map((r: Relation) => r.relationName || r.localKey || "").filter(Boolean);
100
100
  }
101
101
 
102
102
  }
@@ -1,7 +1,7 @@
1
1
  import { eq, SQL } from "drizzle-orm";
2
2
  import { AnyPgColumn } from "drizzle-orm/pg-core";
3
3
  import { NodePgDatabase } from "drizzle-orm/node-postgres";
4
- import { CollectionWithRelations, EntityCollection, Properties, Property, Relation, RelationProperty, Vector, BinaryProperty } from "@rebasepro/types";
4
+ import { EntityCollection, Properties, Property, Relation, RelationProperty, Vector, BinaryProperty } from "@rebasepro/types";
5
5
  import { getTableName, resolveCollectionRelations, findRelation, createRelationRef, DEFAULT_ONE_OF_TYPE, DEFAULT_ONE_OF_VALUE } from "@rebasepro/common";
6
6
  import { PostgresCollectionRegistry } from "./collections/PostgresCollectionRegistry";
7
7
  import { DrizzleConditionBuilder } from "./utils/drizzle-conditions";
@@ -551,7 +551,7 @@ export function parsePropertyFromServer(value: unknown, property: Property, coll
551
551
  relationDef = findRelation(resolvedRelations, propertyKey);
552
552
  }
553
553
  if (!relationDef) {
554
- relationDef = (collection as CollectionWithRelations).relations?.find((rel: Relation) => rel.relationName === (property as RelationProperty).relationName);
554
+ relationDef = collection.relations?.find((rel: Relation) => rel.relationName === (property as RelationProperty).relationName);
555
555
  }
556
556
 
557
557
  if (!relationDef) {
@@ -1,4 +1,4 @@
1
- import { EntityCollection, SecurityRule, SecurityOperation, AuthCollectionConfig, isPostgresCollection } from "@rebasepro/types";
1
+ import { EntityCollection, SecurityRule, SecurityOperation, AuthCollectionConfig, PolicyExpression, isPostgresCollection, policy } from "@rebasepro/types";
2
2
  import { getTableName } from "@rebasepro/common";
3
3
 
4
4
  /**
@@ -37,7 +37,14 @@ import { getTableName } from "@rebasepro/common";
37
37
  * control of an auth collection's write authorization, set
38
38
  * `disableDefaultAuthPolicies: true` on the collection.
39
39
  */
40
- const ADMIN_WRITE_EXPR = "auth.uid() IS NULL OR string_to_array(auth.roles(), ',') && ARRAY['admin']";
40
+ // Expressed structurally (not as raw SQL) so the admin UI can evaluate it
41
+ // exactly — the framework's most security-critical policy must be reflected
42
+ // precisely, not left as an un-evaluable raw clause. Compiles to
43
+ // `auth.uid() IS NULL OR (string_to_array(auth.roles(), ',') && ARRAY['admin'])`.
44
+ const ADMIN_WRITE_EXPR: PolicyExpression = policy.or(
45
+ policy.not(policy.authenticated()),
46
+ policy.rolesOverlap(["admin"])
47
+ );
41
48
 
42
49
  /** Write operations that must be admin-gated by default on auth collections. */
43
50
  const DEFAULT_GUARDED_OPS: SecurityOperation[] = ["insert", "update", "delete"];
@@ -73,8 +80,8 @@ export function getEffectiveSecurityRules(collection: EntityCollection): Securit
73
80
  name: `${tableName}_require_admin_write`,
74
81
  mode: "restrictive",
75
82
  operations: [...DEFAULT_GUARDED_OPS],
76
- using: ADMIN_WRITE_EXPR,
77
- withCheck: ADMIN_WRITE_EXPR
83
+ condition: ADMIN_WRITE_EXPR,
84
+ check: ADMIN_WRITE_EXPR
78
85
  };
79
86
 
80
87
  // Permissive grant: a restrictive policy alone denies everything, so this
@@ -82,8 +89,8 @@ export function getEffectiveSecurityRules(collection: EntityCollection): Securit
82
89
  const allowAdminWrite: SecurityRule = {
83
90
  name: `${tableName}_default_admin_write`,
84
91
  operations: [...DEFAULT_GUARDED_OPS],
85
- using: ADMIN_WRITE_EXPR,
86
- withCheck: ADMIN_WRITE_EXPR
92
+ condition: ADMIN_WRITE_EXPR,
93
+ check: ADMIN_WRITE_EXPR
87
94
  };
88
95
 
89
96
  return [...explicit, allowAdminWrite, requireAdminGate];