@_linked/core 2.0.0 → 2.2.0-next.20260313111019

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 (119) hide show
  1. package/CHANGELOG.md +33 -171
  2. package/README.md +197 -45
  3. package/lib/cjs/expressions/Expr.d.ts +58 -0
  4. package/lib/cjs/expressions/Expr.js +217 -0
  5. package/lib/cjs/expressions/Expr.js.map +1 -0
  6. package/lib/cjs/expressions/ExpressionMethods.d.ts +81 -0
  7. package/lib/cjs/expressions/ExpressionMethods.js +3 -0
  8. package/lib/cjs/expressions/ExpressionMethods.js.map +1 -0
  9. package/lib/cjs/expressions/ExpressionNode.d.ts +95 -0
  10. package/lib/cjs/expressions/ExpressionNode.js +349 -0
  11. package/lib/cjs/expressions/ExpressionNode.js.map +1 -0
  12. package/lib/cjs/index.d.ts +4 -0
  13. package/lib/cjs/index.js +6 -1
  14. package/lib/cjs/index.js.map +1 -1
  15. package/lib/cjs/queries/DeleteBuilder.d.ts +17 -20
  16. package/lib/cjs/queries/DeleteBuilder.js +46 -19
  17. package/lib/cjs/queries/DeleteBuilder.js.map +1 -1
  18. package/lib/cjs/queries/DeleteQuery.d.ts +2 -2
  19. package/lib/cjs/queries/DeleteQuery.js.map +1 -1
  20. package/lib/cjs/queries/FieldSet.d.ts +3 -0
  21. package/lib/cjs/queries/FieldSet.js +16 -0
  22. package/lib/cjs/queries/FieldSet.js.map +1 -1
  23. package/lib/cjs/queries/IRCanonicalize.d.ts +10 -3
  24. package/lib/cjs/queries/IRCanonicalize.js +10 -1
  25. package/lib/cjs/queries/IRCanonicalize.js.map +1 -1
  26. package/lib/cjs/queries/IRDesugar.d.ts +30 -2
  27. package/lib/cjs/queries/IRDesugar.js +29 -9
  28. package/lib/cjs/queries/IRDesugar.js.map +1 -1
  29. package/lib/cjs/queries/IRLower.d.ts +19 -2
  30. package/lib/cjs/queries/IRLower.js +104 -20
  31. package/lib/cjs/queries/IRLower.js.map +1 -1
  32. package/lib/cjs/queries/IRMutation.d.ts +31 -1
  33. package/lib/cjs/queries/IRMutation.js +68 -15
  34. package/lib/cjs/queries/IRMutation.js.map +1 -1
  35. package/lib/cjs/queries/IntermediateRepresentation.d.ts +33 -4
  36. package/lib/cjs/queries/MutationQuery.js +16 -2
  37. package/lib/cjs/queries/MutationQuery.js.map +1 -1
  38. package/lib/cjs/queries/QueryBuilder.d.ts +14 -1
  39. package/lib/cjs/queries/QueryBuilder.js +59 -2
  40. package/lib/cjs/queries/QueryBuilder.js.map +1 -1
  41. package/lib/cjs/queries/QueryFactory.d.ts +2 -1
  42. package/lib/cjs/queries/QueryFactory.js.map +1 -1
  43. package/lib/cjs/queries/SelectQuery.d.ts +6 -2
  44. package/lib/cjs/queries/SelectQuery.js +51 -7
  45. package/lib/cjs/queries/SelectQuery.js.map +1 -1
  46. package/lib/cjs/queries/UpdateBuilder.d.ts +22 -13
  47. package/lib/cjs/queries/UpdateBuilder.js +60 -11
  48. package/lib/cjs/queries/UpdateBuilder.js.map +1 -1
  49. package/lib/cjs/queries/UpdateQuery.d.ts +2 -2
  50. package/lib/cjs/shapes/Shape.d.ts +8 -2
  51. package/lib/cjs/shapes/Shape.js +9 -13
  52. package/lib/cjs/shapes/Shape.js.map +1 -1
  53. package/lib/cjs/sparql/SparqlStore.js +15 -0
  54. package/lib/cjs/sparql/SparqlStore.js.map +1 -1
  55. package/lib/cjs/sparql/irToAlgebra.d.ts +34 -3
  56. package/lib/cjs/sparql/irToAlgebra.js +380 -31
  57. package/lib/cjs/sparql/irToAlgebra.js.map +1 -1
  58. package/lib/cjs/test-helpers/query-fixtures.d.ts +96 -208
  59. package/lib/cjs/test-helpers/query-fixtures.js +96 -19
  60. package/lib/cjs/test-helpers/query-fixtures.js.map +1 -1
  61. package/lib/esm/expressions/Expr.d.ts +58 -0
  62. package/lib/esm/expressions/Expr.js +214 -0
  63. package/lib/esm/expressions/Expr.js.map +1 -0
  64. package/lib/esm/expressions/ExpressionMethods.d.ts +81 -0
  65. package/lib/esm/expressions/ExpressionMethods.js +2 -0
  66. package/lib/esm/expressions/ExpressionMethods.js.map +1 -0
  67. package/lib/esm/expressions/ExpressionNode.d.ts +95 -0
  68. package/lib/esm/expressions/ExpressionNode.js +341 -0
  69. package/lib/esm/expressions/ExpressionNode.js.map +1 -0
  70. package/lib/esm/index.d.ts +4 -0
  71. package/lib/esm/index.js +3 -0
  72. package/lib/esm/index.js.map +1 -1
  73. package/lib/esm/queries/DeleteBuilder.d.ts +17 -20
  74. package/lib/esm/queries/DeleteBuilder.js +46 -19
  75. package/lib/esm/queries/DeleteBuilder.js.map +1 -1
  76. package/lib/esm/queries/DeleteQuery.d.ts +2 -2
  77. package/lib/esm/queries/DeleteQuery.js.map +1 -1
  78. package/lib/esm/queries/FieldSet.d.ts +3 -0
  79. package/lib/esm/queries/FieldSet.js +16 -0
  80. package/lib/esm/queries/FieldSet.js.map +1 -1
  81. package/lib/esm/queries/IRCanonicalize.d.ts +10 -3
  82. package/lib/esm/queries/IRCanonicalize.js +10 -1
  83. package/lib/esm/queries/IRCanonicalize.js.map +1 -1
  84. package/lib/esm/queries/IRDesugar.d.ts +30 -2
  85. package/lib/esm/queries/IRDesugar.js +21 -2
  86. package/lib/esm/queries/IRDesugar.js.map +1 -1
  87. package/lib/esm/queries/IRLower.d.ts +19 -2
  88. package/lib/esm/queries/IRLower.js +101 -19
  89. package/lib/esm/queries/IRLower.js.map +1 -1
  90. package/lib/esm/queries/IRMutation.d.ts +31 -1
  91. package/lib/esm/queries/IRMutation.js +63 -14
  92. package/lib/esm/queries/IRMutation.js.map +1 -1
  93. package/lib/esm/queries/IntermediateRepresentation.d.ts +33 -4
  94. package/lib/esm/queries/MutationQuery.js +16 -2
  95. package/lib/esm/queries/MutationQuery.js.map +1 -1
  96. package/lib/esm/queries/QueryBuilder.d.ts +14 -1
  97. package/lib/esm/queries/QueryBuilder.js +59 -2
  98. package/lib/esm/queries/QueryBuilder.js.map +1 -1
  99. package/lib/esm/queries/QueryFactory.d.ts +2 -1
  100. package/lib/esm/queries/QueryFactory.js.map +1 -1
  101. package/lib/esm/queries/SelectQuery.d.ts +6 -2
  102. package/lib/esm/queries/SelectQuery.js +51 -7
  103. package/lib/esm/queries/SelectQuery.js.map +1 -1
  104. package/lib/esm/queries/UpdateBuilder.d.ts +22 -13
  105. package/lib/esm/queries/UpdateBuilder.js +60 -11
  106. package/lib/esm/queries/UpdateBuilder.js.map +1 -1
  107. package/lib/esm/queries/UpdateQuery.d.ts +2 -2
  108. package/lib/esm/shapes/Shape.d.ts +8 -2
  109. package/lib/esm/shapes/Shape.js +9 -13
  110. package/lib/esm/shapes/Shape.js.map +1 -1
  111. package/lib/esm/sparql/SparqlStore.js +16 -1
  112. package/lib/esm/sparql/SparqlStore.js.map +1 -1
  113. package/lib/esm/sparql/irToAlgebra.d.ts +34 -3
  114. package/lib/esm/sparql/irToAlgebra.js +374 -31
  115. package/lib/esm/sparql/irToAlgebra.js.map +1 -1
  116. package/lib/esm/test-helpers/query-fixtures.d.ts +96 -208
  117. package/lib/esm/test-helpers/query-fixtures.js +96 -19
  118. package/lib/esm/test-helpers/query-fixtures.js.map +1 -1
  119. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
