@_linked/core 2.4.0 → 2.4.1-next.20260406154705

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.
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Serialization and deserialization helpers for QueryBuilder fields that contain
3
+ * live object references (PropertyShape, ExpressionNode, etc.).
4
+ *
5
+ * These convert between runtime WherePath / SortByPath / RawMinusEntry structures
6
+ * and plain JSON-safe representations that can round-trip through JSON.stringify/parse.
7
+ */
8
+ import { getShapeClass } from '../utils/ShapeClass.js';
9
+ import { walkPropertyPath } from './PropertyPath.js';
10
+ import { ExpressionNode } from '../expressions/ExpressionNode.js';
11
+ // ExistsCondition only exists after the WherePath refactor; access dynamically
12
+ // so this file compiles on branches where it doesn't exist yet.
13
+ import * as _exprModule from '../expressions/ExpressionNode.js';
14
+ const ExistsConditionCtor = _exprModule.ExistsCondition;
15
+ // =============================================================================
16
+ // Serialization
17
+ // =============================================================================
18
+ export function serializeQueryPropertyPath(path) {
19
+ return path.map(serializeQueryStep);
20
+ }
21
+ function serializeQueryStep(step) {
22
+ // PropertyQueryStep — has a .property field that is a PropertyShape
23
+ if ('property' in step && step.property) {
24
+ const pqs = step;
25
+ const json = {
26
+ kind: 'property',
27
+ label: pqs.property.label,
28
+ };
29
+ if (pqs.where) {
30
+ json.where = serializeWherePath(pqs.where);
31
+ }
32
+ return json;
33
+ }
34
+ // SizeStep — has a .count field
35
+ if ('count' in step) {
36
+ const ss = step;
37
+ const json = {
38
+ kind: 'size',
39
+ count: serializeQueryPropertyPath(ss.count),
40
+ };
41
+ if (ss.label)
42
+ json.label = ss.label;
43
+ return json;
44
+ }
45
+ // ShapeReferenceValue — has .id and .shape
46
+ const ref = step;
47
+ return { kind: 'shapeRef', id: ref.id, shapeId: ref.shape.id };
48
+ }
49
+ export function serializeWherePath(where) {
50
+ var _a, _b;
51
+ if ('expressionNode' in where) {
52
+ const expr = where.expressionNode;
53
+ const json = {
54
+ kind: 'expression',
55
+ ir: expr.ir,
56
+ };
57
+ if (expr._refs.size > 0) {
58
+ const refs = {};
59
+ for (const [k, v] of expr._refs)
60
+ refs[k] = [...v];
61
+ json.refs = refs;
62
+ }
63
+ return json;
64
+ }
65
+ if ('firstPath' in where) {
66
+ const andOr = where;
67
+ return {
68
+ kind: 'andOr',
69
+ firstPath: serializeWherePath(andOr.firstPath),
70
+ andOr: andOr.andOr.map((token) => {
71
+ const t = {};
72
+ if (token.and)
73
+ t.and = serializeWherePath(token.and);
74
+ if (token.or)
75
+ t.or = serializeWherePath(token.or);
76
+ return t;
77
+ }),
78
+ };
79
+ }
80
+ if ('path' in where && 'method' in where && 'args' in where) {
81
+ const ev = where;
82
+ return {
83
+ kind: 'evaluation',
84
+ path: serializeQueryPropertyPath(ev.path),
85
+ method: ev.method,
86
+ args: ev.args.map(serializeQueryArg),
87
+ };
88
+ }
89
+ if ('existsCondition' in where) {
90
+ // WhereExistsPath — serialize the ExistsCondition structurally
91
+ const ec = where.existsCondition;
92
+ return {
93
+ kind: 'exists',
94
+ pathSegmentIds: [...ec.pathSegmentIds],
95
+ predicate: { ir: ec.predicate.ir, refs: serializePropertyRefMap(ec.predicate._refs) },
96
+ negated: (_a = ec.negated) !== null && _a !== void 0 ? _a : false,
97
+ chain: ((_b = ec.chain) !== null && _b !== void 0 ? _b : []).map((c) => {
98
+ var _a;
99
+ return ({
100
+ op: c.op,
101
+ condition: 'pathSegmentIds' in c.condition
102
+ ? { kind: 'exists', pathSegmentIds: [...c.condition.pathSegmentIds], predicate: { ir: c.condition.predicate.ir, refs: serializePropertyRefMap(c.condition.predicate._refs) }, negated: (_a = c.condition.negated) !== null && _a !== void 0 ? _a : false, chain: [] }
103
+ : { kind: 'expression', ir: c.condition.ir, refs: serializePropertyRefMap(c.condition._refs) },
104
+ });
105
+ }),
106
+ };
107
+ }
108
+ throw new Error(`Cannot serialize WherePath: ${JSON.stringify(Object.keys(where))}`);
109
+ }
110
+ function serializePropertyRefMap(refs) {
111
+ if (!refs || refs.size === 0)
112
+ return undefined;
113
+ const out = {};
114
+ for (const [k, v] of refs)
115
+ out[k] = [...v];
116
+ return out;
117
+ }
118
+ function serializeQueryArg(arg) {
119
+ if (typeof arg === 'string' || typeof arg === 'number' || typeof arg === 'boolean') {
120
+ return { kind: 'primitive', value: arg };
121
+ }
122
+ if (arg instanceof Date) {
123
+ return { kind: 'date', value: arg.toISOString() };
124
+ }
125
+ if (typeof arg === 'object' && arg !== null) {
126
+ if ('expressionNode' in arg || 'existsCondition' in arg || 'firstPath' in arg || ('path' in arg && 'method' in arg && 'args' in arg)) {
127
+ return { kind: 'where', where: serializeWherePath(arg) };
128
+ }
129
+ // ArgPath — has 'subject' and 'path'
130
+ if ('subject' in arg && 'path' in arg) {
131
+ const ap = arg;
132
+ return {
133
+ kind: 'argPath',
134
+ path: serializeQueryPropertyPath(ap.path),
135
+ subject: { id: ap.subject.id, shapeId: ap.subject.shape.id },
136
+ };
137
+ }
138
+ // NodeReferenceValue — has 'id'
139
+ if ('id' in arg) {
140
+ return { kind: 'nodeRef', id: arg.id };
141
+ }
142
+ }
143
+ throw new Error(`Cannot serialize query arg: ${JSON.stringify(arg)}`);
144
+ }
145
+ export function serializeSortByPath(sort) {
146
+ return {
147
+ paths: sort.paths.map((p) => p.toString()),
148
+ direction: sort.direction,
149
+ };
150
+ }
151
+ export function serializeRawMinusEntry(entry) {
152
+ const json = {};
153
+ if (entry.shapeId)
154
+ json.shapeId = entry.shapeId;
155
+ if (entry.where)
156
+ json.where = serializeWherePath(entry.where);
157
+ if (entry.propertyPaths) {
158
+ json.propertyPaths = entry.propertyPaths.map((pp) => pp.map((seg) => seg.propertyShapeId));
159
+ }
160
+ return json;
161
+ }
162
+ // =============================================================================
163
+ // Deserialization
164
+ // =============================================================================
165
+ export function deserializeQueryPropertyPath(shape, steps) {
166
+ var _a;
167
+ const result = [];
168
+ let currentShape = shape;
169
+ for (const step of steps) {
170
+ if (step.kind === 'property') {
171
+ const propertyShape = currentShape.getPropertyShape(step.label);
172
+ if (!propertyShape) {
173
+ throw new Error(`Property '${step.label}' not found on shape '${currentShape.label || currentShape.id}'`);
174
+ }
175
+ const queryStep = { property: propertyShape };
176
+ if (step.where) {
177
+ const nestedShape = propertyShape.valueShape
178
+ ? (_a = getShapeClass(propertyShape.valueShape)) === null || _a === void 0 ? void 0 : _a.shape
179
+ : currentShape;
180
+ queryStep.where = deserializeWherePath(nestedShape || currentShape, step.where);
181
+ }
182
+ result.push(queryStep);
183
+ // Advance shape context for subsequent traversals
184
+ if (propertyShape.valueShape) {
185
+ const cls = getShapeClass(propertyShape.valueShape);
186
+ if (cls === null || cls === void 0 ? void 0 : cls.shape)
187
+ currentShape = cls.shape;
188
+ }
189
+ }
190
+ else if (step.kind === 'size') {
191
+ result.push({
192
+ count: deserializeQueryPropertyPath(currentShape, step.count),
193
+ label: step.label,
194
+ });
195
+ }
196
+ else {
197
+ // shapeRef
198
+ result.push({ id: step.id, shape: { id: step.shapeId } });
199
+ }
200
+ }
201
+ return result;
202
+ }
203
+ export function deserializeWherePath(shape, json) {
204
+ if (json.kind === 'expression') {
205
+ const refs = new Map();
206
+ if (json.refs) {
207
+ for (const [k, v] of Object.entries(json.refs))
208
+ refs.set(k, v);
209
+ }
210
+ return { expressionNode: new ExpressionNode(json.ir, refs) };
211
+ }
212
+ if (json.kind === 'exists') {
213
+ if (!ExistsConditionCtor)
214
+ throw new Error('ExistsCondition is not available on this build');
215
+ const predRefs = deserializeRefMap(json.predicate.refs);
216
+ const predicate = new ExpressionNode(json.predicate.ir, predRefs);
217
+ const chain = json.chain.map((c) => {
218
+ if (c.condition.kind === 'exists') {
219
+ const innerRefs = deserializeRefMap(c.condition.predicate.refs);
220
+ return { op: c.op, condition: new ExistsConditionCtor(c.condition.pathSegmentIds, new ExpressionNode(c.condition.predicate.ir, innerRefs), c.condition.negated) };
221
+ }
222
+ const exprRefs = deserializeRefMap(c.condition.refs);
223
+ return { op: c.op, condition: new ExpressionNode(c.condition.ir, exprRefs) };
224
+ });
225
+ return { existsCondition: new ExistsConditionCtor(json.pathSegmentIds, predicate, json.negated, chain) };
226
+ }
227
+ if (json.kind === 'andOr') {
228
+ return {
229
+ firstPath: deserializeWherePath(shape, json.firstPath),
230
+ andOr: json.andOr.map((token) => {
231
+ const t = {};
232
+ if (token.and)
233
+ t.and = deserializeWherePath(shape, token.and);
234
+ if (token.or)
235
+ t.or = deserializeWherePath(shape, token.or);
236
+ return t;
237
+ }),
238
+ };
239
+ }
240
+ // evaluation (legacy)
241
+ return {
242
+ path: deserializeQueryPropertyPath(shape, json.path),
243
+ method: json.method,
244
+ args: json.args.map((a) => deserializeQueryArg(shape, a)),
245
+ };
246
+ }
247
+ function deserializeRefMap(refs) {
248
+ const m = new Map();
249
+ if (refs) {
250
+ for (const [k, v] of Object.entries(refs))
251
+ m.set(k, v);
252
+ }
253
+ return m;
254
+ }
255
+ function deserializeQueryArg(shape, json) {
256
+ switch (json.kind) {
257
+ case 'primitive':
258
+ return json.value;
259
+ case 'date':
260
+ return new Date(json.value);
261
+ case 'nodeRef':
262
+ return { id: json.id };
263
+ case 'where':
264
+ return deserializeWherePath(shape, json.where);
265
+ case 'argPath':
266
+ return {
267
+ path: deserializeQueryPropertyPath(shape, json.path),
268
+ subject: { id: json.subject.id, shape: { id: json.subject.shapeId } },
269
+ };
270
+ }
271
+ }
272
+ export function deserializeSortByPath(shape, json) {
273
+ return {
274
+ paths: json.paths.map((p) => walkPropertyPath(shape, p)),
275
+ direction: json.direction,
276
+ };
277
+ }
278
+ export function deserializeRawMinusEntry(shape, json) {
279
+ const entry = {};
280
+ if (json.shapeId)
281
+ entry.shapeId = json.shapeId;
282
+ if (json.where)
283
+ entry.where = deserializeWherePath(shape, json.where);
284
+ if (json.propertyPaths) {
285
+ entry.propertyPaths = json.propertyPaths.map((pp) => pp.map((id) => ({ propertyShapeId: id })));
286
+ }
287
+ return entry;
288
+ }
289
+ //# sourceMappingURL=QueryBuilderSerialization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QueryBuilderSerialization.js","sourceRoot":"","sources":["../../../src/queries/QueryBuilderSerialization.ts"],"names":[],"mappings":"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"}
@@ -86,6 +86,34 @@ function joinNodes(left, right) {
86
86
  return right;
87
87
  return { type: 'join', left, right };
88
88
  }
