@rebasepro/server-postgres 0.10.1-canary.d8d45b2 → 0.10.1-canary.ed78a2c

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 (45) 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-CNlIONzj.js → ensure-collection-tables-DGMYK0fr.js} +12 -12
  5. package/dist/ensure-collection-tables-DGMYK0fr.js.map +1 -0
  6. package/dist/index.es.js +913 -391
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/services/FetchService.d.ts +21 -8
  9. package/dist/services/PersistService.d.ts +12 -0
  10. package/dist/services/RelationService.d.ts +39 -8
  11. package/dist/services/cdc/junction-tables.d.ts +38 -0
  12. package/dist/services/nested-path.d.ts +59 -0
  13. package/dist/services/realtimeService.d.ts +19 -0
  14. package/dist/services/row-pipeline.d.ts +2 -2
  15. package/dist/{src-DmsRg8MR.js → src-3VmUJ8Xn.js} +214 -276
  16. package/dist/src-3VmUJ8Xn.js.map +1 -0
  17. package/dist/{src-B0v4IKaI.js → src-D5xBTl32.js} +19 -2
  18. package/dist/src-D5xBTl32.js.map +1 -0
  19. package/dist/utils/drizzle-conditions.d.ts +71 -18
  20. package/package.json +8 -9
  21. package/src/PostgresBootstrapper.ts +15 -2
  22. package/src/auth/ensure-tables.ts +23 -0
  23. package/src/auth/schema-version.ts +260 -0
  24. package/src/cli-errors.ts +1 -1
  25. package/src/cli-helpers.ts +4 -3
  26. package/src/collections/PostgresCollectionRegistry.ts +9 -4
  27. package/src/collections/buildRegistry.ts +7 -0
  28. package/src/collections/validate-relations.ts +280 -0
  29. package/src/data-transformer.ts +28 -38
  30. package/src/schema/doctor.ts +14 -14
  31. package/src/schema/generate-drizzle-schema-logic.ts +62 -110
  32. package/src/schema/generate-postgres-ddl-logic.ts +28 -21
  33. package/src/schema/introspect-db-inference.ts +13 -13
  34. package/src/schema/introspect-db-logic.ts +25 -29
  35. package/src/services/FetchService.ts +116 -126
  36. package/src/services/PersistService.ts +126 -88
  37. package/src/services/RelationService.ts +157 -86
  38. package/src/services/cdc/junction-tables.ts +91 -0
  39. package/src/services/nested-path.ts +145 -0
  40. package/src/services/realtimeService.ts +60 -0
  41. package/src/services/row-pipeline.ts +5 -6
  42. package/src/utils/drizzle-conditions.ts +268 -330
  43. package/dist/ensure-collection-tables-CNlIONzj.js.map +0 -1
  44. package/dist/src-B0v4IKaI.js.map +0 -1
  45. package/dist/src-DmsRg8MR.js.map +0 -1
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from "module";
2
2
  import "process";
3
3
  __createRequire(import.meta.url);
4
4
  import { r as __require, t as __commonJSMin } from "./chunk-DSJWtz9O.js";
5
- import { c as getDeclaredSubcollections, d as REST_TO_CANONICAL, f as toCanonicalOp, l as isPostgresCollectionConfig, n as getDataSourceCapabilities, o as ANONYMOUS_USER_ID, p as EntityRelation, s as policy, u as NULL_OPS } from "./src-B0v4IKaI.js";
5
+ import { d as isPostgresCollectionConfig, f as NULL_OPS, h as EntityRelation, l as isManyToMany, m as toCanonicalOp, n as getDataSourceCapabilities, o as ANONYMOUS_USER_ID, p as REST_TO_CANONICAL, s as policy, u as getDeclaredSubcollections } from "./src-D5xBTl32.js";
6
6
  //#region ../common/src/util/common.ts
7
7
  var DEFAULT_ONE_OF_TYPE = "type";
8
8
  var DEFAULT_ONE_OF_VALUE = "value";
@@ -1405,151 +1405,143 @@ function enumToObjectEntries(enumValues) {
1405
1405
  });
1406
1406
  }
1407
1407
  //#endregion
