@_linked/core 2.8.0-next.20260620064950 → 2.8.0-next.20260624100616

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 (62) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/lib/cjs/index.d.ts +3 -0
  3. package/lib/cjs/index.js +9 -1
  4. package/lib/cjs/index.js.map +1 -1
  5. package/lib/cjs/ontologies/linked-core.d.ts +3 -0
  6. package/lib/cjs/ontologies/linked-core.js +10 -0
  7. package/lib/cjs/ontologies/linked-core.js.map +1 -1
  8. package/lib/cjs/ontologies/shacl.d.ts +7 -0
  9. package/lib/cjs/ontologies/shacl.js +14 -0
  10. package/lib/cjs/ontologies/shacl.js.map +1 -1
  11. package/lib/cjs/shapes/List.d.ts +27 -8
  12. package/lib/cjs/shapes/List.js +36 -28
  13. package/lib/cjs/shapes/List.js.map +1 -1
  14. package/lib/cjs/shapes/PathNode.d.ts +20 -0
  15. package/lib/cjs/shapes/PathNode.js +81 -0
  16. package/lib/cjs/shapes/PathNode.js.map +1 -0
  17. package/lib/cjs/shapes/SHACL.d.ts +19 -0
  18. package/lib/cjs/shapes/SHACL.js +3 -0
  19. package/lib/cjs/shapes/SHACL.js.map +1 -1
  20. package/lib/cjs/shapes/serializePathToNodeData.d.ts +24 -0
  21. package/lib/cjs/shapes/serializePathToNodeData.js +51 -0
  22. package/lib/cjs/shapes/serializePathToNodeData.js.map +1 -0
  23. package/lib/cjs/shapes/syncShapes.d.ts +14 -0
  24. package/lib/cjs/shapes/syncShapes.js +141 -0
  25. package/lib/cjs/shapes/syncShapes.js.map +1 -0
  26. package/lib/cjs/sparql/irToAlgebra.d.ts +10 -1
  27. package/lib/cjs/sparql/irToAlgebra.js +148 -6
  28. package/lib/cjs/sparql/irToAlgebra.js.map +1 -1
  29. package/lib/cjs/utils/Package.d.ts +14 -0
  30. package/lib/cjs/utils/Package.js +43 -2
  31. package/lib/cjs/utils/Package.js.map +1 -1
  32. package/lib/esm/index.d.ts +3 -0
  33. package/lib/esm/index.js +5 -0
  34. package/lib/esm/index.js.map +1 -1
  35. package/lib/esm/ontologies/linked-core.d.ts +3 -0
  36. package/lib/esm/ontologies/linked-core.js +10 -0
  37. package/lib/esm/ontologies/linked-core.js.map +1 -1
  38. package/lib/esm/ontologies/shacl.d.ts +7 -0
  39. package/lib/esm/ontologies/shacl.js +14 -0
  40. package/lib/esm/ontologies/shacl.js.map +1 -1
  41. package/lib/esm/shapes/List.d.ts +27 -8
  42. package/lib/esm/shapes/List.js +35 -28
  43. package/lib/esm/shapes/List.js.map +1 -1
  44. package/lib/esm/shapes/PathNode.d.ts +20 -0
  45. package/lib/esm/shapes/PathNode.js +78 -0
  46. package/lib/esm/shapes/PathNode.js.map +1 -0
  47. package/lib/esm/shapes/SHACL.d.ts +19 -0
  48. package/lib/esm/shapes/SHACL.js +3 -0
  49. package/lib/esm/shapes/SHACL.js.map +1 -1
  50. package/lib/esm/shapes/serializePathToNodeData.d.ts +24 -0
  51. package/lib/esm/shapes/serializePathToNodeData.js +48 -0
  52. package/lib/esm/shapes/serializePathToNodeData.js.map +1 -0
  53. package/lib/esm/shapes/syncShapes.d.ts +14 -0
  54. package/lib/esm/shapes/syncShapes.js +138 -0
  55. package/lib/esm/shapes/syncShapes.js.map +1 -0
  56. package/lib/esm/sparql/irToAlgebra.d.ts +10 -1
  57. package/lib/esm/sparql/irToAlgebra.js +147 -6
  58. package/lib/esm/sparql/irToAlgebra.js.map +1 -1
  59. package/lib/esm/utils/Package.d.ts +14 -0
  60. package/lib/esm/utils/Package.js +43 -2
  61. package/lib/esm/utils/Package.js.map +1 -1
  62. package/package.json +1 -1