- import { IRSelectQuery, IRCreateMutation, IRUpdateMutation, IRDeleteMutation } from '../queries/IntermediateRepresentation.js';
1
+ import { IRSelectQuery, IRCreateMutation, IRUpdateMutation, IRDeleteMutation, IRDeleteAllMutation, IRDeleteWhereMutation, IRUpdateWhereMutation } from '../queries/IntermediateRepresentation.js';
2
2
  import { SparqlSelectPlan, SparqlInsertDataPlan, SparqlDeleteInsertPlan } from './SparqlAlgebra.js';
3
3
  import { SparqlOptions } from './sparqlUtils.js';
4
4
  /**
@@ -17,9 +17,29 @@ export declare function updateToAlgebra(query: IRUpdateMutation, options?: Sparq
17
17
  * Converts an IRDeleteMutation to a SparqlDeleteInsertPlan (DELETE + WHERE).
18
18
  */
19
19
  export declare function deleteToAlgebra(query: IRDeleteMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
20
+ /**
21
+ * Converts an IRDeleteAllMutation to a SparqlDeleteInsertPlan.
22
+ *
23
+ * Generates DELETE { ?a0 ?p ?o . [blank node wildcards] }
24
+ * WHERE { ?a0 a <Shape> . ?a0 ?p ?o . OPTIONAL { [blank node traversals] } }
25
+ */
26
+ export declare function deleteAllToAlgebra(query: IRDeleteAllMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
27
+ /**
28
+ * Converts an IRDeleteWhereMutation to a SparqlDeleteInsertPlan.
29
+ *
30
+ * Like deleteAllToAlgebra but adds filter conditions from the where clause.
31
+ */
32
+ export declare function deleteWhereToAlgebra(query: IRDeleteWhereMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
33
+ /**
34
+ * Converts an IRUpdateWhereMutation to a SparqlDeleteInsertPlan.
35
+ *
36
+ * Like updateToAlgebra but uses a variable subject (?a0) instead of a
37
+ * hardcoded entity IRI, adds a type triple, and optionally includes
38
+ * filter conditions from the where clause.
39
+ */
40
+ export declare function updateWhereToAlgebra(query: IRUpdateWhereMutation, options?: SparqlOptions): SparqlDeleteInsertPlan;
20
41
  /**
21
42
  * Converts an IRSelectQuery to a SPARQL string.
22
- * Stub: will be implemented when algebraToString is available.
23
43
  */
24
44
  export declare function selectToSparql(query: IRSelectQuery, options?: SparqlOptions): string;
25
45
  /**
@@ -34,6 +54,17 @@ export declare function createToSparql(query: IRCreateMutation, options?: Sparql
34
54
  export declare function updateToSparql(query: IRUpdateMutation, options?: SparqlOptions): string;
35
55
  /**
36
56
  * Converts an IRDeleteMutation to a SPARQL string.
37
- * Stub: will be implemented when algebraToString is available.
38
57
  */
39
58
  export declare function deleteToSparql(query: IRDeleteMutation, options?: SparqlOptions): string;
59
+ /**
60
+ * Converts an IRDeleteAllMutation to a SPARQL string.
61
+ */
62
+ export declare function deleteAllToSparql(query: IRDeleteAllMutation, options?: SparqlOptions): string;
63
+ /**
64
+ * Converts an IRDeleteWhereMutation to a SPARQL string.
65
+ */
66
+ export declare function deleteWhereToSparql(query: IRDeleteWhereMutation, options?: SparqlOptions): string;
67
+ /**
68
+ * Converts an IRUpdateWhereMutation to a SPARQL string.
69
+ */
70
+ export declare function updateWhereToSparql(query: IRUpdateWhereMutation, options?: SparqlOptions): string;
@@ -4,12 +4,36 @@ exports.selectToAlgebra = selectToAlgebra;
4
4
  exports.createToAlgebra = createToAlgebra;
5
5
  exports.updateToAlgebra = updateToAlgebra;
6
6
  exports.deleteToAlgebra = deleteToAlgebra;
7
+ exports.deleteAllToAlgebra = deleteAllToAlgebra;
8
+ exports.deleteWhereToAlgebra = deleteWhereToAlgebra;
9
+ exports.updateWhereToAlgebra = updateWhereToAlgebra;
7
10
  exports.selectToSparql = selectToSparql;
8
11
  exports.createToSparql = createToSparql;
9
12
  exports.updateToSparql = updateToSparql;
10
13
  exports.deleteToSparql = deleteToSparql;
14
+ exports.deleteAllToSparql = deleteAllToSparql;
15
+ exports.deleteWhereToSparql = deleteWhereToSparql;
16
+ exports.updateWhereToSparql = updateWhereToSparql;
11
17
  const sparqlUtils_js_1 = require("./sparqlUtils.js");
12
18
  const algebraToString_js_1 = require("./algebraToString.js");
19
+ // Lazy-loaded to avoid circular dependency:
20
+ // irToAlgebra → ShapeClass → Shape → CreateBuilder → … → SHACL → Shape (circular)
21
+ let _getShapeClass;
22
+ function lazyGetShapeClass(id) {
23
+ if (!_getShapeClass) {
24
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
25
+ _getShapeClass = require('../utils/ShapeClass.js').getShapeClass;
26
+ }
27
+ return _getShapeClass(id);
28
+ }
29
+ let _shacl;
30
+ function lazyShacl() {
31
+ if (!_shacl) {
32
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
33
+ _shacl = require('../ontologies/shacl.js').shacl;
34
+ }
35
+ return _shacl;
36
+ }
13
37
  const rdf_js_1 = require("../ontologies/rdf.js");
14
38
  const xsd_js_1 = require("../ontologies/xsd.js");
15
39
  // ---------------------------------------------------------------------------
@@ -53,6 +77,16 @@ function propertySuffix(propertyUri) {
53
77
  function sanitizeVarName(name) {
54
78
  return name.replace(/[^A-Za-z0-9_]/g, '_');
55
79
  }
80
+ const IR_EXPRESSION_KINDS = new Set([
81
+ 'literal_expr', 'property_expr', 'binary_expr', 'logical_expr',
82
+ 'not_expr', 'function_expr', 'aggregate_expr', 'reference_expr',
83
+ 'alias_expr', 'context_property_expr', 'exists_expr',
84
+ ]);
85
+ function isIRExpression(value) {
86
+ return !!value && typeof value === 'object' && 'kind' in value &&
87
+ typeof value.kind === 'string' &&
88
+ IR_EXPRESSION_KINDS.has(value.kind);
89
+ }
56
90
  /**
57
91
  * Wrap a single node in a LeftJoin, making `right` optional relative to `left`.
58
92
  */
@@ -238,6 +272,23 @@ function selectToAlgebra(query, _options) {
238
272
  };
239
273
  }
240
274
  }
275
+ // 5b. MINUS patterns — wrap algebra in SparqlMinus for each minus pattern
276
+ for (const pattern of query.patterns) {
277
+ if (pattern.kind === 'minus') {
278
+ let minusAlgebra = convertExistsPattern(pattern.pattern, registry);
279
+ if (pattern.filter) {
280
+ const minusPropertyTriples = [];
281
+ processExpressionForProperties(pattern.filter, registry, minusPropertyTriples);
282
+ // Add property triples into the MINUS block
283
+ if (minusPropertyTriples.length > 0) {
284
+ minusAlgebra = joinNodes(minusAlgebra, { type: 'bgp', triples: minusPropertyTriples });
285
+ }
286
+ const filterExpr = convertExpression(pattern.filter, registry, minusPropertyTriples);
287
+ minusAlgebra = { type: 'filter', expression: filterExpr, inner: minusAlgebra };
288
+ }
289
+ algebra = { type: 'minus', left: algebra, right: minusAlgebra };
290
+ }
291
+ }
241
292
  // 6. SubjectId → Filter / SubjectIds → VALUES
242
293
  if (query.subjectIds && query.subjectIds.length > 0) {
243
294
  // Multiple subjects: use VALUES clause for efficient filtering
@@ -403,6 +454,10 @@ function processPattern(pattern, registry, traverseTriples, optionalPropertyTrip
403
454
  processPattern(pattern.pattern, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks);
404
455
  break;
405
456
  }
457
+ case 'minus': {
458
+ // MINUS patterns are handled separately in selectToAlgebra — skip in processPattern.
459
+ break;
460
+ }
406
461
  }
407
462
  }
408
463
  // ---------------------------------------------------------------------------
@@ -614,6 +669,9 @@ function convertExistsPattern(pattern, registry) {
614
669
  case 'exists': {
615
670
  return convertExistsPattern(pattern.pattern, registry);
616
671
  }
672
+ case 'minus': {
673
+ return convertExistsPattern(pattern.pattern, registry);
674
+ }
617
675
  default:
618
676
  throw new Error(`Unsupported pattern kind in EXISTS: ${pattern.kind}`);
619
677
  }
@@ -733,15 +791,19 @@ function createToAlgebra(query, options) {
733
791
  triples,
734
792
  };
735
793
  }