1408
- //#region ../common/src/util/relations.ts
1409
- function sanitizeRelation(relation, sourceCollection, resolveCollection) {
1410
- if (!relation.target) throw new Error("Relation is missing a `target` collection.");
1411
- const rawTarget = relation.target;
1412
- let targetCollection;
1413
- if (typeof rawTarget === "string") {
1414
- if (resolveCollection) targetCollection = resolveCollection(rawTarget);
1415
- if (!targetCollection) targetCollection = {
1416
- slug: rawTarget,
1417
- name: rawTarget
1418
- };
1419
- } else if (typeof rawTarget === "function") {
1420
- const evaluated = rawTarget();
1421
- if (typeof evaluated === "string") {
1422
- if (resolveCollection) targetCollection = resolveCollection(evaluated);
1423
- if (!targetCollection) targetCollection = {
1424
- slug: evaluated,
1425
- name: evaluated
1426
- };
1427
- } else targetCollection = evaluated;
1428
- } else if (rawTarget && typeof rawTarget === "object") targetCollection = rawTarget;
1429
- if (!targetCollection) throw new Error("Relation is missing a valid `target` collection.");
1430
- const newRelation = { ...relation };
1431
- newRelation.target = () => {
1432
- if (typeof rawTarget === "string") return resolveCollection && resolveCollection(rawTarget) || targetCollection;
1433
- else if (typeof rawTarget === "function") {
1434
- const evaluated = rawTarget();
1435
- if (typeof evaluated === "string") return resolveCollection && resolveCollection(evaluated) || targetCollection;
1436
- return evaluated;
1437
- }
1438
- return targetCollection;
1408
+ //#region ../common/src/util/resolve-relation.ts
1409
+ /**
1410
+ * Fill in a relation's defaults.
1411
+ *
1412
+ * This replaces `sanitizeRelation`, which had to work out *which kind of link
1413
+ * you meant* from whichever optional fields happened to be set — 194 lines of
1414
+ * it, including a pass that inspected the target collection's own relations to
1415
+ * decide whether a `many`/`inverse` pair was a one-to-many or the far side of a
1416
+ * many-to-many, wrapped in a `try/catch` that fell through to the wrong answer
1417
+ * when it could not tell. Two consumers running that logic at different moments
1418
+ * could reach different conclusions about the same relation.
1419
+ *
1420
+ * With the kind declared there is nothing to work out. What remains is
1421
+ * defaulting — a table name, a column name — which is deterministic, depends
1422
+ * only on the relation and its two endpoints, and cannot fail. That is why this
1423
+ * function returns rather than throws, and why it needs no cache to be
1424
+ * consistent.
1425
+ */
1426
+ function resolveRelation(relation, sourceCollection, propertyKey) {
1427
+ const target = relation.target;
1428
+ if (typeof target !== "function") throw new Error(`Relation${relation.relationName ? ` '${relation.relationName}'` : ""} on '${sourceCollection.slug}' has no \`target\`. Give it a thunk: \`target: () => otherCollection\`.`);
1429
+ const targetCollection = target();
1430
+ if (!targetCollection?.slug) throw new Error(`Relation${relation.relationName ? ` '${relation.relationName}'` : ""} on '${sourceCollection.slug}' has a \`target\` that did not resolve to a collection.`);
1431
+ const relationName = relation.relationName ?? propertyKey ?? toSnakeCase(targetCollection.slug);
1432
+ const shared = {
1433
+ relationName,
1434
+ target,
1435
+ targetSlug: targetCollection.slug,
1436
+ onUpdate: relation.onUpdate,
1437
+ onDelete: relation.onDelete,
1438
+ overrides: relation.overrides,
1439
+ validation: relation.validation
1439
1440
  };
1440
- if (!newRelation.relationName) newRelation.relationName = toSnakeCase(targetCollection.slug);
1441
- if (!newRelation.direction) if (newRelation.foreignKeyOnTarget) newRelation.direction = "inverse";
1442
- else if (newRelation.through) newRelation.direction = "owning";
1443
- else if (newRelation.cardinality === "many") newRelation.direction = "inverse";
1444
- else newRelation.direction = "owning";
1445
- if (!newRelation.joinPath) {
1446
- const sourceName = toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
1447
- if (newRelation.cardinality === "one" && newRelation.direction === "owning") {
1448
- if (!newRelation.localKey) newRelation.localKey = generateForeignKeyName(newRelation.relationName);
1449
- } else if (newRelation.cardinality === "one" && newRelation.direction === "inverse") {
1450
- if (!newRelation.foreignKeyOnTarget) {
1451
- let foundForeignKey = false;
1452
- try {
1453
- const targetRelations = getDataSourceCapabilities(targetCollection.engine).supportsRelations ? targetCollection.relations || [] : [];
1454
- for (const targetRel of targetRelations) if (targetRel.direction === "owning" && targetRel.cardinality === "one" && targetRel.localKey) try {
1455
- if (targetRel.target().slug === sourceCollection.slug) {
1456
- newRelation.foreignKeyOnTarget = targetRel.localKey;
1457
- foundForeignKey = true;
1458
- break;
1459
- }
1460
- } catch (e) {
1461
- continue;
1462
- }
1463
- } catch (e) {}
1464
- if (!foundForeignKey) newRelation.foreignKeyOnTarget = generateForeignKeyName(newRelation.inverseRelationName ? toSnakeCase(newRelation.inverseRelationName) : sourceName);
1465
- }
1466
- } else if (newRelation.cardinality === "many" && newRelation.direction === "inverse") {
1467
- let isManyToManyInverse = false;
1468
- if (newRelation.inverseRelationName && !newRelation.foreignKeyOnTarget) try {
1469
- const targetRelations = getDataSourceCapabilities(targetCollection.engine).supportsRelations ? targetCollection.relations || [] : [];
1470
- for (const targetRel of targetRelations) if (targetRel.cardinality === "many" && (targetRel.direction === "owning" || !targetRel.direction) && targetRel.relationName === newRelation.inverseRelationName) {
1471
- isManyToManyInverse = true;
1472
- break;
1473
- }
1474
- if (!isManyToManyInverse && targetCollection.properties) for (const [propKey, prop] of Object.entries(targetCollection.properties)) {
1475
- if (prop.type !== "relation") continue;
1476
- const relProp = prop;
1477
- if ((relProp.relationName || propKey) === newRelation.inverseRelationName && relProp.cardinality === "many" && (relProp.direction === "owning" || !relProp.direction)) {
1478
- isManyToManyInverse = true;
1479
- break;
1480
- }
1441
+ const sourceName = toSnakeCase(sourceCollection.slug ?? sourceCollection.name);
1442
+ switch (relation.kind) {
1443
+ case "belongsTo": return {
1444
+ ...shared,
1445
+ kind: "belongsTo",
1446
+ cardinality: "one",
1447
+ writable: true,
1448
+ shared: false,
1449
+ localKey: relation.localKey ?? generateForeignKeyName(relationName)
1450
+ };
1451
+ case "hasOne": return {
1452
+ ...shared,
1453
+ kind: "hasOne",
1454
+ cardinality: "one",
1455
+ writable: true,
1456
+ shared: false,
1457
+ foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName)
1458
+ };
1459
+ case "hasMany": return {
1460
+ ...shared,
1461
+ kind: "hasMany",
1462
+ cardinality: "many",
1463
+ writable: true,
1464
+ shared: false,
1465
+ foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName)
1466
+ };
1467
+ case "manyToMany": {
1468
+ const sourceTable = getTableName(sourceCollection);
1469
+ const targetTable = getTableName(targetCollection);
1470
+ return {
1471
+ ...shared,
1472
+ kind: "manyToMany",
1473
+ cardinality: "many",
1474
+ writable: true,
1475
+ shared: true,
1476
+ through: {
1477
+ table: relation.through?.table ?? [sourceTable, targetTable].sort().join("_"),
1478
+ sourceColumn: relation.through?.sourceColumn ?? generateForeignKeyName(sourceName),
1479
+ targetColumn: relation.through?.targetColumn ?? generateForeignKeyName(relationName)
1481
1480
  }
1482
- } catch (e) {}
1483
- if (!isManyToManyInverse && !newRelation.foreignKeyOnTarget) newRelation.foreignKeyOnTarget = generateForeignKeyName(sourceName);
1484
- } else if (newRelation.cardinality === "many" && newRelation.direction === "owning") {
1485
- const sourceTableName = getTableName(sourceCollection);
1486
- const targetTableName = getTableName(targetCollection);
1487
- newRelation.through = {
1488
- table: newRelation.through?.table ?? [sourceTableName, targetTableName].sort().join("_"),
1489
- sourceColumn: newRelation.through?.sourceColumn ?? generateForeignKeyName(sourceName),
1490
- targetColumn: newRelation.through?.targetColumn ?? generateForeignKeyName(newRelation.relationName)
1491
1481
  };
1492
1482
  }
1483
+ case "via": return {
1484
+ ...shared,
1485
+ kind: "via",
1486
+ cardinality: relation.cardinality,
1487
+ writable: false,
1488
+ shared: true,
1489
+ joinPath: relation.joinPath
1490
+ };
1491
+ default: throw new Error(`Unknown relation kind: ${JSON.stringify(relation)}`);
1493
1492
  }
1494
- if (newRelation.cardinality === "one" && newRelation.direction === "owning" && !newRelation.localKey && !newRelation.joinPath) throw new Error(`Configuration Error in relation from '${sourceCollection.name}': An 'owning' one-to-one relation requires a 'localKey'. Check the relation config for '${newRelation.relationName}'`);
1495
- if (newRelation.cardinality === "one" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath) throw new Error(`Configuration Error in relation from '${sourceCollection.name}': An 'inverse' one-to-one relation requires a 'foreignKeyOnTarget'. Check the relation config for '${newRelation.relationName}'`);
1496
- if (newRelation.cardinality === "many" && newRelation.direction === "inverse" && !newRelation.foreignKeyOnTarget && !newRelation.joinPath && !newRelation.inverseRelationName) throw new Error(`Configuration Error in relation from '${sourceCollection.name}': An 'inverse' one-to-many relation requires a 'foreignKeyOnTarget'. Check the relation config for '${newRelation.relationName}'`);
1497
- return newRelation;
1493
+ }
1494
+ //#endregion
1495
+ //#region ../common/src/util/relations.ts
1496
+ /**
1497
+ * Whether the target rows are shared with other parents — a many-to-many, or a
1498
+ * multi-hop `via` chain.
1499
+ *
1500
+ * Decides what a write "through" the relation may touch: a shared target
1501
+ * belongs to every parent that links it, so the parent owns the *link* and not
1502
+ * the row. The backend enforces that (an unlink rather than a delete) and the
1503
+ * admin renders it (remove-from-parent rather than delete).
1504
+ *
1505
+ * Now a field on the resolved relation rather than a re-derivation, so both
1506
+ * sides read the same answer instead of each computing one.
1507
+ */
1508
+ function isJunctionBackedRelation(relation) {
1509
+ return relation.shared;
1498
1510
  }
1499
1511
  /** WeakMap cache — same collection instance always yields the same relation map. */
1500
1512
  var _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
1513
+ /**
1514
+ * Every relation a collection declares, keyed by the name it is addressed by.
1515
+ *
1516
+ * A relation reaches the map from either of two places — the collection's
1517
+ * `relations` array, or a `relation` property that declares one inline — and is
1518
+ * keyed by its resolved `relationName`, which is what a nested path segment,
1519
+ * an `include` key and an admin tab all match against.
1520
+ *
1521
+ * Resolution no longer swallows failures. It used to wrap each relation in a
1522
+ * `try/catch` that dropped anything it could not work out, so a
1523
+ * mis-declared relation silently vanished instead of being reported; with the
1524
+ * kind declared, the only remaining failure is a `target` that does not resolve,
1525
+ * which is worth hearing about.
1526
+ */
1501
1527
  function resolveCollectionRelations(collection) {
1502
1528
  const cached = _resolvedRelationsCache.get(collection);
1503
1529
  if (cached) return cached;
1504
1530
  if (!getDataSourceCapabilities(collection.engine).supportsRelations) return {};
1505
1531
  const relations = {};
1506
- const registeredRelationNames = /* @__PURE__ */ new Set();
1507
- if (collection.relations) collection.relations.forEach((relation) => {
1508
- try {
1509
- const normalizedRelation = sanitizeRelation(relation, collection);
1510
- const relationKey = normalizedRelation.relationName;
1511
- if (relationKey) {
1512
- relations[relationKey] = normalizedRelation;
1513
- registeredRelationNames.add(relationKey);
1514
- }
1515
- } catch (e) {}
1516
- });
1517
- if (collection.properties) Object.entries(collection.properties).forEach(([propKey, prop]) => {
1518
- const relation = resolvePropertyRelation({
1519
- propertyKey: propKey,
1520
- property: prop,
1521
- sourceCollection: collection
1522
- });
1523
- if (relation) {
1524
- if (relations[propKey]) return;
1525
- if (!relation.relationName) relation.relationName = propKey;
1526
- const normalizedRelation = sanitizeRelation(relation, collection);
1527
- relations[propKey] = normalizedRelation;
1528
- registeredRelationNames.add(normalizedRelation.relationName ?? propKey);
1529
- }
1530
- });
1532
+ for (const relation of collection.relations ?? []) {
1533
+ const resolved = resolveRelation(relation, collection);
1534
+ relations[resolved.relationName] = resolved;
1535
+ }
1536
+ for (const [propertyKey, property] of Object.entries(collection.properties ?? {})) {
1537
+ if (property?.type !== "relation") continue;
1538
+ const declared = property.relation;
1539
+ if (!declared || relations[propertyKey]) continue;
1540
+ relations[propertyKey] = resolveRelation(declared, collection, propertyKey);
1541
+ }
1531
1542
  _resolvedRelationsCache.set(collection, relations);
1532
1543
  return relations;
1533
1544
  }
1534
- function resolvePropertyRelation({ propertyKey, property, sourceCollection }) {
1535
- if (property.type !== "relation") return void 0;
1536
- const relProp = property;
1537
- if (relProp.target) return {
1538
- relationName: relProp.relationName || propertyKey,
1539
- target: relProp.target,
1540
- cardinality: relProp.cardinality || "one",
1541
- direction: relProp.direction || "owning",
1542
- inverseRelationName: relProp.inverseRelationName,
1543
- localKey: relProp.localKey,
1544
- foreignKeyOnTarget: relProp.foreignKeyOnTarget,
1545
- through: relProp.through,
1546
- joinPath: relProp.joinPath,
1547
- onUpdate: relProp.onUpdate,
1548
- onDelete: relProp.onDelete,
1549
- overrides: relProp.overrides
1550
- };
1551
- console.warn(`Unrecognized or missing relation target for property '${propertyKey}' in collection '${sourceCollection.slug}'`);
1552
- }
1553
1545
  function getTableName(collection) {
1554
1546
  if (getDataSourceCapabilities(collection.engine).supportsRelations) return collection.table ?? toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
1555
1547
  return toSnakeCase(collection.slug) ?? toSnakeCase(collection.name);
@@ -1581,34 +1573,79 @@ function findRelation(resolvedRelations, key) {
1581
1573
  }
1582
1574
  //#endregion
1583
1575
  //#region ../common/src/util/resolutions.ts
1584
- function getSubcollections(collection) {
1585
- if (collection.childCollections) return collection.childCollections() ?? [];
1576
+ /**
1577
+ * The lists rendered inside an entity view of `collection` — its tabs.
1578
+ *
1579
+ * The single derivation. There used to be two that disagreed: this one, and a
1580
+ * copy in `CollectionRegistry.normalizeCollection` that stamped each child with
1581
+ * the *target collection's* slug instead of the relation key. Since the
1582
+ * registry ran first and cached its answer onto `childCollections`, its version
1583
+ * was the one that won, and the frontend addressed child listings by a segment
1584
+ * the backend could not resolve.
1585
+ *
1586
+ * Order of precedence:
1587
+ * 1. `childCollections` — the explicit escape hatch for custom drivers.
1588
+ * 2. `subcollections` on an engine that has real containment (Firestore).
1589
+ * 3. many-relations on an engine that has relations (SQL).
1590
+ */
1591
+ function getEntityChildViews(collection) {
1592
+ const asSubcollections = (collections) => collections.filter(Boolean).map((child) => ({
1593
+ key: child.slug,
1594
+ collection: child,
1595
+ source: { kind: "subcollection" }
1596
+ }));
1597
+ if (collection.childCollections) return asSubcollections(collection.childCollections() ?? []);
1598
+ const capabilities = getDataSourceCapabilities(collection.engine);
1586
1599
  const declaredSubcollections = getDeclaredSubcollections(collection);
1587
- if (getDataSourceCapabilities(collection.engine).supportsSubcollections && declaredSubcollections) return declaredSubcollections() ?? [];
1588
- if (getDataSourceCapabilities(collection.engine).supportsRelations) {
1589
- const resolvedRelations = resolveCollectionRelations(collection);
1590
- return Object.values(resolvedRelations).filter((r) => r.cardinality === "many").map((r) => {
1591
- const target = r.target();
1592
- if (!target) return void 0;
1593
- const relationKey = r.relationName || target.slug;
1594
- let customName;
1595
- if (collection.properties) {
1596
- const prop = Object.entries(collection.properties).find(([_, p]) => p.type === "relation" && p.relationName === relationKey);
1597
- if (prop && prop[1].name) customName = prop[1].name;
1598
- }
1599
- const baseOverrides = { slug: relationKey };
1600
- if (customName) {
1601
- baseOverrides.name = customName;
1602
- baseOverrides.singularName = customName;
1600
+ if (capabilities.supportsSubcollections && declaredSubcollections) return asSubcollections(declaredSubcollections() ?? []);
1601
+ if (!capabilities.supportsRelations) return [];
1602
+ const resolvedRelations = resolveCollectionRelations(collection);
1603
+ const views = [];
1604
+ const seen = /* @__PURE__ */ new Set();
1605
+ for (const [relationKey, relation] of Object.entries(resolvedRelations)) {
1606
+ if (relation.cardinality !== "many") continue;
1607
+ const identity = relation.relationName ?? relationKey;
1608
+ if (seen.has(identity)) continue;
1609
+ let target;
1610
+ try {
1611
+ target = relation.target();
1612
+ } catch {
1613
+ continue;
1614
+ }
1615
+ if (!target) continue;
1616
+ seen.add(identity);
1617
+ const customName = Object.entries(collection.properties ?? {}).find(([propKey, p]) => p.type === "relation" && (p.relation?.relationName ?? propKey) === identity)?.[1]?.name;
1618
+ const base = {
1619
+ ...target,
1620
+ slug: relationKey,
1621
+ ...customName ? {
1622
+ name: customName,
1623
+ singularName: customName
1624
+ } : {}
1625
+ };
1626
+ views.push({
1627
+ key: relationKey,
1628
+ collection: relation.overrides ? mergeDeep(base, relation.overrides) : base,
1629
+ source: {
1630
+ kind: "relation",
1631
+ relationKey,
1632
+ mode: isJunctionBackedRelation(relation) ? "linked" : "owned",
1633
+ targetSlug: target.slug
1603
1634
  }
1604
- const targetWithOverrides = {
1605
- ...target,
1606
- ...baseOverrides
1607
- };
1608
- return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
1609
- }).filter((c) => Boolean(c));
1635
+ });
1610
1636
  }
1611
- return [];
1637
+ return views;
1638
+ }
1639
+ /**
1640
+ * The child views of `collection` as bare collections.
1641
+ *
1642
+ * The flattened view of {@link getEntityChildViews}, for navigation code that
1643
+ * only needs to match a path segment against a slug. Anything that cares *what
1644
+ * kind* of list it is showing — chiefly the admin, which must not offer a
1645
+ * global delete on a shared row — should read the views instead.
1646
+ */
1647
+ function getSubcollections(collection) {
1648
+ return getEntityChildViews(collection).map((view) => view.collection);
1612
1649
  }
1613
1650
  //#endregion
1614
1651
  //#region ../common/src/util/policy/sqlToPolicy.ts
@@ -2160,8 +2197,8 @@ function resolveJunctionSpecs(collections) {
2160
2197
  for (const collection of collections) {
2161
2198
  const resolved = resolveCollectionRelations(collection);
2162
2199
  for (const relation of Object.values(resolved)) {
2163
- if (!relation.through) continue;
2164
- const targetCollection = typeof relation.target === "function" ? relation.target() : void 0;
2200
+ if (!isManyToMany(relation)) continue;
2201
+ const targetCollection = relation.target();
2165
2202
  if (!targetCollection) continue;
2166
2203
  const rawName = relation.through.table;
2167
2204
  const table = rawName.includes(".") ? rawName.split(".").pop() : rawName;
@@ -2621,39 +2658,9 @@ function getJunctionSecurityRules(spec) {
2621
2658
  return jsonLogic;
2622
2659
  });
2623
2660
  })))();
2624
- //#endregion
2625
- //#region ../common/src/util/filter-operator-resolution.ts
2626
- /**
2627
- * Default operators offered per property type, before engine capabilities and
2628
- * per-property narrowing are applied. These mirror what the built-in filter
2629
- * fields can render.
2630
- */
2631
- var COMPARISON_OPS = [
2632
- "==",
2633
- "!=",
2634
- ">",
2635
- ">=",
2636
- "<",
2637
- "<="
2638
- ];
2639
- var NULL_CHECK_OPS = ["is-null", "is-not-null"];
2640
- var MEMBERSHIP_OPS = ["in", "not-in"];
2641
- var PATTERN_OPS = [
2642
- "like",
2643
- "ilike",
2644
- "not-like",
2645
- "not-ilike"
2646
- ];
2647
- [
2648
- ...COMPARISON_OPS,
2649
- ...MEMBERSHIP_OPS,
2650
- ...PATTERN_OPS,
2651
- ...NULL_CHECK_OPS
2652
- ], [
2653
- ...COMPARISON_OPS,
2654
- ...MEMBERSHIP_OPS,
2655
- ...NULL_CHECK_OPS
2656
- ], [...COMPARISON_OPS, ...NULL_CHECK_OPS], [...NULL_CHECK_OPS], [...MEMBERSHIP_OPS, ...NULL_CHECK_OPS], [...MEMBERSHIP_OPS, ...NULL_CHECK_OPS];
2661
+ /**
2662
+ * Apply PropertyConditions to a resolved property, evaluating all JSON Logic rules.
2663
+ */
2657
2664
  //#endregion
2658
2665
  //#region ../../node_modules/.pnpm/fast-equals@6.0.0/node_modules/fast-equals/dist/es/index.mjs
2659
2666
  var { getOwnPropertyNames, getOwnPropertySymbols } = Object;
@@ -3233,100 +3240,33 @@ var CollectionRegistry = class {
3233
3240
  if (!result.dataSource) result.dataSource = resolved.key;
3234
3241
  if (!result.engine) result.engine = resolved.engine;
3235
3242
  }
3236
- const extractedRelations = this.extractRelationsFromProperties(result.properties);
3237
- const relResult = result;
3238
- const manualRelations = getDataSourceCapabilities(result.engine).supportsRelations ? relResult.relations ?? [] : [];
3239
- const mergedRelationsRaw = [...extractedRelations];
3240
- for (const manual of manualRelations) {
3241
- const name = manual.relationName;
3242
- if (!name) mergedRelationsRaw.push(manual);
3243
- else {
3244
- const existingIndex = mergedRelationsRaw.findIndex((r) => r.relationName === name);
3245
- if (existingIndex === -1) mergedRelationsRaw.push(manual);
3246
- else mergedRelationsRaw[existingIndex] = {
3247
- ...manual,
3248
- ...mergedRelationsRaw[existingIndex]
3249
- };
3250
- }
3251
- }
3252
- let mergedRelations = mergedRelationsRaw;
3253
- if (getDataSourceCapabilities(result.engine).supportsRelations) {
3254
- mergedRelations = mergedRelationsRaw.map((r) => {
3255
- try {
3256
- return sanitizeRelation(r, result, (slug) => this.get(slug));
3257
- } catch {
3258
- return r;
3259
- }
3260
- });
3261
- relResult.relations = mergedRelations;
3262
- }
3263
- result.properties = this.normalizeProperties(result.properties, mergedRelations);
3264
- if (!result.childCollections) {
3265
- const capabilities = getDataSourceCapabilities(result.engine);
3266
- const declaredSubcollections = getDeclaredSubcollections(result);
3267
- if (capabilities.supportsSubcollections && declaredSubcollections) result.childCollections = declaredSubcollections;
3268
- else if (capabilities.supportsRelations && relResult.relations) {
3269
- const manyRelations = relResult.relations.filter((r) => r.cardinality === "many");
3270
- if (manyRelations.length > 0) result.childCollections = () => manyRelations.map((r) => {
3271
- const target = r.target();
3272
- return r.overrides ? mergeDeep(target, r.overrides) : target;
3273
- });
3274
- }
3275
- }
3243
+ result.properties = this.normalizeProperties(result.properties, result);
3276
3244
  return result;
3277
3245
  }
3278
- /**
3279
- * Extract Relation[] from properties that have inline relation config (i.e. `target` is set).
3280
- * This allows developers to define relations directly on properties without a separate
3281
- * `relations[]` entry on the collection.
3282
- */
3283
- extractRelationsFromProperties(properties) {
3284
- const relations = [];
3285
- for (const [key, property] of Object.entries(properties)) if (property.type === "relation") {
3286
- const relProp = property;
3287
- const target = relProp.target ?? relProp.relation?.target;
3288
- if (target) {
3289
- const relationName = relProp.relationName ?? relProp.relation?.relationName ?? key;
3290
- relations.push({
3291
- relationName,
3292
- target,
3293
- cardinality: relProp.cardinality ?? relProp.relation?.cardinality ?? "one",
3294
- direction: relProp.direction ?? relProp.relation?.direction ?? "owning",
3295
- inverseRelationName: relProp.inverseRelationName ?? relProp.relation?.inverseRelationName,
3296
- localKey: relProp.localKey ?? relProp.relation?.localKey,
3297
- foreignKeyOnTarget: relProp.foreignKeyOnTarget ?? relProp.relation?.foreignKeyOnTarget,
3298
- through: relProp.through ?? relProp.relation?.through,
3299
- joinPath: relProp.joinPath ?? relProp.relation?.joinPath,
3300
- onUpdate: relProp.onUpdate ?? relProp.relation?.onUpdate,
3301
- onDelete: relProp.onDelete ?? relProp.relation?.onDelete,
3302
- overrides: relProp.overrides ?? relProp.relation?.overrides
3303
- });
3304
- }
3305
- } else if (property.type === "map" && property.properties) relations.push(...this.extractRelationsFromProperties(property.properties));
3306
- return relations;
3307
- }
3308
- normalizeProperties(properties, relations) {
3246
+ normalizeProperties(properties, collection) {
3309
3247
  const newProperties = {};
3310
- for (const key in properties) newProperties[key] = this.normalizeProperty(key, properties[key], relations);
3248
+ for (const key in properties) newProperties[key] = this.normalizeProperty(key, properties[key], collection);
3311
3249
  return newProperties;
3312
3250
  }
3313
- normalizeProperty(key, property, relations) {
3251
+ normalizeProperty(key, property, collection) {
3314
3252
  const newProperty = { ...property };
3315
- if (newProperty.type === "map" && newProperty.properties) newProperty.properties = this.normalizeProperties(newProperty.properties, relations);
3253
+ if (newProperty.type === "map" && newProperty.properties) newProperty.properties = this.normalizeProperties(newProperty.properties, collection);
3316
3254
  else if (newProperty.type === "array") {
3317
3255
  const arrayProp = newProperty;
3318
- if (arrayProp.of) if (Array.isArray(arrayProp.of)) arrayProp.of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, relations));
3319
- else arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, relations);
3320
- else if (arrayProp.oneOf && arrayProp.oneOf.properties) arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, relations);
3256
+ if (arrayProp.of) if (Array.isArray(arrayProp.of)) arrayProp.of = arrayProp.of.map((p, i) => this.normalizeProperty(`${key}[${i}]`, p, collection));
3257
+ else arrayProp.of = this.normalizeProperty(`${key}.of`, arrayProp.of, collection);
3258
+ else if (arrayProp.oneOf && arrayProp.oneOf.properties) arrayProp.oneOf.properties = this.normalizeProperties(arrayProp.oneOf.properties, collection);
3321
3259
  } else if ((newProperty.type === "string" || newProperty.type === "number") && newProperty.enum) {
3322
3260
  const stringOrNumberProperty = newProperty;
3323
3261
  if (typeof stringOrNumberProperty.enum === "object" && !Array.isArray(stringOrNumberProperty.enum)) stringOrNumberProperty.enum = enumToObjectEntries(stringOrNumberProperty.enum)?.filter((value) => value && (value.id || value.id === 0) && value.label) ?? [];
3324
3262
  } else if (newProperty.type === "relation") {
3325
3263
  const relationProperty = newProperty;
3326
- const name = relationProperty.relationName || key;
3327
- const relation = relations.find((r) => r.relationName === name);
3328
- if (relation) relationProperty.relation = relation;
3329
- else console.warn(`Could not find relation for property '${key}' with relationName: ${name}`);
3264
+ if (relationProperty.relation) relationProperty.resolvedRelation = resolveRelation(relationProperty.relation, collection, key);
3265
+ else {
3266
+ const declared = resolveCollectionRelations(collection)[key];
3267
+ if (declared) relationProperty.resolvedRelation = declared;
3268
+ else console.warn(`Relation property '${key}' on '${collection.slug}' declares no \`relation\`, and the collection has no relation of that name.`);
3269
+ }
3330
3270
  }
3331
3271
  return newProperty;
3332
3272
  }
@@ -3371,9 +3311,7 @@ var CollectionRegistry = class {
3371
3311
  const relation = findRelation(resolveCollectionRelations(currentCollection), relationKey);
3372
3312
  if (!relation) throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
3373
3313
  const target = relation.target();
3374
- const targetRelationKey = relation.relationName || target.slug;
3375
- const targetSlug = relation.overrides?.slug ?? targetRelationKey;
3376
- currentCollection = this.get(targetSlug) || this.normalizeCollection(target);
3314
+ currentCollection = this.collectionsByTableName.get(getTableName(target)) ?? this.normalizeCollection(target);
3377
3315
  if (i + 1 < pathSegments.length) {}
3378
3316
  }
3379
3317
  return currentCollection;
@@ -3408,7 +3346,7 @@ var CollectionRegistry = class {
3408
3346
  if (!subcollections || subcollections.length === 0) throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
3409
3347
  const subcollection = subcollections.find((c) => c.slug === subcollectionSlug);
3410
3348
  if (!subcollection) throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
3411
- currentCollection = this.get(subcollection.slug) || this.normalizeCollection(subcollection);
3349
+ currentCollection = this.normalizeCollection(subcollection);
3412
3350
  collections.push(currentCollection);
3413
3351
  }
3414
3352
  }
@@ -4053,4 +3991,4 @@ async function detectJunctionTables(executeSql) {
4053
3991
  //#endregion
4054
3992
  export { DEFAULT_ONE_OF_TYPE as A, createRelationRefWithData as C, mergeDeep as D, getPolicyNamesForRule as E, camelCase as O, createRelationRef as S, updateDateAutoValues as T, getTableVarName as _, getJunctionCollectionConfig as a, getDeclaredPrimaryKeys as b, getEffectiveSecurityRules as c, securityRuleToConditions as d, findAnonymousGrants as f, getTableName as g, getEnumVarName as h, CollectionRegistry as i, DEFAULT_ONE_OF_VALUE as j, toSnakeCase as k, buildPropertyCallbacks as l, getColumnName as m, detectJunctionTables as n, getJunctionSecurityRules as o, findRelation as p, buildSdkData as r, resolveJunctionSpecs as s, classifyTable as t, policyToPostgres as u, resolveCollectionRelations as v, normalizeToEntityRelation as w, parseIdValues as x, buildCompositeId as y };
4055
3993
 
4056
- //# sourceMappingURL=src-DmsRg8MR.js.map
3994
+ //# sourceMappingURL=src-3VmUJ8Xn.js.map