@@ -0,0 +1,24 @@
1
+ import type { PathExpr } from '../paths/PropertyPathExpr.js';
2
+ import type { NodeReferenceValue } from '../utils/NodeReference.js';
3
+ import { List } from './List.js';
4
+ import { PathNode } from './PathNode.js';
5
+ /**
6
+ * Node-data the create pipeline accepts for a `sh:path` value: an IRI reference (simple path),
7
+ * an `rdf:List` (sequence / alternative members), or a `PathNode` operator node.
8
+ */
9
+ export type PathNodeData = NodeReferenceValue | {
10
+ shape: typeof List | typeof PathNode;
11
+ __id?: string;
12
+ [k: string]: unknown;
13
+ };
14
+ /**
15
+ * Translate a `PathExpr` into create-pipeline node-data for `sh:path`.
16
+ *
17
+ * - simple `PathRef` → `{id}` (a shared predicate IRI; NOT owned)
18
+ * - sequence → an `rdf:List` of segments (via {@link rdfList})
19
+ * - inverse / alternative / cardinality → a `PathNode` operator node (alt's operand is an `rdf:List`)
20
+ *
21
+ * `base` seeds deterministic ids for the minted owned nodes (`{base}/inv`, `{base}/seq/0`, …) so a
22
+ * re-sync overwrites them in place; the owned subtree is cleaned by the containment cascade.
23
+ */
24
+ export declare function serializePathToNodeData(path: PathExpr, base: string): PathNodeData;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.serializePathToNodeData = serializePathToNodeData;
4
+ const PropertyPathExpr_js_1 = require("../paths/PropertyPathExpr.js");
5
+ const List_js_1 = require("./List.js");
6
+ const PathNode_js_1 = require("./PathNode.js");
7
+ /**
8
+ * Translate a `PathExpr` into create-pipeline node-data for `sh:path`.
9
+ *
10
+ * - simple `PathRef` → `{id}` (a shared predicate IRI; NOT owned)
11
+ * - sequence → an `rdf:List` of segments (via {@link rdfList})
12
+ * - inverse / alternative / cardinality → a `PathNode` operator node (alt's operand is an `rdf:List`)
13
+ *
14
+ * `base` seeds deterministic ids for the minted owned nodes (`{base}/inv`, `{base}/seq/0`, …) so a
15
+ * re-sync overwrites them in place; the owned subtree is cleaned by the containment cascade.
16
+ */
17
+ function serializePathToNodeData(path, base) {
18
+ const serialize = (e, b) => {
19
+ if ((0, PropertyPathExpr_js_1.isPathRef)(e)) {
20
+ return { id: typeof e === 'string' ? e : e.id };
21
+ }
22
+ if ('seq' in e) {
23
+ return (0, List_js_1.rdfList)(e.seq.map((s, i) => serialize(s, `${b}/seq/${i}`)), { base: `${b}/seq` });
24
+ }
25
+ if ('inv' in e) {
26
+ return { shape: PathNode_js_1.PathNode, __id: `${b}/inv`, inversePath: serialize(e.inv, `${b}/inv`) };
27
+ }
28
+ if ('alt' in e) {
29
+ return {
30
+ shape: PathNode_js_1.PathNode,
31
+ __id: `${b}/alt`,
32
+ alternativePath: (0, List_js_1.rdfList)(e.alt.map((a, i) => serialize(a, `${b}/alt/${i}`)), { base: `${b}/alt` }),
33
+ };
34
+ }
35
+ if ('zeroOrMore' in e) {
36
+ return { shape: PathNode_js_1.PathNode, __id: `${b}/zom`, zeroOrMorePath: serialize(e.zeroOrMore, `${b}/zom`) };
37
+ }
38
+ if ('oneOrMore' in e) {
39
+ return { shape: PathNode_js_1.PathNode, __id: `${b}/oom`, oneOrMorePath: serialize(e.oneOrMore, `${b}/oom`) };
40
+ }
41
+ if ('zeroOrOne' in e) {
42
+ return { shape: PathNode_js_1.PathNode, __id: `${b}/zoo`, zeroOrOnePath: serialize(e.zeroOrOne, `${b}/zoo`) };
43
+ }
44
+ if ('negatedPropertySet' in e) {
45
+ throw new Error('negatedPropertySet cannot be serialized to SHACL sh:path — no SHACL representation.');
46
+ }
47
+ throw new Error(`Unknown PathExpr shape: ${JSON.stringify(e)}`);
48
+ };
49
+ return serialize(path, base);
50
+ }
51
+ //# sourceMappingURL=serializePathToNodeData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serializePathToNodeData.js","sourceRoot":"","sources":["../../../src/shapes/serializePathToNodeData.ts"],"names":[],"mappings":";;AA6BA,0DAyCC;AAhED,sEAAuD;AAEvD,uCAAwC;AACxC,+CAAuC;AAUvC;;;;;;;;;GASG;AACH,SAAgB,uBAAuB,CAAC,IAAc,EAAE,IAAY;IAClE,MAAM,SAAS,GAAG,CAAC,CAAW,EAAE,CAAS,EAAgB,EAAE;QACzD,IAAI,IAAA,+BAAS,EAAC,CAAC,CAAC,EAAE,CAAC;YACjB,OAAO,EAAC,EAAE,EAAE,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAC,CAAC;QAChD,CAAC;QACD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,OAAO,IAAA,iBAAO,EACZ,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAClD,EAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAC,CACH,CAAC;QACpB,CAAC;QACD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,OAAO,EAAC,KAAK,EAAE,sBAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,EAAC,CAAC;QACxF,CAAC;QACD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACf,OAAO;gBACL,KAAK,EAAE,sBAAQ;gBACf,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,eAAe,EAAE,IAAA,iBAAO,EACtB,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAClD,EAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAC,CACnB;aACF,CAAC;QACJ,CAAC;QACD,IAAI,YAAY,IAAI,CAAC,EAAE,CAAC;YACtB,OAAO,EAAC,KAAK,EAAE,sBAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EAAC,CAAC;QAClG,CAAC;QACD,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO,EAAC,KAAK,EAAE,sBAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAC,CAAC;QAChG,CAAC;QACD,IAAI,WAAW,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO,EAAC,KAAK,EAAE,sBAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,EAAC,CAAC;QAChG,CAAC;QACD,IAAI,oBAAoB,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,KAAK,CACb,qFAAqF,CACtF,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC;IACF,OAAO,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Plan an idempotent sync of all code-registered (non-framework) NodeShapes into the store as
3
+ * SHACL data — the forward (code → graph) materialization of arch-05 "code is canonical".
4
+ *
5
+ * Returns built-but-unexecuted thunks; the caller controls execution and batching:
6
+ * ```ts
7
+ * await Promise.all((await syncShapes()).map((run) => run()));
8
+ * ```
9
+ * Each in-code shape's thunk runs `delete → create` in order (the delete cascade-cleans the old
10
+ * property shapes / list / path subtrees via the containment cascade, the create rebuilds them).
11
+ * Shapes present in the store but no longer in code are deleted as orphans (their owned subtree
12
+ * cascades too). Reads existing shape IRIs via the global query dispatch for orphan detection.
13
+ */
14
+ export declare function syncShapes(): Promise<Array<() => Promise<void>>>;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.syncShapes = syncShapes;
13
+ /*
14
+ * This Source Code Form is subject to the terms of the Mozilla Public
15
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
16
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
17
+ */
18
+ const ShapeClass_js_1 = require("../utils/ShapeClass.js");
19
+ const SHACL_js_1 = require("./SHACL.js");
20
+ const DeleteBuilder_js_1 = require("../queries/DeleteBuilder.js");
21
+ const List_js_1 = require("./List.js");
22
+ const serializePathToNodeData_js_1 = require("./serializePathToNodeData.js");
23
+ /**
24
+ * The npm scope of framework shapes (NodeShape/PropertyShape/List/PathNode/Shape) that
25
+ * describe the meta-model itself and must never be synced as user data.
26
+ */
27
+ const FRAMEWORK_PACKAGE = '@_linked/core';
28
+ /** Build the create-pipeline data object for a single PropertyShape (flattened under shapeIri). */
29
+ function buildPropertyShapeData(ps, shapeIri) {
30
+ const psIri = `${shapeIri}/${ps.label}`;
31
+ const d = {
32
+ __id: psIri,
33
+ path: (0, serializePathToNodeData_js_1.serializePathToNodeData)(ps.path, psIri),
34
+ };
35
+ if (ps.nodeKind)
36
+ d.nodeKind = ps.nodeKind;
37
+ if (ps.datatype)
38
+ d.datatype = ps.datatype;
39
+ if (typeof ps.minCount === 'number')
40
+ d.minCount = ps.minCount;
41
+ if (typeof ps.maxCount === 'number')
42
+ d.maxCount = ps.maxCount;
43
+ if (ps.name)
44
+ d.name = ps.name;
45
+ if (ps.description)
46
+ d.description = ps.description;
47
+ if (typeof ps.order === 'number')
48
+ d.order = ps.order;
49
+ if (ps.group)
50
+ d.group = ps.group;
51
+ if (ps.class)
52
+ d.class = ps.class;
53
+ if (ps.in)
54
+ d.in = (0, List_js_1.rdfList)(ps.in, { base: `${psIri}/in` });
55
+ if (ps.equalsConstraint)
56
+ d.equals = ps.equalsConstraint;
57
+ if (ps.disjoint)
58
+ d.disjoint = ps.disjoint;
59
+ if (ps.lessThan)
60
+ d.lessThan = ps.lessThan;
61
+ if (ps.lessThanOrEquals)
62
+ d.lessThanOrEquals = ps.lessThanOrEquals;
63
+ if (ps.hasValueConstraint !== undefined)
64
+ d.hasValue = ps.hasValueConstraint;
65
+ if (ps.valueShape)
66
+ d.valueShape = ps.valueShape;
67
+ if (ps.contains)
68
+ d.contains = true;
69
+ return d;
70
+ }
71
+ /** Build the create-pipeline data object for a NodeShape and its (flattened) property shapes. */
72
+ function buildNodeShapeData(nodeShape, shapeIri) {
73
+ const d = {};
74
+ if (nodeShape.targetClass)
75
+ d.targetClass = nodeShape.targetClass;
76
+ if (nodeShape.description)
77
+ d.description = nodeShape.description;
78
+ if (nodeShape.extends)
79
+ d.extends = nodeShape.extends;
80
+ if (nodeShape.dependent)
81
+ d.dependent = true;
82
+ if (nodeShape.closed !== undefined)
83
+ d.closed = nodeShape.closed;
84
+ if (nodeShape.ignoredProperties && nodeShape.ignoredProperties.length) {
85
+ d.ignoredProperties = nodeShape.ignoredProperties;
86
+ }
87
+ // Flatten: emit own + inherited property shapes (deduped by label) under this shape.
88
+ d.properties = nodeShape
89
+ .getUniquePropertyShapes()
90
+ .map((ps) => buildPropertyShapeData(ps, shapeIri));
91
+ return d;
92
+ }
93
+ /**
94
+ * Plan an idempotent sync of all code-registered (non-framework) NodeShapes into the store as
95
+ * SHACL data — the forward (code → graph) materialization of arch-05 "code is canonical".
96
+ *
97
+ * Returns built-but-unexecuted thunks; the caller controls execution and batching:
98
+ * ```ts
99
+ * await Promise.all((await syncShapes()).map((run) => run()));
100
+ * ```
101
+ * Each in-code shape's thunk runs `delete → create` in order (the delete cascade-cleans the old
102
+ * property shapes / list / path subtrees via the containment cascade, the create rebuilds them).
103
+ * Shapes present in the store but no longer in code are deleted as orphans (their owned subtree
104
+ * cascades too). Reads existing shape IRIs via the global query dispatch for orphan detection.
105
+ */
106
+ function syncShapes() {
107
+ return __awaiter(this, void 0, void 0, function* () {
108
+ // 1. Enumerate code-registered user shapes (exclude framework/meta shapes).
109
+ const userShapes = [];
110
+ for (const [iri, shapeClass] of (0, ShapeClass_js_1.getAllShapeClasses)()) {
111
+ if (!(shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.shape))
112
+ continue;
113
+ const pkg = shapeClass.packageName;
114
+ // Skip framework/meta shapes: the core package, and the base `Shape` which is registered
115
+ // directly (no applyLinkedShape) and therefore carries no packageName.
116
+ if (!pkg || pkg === FRAMEWORK_PACKAGE)
117
+ continue;
118
+ userShapes.push({ iri, nodeShape: shapeClass.shape });
119
+ }
120
+ const localShapeIris = new Set(userShapes.map((s) => s.iri));
121
+ // 2. Identity-read existing NodeShape IRIs (ids only) for orphan detection.
122
+ const existingRows = (yield SHACL_js_1.NodeShape.select());
123
+ const existingShapeIris = new Set(existingRows.map((r) => r.id));
124
+ const thunks = [];
125
+ // 3. Per in-code shape: delete (cascade) then recreate.
126
+ for (const { iri, nodeShape } of userShapes) {
127
+ const data = buildNodeShapeData(nodeShape, iri);
128
+ thunks.push(() => DeleteBuilder_js_1.DeleteBuilder.from(SHACL_js_1.NodeShape, { id: iri }).exec()
129
+ .then(() => (SHACL_js_1.NodeShape.create(data).withId(iri).exec()))
130
+ .then(() => undefined));
131
+ }
132
+ // 4. Orphan shapes (in store, not in code) → delete (cascade cleans their owned subtree).
133
+ for (const iri of existingShapeIris) {
134
+ if (!localShapeIris.has(iri)) {
135
+ thunks.push(() => DeleteBuilder_js_1.DeleteBuilder.from(SHACL_js_1.NodeShape, { id: iri }).exec().then(() => undefined));
136
+ }
137
+ }
138
+ return thunks;
139
+ });
140
+ }
141
+ //# sourceMappingURL=syncShapes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncShapes.js","sourceRoot":"","sources":["../../../src/shapes/syncShapes.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import type { IRSelectQuery, IRCreateMutation, IRUpdateMutation, IRDeleteMutation, IRDeleteAllMutation, IRDeleteWhereMutation, IRUpdateWhereMutation } from '../queries/IntermediateRepresentation.js';
2
- import type { SparqlSelectPlan, SparqlInsertDataPlan, SparqlDeleteInsertPlan } from './SparqlAlgebra.js';
2
+ import type { SparqlSelectPlan, SparqlInsertDataPlan, SparqlDeleteInsertPlan, SparqlAlgebraNode, SparqlTriple, SparqlTerm } from './SparqlAlgebra.js';
3
3
  import { type SparqlOptions } from './sparqlUtils.js';