794
+ // ---------------------------------------------------------------------------
795
+ // Shared update field processing
796
+ // ---------------------------------------------------------------------------
736
797
  /**
737
- * Converts an IRUpdateMutation to a SparqlDeleteInsertPlan.
798
+ * Processes IRNodeData fields into DELETE/INSERT/WHERE triples.
799
+ * Shared between updateToAlgebra (IRI subject) and updateWhereToAlgebra (variable subject).
738
800
  */
739
- function updateToAlgebra(query, options) {
740
- const subjectTerm = iriTerm(query.id);
801
+ function processUpdateFields(data, subjectTerm, options) {
741
802
  const deletePatterns = [];
742
803
  const insertPatterns = [];
743
- const whereTriples = [];
744
- for (const field of query.data.fields) {
804
+ const oldValueTriples = [];
805
+ const extends_ = [];
806
+ for (const field of data.fields) {
745
807
  const propertyTerm = iriTerm(field.property);
746
808
  const suffix = propertySuffix(field.property);
747
809
  // Check for set modification ({add, remove})
@@ -753,19 +815,16 @@ function updateToAlgebra(query, options) {
753
815
  !('shape' in field.value) &&
754
816
  ('add' in field.value || 'remove' in field.value)) {
755
817
  const setMod = field.value;
756
- // Remove specific values
757
818
  if (setMod.remove) {
758
819
  for (const removeItem of setMod.remove) {
759
820
  const removeTerm = iriTerm(removeItem.id);
760
821
  deletePatterns.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
761
- whereTriples.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
822
+ oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
762
823
  }
763
824
  }
764
- // Add new values
765
825
  if (setMod.add) {
766
826
  for (const addItem of setMod.add) {
767
827
  if (addItem && typeof addItem === 'object' && 'shape' in addItem && 'fields' in addItem) {
768
- // Nested create in add
769
828
  const nested = generateNodeDataTriples(addItem, options);
770
829
  insertPatterns.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
771
830
  insertPatterns.push(...nested.triples);
@@ -784,14 +843,14 @@ function updateToAlgebra(query, options) {
784
843
  if (field.value === undefined || field.value === null) {
785
844
  const oldVar = varTerm(`old_${suffix}`);
786
845
  deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
787
- whereTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
846
+ oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
788
847
  continue;
789
848
  }
790
849
  // Array overwrite — delete old values + insert new ones
791
850
  if (Array.isArray(field.value)) {
792
851
  const oldVar = varTerm(`old_${suffix}`);
793
852
  deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
794
- whereTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
853
+ oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
795
854
  for (const item of field.value) {
796
855
  if (item && typeof item === 'object' && 'shape' in item && 'fields' in item) {
797
856
  const nested = generateNodeDataTriples(item, options);
@@ -811,49 +870,109 @@ function updateToAlgebra(query, options) {
811
870
  if (typeof field.value === 'object' && 'shape' in field.value && 'fields' in field.value) {
812
871
  const oldVar = varTerm(`old_${suffix}`);
813
872
  deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
814
- whereTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
873
+ oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
815
874
  const nested = generateNodeDataTriples(field.value, options);
816
875
  insertPatterns.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
817
876
  insertPatterns.push(...nested.triples);
818
877
  continue;
819
878
  }
879
+ // IRExpression — computed value update (e.g. p.age.plus(1))
880
+ if (isIRExpression(field.value)) {
881
+ const expr = field.value;
882
+ const oldVar = varTerm(`old_${suffix}`);
883
+ const computedVarName = `computed_${suffix}`;
884
+ const computedVar = varTerm(computedVarName);
885
+ // DELETE old value
886
+ deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
887
+ // WHERE: OPTIONAL for old value
888
+ oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
889
+ // Discover additional property references in the expression and add OPTIONAL triples
890
+ const registry = new VariableRegistry();
891
+ const mutationSubjectAlias = '__mutation_subject__';
892
+ // Pre-register the subject variable mapping for the field being updated
893
+ registry.set(mutationSubjectAlias, field.property, `old_${suffix}`);
894
+ const additionalOptionals = [];
895
+ processExpressionForProperties(expr, registry, additionalOptionals);
896
+ // Add any additional property OPTIONAL triples (for refs to other properties)
897
+ for (const triple of additionalOptionals) {
898
+ // Rewrite the subject from the placeholder variable to the actual subject term
899
+ if (triple.subject.kind === 'variable' && triple.subject.name === mutationSubjectAlias) {
900
+ oldValueTriples.push(tripleOf(subjectTerm, triple.predicate, triple.object));
901
+ }
902
+ else {
903
+ oldValueTriples.push(triple);
904
+ }
905
+ }
906
+ // Convert IRExpression to SparqlExpression
907
+ const sparqlExpr = convertExpression(expr, registry, additionalOptionals);
908
+ // BIND computed expression
909
+ extends_.push({ variable: computedVarName, expression: sparqlExpr });
910
+ // INSERT computed value
911
+ insertPatterns.push(tripleOf(subjectTerm, propertyTerm, computedVar));
912
+ continue;
913
+ }
820
914
  // Simple value update — delete old + insert new
821
915
  const oldVar = varTerm(`old_${suffix}`);
822
916
  deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
823
- whereTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
917
+ oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
824
918
  const terms = fieldValueToTerms(field.value, options);
825
919
  for (const term of terms) {
826
920
  insertPatterns.push(tripleOf(subjectTerm, propertyTerm, term));
827
921
  }
828
922
  }
829
- // Wrap WHERE triples in OPTIONAL so UPDATE succeeds even when the old
830
- // value doesn't exist (e.g. setting bestFriend when none was set before).
831
- let whereAlgebra;
832
- if (whereTriples.length === 0) {
833
- whereAlgebra = { type: 'bgp', triples: [] };
923
+ return { deletePatterns, insertPatterns, oldValueTriples, extends: extends_ };
924
+ }
925
+ /**
926
+ * Wraps old-value triples in OPTIONAL (LEFT JOIN) so UPDATE succeeds
927
+ * even when the old value doesn't exist.
928
+ */
929
+ function wrapOldValueOptionals(base, oldValueTriples) {
930
+ let algebra = base;
931
+ if (oldValueTriples.length === 0) {
932
+ return algebra;
834
933
  }
835
- else if (whereTriples.length === 1) {
836
- whereAlgebra = {
934
+ for (const triple of oldValueTriples) {
935
+ algebra = {
837
936
  type: 'left_join',
838
- left: { type: 'bgp', triples: [] },
839
- right: { type: 'bgp', triples: whereTriples },
937
+ left: algebra,
938
+ right: { type: 'bgp', triples: [triple] },
840
939
  };
841
940
  }
842
- else {
843
- // Wrap each triple in its own OPTIONAL for independent matching
844
- whereAlgebra = { type: 'bgp', triples: [] };
845
- for (const triple of whereTriples) {
941
+ return algebra;
942
+ }
943
+ /**
944
+ * Converts an IRUpdateMutation to a SparqlDeleteInsertPlan.
945
+ */
946
+ function updateToAlgebra(query, options) {
947
+ const subjectTerm = iriTerm(query.id);
948
+ const result = processUpdateFields(query.data, subjectTerm, options);
949
+ let whereAlgebra = wrapOldValueOptionals({ type: 'bgp', triples: [] }, result.oldValueTriples);
950
+ // Add traversal OPTIONAL patterns (for multi-segment expression refs)
951
+ // These must come BEFORE expression BINDs since the BINDs reference traversal variables.
952
+ if (query.traversalPatterns) {
953
+ for (const trav of query.traversalPatterns) {
954
+ const fromTerm = trav.from === '__mutation_subject__' ? subjectTerm : varTerm(trav.from);
955
+ const traversalTriple = tripleOf(fromTerm, iriTerm(trav.property), varTerm(trav.to));
846
956
  whereAlgebra = {
847
957
  type: 'left_join',
848
958
  left: whereAlgebra,
849
- right: { type: 'bgp', triples: [triple] },
959
+ right: { type: 'bgp', triples: [traversalTriple] },
850
960
  };
851
961
  }
852
962
  }
963
+ // Add BIND expressions for computed fields
964
+ for (const ext of result.extends) {
965
+ whereAlgebra = {
966
+ type: 'extend',
967
+ inner: whereAlgebra,
968
+ variable: ext.variable,
969
+ expression: ext.expression,
970
+ };
971
+ }
853
972
  return {
854
973
  type: 'delete_insert',
855
- deletePatterns,
856
- insertPatterns,
974
+ deletePatterns: result.deletePatterns,
975
+ insertPatterns: result.insertPatterns,
857
976
  whereAlgebra,
858
977
  };
859
978
  }
@@ -894,11 +1013,221 @@ function deleteToAlgebra(query, _options) {
894
1013
  };
895
1014
  }
896
1015
  // ---------------------------------------------------------------------------
1016
+ // Blank node tree walking for schema-aware delete cleanup
1017
+ // ---------------------------------------------------------------------------
1018
+ /**
1019
+ * Checks whether a PropertyShape points to blank nodes (sh:BlankNode or
1020
+ * sh:BlankNodeOrIRI). Returns true when the property's range *may* include
1021
+ * blank node values that should be cleaned up on delete.
1022
+ */
1023
+ function isBlankNodeProperty(prop) {
1024
+ var _a;
1025
+ const nk = (_a = prop.nodeKind) === null || _a === void 0 ? void 0 : _a.id;
1026
+ if (!nk)
1027
+ return false;
1028
+ const s = lazyShacl();
1029
+ return nk === s.BlankNode.id || nk === s.BlankNodeOrIRI.id;
1030
+ }
1031
+ /**
1032
+ * Recursively builds DELETE + WHERE patterns for blank-node-typed properties.
1033
+ *
1034
+ * For each blank-node property on the shape:
1035
+ * - DELETE: `?bnVar ?pN ?oN .` (wildcard all triples on the blank node)
1036
+ * - WHERE: `OPTIONAL { ?parent <property> ?bnVar . FILTER(isBlank(?bnVar)) . ?bnVar ?pN ?oN . }`
1037
+ *
1038
+ * Recurses into the property's valueShape to handle nested blank nodes
1039
+ * (e.g. Person → Address (blank) → GeoPoint (blank)).
1040
+ */
1041
+ function walkBlankNodeTree(shapeId, parentVar, depth, deletePatterns) {
1042
+ var _a;
1043
+ const shapeClass = lazyGetShapeClass(shapeId);
1044
+ if (!(shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.shape))
1045
+ return null;
1046
+ let optionals = null;
1047
+ const props = shapeClass.shape.getPropertyShapes(true);
1048
+ for (const prop of props) {
1049
+ if (!isBlankNodeProperty(prop))
1050
+ continue;
1051
+ const bnVar = `bn${depth}`;
1052
+ const pVar = `p${depth}`;
1053
+ const oVar = `o${depth}`;
1054
+ // DELETE pattern: wildcard all triples on the blank node
1055
+ deletePatterns.push(tripleOf(varTerm(bnVar), varTerm(pVar), varTerm(oVar)));
1056
+ // WHERE: parent --<property>--> ?bnVar
1057
+ const traverseTriple = tripleOf(varTerm(parentVar), iriTerm(prop.path[0].id), varTerm(bnVar));
1058
+ // FILTER(isBlank(?bnVar))
1059
+ const isBlankFilter = {
1060
+ kind: 'function_expr',
1061
+ name: 'isBlank',
1062
+ args: [{ kind: 'variable_expr', name: bnVar }],
1063
+ };
1064
+ // ?bnVar ?pN ?oN
1065
+ const wildcardTriple = tripleOf(varTerm(bnVar), varTerm(pVar), varTerm(oVar));
1066
+ // Build inner pattern: traverse + filter + wildcard
1067
+ let innerPattern = {
1068
+ type: 'bgp',
1069
+ triples: [traverseTriple, wildcardTriple],
1070
+ };
1071
+ innerPattern = { type: 'filter', expression: isBlankFilter, inner: innerPattern };
1072
+ // Recurse into valueShape for nested blank nodes
1073
+ if ((_a = prop.valueShape) === null || _a === void 0 ? void 0 : _a.id) {
1074
+ const nestedOptional = walkBlankNodeTree(prop.valueShape.id, bnVar, depth + 1, deletePatterns);
1075
+ if (nestedOptional) {
1076
+ innerPattern = { type: 'left_join', left: innerPattern, right: nestedOptional };
1077
+ }
1078
+ }
1079
+ // Wrap in OPTIONAL (left_join)
1080
+ if (optionals) {
1081
+ optionals = { type: 'left_join', left: optionals, right: innerPattern };
1082
+ }
1083
+ else {
1084
+ optionals = innerPattern;
1085
+ }
1086
+ depth++;
1087
+ }
1088
+ return optionals;
1089
+ }
1090
+ /**
1091
+ * Converts an IRDeleteAllMutation to a SparqlDeleteInsertPlan.
1092
+ *
1093
+ * Generates DELETE { ?a0 ?p ?o . [blank node wildcards] }
1094
+ * WHERE { ?a0 a <Shape> . ?a0 ?p ?o . OPTIONAL { [blank node traversals] } }
1095
+ */
1096
+ function deleteAllToAlgebra(query, _options) {
1097
+ const subjectVar = 'a0';
1098
+ // DELETE patterns: root wildcard
1099
+ const deletePatterns = [
1100
+ tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o')),
1101
+ ];
1102
+ // WHERE: type triple + root wildcard
1103
+ const typeTriple = tripleOf(varTerm(subjectVar), iriTerm(RDF_TYPE), iriTerm(query.shape));
1104
+ const rootWildcard = tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o'));
1105
+ let whereAlgebra = { type: 'bgp', triples: [typeTriple, rootWildcard] };
1106
+ // Walk blank node tree for cleanup
1107
+ const blankNodeOptional = walkBlankNodeTree(query.shape, subjectVar, 1, deletePatterns);
1108
+ if (blankNodeOptional) {
1109
+ whereAlgebra = { type: 'left_join', left: whereAlgebra, right: blankNodeOptional };
1110
+ }
1111
+ return {
1112
+ type: 'delete_insert',
1113
+ deletePatterns,
1114
+ insertPatterns: [],
1115
+ whereAlgebra,
1116
+ };
1117
+ }
1118
+ /**
1119
+ * Converts an IRDeleteWhereMutation to a SparqlDeleteInsertPlan.
1120
+ *
1121
+ * Like deleteAllToAlgebra but adds filter conditions from the where clause.
1122
+ */
1123
+ function deleteWhereToAlgebra(query, _options) {
1124
+ const subjectVar = 'a0';
1125
+ const registry = new VariableRegistry();
1126
+ // DELETE patterns: root wildcard
1127
+ const deletePatterns = [
1128
+ tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o')),
1129
+ ];
1130
+ // WHERE: type triple + root wildcard
1131
+ const typeTriple = tripleOf(varTerm(subjectVar), iriTerm(RDF_TYPE), iriTerm(query.shape));
1132
+ const rootWildcard = tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o'));
1133
+ let whereAlgebra = { type: 'bgp', triples: [typeTriple, rootWildcard] };
1134
+ // Process where patterns (traversals from the where clause)
1135
+ const traverseTriples = [];
1136
+ const optionalPropertyTriples = [];
1137
+ for (const pattern of query.wherePatterns) {
1138
+ processPattern(pattern, registry, traverseTriples, optionalPropertyTriples);
1139
+ }
1140
+ // Add traverse triples to required BGP
1141
+ if (traverseTriples.length > 0) {
1142
+ whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: traverseTriples });
1143
+ }
1144
+ // Process expression to discover property triples
1145
+ processExpressionForProperties(query.where, registry, optionalPropertyTriples);
1146
+ // Add optional property triples
1147
+ for (const triple of optionalPropertyTriples) {
1148
+ whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: [triple] });
1149
+ }
1150
+ // Convert and add filter expression
1151
+ const filterExpr = convertExpression(query.where, registry, []);
1152
+ whereAlgebra = { type: 'filter', expression: filterExpr, inner: whereAlgebra };
1153
+ // Walk blank node tree for cleanup
1154
+ const blankNodeOptional = walkBlankNodeTree(query.shape, subjectVar, 1, deletePatterns);
1155
+ if (blankNodeOptional) {
1156
+ whereAlgebra = { type: 'left_join', left: whereAlgebra, right: blankNodeOptional };
1157
+ }
1158
+ return {
1159
+ type: 'delete_insert',
1160
+ deletePatterns,
1161
+ insertPatterns: [],
1162
+ whereAlgebra,
1163
+ };
1164
+ }
1165
+ /**
1166
+ * Converts an IRUpdateWhereMutation to a SparqlDeleteInsertPlan.
1167
+ *
1168
+ * Like updateToAlgebra but uses a variable subject (?a0) instead of a
1169
+ * hardcoded entity IRI, adds a type triple, and optionally includes
1170
+ * filter conditions from the where clause.
1171
+ */
1172
+ function updateWhereToAlgebra(query, options) {
1173
+ const subjectTerm = varTerm('a0');
1174
+ const result = processUpdateFields(query.data, subjectTerm, options);
1175
+ // WHERE: type triple is always required
1176
+ const typeTriple = tripleOf(subjectTerm, iriTerm(RDF_TYPE), iriTerm(query.data.shape));
1177
+ let whereAlgebra = { type: 'bgp', triples: [typeTriple] };
1178
+ // Process where filter conditions (if any)
1179
+ if (query.where && query.wherePatterns) {
1180
+ const registry = new VariableRegistry();
1181
+ const traverseTriples = [];
1182
+ const optionalPropertyTriples = [];
1183
+ for (const pattern of query.wherePatterns) {
1184
+ processPattern(pattern, registry, traverseTriples, optionalPropertyTriples);
1185
+ }
1186
+ if (traverseTriples.length > 0) {
1187
+ whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: traverseTriples });
1188
+ }
1189
+ processExpressionForProperties(query.where, registry, optionalPropertyTriples);
1190
+ for (const triple of optionalPropertyTriples) {
1191
+ whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: [triple] });
1192
+ }
1193
+ const filterExpr = convertExpression(query.where, registry, []);
1194
+ whereAlgebra = { type: 'filter', expression: filterExpr, inner: whereAlgebra };
1195
+ }
1196
+ whereAlgebra = wrapOldValueOptionals(whereAlgebra, result.oldValueTriples);
1197
+ // Add traversal OPTIONAL patterns (for multi-segment expression refs)
1198
+ // These must come BEFORE expression BINDs since the BINDs reference traversal variables.
1199
+ if (query.traversalPatterns) {
1200
+ for (const trav of query.traversalPatterns) {
1201
+ const fromTerm = trav.from === '__mutation_subject__' ? varTerm('a0') : varTerm(trav.from);
1202
+ const traversalTriple = tripleOf(fromTerm, iriTerm(trav.property), varTerm(trav.to));
1203
+ whereAlgebra = {
1204
+ type: 'left_join',
1205
+ left: whereAlgebra,
1206
+ right: { type: 'bgp', triples: [traversalTriple] },
1207
+ };
1208
+ }
1209
+ }
1210
+ // Add BIND expressions for computed fields
1211
+ for (const ext of result.extends) {
1212
+ whereAlgebra = {
1213
+ type: 'extend',
1214
+ inner: whereAlgebra,
1215
+ variable: ext.variable,
1216
+ expression: ext.expression,
1217
+ };
1218
+ }
1219
+ return {
1220
+ type: 'delete_insert',
1221
+ deletePatterns: result.deletePatterns,
1222
+ insertPatterns: result.insertPatterns,
1223
+ whereAlgebra,
1224
+ };
1225
+ }
1226
+ // ---------------------------------------------------------------------------
897
1227
  // Convenience wrappers: IR → algebra → SPARQL string in one call
898
1228
  // ---------------------------------------------------------------------------
899
1229
  /**
900
1230
  * Converts an IRSelectQuery to a SPARQL string.
901
- * Stub: will be implemented when algebraToString is available.
902
1231
  */
903
1232
  function selectToSparql(query, options) {
904
1233
  const plan = selectToAlgebra(query, options);
@@ -922,10 +1251,30 @@ function updateToSparql(query, options) {
922
1251
  }
923
1252
  /**
924
1253
  * Converts an IRDeleteMutation to a SPARQL string.
925
- * Stub: will be implemented when algebraToString is available.
926
1254
  */
927
1255
  function deleteToSparql(query, options) {
928
1256
  const plan = deleteToAlgebra(query, options);
929
1257
  return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
930
1258
  }
1259
+ /**
1260
+ * Converts an IRDeleteAllMutation to a SPARQL string.
1261
+ */
1262
+ function deleteAllToSparql(query, options) {
1263
+ const plan = deleteAllToAlgebra(query, options);
1264
+ return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
1265
+ }
1266
+ /**
1267
+ * Converts an IRDeleteWhereMutation to a SPARQL string.
1268
+ */
1269
+ function deleteWhereToSparql(query, options) {
1270
+ const plan = deleteWhereToAlgebra(query, options);
1271
+ return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
1272
+ }
1273
+ /**
1274
+ * Converts an IRUpdateWhereMutation to a SPARQL string.
1275
+ */
1276
+ function updateWhereToSparql(query, options) {
1277
+ const plan = updateWhereToAlgebra(query, options);
1278
+ return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
1279
+ }
931
1280
  //# sourceMappingURL=irToAlgebra.js.map