@rebasepro/server-postgres 0.10.1-canary.ed8caed → 0.10.1-canary.gdaf6ba4

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 (42) hide show
  1. package/dist/auth/schema-version.d.ts +106 -0
  2. package/dist/collections/validate-relations.d.ts +53 -0
  3. package/dist/data-transformer.d.ts +3 -3
  4. package/dist/{ensure-collection-tables-BVvtkkRm.js → ensure-collection-tables-DGMYK0fr.js} +5 -5
  5. package/dist/ensure-collection-tables-DGMYK0fr.js.map +1 -0
  6. package/dist/index.es.js +610 -313
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/services/RelationService.d.ts +9 -8
  9. package/dist/services/cdc/junction-tables.d.ts +38 -0
  10. package/dist/services/nested-path.d.ts +3 -3
  11. package/dist/services/realtimeService.d.ts +19 -0
  12. package/dist/services/row-pipeline.d.ts +2 -2
  13. package/dist/{src-lcfUP4xg.js → src-3VmUJ8Xn.js} +135 -247
  14. package/dist/src-3VmUJ8Xn.js.map +1 -0
  15. package/dist/{src-CBgtrPhJ.js → src-D5xBTl32.js} +12 -2
  16. package/dist/src-D5xBTl32.js.map +1 -0
  17. package/dist/utils/drizzle-conditions.d.ts +35 -23
  18. package/package.json +6 -6
  19. package/src/PostgresBootstrapper.ts +15 -2
  20. package/src/auth/ensure-tables.ts +23 -0
  21. package/src/auth/schema-version.ts +260 -0
  22. package/src/cli-errors.ts +1 -1
  23. package/src/cli-helpers.ts +4 -3
  24. package/src/collections/PostgresCollectionRegistry.ts +9 -4
  25. package/src/collections/buildRegistry.ts +7 -0
  26. package/src/collections/validate-relations.ts +280 -0
  27. package/src/data-transformer.ts +28 -38
  28. package/src/schema/doctor.ts +8 -8
  29. package/src/schema/generate-drizzle-schema-logic.ts +49 -101
  30. package/src/schema/generate-postgres-ddl-logic.ts +9 -9
  31. package/src/schema/introspect-db-logic.ts +19 -23
  32. package/src/services/FetchService.ts +12 -12
  33. package/src/services/PersistService.ts +23 -27
  34. package/src/services/RelationService.ts +65 -83
  35. package/src/services/cdc/junction-tables.ts +91 -0
  36. package/src/services/nested-path.ts +24 -9
  37. package/src/services/realtimeService.ts +60 -0
  38. package/src/services/row-pipeline.ts +5 -6
  39. package/src/utils/drizzle-conditions.ts +177 -382
  40. package/dist/ensure-collection-tables-BVvtkkRm.js.map +0 -1
  41. package/dist/src-CBgtrPhJ.js.map +0 -1
  42. package/dist/src-lcfUP4xg.js.map +0 -1
@@ -1,5 +1,6 @@
1
1
  import { DrizzleClient } from "../interfaces";
2
- import { CollectionConfig, FilterValues, Relation } from "@rebasepro/types";
2
+ import { CollectionConfig, FilterValues, ResolvedRelation, ResolvedManyToMany } from "@rebasepro/types";
3
+ import { type ResolvedVia } from "@rebasepro/types";
3
4
  import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
4
5
  import type { NestedPathHop } from "./nested-path";
5
6
  /**
@@ -70,7 +71,7 @@ export declare class RelationService {
70
71
  /**
71
72
  * Fetch rows using join paths for complex relations
72
73
  */