89
+ function bindingKey(alias, property) {
90
+ return `${alias}::${property}`;
91
+ }
92
+ function contextAliasKey(contextIri) {
93
+ return `__ctx__${contextIri}`;
94
+ }
95
+ function mergeKeySets(...sets) {
96
+ const merged = new Set();
97
+ for (const set of sets) {
98
+ for (const key of set) {
99
+ merged.add(key);
100
+ }
101
+ }
102
+ return merged;
103
+ }
104
+ function intersectKeySets(sets) {
105
+ if (sets.length === 0) {
106
+ return new Set();
107
+ }
108
+ const [first, ...rest] = sets;
109
+ const intersection = new Set(first);
110
+ for (const value of intersection) {
111
+ if (!rest.every((set) => set.has(value))) {
112
+ intersection.delete(value);
113
+ }
114
+ }
115
+ return intersection;
116
+ }
89
117
  // ---------------------------------------------------------------------------
90
118
  // Pattern helpers
91
119
  // ---------------------------------------------------------------------------
@@ -126,7 +154,7 @@ class VariableRegistry {
126
154
  this.usedVarNames = new Set();
127
155
  }
128
156
  key(alias, property) {
129
- return `${alias}::${property}`;
157
+ return bindingKey(alias, property);
130
158
  }