4
4
  /**
5
5
  * Converts an IRSelectQuery to a SparqlSelectPlan.
@@ -13,6 +13,15 @@ export declare function createToAlgebra(query: IRCreateMutation, options?: Sparq
13
13
  * Converts an IRUpdateMutation to a SparqlDeleteInsertPlan.
14
14
  */
15
15
  export declare function updateToAlgebra(query: IRUpdateMutation, options?: SparqlOptions): SparqlDeleteInsertPlan;
16
+ /**
17
+ * Build DELETE patterns + WHERE OPTIONAL blocks that cascade-delete the owned subtree
18
+ * reachable from `rootTerm` via `contains` edges. `varPrefix` keeps generated variables
19
+ * unique across multiple roots in one query. Returns empty when nothing is owned.
20
+ */
21
+ export declare function buildOwnedCascade(rootTerm: SparqlTerm, varPrefix: string): {
22
+ deletePatterns: SparqlTriple[];
23
+ whereOptionals: SparqlAlgebraNode[];
24
+ };
16
25
  /**
17
26
  * Converts an IRDeleteMutation to a SparqlDeleteInsertPlan (DELETE + WHERE).
18
27
  */
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.selectToAlgebra = selectToAlgebra;
4
4
  exports.createToAlgebra = createToAlgebra;