73
- fetchEntitiesUsingJoins<M extends Record<string, unknown>>(parentCollection: CollectionConfig, parentId: string | number, relation: Relation, options?: {
74
+ fetchEntitiesUsingJoins<M extends Record<string, unknown>>(parentCollection: CollectionConfig, parentId: string | number, relation: ResolvedRelation, options?: {
74
75
  filter?: FilterValues<Extract<keyof M, string>>;
75
76
  orderBy?: string;
76
77
  order?: "desc" | "asc";
@@ -118,13 +119,13 @@ export declare class RelationService {
118
119
  /**
119
120
  * Batch fetch related rows for multiple parent rows to avoid N+1 queries
120
121
  */
121
- batchFetchRelatedEntities(parentCollectionPath: string, parentIds: (string | number)[], _relationKey: string, relation: Relation): Promise<Map<string, RelatedRow<Record<string, unknown>>>>;
122
+ batchFetchRelatedEntities(parentCollectionPath: string, parentIds: (string | number)[], _relationKey: string, relation: ResolvedRelation): Promise<Map<string, RelatedRow<Record<string, unknown>>>>;
122
123
  /**
123
124
  * Batch fetch many-cardinality related rows for multiple parent rows.
124
125
  * Returns a Map<parentId, RelatedRow[]> instead of Map<parentId, RelatedRow>.
125
126
  * Uses a single SQL query with IN clause to avoid N+1.
126
127
  */
127
- batchFetchRelatedEntitiesMany(parentCollectionPath: string, parentIds: (string | number)[], _relationKey: string, relation: Relation): Promise<Map<string, RelatedRow<Record<string, unknown>>[]>>;
128
+ batchFetchRelatedEntitiesMany(parentCollectionPath: string, parentIds: (string | number)[], _relationKey: string, relation: ResolvedRelation): Promise<Map<string, RelatedRow<Record<string, unknown>>[]>>;
128
129
  /**
129
130
  * Update many-to-many and junction relations
130
131
  */
@@ -134,7 +135,7 @@ export declare class RelationService {
134
135
  */
135
136
  updateInverseRelations(tx: DrizzleClient, sourceCollection: CollectionConfig, sourceEntityId: string | number, inverseRelationUpdates: Array<{
136
137
  relationKey: string;
137
- relation: Relation;
138
+ relation: ResolvedRelation;
138
139
  newValue: unknown;
139
140
  }>): Promise<void>;
140
141
  /**
@@ -150,13 +151,13 @@ export declare class RelationService {
150
151
  */
151
152
  updateJoinPathOneToOneRelations(tx: DrizzleClient, parentCollection: CollectionConfig, parentId: string | number, updates: Array<{
152
153
  relationKey: string;
153
- relation: Relation;
154
+ relation: ResolvedVia;
154
155
  newTargetId: string | number | null;
155
156
  }>): Promise<void>;
156
157
  /**
157
158
  * Resolve joinPath write mapping for one-to-one relations
158
159
  */
159
- resolveJoinPathWriteMapping(parentCollection: CollectionConfig, relation: Relation): {
160
+ resolveJoinPathWriteMapping(parentCollection: CollectionConfig, relation: ResolvedVia): {
160
161
  targetFKColName: string;
161
162
  parentSourceColName: string;
162
163
  };
@@ -166,7 +167,7 @@ export declare class RelationService {
166
167
  handleJunctionTableCreation(tx: DrizzleClient, newEntityId: string | number, junctionTableInfo: {
167
168
  parentCollection: CollectionConfig;
168
169
  parentId: string | number;
169
- relation: Relation;
170
+ relation: ResolvedManyToMany;
170
171
  relationKey: string;
171
172
  }): Promise<void>;
172
173
  }
@@ -0,0 +1,38 @@
1
+ import { CollectionConfig } from "@rebasepro/types";
2
+ import { PostgresCollectionRegistry } from "../../collections/PostgresCollectionRegistry";
3
+ /**
4
+ * One end of a many-to-many, as seen from the junction table.
5
+ *
6
+ * A junction table is not a collection, so nothing in the registry maps it to
7
+ * one — which is why a change to it was invisible to change capture. But its
8
+ * rows are exactly the contents of a parent's child list, so a write to it is a
9
+ * change to `<parentSlug>/<sourceId>/<relationKey>` and to nothing else.
10
+ */
11
+ export interface JunctionLink {
12
+ schema: string;
13
+ /** The junction table itself, e.g. `posts_tags`. */
14
+ table: string;
15
+ /** The collection whose relation this is, e.g. `posts`. */
16
+ parentCollection: CollectionConfig;
17
+ /** The relation's key — the path segment a child list is addressed by. */
18
+ relationKey: string;
19
+ /** Junction column holding the parent's id. */
20
+ sourceColumn: string;
21
+ /** Junction column holding the target's id. */
22
+ targetColumn: string;
23
+ }
24
+ /**
25
+ * Every junction table reachable from a registered collection, once per
26
+ * relation that uses it.
27
+ *
28
+ * A junction is listed once per *direction* when both sides declare it, because
29
+ * each direction addresses a different child list: `posts/1/tags` and
30
+ * `tags/t/posts` both change when one link is written.
31
+ */
32
+ export declare function collectJunctionLinks(registry: PostgresCollectionRegistry): JunctionLink[];
33
+ /**
34
+ * Index {@link collectJunctionLinks} by table, under both the qualified and the
35
+ * bare name — a change event carries whatever the trigger reports, and a
36
+ * collection need not declare a schema.
37
+ */
38
+ export declare function buildJunctionLinkMap(registry: PostgresCollectionRegistry): Map<string, JunctionLink[]>;
@@ -1,4 +1,4 @@
1
- import { CollectionConfig, Relation } from "@rebasepro/types";
1
+ import { CollectionConfig, ResolvedRelation } from "@rebasepro/types";
2
2
  import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
3
3
  /**
4
4
  * The last hop of a nested collection path, e.g. `authors/1/posts`.
@@ -16,7 +16,7 @@ export interface NestedPathHop {
16
16
  parentId: string;
17
17
  /** The path segment that named the relation (e.g. `posts`). */
18
18
  relationKey: string;
19
- relation: Relation;
19
+ relation: ResolvedRelation;
20
20
  /** `relation.target()`, resolved once. */
21
21
  targetCollection: CollectionConfig;
22
22
  }
@@ -45,7 +45,7 @@ export declare function resolveNestedPath(path: string, registry: PostgresCollec
45
45
  * `joinPath`. The target row is shared with other parents, so writing "through"
46
46
  * such a path addresses the *link*, not the row.
47
47
  */
48
- export declare function isJunctionBackedRelation(relation: Relation): boolean;
48
+ export declare function isJunctionBackedRelation(relation: ResolvedRelation): boolean;
49
49
  /**
50
50
  * Reject a nested write whose final segment is a to-one relation.
51
51
  *
@@ -95,6 +95,8 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
95
95
  private cdcListener?;
96
96
  /** Whether database-level CDC is the active cross-instance change source. */
97
97
  private cdcActive;
98
+ /** Junction table → the child lists its rows belong to, built when CDC starts. */
99
+ private junctionLinkMap?;
98
100
  /** Reverse lookup: `schema.table` (and bare `table`) → collection, built when CDC starts. */
99
101
  private cdcTableMap?;
100
102
  /**
@@ -437,6 +439,23 @@ export declare class RealtimeService extends EventEmitter implements RealtimePro
437
439
  * subscriber, never per publisher.
438
440
  */
439
441
  private handleCdcEvent;
442
+ /**
443
+ * Deliver a change on a many-to-many junction table as a change to the child
444
+ * lists it belongs to.
445
+ *
446
+ * Linking a tag to a post writes only `posts_tags`. That table backs no
447
+ * collection, so change capture dropped the event as unmapped and the
448
+ * subscribers of `posts/1/tags` never heard about it — every other write in
449
+ * the system was realtime, and this one silently was not. The junction row
450
+ * carries both ids, so it names its own paths exactly.
451
+ *
452
+ * Notifies the nested path rather than either endpoint collection, because
453
+ * invalidation walks *parent* paths and never child ones: telling `tags` it
454
+ * changed would not reach a subscription on `posts/1/tags`.
455
+ *
456
+ * Returns whether the table was recognised as a junction.
457
+ */
458
+ private handleJunctionCdcEvent;
440
459
  /** Compute the canonical (possibly composite) id string from a captured row. */
441
460
  private extractIdFromCdcRow;
442
461
  private dedupKey;
@@ -1,4 +1,4 @@
1
- import { CollectionConfig, Relation } from "@rebasepro/types";
1
+ import { CollectionConfig, ResolvedRelation } from "@rebasepro/types";
2
2
  import { PostgresCollectionRegistry } from "../collections/PostgresCollectionRegistry";
3
3
  /**
4
4
  * Turning a drizzle result into a row we serve.
@@ -26,7 +26,7 @@ export type RelationStyle = "ref" | "inline";
26
26
  * the query has to nest one level deeper for a junction, and the row walk has
27
27
  * to unwrap that same level back out.
28
28
  */
29
- export declare function isJunctionRelation(relation: Relation): boolean;
29
+ export declare function isJunctionRelation(relation: ResolvedRelation): boolean;
30
30
  /**
31
31
  * The address a relation ref points at.
32
32
  *