131
159
  has(alias, property) {
132
160
  return this.map.has(this.key(alias, property));
@@ -186,6 +214,13 @@ function containsAggregate(expr) {
186
214
  */
187
215
  export function selectToAlgebra(query, _options) {
188
216
  const registry = new VariableRegistry();
217
+ // Promote bindings only when the top-level WHERE would reject rows without
218
+ // them. This keeps human-like SPARQL for null-rejecting filters without
219
+ // over-constraining OR cases that can still match through other branches.
220
+ const requiredPropertyKeys = query.where
221
+ ? collectRequiredBindingKeys(query.where)
222
+ : new Set();
223
+ const requiredPropertyTriples = [];
189
224
  // Track property triples that need to be added as OPTIONAL
190
225
  const optionalPropertyTriples = [];
191
226
  // Track filtered traversals (inline where) — these get their own OPTIONAL blocks
@@ -218,14 +253,14 @@ export function selectToAlgebra(query, _options) {
218
253
  // to discover any additional property_expr references.
219
254
  // Properties already registered by inline filters (above) will be skipped.
220
255
  for (const item of query.projection) {
221
- processExpressionForProperties(item.expression, registry, optionalPropertyTriples);
256
+ processExpressionForProperties(item.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
222
257
  }
223
258
  if (query.where) {
224
- processExpressionForProperties(query.where, registry, optionalPropertyTriples);
259
+ processExpressionForProperties(query.where, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
225
260
  }
226
261
  if (query.orderBy) {
227
262
  for (const orderItem of query.orderBy) {
228
- processExpressionForProperties(orderItem.expression, registry, optionalPropertyTriples);
263
+ processExpressionForProperties(orderItem.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
229
264
  }
230
265
  }
231
266
  // 5. Build the algebra tree
@@ -233,7 +268,7 @@ export function selectToAlgebra(query, _options) {
233
268
  // - Wrap each optional property triple in a LeftJoin
234
269
  const requiredBgp = {
235
270
  type: 'bgp',
236
- triples: [...requiredTriples, ...traverseTriples],
271
+ triples: [...requiredTriples, ...traverseTriples, ...requiredPropertyTriples],
237
272
  };
238
273
  let algebra = requiredBgp;
239
274
  // 5b. Build filtered OPTIONAL blocks for inline where traversals.
@@ -469,39 +504,42 @@ function processPattern(pattern, registry, traverseTriples, optionalPropertyTrip
469
504
  // ---------------------------------------------------------------------------
470
505
  // Expression processing — discover property_expr references
471
506
  // ---------------------------------------------------------------------------
472
- function processExpressionForProperties(expr, registry, optionalPropertyTriples) {
507
+ function processExpressionForProperties(expr, registry, optionalPropertyTriples, requiredPropertyTriples = [], requiredPropertyKeys = new Set()) {
473
508
  switch (expr.kind) {
474
509
  case 'property_expr': {
475
510
  if (!registry.has(expr.sourceAlias, expr.property)) {
476
- // Create a new OPTIONAL triple for this property
477
511
  const varName = registry.getOrCreate(expr.sourceAlias, expr.property);
478
512
  const predicate = expr.pathExpr
479
513
  ? { kind: 'path', value: pathExprToSparql(expr.pathExpr), uris: collectPathUris(expr.pathExpr) }
480
514
  : iriTerm(expr.property);
481
- optionalPropertyTriples.push(tripleOf(varTerm(expr.sourceAlias), predicate, varTerm(varName)));
515
+ const triple = tripleOf(varTerm(expr.sourceAlias), predicate, varTerm(varName));
516
+ const triples = requiredPropertyKeys.has(bindingKey(expr.sourceAlias, expr.property))
517
+ ? requiredPropertyTriples
518
+ : optionalPropertyTriples;
519
+ triples.push(triple);
482
520
  }
483
521
  break;
484
522
  }
485
523
  case 'binary_expr':
486
- processExpressionForProperties(expr.left, registry, optionalPropertyTriples);
487
- processExpressionForProperties(expr.right, registry, optionalPropertyTriples);
524
+ processExpressionForProperties(expr.left, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
525
+ processExpressionForProperties(expr.right, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
488
526
  break;
489
527
  case 'logical_expr':
490
528
  for (const sub of expr.expressions) {
491
- processExpressionForProperties(sub, registry, optionalPropertyTriples);
529
+ processExpressionForProperties(sub, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
492
530
  }
493
531
  break;
494
532
  case 'not_expr':
495
- processExpressionForProperties(expr.expression, registry, optionalPropertyTriples);
533
+ processExpressionForProperties(expr.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
496
534
  break;
497
535
  case 'function_expr':
498
536
  for (const arg of expr.args) {
499
- processExpressionForProperties(arg, registry, optionalPropertyTriples);
537
+ processExpressionForProperties(arg, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
500
538
  }
501
539
  break;
502
540
  case 'aggregate_expr':
503
541
  for (const arg of expr.args) {
504
- processExpressionForProperties(arg, registry, optionalPropertyTriples);
542
+ processExpressionForProperties(arg, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
505
543
  }
506
544
  break;
507
545
  case 'exists_expr':
@@ -513,10 +551,14 @@ function processExpressionForProperties(expr, registry, optionalPropertyTriples)
513
551
  // Context entity property — emit a triple with fixed IRI as subject.
514
552
  // Use raw IRI as registry key to avoid collision between IRIs that
515
553
  // sanitize to the same string (e.g. ctx-1 vs ctx_1).
516
- const ctxKey = `__ctx__${expr.contextIri}`;
554
+ const ctxKey = contextAliasKey(expr.contextIri);
517
555
  if (!registry.has(ctxKey, expr.property)) {
518
556
  const varName = registry.getOrCreate(ctxKey, expr.property);
519
- optionalPropertyTriples.push(tripleOf(iriTerm(expr.contextIri), iriTerm(expr.property), varTerm(varName)));
557
+ const triple = tripleOf(iriTerm(expr.contextIri), iriTerm(expr.property), varTerm(varName));
558
+ const triples = requiredPropertyKeys.has(bindingKey(ctxKey, expr.property))
559
+ ? requiredPropertyTriples
560
+ : optionalPropertyTriples;
561
+ triples.push(triple);
520
562
  }
521
563
  break;
522
564
  }
@@ -527,6 +569,37 @@ function processExpressionForProperties(expr, registry, optionalPropertyTriples)
527
569
  break;
528
570
  }
529
571
  }
572
+ /**
573
+ * Compute which bindings are mandatory for a top-level FILTER to keep a row.
574
+ * AND makes either side required; OR only keeps bindings required by every branch.
575
+ */
576
+ function collectRequiredBindingKeys(expr) {
577
+ switch (expr.kind) {
578
+ case 'property_expr':
579
+ return new Set([bindingKey(expr.sourceAlias, expr.property)]);
580
+ case 'context_property_expr':
581
+ return new Set([bindingKey(contextAliasKey(expr.contextIri), expr.property)]);
582
+ case 'binary_expr':
583
+ return mergeKeySets(collectRequiredBindingKeys(expr.left), collectRequiredBindingKeys(expr.right));
584
+ case 'function_expr':
585
+ return mergeKeySets(...expr.args.map((arg) => collectRequiredBindingKeys(arg)));
586
+ case 'not_expr':
587
+ return collectRequiredBindingKeys(expr.expression);
588
+ case 'logical_expr': {
589
+ const childSets = expr.expressions.map((sub) => collectRequiredBindingKeys(sub));
590
+ if (expr.operator === 'and') {
591
+ return mergeKeySets(...childSets);
592
+ }
593
+ return intersectKeySets(childSets);
594
+ }
595
+ case 'aggregate_expr':
596
+ case 'exists_expr':
597
+ case 'literal_expr':
598
+ case 'reference_expr':
599
+ case 'alias_expr':
600
+ return new Set();
601
+ }
602
+ }
530
603
  // ---------------------------------------------------------------------------
531
604
  // Expression conversion
532
605
  // ---------------------------------------------------------------------------