5
5
  exports.updateToAlgebra = updateToAlgebra;
6
+ exports.buildOwnedCascade = buildOwnedCascade;
6
7
  exports.deleteToAlgebra = deleteToAlgebra;
7
8
  exports.deleteAllToAlgebra = deleteAllToAlgebra;
8
9
  exports.deleteWhereToAlgebra = deleteWhereToAlgebra;
@@ -487,10 +488,19 @@ function selectToAlgebra(query, _options) {
487
488
  const requiredTraversalAliases = collectRequiredTraversalAliases(query, traversePatternMap);
488
489
  const optionalTraversalAliases = new Set([...projectedTraversalAliases].filter((alias) => {
489
490
  const pattern = traversePatternMap.get(alias);
491
+ // Lower projection-only traversals into OPTIONAL subtrees regardless of
492
+ // cardinality, so a parent with an empty relationship is preserved (with
493
+ // an empty array / null child) instead of being inner-joined away. The
494
+ // result grouper already collects multiple child bindings into an array.
495
+ //
496
+ // Excluded:
497
+ // - filtered traversals (`.where(...)`) — the filter makes the child required
498
+ // - aliases otherwise required by the query (e.g. used in a top-level filter)
499
+ // - paginated traversals — emitted separately as a sub-SELECT (section 5a),
500
+ // so lowering them here too would double-emit the traverse triple
490
501
  return !!pattern &&
491
- typeof pattern.maxCount === 'number' &&
492
- pattern.maxCount <= 1 &&
493
502
  !pattern.filter &&
503
+ !subSelectAliases.has(alias) &&
494
504
  !requiredTraversalAliases.has(alias);
495
505
  }));
496
506
  const requiredTraverseTriples = traverseTriples.filter((triple) => !(triple.object.kind === 'variable' &&
@@ -1226,9 +1236,15 @@ function processUpdateFields(data, subjectTerm, options) {
1226
1236
  const insertPatterns = [];
1227
1237
  const oldValueTriples = [];
1228
1238
  const extends_ = [];
1239
+ // Owned-subtree cascade: replacing a `contains` property must also
1240
+ // delete the old object's owned subtree, not just the one-hop edge.
1241
+ const { containsPreds: updContainsPreds } = collectContainment();
1242
+ const containsOldVars = [];
1243
+ const cascadeOptionals = [];
1229
1244
  for (const field of data.fields) {
1230
1245
  const propertyTerm = resolvePropertyPredicateTerm(field.property);
1231
1246
  const suffix = propertySuffix(field.property);
1247
+ const isContainsField = propertyTerm.kind === 'iri' && updContainsPreds.includes(propertyTerm.value);
1232
1248
  // Check for set modification ({add, remove})
1233
1249
  if (field.value &&
1234
1250
  typeof field.value === 'object' &&
@@ -1239,11 +1255,17 @@ function processUpdateFields(data, subjectTerm, options) {
1239
1255
  ('add' in field.value || 'remove' in field.value)) {
1240
1256
  const setMod = field.value;
1241
1257
  if (setMod.remove) {
1242
- for (const removeItem of setMod.remove) {
1258
+ setMod.remove.forEach((removeItem, ri) => {
1243
1259
  const removeTerm = iriTerm(removeItem.id);
1244
1260
  deletePatterns.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
1245
1261
  oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
1246
- }
1262
+ // For a `contains` set, removing a value also removes its owned subtree.
1263
+ if (isContainsField) {
1264
+ const cascade = buildOwnedCascade(removeTerm, `ucr_${suffix}_${ri}_`);
1265
+ deletePatterns.push(...cascade.deletePatterns);
1266
+ cascadeOptionals.push(...cascade.whereOptionals);
1267
+ }
1268
+ });
1247
1269
  }
1248
1270
  if (setMod.add) {
1249
1271
  for (const addItem of setMod.add) {
@@ -1262,6 +1284,11 @@ function processUpdateFields(data, subjectTerm, options) {
1262
1284
  }
1263
1285
  continue;
1264
1286
  }
1287
+ // Non-set-modification replace of a `contains` property: cascade the old value's
1288
+ // subtree (every branch below binds `old_${suffix}` in the WHERE).
1289
+ if (isContainsField) {
1290
+ containsOldVars.push(`old_${suffix}`);
1291
+ }
1265
1292
  // Unset (undefined/null) — delete only
1266
1293
  if (field.value === undefined || field.value === null) {
1267
1294
  const oldVar = varTerm(`old_${suffix}`);
@@ -1343,7 +1370,19 @@ function processUpdateFields(data, subjectTerm, options) {
1343
1370
  insertPatterns.push(tripleOf(subjectTerm, propertyTerm, term));
1344
1371
  }
1345
1372
  }
1346
- return { deletePatterns, insertPatterns, oldValueTriples, extends: extends_ };
1373
+ // Append cascade for each replaced `contains` property's old value.
1374
+ containsOldVars.forEach((oldVar, n) => {
1375
+ const cascade = buildOwnedCascade(varTerm(oldVar), `uc${n}_`);
1376
+ deletePatterns.push(...cascade.deletePatterns);
1377
+ cascadeOptionals.push(...cascade.whereOptionals);
1378
+ });
1379
+ return {
1380
+ deletePatterns,
1381
+ insertPatterns,
1382
+ oldValueTriples,
1383
+ extends: extends_,
1384
+ cascadeOptionals,
1385
+ };
1347
1386
  }
1348
1387
  /**
1349
1388
  * Wraps old-value triples in OPTIONAL (LEFT JOIN) so UPDATE succeeds
@@ -1383,6 +1422,10 @@ function updateToAlgebra(query, options) {
1383
1422
  };
1384
1423
  }
1385
1424
  }
1425
+ // Owned-subtree cascade OPTIONALs for replaced `contains` properties.
1426
+ for (const optional of result.cascadeOptionals) {
1427
+ whereAlgebra = { type: 'left_join', left: whereAlgebra, right: optional };
1428
+ }
1386
1429
  // Add BIND expressions for computed fields
1387
1430
  for (const ext of result.extends) {
1388
1431
  whereAlgebra = {
@@ -1399,6 +1442,90 @@ function updateToAlgebra(query, options) {
1399
1442
  whereAlgebra,
1400
1443
  };
1401
1444
  }
1445
+ // ---------------------------------------------------------------------------
1446
+ // Owned-subtree cascade
1447
+ //
1448
+ // Composition cleanup driven by two declarative flags:
1449
+ // - a property marked `contains` → the cascade FOLLOWS that edge,
1450
+ // - a shape marked `dependent` → its instances may be DELETED when reached.
1451
+ // Both are read from the live registry, so no vocabulary is hardcoded. From a root
1452
+ // node we follow `(c1|c2|…)+` over all contains predicates and, for each dependent
1453
+ // targetClass, wildcard-delete the reached typed nodes. Requiring an asserted
1454
+ // `?owned a <dependentType>` naturally excludes shared predicate IRIs (e.g. a simple
1455
+ // `sh:path`) and `rdf:nil` (never typed), so only owned structural nodes are removed.
1456
+ // ---------------------------------------------------------------------------
1457
+ /** True when the shape (or an ancestor) declares at least one `contains` property. */
1458
+ function shapeHasContainsProperty(shapeId) {
1459
+ var _a;
1460
+ const nodeShape = (_a = (0, ShapeClass_js_1.getShapeClass)(shapeId)) === null || _a === void 0 ? void 0 : _a.shape;
1461
+ if (!nodeShape || typeof nodeShape.getPropertyShapes !== 'function')
1462
+ return false;
1463
+ return nodeShape
1464
+ .getPropertyShapes(true)
1465
+ .some((ps) => ps.contains);
1466
+ }
1467
+ /** Gather contains-predicate IRIs and dependent targetClass IRIs from the registry. */
1468
+ function collectContainment() {
1469
+ var _a;
1470
+ const containsPreds = new Set();
1471
+ const dependentTypes = new Set();
1472
+ for (const [, shapeClass] of (0, ShapeClass_js_1.getAllShapeClasses)()) {
1473
+ const nodeShape = shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.shape;
1474
+ if (!nodeShape)
1475
+ continue;
1476
+ if (nodeShape.dependent && ((_a = nodeShape.targetClass) === null || _a === void 0 ? void 0 : _a.id)) {
1477
+ dependentTypes.add(nodeShape.targetClass.id);
1478
+ }
1479
+ const propertyShapes = typeof nodeShape.getPropertyShapes === 'function'
1480
+ ? nodeShape.getPropertyShapes(false)
1481
+ : [];
1482
+ for (const ps of propertyShapes) {
1483
+ if (ps.contains) {
1484
+ const predId = (0, normalizePropertyPath_js_1.getSimplePathId)(ps.path);
1485
+ if (predId)
1486
+ containsPreds.add(predId);
1487
+ }
1488
+ }
1489
+ }
1490
+ return { containsPreds: [...containsPreds], dependentTypes: [...dependentTypes] };
1491
+ }
1492
+ /**
1493
+ * Build DELETE patterns + WHERE OPTIONAL blocks that cascade-delete the owned subtree
1494
+ * reachable from `rootTerm` via `contains` edges. `varPrefix` keeps generated variables
1495
+ * unique across multiple roots in one query. Returns empty when nothing is owned.
1496
+ */
1497
+ function buildOwnedCascade(rootTerm, varPrefix) {
1498
+ const { containsPreds, dependentTypes } = collectContainment();
1499
+ if (containsPreds.length === 0 || dependentTypes.length === 0) {
1500
+ return { deletePatterns: [], whereOptionals: [] };
1501
+ }
1502
+ // (c1|c2|…)+ — follow one-or-more contains edges from the root.
1503
+ const pathExpr = containsPreds.length === 1
1504
+ ? { oneOrMore: { id: containsPreds[0] } }
1505
+ : { oneOrMore: { alt: containsPreds.map((id) => ({ id })) } };
1506
+ const pathTerm = {
1507
+ kind: 'path',
1508
+ value: (0, pathExprToSparql_js_1.pathExprToSparql)(pathExpr),
1509
+ uris: (0, pathExprToSparql_js_1.collectPathUris)(pathExpr),
1510
+ };
1511
+ const deletePatterns = [];
1512
+ const whereOptionals = [];
1513
+ dependentTypes.forEach((typeId, i) => {
1514
+ const owned = varTerm(`${varPrefix}own${i}`);
1515
+ const p = varTerm(`${varPrefix}op${i}`);
1516
+ const o = varTerm(`${varPrefix}ov${i}`);
1517
+ deletePatterns.push(tripleOf(owned, p, o));
1518
+ whereOptionals.push({
1519
+ type: 'bgp',
1520
+ triples: [
1521
+ tripleOf(rootTerm, pathTerm, owned),
1522
+ tripleOf(owned, iriTerm(RDF_TYPE), iriTerm(typeId)),
1523
+ tripleOf(owned, p, o),
1524
+ ],
1525
+ });
1526
+ });
1527
+ return { deletePatterns, whereOptionals };
1528
+ }
1402
1529
  /**
1403
1530
  * Converts an IRDeleteMutation to a SparqlDeleteInsertPlan (DELETE + WHERE).
1404
1531
  */
@@ -1406,6 +1533,7 @@ function deleteToAlgebra(query, _options) {
1406
1533
  const deletePatterns = [];
1407
1534
  const requiredTriples = [];
1408
1535
  const optionalTriples = [];
1536
+ const cascadeOptionals = [];
1409
1537
  for (let i = 0; i < query.ids.length; i++) {
1410
1538
  const subjectTerm = iriTerm(query.ids[i].id);
1411
1539
  const idx = query.ids.length > 1 ? `_${i}` : '';
@@ -1418,8 +1546,15 @@ function deleteToAlgebra(query, _options) {
1418
1546
  // object-wildcard is OPTIONAL (entity may have no incoming references)
1419
1547
  requiredTriples.push(subjWild, typeGuard);
1420
1548
  optionalTriples.push(objWild);
1549
+ // Cascade-delete the owned subtree — only for shapes that actually own something
1550
+ // (have a `contains` property); a plain entity delete is left untouched.
1551
+ if (shapeHasContainsProperty(query.shape)) {
1552
+ const cascade = buildOwnedCascade(subjectTerm, `c${idx}_`);
1553
+ deletePatterns.push(...cascade.deletePatterns);
1554
+ cascadeOptionals.push(...cascade.whereOptionals);
1555
+ }
1421
1556
  }
1422
- // Build WHERE algebra: required BGP + OPTIONAL for each object-wildcard
1557
+ // Build WHERE algebra: required BGP + OPTIONAL for each object-wildcard + cascade OPTIONALs
1423
1558
  let whereAlgebra = { type: 'bgp', triples: requiredTriples };
1424
1559
  for (const triple of optionalTriples) {
1425
1560
  whereAlgebra = {
@@ -1428,6 +1563,9 @@ function deleteToAlgebra(query, _options) {
1428
1563
  right: { type: 'bgp', triples: [triple] },
1429
1564
  };
1430
1565
  }
1566
+ for (const optional of cascadeOptionals) {
1567
+ whereAlgebra = { type: 'left_join', left: whereAlgebra, right: optional };
1568
+ }
1431
1569
  return {
1432
1570
  type: 'delete_insert',
1433
1571
  deletePatterns,
@@ -1629,6 +1767,10 @@ function updateWhereToAlgebra(query, options) {
1629
1767
  };
1630
1768
  }
1631
1769
  }
1770
+ // Owned-subtree cascade OPTIONALs for replaced `contains` properties.
1771
+ for (const optional of result.cascadeOptionals) {
1772
+ whereAlgebra = { type: 'left_join', left: whereAlgebra, right: optional };
1773
+ }
1632
1774
  // Add BIND expressions for computed fields
1633
1775
  for (const ext of result.extends) {
1634
1776
  whereAlgebra = {