@rebasepro/server-postgres 0.10.1-canary.8d27c46 → 0.10.1-canary.af2af91
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/dist/{ensure-collection-tables-C9gy4STB.js → ensure-collection-tables-BVvtkkRm.js} +2 -2
- package/dist/{ensure-collection-tables-C9gy4STB.js.map → ensure-collection-tables-BVvtkkRm.js.map} +1 -1
- package/dist/index.es.js +437 -113
- package/dist/index.es.js.map +1 -1
- package/dist/services/FetchService.d.ts +21 -8
- package/dist/services/PersistService.d.ts +12 -0
- package/dist/services/RelationService.d.ts +30 -0
- package/dist/services/cdc/junction-tables.d.ts +38 -0
- package/dist/services/nested-path.d.ts +59 -0
- package/dist/services/realtimeService.d.ts +19 -0
- package/dist/src-CBgtrPhJ.js.map +1 -1
- package/dist/{src-DG6ZsQQ3.js → src-lcfUP4xg.js} +122 -42
- package/dist/src-lcfUP4xg.js.map +1 -0
- package/dist/utils/drizzle-conditions.d.ts +41 -0
- package/package.json +6 -6
- package/src/PostgresBootstrapper.ts +10 -1
- package/src/services/FetchService.ts +104 -114
- package/src/services/PersistService.ts +127 -85
- package/src/services/RelationService.ts +95 -6
- package/src/services/cdc/junction-tables.ts +91 -0
- package/src/services/nested-path.ts +130 -0
- package/src/services/realtimeService.ts +60 -0
- package/src/utils/drizzle-conditions.ts +154 -0
- package/dist/src-DG6ZsQQ3.js.map +0 -1
|
@@ -1496,6 +1496,19 @@ function sanitizeRelation(relation, sourceCollection, resolveCollection) {
|
|
|
1496
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
1497
|
return newRelation;
|
|
1498
1498
|
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Whether the target rows of this relation are reached through a junction — a
|
|
1501
|
+
* many-to-many, or a multi-hop `joinPath`.
|
|
1502
|
+
*
|
|
1503
|
+
* The distinction decides what a write "through" the relation may touch: a
|
|
1504
|
+
* junction-backed target is shared with other parents, so the parent owns the
|
|
1505
|
+
* *link* and not the row. The backend enforces that (an unlink rather than a
|
|
1506
|
+
* delete) and the admin renders it (remove-from-parent rather than delete), so
|
|
1507
|
+
* the predicate lives here rather than once per side.
|
|
1508
|
+
*/
|
|
1509
|
+
function isJunctionBackedRelation(relation) {
|
|
1510
|
+
return Boolean(relation.through) || Boolean(relation.joinPath && relation.joinPath.length > 1);
|
|
1511
|
+
}
|
|
1499
1512
|
/** WeakMap cache — same collection instance always yields the same relation map. */
|
|
1500
1513
|
var _resolvedRelationsCache = /* @__PURE__ */ new WeakMap();
|
|
1501
1514
|
function resolveCollectionRelations(collection) {
|
|
@@ -1581,34 +1594,115 @@ function findRelation(resolvedRelations, key) {
|
|
|
1581
1594
|
}
|
|
1582
1595
|
//#endregion
|
|
1583
1596
|
//#region ../common/src/util/resolutions.ts
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1597
|
+
/**
|
|
1598
|
+
* Relation identities that only ever appear *below* the top level of
|
|
1599
|
+
* `properties` — inside a `map`, an `array`, or a `oneOf` branch.
|
|
1600
|
+
*
|
|
1601
|
+
* A relation declared at the top level is a list hanging off the record and
|
|
1602
|
+
* earns a tab. One declared inside a map is a field of that map. Both end up in
|
|
1603
|
+
* the collection's flat `relations` array after normalization, so the shape of
|
|
1604
|
+
* `properties` is the only remaining evidence of which is which.
|
|
1605
|
+
*
|
|
1606
|
+
* An identity declared in both places is *not* nested-only, and keeps its tab.
|
|
1607
|
+
*/
|
|
1608
|
+
function getNestedRelationIdentities(properties) {
|
|
1609
|
+
const topLevel = /* @__PURE__ */ new Set();
|
|
1610
|
+
const nested = /* @__PURE__ */ new Set();
|
|
1611
|
+
const walk = (props, depth) => {
|
|
1612
|
+
if (!props) return;
|
|
1613
|
+
for (const [key, property] of Object.entries(props)) {
|
|
1614
|
+
if (!property) continue;
|
|
1615
|
+
if (property.type === "relation") {
|
|
1616
|
+
const relProp = property;
|
|
1617
|
+
(depth === 0 ? topLevel : nested).add(relProp.relationName ?? key);
|
|
1618
|
+
} else if (property.type === "map") walk(property.properties, depth + 1);
|
|
1619
|
+
else if (property.type === "array") {
|
|
1620
|
+
const arrayProp = property;
|
|
1621
|
+
if (Array.isArray(arrayProp.of)) arrayProp.of.forEach((p) => walk({ entry: p }, depth + 1));
|
|
1622
|
+
else if (arrayProp.of) walk({ entry: arrayProp.of }, depth + 1);
|
|
1623
|
+
if (arrayProp.oneOf?.properties) walk(arrayProp.oneOf.properties, depth + 1);
|
|
1598
1624
|
}
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1625
|
+
}
|
|
1626
|
+
};
|
|
1627
|
+
walk(properties, 0);
|
|
1628
|
+
for (const identity of topLevel) nested.delete(identity);
|
|
1629
|
+
return nested;
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* The lists rendered inside an entity view of `collection` — its tabs.
|
|
1633
|
+
*
|
|
1634
|
+
* The single derivation. There used to be two that disagreed: this one, and a
|
|
1635
|
+
* copy in `CollectionRegistry.normalizeCollection` that stamped each child with
|
|
1636
|
+
* the *target collection's* slug instead of the relation key. Since the
|
|
1637
|
+
* registry ran first and cached its answer onto `childCollections`, its version
|
|
1638
|
+
* was the one that won, and the frontend addressed child listings by a segment
|
|
1639
|
+
* the backend could not resolve.
|
|
1640
|
+
*
|
|
1641
|
+
* Order of precedence:
|
|
1642
|
+
* 1. `childCollections` — the explicit escape hatch for custom drivers.
|
|
1643
|
+
* 2. `subcollections` on an engine that has real containment (Firestore).
|
|
1644
|
+
* 3. many-relations on an engine that has relations (SQL).
|
|
1645
|
+
*/
|
|
1646
|
+
function getEntityChildViews(collection) {
|
|
1647
|
+
const asSubcollections = (collections) => collections.filter(Boolean).map((child) => ({
|
|
1648
|
+
key: child.slug,
|
|
1649
|
+
collection: child,
|
|
1650
|
+
source: { kind: "subcollection" }
|
|
1651
|
+
}));
|
|
1652
|
+
if (collection.childCollections) return asSubcollections(collection.childCollections() ?? []);
|
|
1653
|
+
const capabilities = getDataSourceCapabilities(collection.engine);
|
|
1654
|
+
const declaredSubcollections = getDeclaredSubcollections(collection);
|
|
1655
|
+
if (capabilities.supportsSubcollections && declaredSubcollections) return asSubcollections(declaredSubcollections() ?? []);
|
|
1656
|
+
if (!capabilities.supportsRelations) return [];
|
|
1657
|
+
const resolvedRelations = resolveCollectionRelations(collection);
|
|
1658
|
+
const nestedOnly = getNestedRelationIdentities(collection.properties);
|
|
1659
|
+
const views = [];
|
|
1660
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1661
|
+
for (const [relationKey, relation] of Object.entries(resolvedRelations)) {
|
|
1662
|
+
if (relation.cardinality !== "many") continue;
|
|
1663
|
+
const identity = relation.relationName ?? relationKey;
|
|
1664
|
+
if (seen.has(identity)) continue;
|
|
1665
|
+
if (nestedOnly.has(identity)) continue;
|
|
1666
|
+
let target;
|
|
1667
|
+
try {
|
|
1668
|
+
target = relation.target();
|
|
1669
|
+
} catch {
|
|
1670
|
+
continue;
|
|
1671
|
+
}
|
|
1672
|
+
if (!target) continue;
|
|
1673
|
+
seen.add(identity);
|
|
1674
|
+
const customName = Object.entries(collection.properties ?? {}).find(([propKey, p]) => p.type === "relation" && (p.relationName ?? propKey) === identity)?.[1]?.name;
|
|
1675
|
+
const base = {
|
|
1676
|
+
...target,
|
|
1677
|
+
slug: relationKey,
|
|
1678
|
+
...customName ? {
|
|
1679
|
+
name: customName,
|
|
1680
|
+
singularName: customName
|
|
1681
|
+
} : {}
|
|
1682
|
+
};
|
|
1683
|
+
views.push({
|
|
1684
|
+
key: relationKey,
|
|
1685
|
+
collection: relation.overrides ? mergeDeep(base, relation.overrides) : base,
|
|
1686
|
+
source: {
|
|
1687
|
+
kind: "relation",
|
|
1688
|
+
relationKey,
|
|
1689
|
+
mode: isJunctionBackedRelation(relation) ? "linked" : "owned",
|
|
1690
|
+
targetSlug: target.slug
|
|
1603
1691
|
}
|
|
1604
|
-
|
|
1605
|
-
...target,
|
|
1606
|
-
...baseOverrides
|
|
1607
|
-
};
|
|
1608
|
-
return r.overrides ? mergeDeep(targetWithOverrides, r.overrides) : targetWithOverrides;
|
|
1609
|
-
}).filter((c) => Boolean(c));
|
|
1692
|
+
});
|
|
1610
1693
|
}
|
|
1611
|
-
return
|
|
1694
|
+
return views;
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* The child views of `collection` as bare collections.
|
|
1698
|
+
*
|
|
1699
|
+
* The flattened view of {@link getEntityChildViews}, for navigation code that
|
|
1700
|
+
* only needs to match a path segment against a slug. Anything that cares *what
|
|
1701
|
+
* kind* of list it is showing — chiefly the admin, which must not offer a
|
|
1702
|
+
* global delete on a shared row — should read the views instead.
|
|
1703
|
+
*/
|
|
1704
|
+
function getSubcollections(collection) {
|
|
1705
|
+
return getEntityChildViews(collection).map((view) => view.collection);
|
|
1612
1706
|
}
|
|
1613
1707
|
//#endregion
|
|
1614
1708
|
//#region ../common/src/util/policy/sqlToPolicy.ts
|
|
@@ -3231,18 +3325,6 @@ var CollectionRegistry = class {
|
|
|
3231
3325
|
relResult.relations = mergedRelations;
|
|
3232
3326
|
}
|
|
3233
3327
|
result.properties = this.normalizeProperties(result.properties, mergedRelations);
|
|
3234
|
-
if (!result.childCollections) {
|
|
3235
|
-
const capabilities = getDataSourceCapabilities(result.engine);
|
|
3236
|
-
const declaredSubcollections = getDeclaredSubcollections(result);
|
|
3237
|
-
if (capabilities.supportsSubcollections && declaredSubcollections) result.childCollections = declaredSubcollections;
|
|
3238
|
-
else if (capabilities.supportsRelations && relResult.relations) {
|
|
3239
|
-
const manyRelations = relResult.relations.filter((r) => r.cardinality === "many");
|
|
3240
|
-
if (manyRelations.length > 0) result.childCollections = () => manyRelations.map((r) => {
|
|
3241
|
-
const target = r.target();
|
|
3242
|
-
return r.overrides ? mergeDeep(target, r.overrides) : target;
|
|
3243
|
-
});
|
|
3244
|
-
}
|
|
3245
|
-
}
|
|
3246
3328
|
return result;
|
|
3247
3329
|
}
|
|
3248
3330
|
/**
|
|
@@ -3341,9 +3423,7 @@ var CollectionRegistry = class {
|
|
|
3341
3423
|
const relation = findRelation(resolveCollectionRelations(currentCollection), relationKey);
|
|
3342
3424
|
if (!relation) throw new Error(`Relation '${relationKey}' not found in collection '${currentCollection.slug}'`);
|
|
3343
3425
|
const target = relation.target();
|
|
3344
|
-
|
|
3345
|
-
const targetSlug = relation.overrides?.slug ?? targetRelationKey;
|
|
3346
|
-
currentCollection = this.get(targetSlug) || this.normalizeCollection(target);
|
|
3426
|
+
currentCollection = this.collectionsByTableName.get(getTableName(target)) ?? this.normalizeCollection(target);
|
|
3347
3427
|
if (i + 1 < pathSegments.length) {}
|
|
3348
3428
|
}
|
|
3349
3429
|
return currentCollection;
|
|
@@ -3378,7 +3458,7 @@ var CollectionRegistry = class {
|
|
|
3378
3458
|
if (!subcollections || subcollections.length === 0) throw new Error(`No subcollections found for ${currentCollection.slug} in path: ${path}`);
|
|
3379
3459
|
const subcollection = subcollections.find((c) => c.slug === subcollectionSlug);
|
|
3380
3460
|
if (!subcollection) throw new Error(`Subcollection '${subcollectionSlug}' not found in ${currentCollection.slug}`);
|
|
3381
|
-
currentCollection = this.
|
|
3461
|
+
currentCollection = this.normalizeCollection(subcollection);
|
|
3382
3462
|
collections.push(currentCollection);
|
|
3383
3463
|
}
|
|
3384
3464
|
}
|
|
@@ -4023,4 +4103,4 @@ async function detectJunctionTables(executeSql) {
|
|
|
4023
4103
|
//#endregion
|
|
4024
4104
|
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 };
|
|
4025
4105
|
|
|
4026
|
-
//# sourceMappingURL=src-
|
|
4106
|
+
//# sourceMappingURL=src-lcfUP4xg.js.map
|