flowquery 1.0.67 → 1.0.69

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.
@@ -5,18 +5,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const database_1 = __importDefault(require("../graph/database"));
7
7
  const node_1 = __importDefault(require("../graph/node"));
8
+ const node_reference_1 = __importDefault(require("../graph/node_reference"));
8
9
  const relationship_1 = __importDefault(require("../graph/relationship"));
9
10
  const ast_node_1 = __importDefault(require("./ast_node"));
10
11
  const lookup_1 = __importDefault(require("./data_structures/lookup"));
12
+ const expression_1 = __importDefault(require("./expressions/expression"));
13
+ const f_string_1 = __importDefault(require("./expressions/f_string"));
11
14
  const identifier_1 = __importDefault(require("./expressions/identifier"));
15
+ const operator_1 = require("./expressions/operator");
16
+ const parameter_reference_1 = __importDefault(require("./expressions/parameter_reference"));
12
17
  const reference_1 = __importDefault(require("./expressions/reference"));
13
18
  const subquery_expression_1 = __importDefault(require("./expressions/subquery_expression"));
19
+ const aggregated_return_1 = __importDefault(require("./operations/aggregated_return"));
20
+ const aggregated_with_1 = __importDefault(require("./operations/aggregated_with"));
14
21
  const create_node_1 = __importDefault(require("./operations/create_node"));
15
22
  const create_relationship_1 = __importDefault(require("./operations/create_relationship"));
16
23
  const delete_node_1 = __importDefault(require("./operations/delete_node"));
17
24
  const delete_relationship_1 = __importDefault(require("./operations/delete_relationship"));
18
25
  const load_1 = __importDefault(require("./operations/load"));
19
26
  const match_1 = __importDefault(require("./operations/match"));
27
+ const order_by_1 = __importDefault(require("./operations/order_by"));
28
+ const return_1 = __importDefault(require("./operations/return"));
29
+ const with_1 = __importDefault(require("./operations/with"));
20
30
  /**
21
31
  * Walks one or more parsed FlowQuery statement ASTs and extracts a
22
32
  * `StatementInfo` describing the labels, relationship types, sources, and
@@ -41,12 +51,18 @@ class StatementInfoCrawler {
41
51
  this._relProps = new Map();
42
52
  this._nodeSources = new Map();
43
53
  this._relSources = new Map();
54
+ this._nodeLiterals = new Map();
55
+ this._relLiterals = new Map();
56
+ this._nodeDeclaredProps = new Map();
57
+ this._relDeclaredProps = new Map();
58
+ this._nodeDeclaredSources = new Map();
59
+ this._relDeclaredSources = new Map();
44
60
  this._ownCreatedNodeLabels = new Set();
45
61
  this._ownCreatedRelTypes = new Set();
46
62
  /**
47
63
  * For each inline CREATE VIRTUAL clause, the (label or type) it
48
64
  * declares and its inner statement AST. The label key receives the
49
- * sources collected from the statement during {@link resolveRegisteredSources}.
65
+ * sources collected from the statement during {@link resolveRegisteredDefinitions}.
50
66
  */
51
67
  this._ownNodeCreates = [];
52
68
  this._ownRelCreates = [];
@@ -63,7 +79,7 @@ class StatementInfoCrawler {
63
79
  for (const root of roots) {
64
80
  this.crawlStatement(root);
65
81
  }
66
- this.resolveRegisteredSources();
82
+ this.resolveRegisteredDefinitions();
67
83
  return this.snapshot();
68
84
  }
69
85
  toIterable(statements) {
@@ -79,6 +95,12 @@ class StatementInfoCrawler {
79
95
  this._relProps = new Map();
80
96
  this._nodeSources = new Map();
81
97
  this._relSources = new Map();
98
+ this._nodeLiterals = new Map();
99
+ this._relLiterals = new Map();
100
+ this._nodeDeclaredProps = new Map();
101
+ this._relDeclaredProps = new Map();
102
+ this._nodeDeclaredSources = new Map();
103
+ this._relDeclaredSources = new Map();
82
104
  this._ownCreatedNodeLabels = new Set();
83
105
  this._ownCreatedRelTypes = new Set();
84
106
  this._ownNodeCreates = [];
@@ -140,17 +162,26 @@ class StatementInfoCrawler {
140
162
  for (const pattern of op.patterns) {
141
163
  for (const element of pattern.chain) {
142
164
  if (element instanceof node_1.default) {
143
- for (const lbl of element.labels)
165
+ // For a WITH-rebound `MATCH (u)`, the chain
166
+ // element's own labels are empty; chase the
167
+ // underlying binding so labels/properties land
168
+ // on the original :User node.
169
+ const effectiveLabels = element.labels.length > 0
170
+ ? element.labels
171
+ : this.effectiveLabelsFor(element);
172
+ for (const lbl of effectiveLabels)
144
173
  this._nodeLabels.add(lbl);
145
- for (const propKey of element.properties.keys()) {
146
- this.addNodeProp(element.labels, propKey);
174
+ for (const [propKey, expr] of element.properties) {
175
+ this.addNodeProp(effectiveLabels, propKey);
176
+ this.tryAddNodeLiteral(effectiveLabels, propKey, expr);
147
177
  }
148
178
  }
149
179
  else if (element instanceof relationship_1.default) {
150
180
  for (const t of element.types)
151
181
  this._relTypes.add(t);
152
- for (const propKey of element.properties.keys()) {
182
+ for (const [propKey, expr] of element.properties) {
153
183
  this.addRelProp(element.types, propKey);
184
+ this.tryAddRelLiteral(element.types, propKey, expr);
154
185
  }
155
186
  }
156
187
  }
@@ -166,16 +197,22 @@ class StatementInfoCrawler {
166
197
  this.collectPropertyAccesses(op);
167
198
  }
168
199
  }
169
- resolveRegisteredSources() {
170
- // Sources from inline CREATE VIRTUAL clauses in the crawled statements.
200
+ resolveRegisteredDefinitions() {
201
+ // Sources and declared properties from inline CREATE VIRTUAL clauses
202
+ // in the crawled statements.
171
203
  for (const { label, statement } of this._ownNodeCreates) {
172
204
  this.collectSources(statement, this.getOrCreate(this._nodeSources, label));
205
+ this.collectSources(statement, this.getOrCreate(this._nodeDeclaredSources, label));
206
+ this.collectDeclaredProps(statement, this.getOrCreate(this._nodeDeclaredProps, label));
173
207
  }
174
208
  for (const { type, statement } of this._ownRelCreates) {
175
209
  this.collectSources(statement, this.getOrCreate(this._relSources, type));
210
+ this.collectSources(statement, this.getOrCreate(this._relDeclaredSources, type));
211
+ this.collectDeclaredProps(statement, this.getOrCreate(this._relDeclaredProps, type));
176
212
  }
177
- // Sources from already-registered virtuals that the crawled statements
178
- // reference (e.g. MATCH/DELETE against a virtual registered earlier).
213
+ // Sources / declared properties from already-registered virtuals
214
+ // that the crawled statements reference (e.g. MATCH/DELETE against
215
+ // a virtual registered earlier).
179
216
  const db = database_1.default.getInstance();
180
217
  for (const label of this._nodeLabels) {
181
218
  if (this._ownCreatedNodeLabels.has(label))
@@ -183,6 +220,8 @@ class StatementInfoCrawler {
183
220
  const physical = db.nodes.get(label);
184
221
  if (physical === null || physical === void 0 ? void 0 : physical.statement) {
185
222
  this.collectSources(physical.statement, this.getOrCreate(this._nodeSources, label));
223
+ this.collectSources(physical.statement, this.getOrCreate(this._nodeDeclaredSources, label));
224
+ this.collectDeclaredProps(physical.statement, this.getOrCreate(this._nodeDeclaredProps, label));
186
225
  }
187
226
  }
188
227
  for (const type of this._relTypes) {
@@ -194,6 +233,8 @@ class StatementInfoCrawler {
194
233
  for (const physical of typeMap.values()) {
195
234
  if (physical.statement) {
196
235
  this.collectSources(physical.statement, this.getOrCreate(this._relSources, type));
236
+ this.collectSources(physical.statement, this.getOrCreate(this._relDeclaredSources, type));
237
+ this.collectDeclaredProps(physical.statement, this.getOrCreate(this._relDeclaredProps, type));
197
238
  }
198
239
  }
199
240
  }
@@ -206,6 +247,13 @@ class StatementInfoCrawler {
206
247
  }
207
248
  return set;
208
249
  }
250
+ /**
251
+ * Walks the AST rooted at `root` and records every property access
252
+ * (via `Lookup`) on a known node/relationship binding, every literal
253
+ * value supplied via equality / `IN` predicates, and descends into
254
+ * subqueries plus the privately-held WHERE / ORDER BY ASTs of
255
+ * RETURN-style operations.
256
+ */
209
257
  collectPropertyAccesses(root) {
210
258
  const visited = new Set();
211
259
  const stack = [root];
@@ -215,20 +263,13 @@ class StatementInfoCrawler {
215
263
  continue;
216
264
  visited.add(node);
217
265
  if (node instanceof lookup_1.default) {
218
- const variable = node.variable;
219
- const index = node.index;
220
- if (variable instanceof reference_1.default && index instanceof identifier_1.default) {
221
- const referred = variable.referred;
222
- const propName = index.value();
223
- if (typeof propName === "string" && propName.length > 0) {
224
- if (referred instanceof node_1.default) {
225
- this.addNodeProp(referred.labels, propName);
226
- }
227
- else if (referred instanceof relationship_1.default) {
228
- this.addRelProp(referred.types, propName);
229
- }
230
- }
231
- }
266
+ this.handleLookupAccess(node);
267
+ }
268
+ if (node instanceof operator_1.Equals) {
269
+ this.handleEqualityLiteral(node);
270
+ }
271
+ else if (node instanceof operator_1.In) {
272
+ this.handleInLiteral(node);
232
273
  }
233
274
  for (const child of node.getChildren()) {
234
275
  stack.push(child);
@@ -240,6 +281,269 @@ class StatementInfoCrawler {
240
281
  if (inner !== undefined)
241
282
  stack.push(inner);
242
283
  }
284
+ // RETURN / AggregatedReturn hold WHERE and ORDER BY clauses in
285
+ // private fields; descend into the ones with expression trees.
286
+ if (node instanceof return_1.default) {
287
+ const w = node._where;
288
+ const o = node._orderBy;
289
+ if (w)
290
+ stack.push(w);
291
+ if (o)
292
+ stack.push(o);
293
+ }
294
+ // OrderBy stores its sort expressions in a private array of
295
+ // SortField objects rather than as AST children; descend
296
+ // explicitly.
297
+ if (node instanceof order_by_1.default) {
298
+ for (const field of node.fields) {
299
+ stack.push(field.expression);
300
+ }
301
+ }
302
+ }
303
+ }
304
+ handleLookupAccess(node) {
305
+ const target = this.resolveLookupTarget(node);
306
+ if (target === null)
307
+ return;
308
+ if (target.kind === "node") {
309
+ this.addNodeProp(target.labels, target.prop);
310
+ }
311
+ else {
312
+ this.addRelProp(target.types, target.prop);
313
+ }
314
+ }
315
+ /**
316
+ * Resolves a `Lookup` of the shape `alias.prop` to the labels/types
317
+ * of the bound entity and the property name. Returns `null` if the
318
+ * lookup isn't of that shape or its variable doesn't resolve to a
319
+ * Node/Relationship.
320
+ */
321
+ resolveLookupTarget(lookup) {
322
+ const variable = lookup.variable;
323
+ const index = lookup.index;
324
+ if (!(variable instanceof reference_1.default) || !(index instanceof identifier_1.default))
325
+ return null;
326
+ const propName = index.value();
327
+ if (typeof propName !== "string" || propName.length === 0)
328
+ return null;
329
+ const referred = this.resolveBoundEntity(variable.referred);
330
+ if (referred instanceof node_1.default) {
331
+ return { kind: "node", labels: referred.labels, prop: propName };
332
+ }
333
+ if (referred instanceof relationship_1.default) {
334
+ return { kind: "rel", types: referred.types, prop: propName };
335
+ }
336
+ return null;
337
+ }
338
+ /**
339
+ * Unwraps a binding chain to the underlying graph entity that
340
+ * carries the labels / types relevant for lineage. Handles the
341
+ * common WITH-rebind pattern:
342
+ *
343
+ * ```
344
+ * MATCH (u:User) ... WITH u MATCH (u) RETURN u.name
345
+ * ```
346
+ *
347
+ * After `WITH u`, the variable `u` is bound to the WITH's
348
+ * `Expression` (a passthrough Reference to the original Node). The
349
+ * second `MATCH (u)` then creates a `NodeReference` whose own
350
+ * `labels` are empty. Without unwrapping, `u.name` in `RETURN`
351
+ * resolves to that empty-labels rebind and the property is silently
352
+ * dropped from lineage. By chasing through `Reference.referred`,
353
+ * single-child `Expression` nodes, and `NodeReference.reference`
354
+ * (when the immediate labels are empty), we recover the original
355
+ * `Node(:User)` binding.
356
+ */
357
+ resolveBoundEntity(node) {
358
+ const seen = new Set();
359
+ let current = node;
360
+ while (current !== undefined && !seen.has(current)) {
361
+ seen.add(current);
362
+ // NodeReference subclasses Node and copies its base's
363
+ // labels at construction. When those copied labels are
364
+ // empty (`MATCH (u)` after a WITH-rebind), chase the
365
+ // underlying binding to recover the original labels.
366
+ if (current instanceof node_reference_1.default) {
367
+ if (current.labels.length > 0)
368
+ return current;
369
+ const next = current.reference;
370
+ current = next === null ? undefined : next;
371
+ continue;
372
+ }
373
+ if (current instanceof node_1.default)
374
+ return current;
375
+ if (current instanceof relationship_1.default)
376
+ return current;
377
+ if (current instanceof reference_1.default) {
378
+ current = current.referred;
379
+ continue;
380
+ }
381
+ if (current instanceof expression_1.default) {
382
+ // Pass-through projections (`WITH u`, `RETURN u AS u`)
383
+ // wrap a single Reference; arbitrary computed
384
+ // expressions have no useful binding to attribute
385
+ // properties to.
386
+ const children = current.getChildren();
387
+ if (children.length === 1) {
388
+ current = children[0];
389
+ continue;
390
+ }
391
+ return null;
392
+ }
393
+ return null;
394
+ }
395
+ return null;
396
+ }
397
+ /**
398
+ * Returns the effective labels for a MATCH chain `Node` element.
399
+ * If the element itself is unlabeled (typical for a WITH-rebound
400
+ * `MATCH (u)`), unwrap its binding to inherit the original labels.
401
+ */
402
+ effectiveLabelsFor(element) {
403
+ if (element instanceof node_reference_1.default) {
404
+ const resolved = this.resolveBoundEntity(element);
405
+ if (resolved instanceof node_1.default) {
406
+ return resolved.labels;
407
+ }
408
+ }
409
+ return element.labels;
410
+ }
411
+ handleEqualityLiteral(op) {
412
+ const lhs = op.lhs;
413
+ const rhs = op.rhs;
414
+ // Try both orientations: `alias.prop = literal` and `literal = alias.prop`.
415
+ this.tryRecordPropEquality(lhs, rhs);
416
+ this.tryRecordPropEquality(rhs, lhs);
417
+ }
418
+ tryRecordPropEquality(side, other) {
419
+ if (!(side instanceof lookup_1.default))
420
+ return;
421
+ if (!this.isLiteralAst(other))
422
+ return;
423
+ const target = this.resolveLookupTarget(side);
424
+ if (target === null)
425
+ return;
426
+ const value = this.safeEvaluate(other);
427
+ if (value === undefined)
428
+ return;
429
+ if (target.kind === "node") {
430
+ this.addNodeLiteralValue(target.labels, target.prop, value);
431
+ }
432
+ else {
433
+ this.addRelLiteralValue(target.types, target.prop, value);
434
+ }
435
+ }
436
+ handleInLiteral(op) {
437
+ const lhs = op.lhs;
438
+ const rhs = op.rhs;
439
+ if (!(lhs instanceof lookup_1.default))
440
+ return;
441
+ if (!this.isLiteralAst(rhs))
442
+ return;
443
+ const target = this.resolveLookupTarget(lhs);
444
+ if (target === null)
445
+ return;
446
+ const value = this.safeEvaluate(rhs);
447
+ if (!Array.isArray(value))
448
+ return;
449
+ for (const item of value) {
450
+ if (target.kind === "node") {
451
+ this.addNodeLiteralValue(target.labels, target.prop, item);
452
+ }
453
+ else {
454
+ this.addRelLiteralValue(target.types, target.prop, item);
455
+ }
456
+ }
457
+ }
458
+ /**
459
+ * Returns true iff the AST subtree contains only literal nodes (no
460
+ * References, ParameterReferences, Lookups, FStrings, or
461
+ * SubqueryExpressions). Used to guard literal-value extraction
462
+ * against runtime-dependent expressions.
463
+ */
464
+ isLiteralAst(node) {
465
+ if (node instanceof reference_1.default ||
466
+ node instanceof parameter_reference_1.default ||
467
+ node instanceof lookup_1.default ||
468
+ node instanceof f_string_1.default ||
469
+ node instanceof subquery_expression_1.default) {
470
+ return false;
471
+ }
472
+ for (const child of node.getChildren()) {
473
+ if (!this.isLiteralAst(child))
474
+ return false;
475
+ }
476
+ return true;
477
+ }
478
+ safeEvaluate(node) {
479
+ try {
480
+ return node.value();
481
+ }
482
+ catch (_a) {
483
+ return undefined;
484
+ }
485
+ }
486
+ tryAddNodeLiteral(labels, prop, expr) {
487
+ if (!this.isLiteralAst(expr))
488
+ return;
489
+ const value = this.safeEvaluate(expr);
490
+ if (value === undefined)
491
+ return;
492
+ this.addNodeLiteralValue(labels, prop, value);
493
+ }
494
+ tryAddRelLiteral(types, prop, expr) {
495
+ if (!this.isLiteralAst(expr))
496
+ return;
497
+ const value = this.safeEvaluate(expr);
498
+ if (value === undefined)
499
+ return;
500
+ this.addRelLiteralValue(types, prop, value);
501
+ }
502
+ /**
503
+ * Walks a virtual definition's inner statement to find the final
504
+ * RETURN-style projection and records its aliases as the declared
505
+ * property set. Falls back to the last WITH if no RETURN exists.
506
+ */
507
+ collectDeclaredProps(statement, target) {
508
+ let op = null;
509
+ try {
510
+ op = statement.firstChild();
511
+ }
512
+ catch (_a) {
513
+ return;
514
+ }
515
+ let lastReturn = null;
516
+ let lastWith = null;
517
+ while (op !== null) {
518
+ if (op instanceof return_1.default || op instanceof aggregated_return_1.default) {
519
+ lastReturn = op;
520
+ }
521
+ else if (op instanceof with_1.default || op instanceof aggregated_with_1.default) {
522
+ lastWith = op;
523
+ }
524
+ op = op.next;
525
+ }
526
+ const projection = lastReturn !== null && lastReturn !== void 0 ? lastReturn : lastWith;
527
+ if (projection === null)
528
+ return;
529
+ for (const alias of this.projectionAliases(projection)) {
530
+ target.add(alias);
531
+ }
532
+ }
533
+ /**
534
+ * Yields the alias of every projected expression in a Projection.
535
+ * Mirrors the alias-resolution logic of `Projection.expressions()`
536
+ * (which is protected).
537
+ */
538
+ *projectionAliases(projection) {
539
+ var _a;
540
+ const children = projection.getChildren();
541
+ for (let i = 0; i < children.length; i++) {
542
+ const expr = children[i];
543
+ const alias = (_a = expr.alias) !== null && _a !== void 0 ? _a : `expr${i}`;
544
+ if (typeof alias === "string" && alias.length > 0) {
545
+ yield alias;
546
+ }
243
547
  }
244
548
  }
245
549
  collectSources(statement, target) {
@@ -297,6 +601,85 @@ class StatementInfoCrawler {
297
601
  set.add(prop);
298
602
  }
299
603
  }
604
+ addNodeLiteralValue(labels, prop, value) {
605
+ for (const label of labels) {
606
+ if (!label)
607
+ continue;
608
+ let propMap = this._nodeLiterals.get(label);
609
+ if (propMap === undefined) {
610
+ propMap = new Map();
611
+ this._nodeLiterals.set(label, propMap);
612
+ }
613
+ this.appendUniqueLiteral(propMap, prop, value);
614
+ }
615
+ }
616
+ addRelLiteralValue(types, prop, value) {
617
+ for (const type of types) {
618
+ if (!type)
619
+ continue;
620
+ let propMap = this._relLiterals.get(type);
621
+ if (propMap === undefined) {
622
+ propMap = new Map();
623
+ this._relLiterals.set(type, propMap);
624
+ }
625
+ this.appendUniqueLiteral(propMap, prop, value);
626
+ }
627
+ }
628
+ appendUniqueLiteral(propMap, prop, value) {
629
+ let arr = propMap.get(prop);
630
+ if (arr === undefined) {
631
+ arr = [];
632
+ propMap.set(prop, arr);
633
+ }
634
+ for (const existing of arr) {
635
+ if (this.literalsEqual(existing, value))
636
+ return;
637
+ }
638
+ arr.push(value);
639
+ }
640
+ literalsEqual(a, b) {
641
+ if (a === b)
642
+ return true;
643
+ if (a === null || b === null || a === undefined || b === undefined)
644
+ return false;
645
+ if (typeof a !== typeof b)
646
+ return false;
647
+ if (typeof a !== "object")
648
+ return false;
649
+ if (Array.isArray(a) !== Array.isArray(b))
650
+ return false;
651
+ if (Array.isArray(a)) {
652
+ if (a.length !== b.length)
653
+ return false;
654
+ for (let i = 0; i < a.length; i++) {
655
+ if (!this.literalsEqual(a[i], b[i]))
656
+ return false;
657
+ }
658
+ return true;
659
+ }
660
+ const ka = Object.keys(a);
661
+ const kb = Object.keys(b);
662
+ if (ka.length !== kb.length)
663
+ return false;
664
+ for (const k of ka) {
665
+ if (!Object.prototype.hasOwnProperty.call(b, k))
666
+ return false;
667
+ if (!this.literalsEqual(a[k], b[k]))
668
+ return false;
669
+ }
670
+ return true;
671
+ }
672
+ literalsSnapshot(map, key) {
673
+ const propMap = map.get(key);
674
+ if (propMap === undefined || propMap.size === 0)
675
+ return {};
676
+ const out = {};
677
+ const sortedKeys = Array.from(propMap.keys()).sort();
678
+ for (const propKey of sortedKeys) {
679
+ out[propKey] = [...propMap.get(propKey)];
680
+ }
681
+ return out;
682
+ }
300
683
  snapshot() {
301
684
  const allSources = new Set();
302
685
  const nodes = {};
@@ -310,6 +693,7 @@ class StatementInfoCrawler {
310
693
  nodes[label] = {
311
694
  properties: props ? Array.from(props).sort() : [],
312
695
  sources: sources ? Array.from(sources).sort() : [],
696
+ literal_values: this.literalsSnapshot(this._nodeLiterals, label),
313
697
  };
314
698
  }
315
699
  const relationships = {};
@@ -323,8 +707,31 @@ class StatementInfoCrawler {
323
707
  relationships[type] = {
324
708
  properties: props ? Array.from(props).sort() : [],
325
709
  sources: sources ? Array.from(sources).sort() : [],
710
+ literal_values: this.literalsSnapshot(this._relLiterals, type),
326
711
  };
327
712
  }
713
+ const declaredNodes = {};
714
+ for (const label of this._nodeLabels) {
715
+ const props = this._nodeDeclaredProps.get(label);
716
+ const sources = this._nodeDeclaredSources.get(label);
717
+ if ((props && props.size > 0) || (sources && sources.size > 0)) {
718
+ declaredNodes[label] = {
719
+ properties: props ? Array.from(props).sort() : [],
720
+ sources: sources ? Array.from(sources).sort() : [],
721
+ };
722
+ }
723
+ }
724
+ const declaredRelationships = {};
725
+ for (const type of this._relTypes) {
726
+ const props = this._relDeclaredProps.get(type);
727
+ const sources = this._relDeclaredSources.get(type);
728
+ if ((props && props.size > 0) || (sources && sources.size > 0)) {
729
+ declaredRelationships[type] = {
730
+ properties: props ? Array.from(props).sort() : [],
731
+ sources: sources ? Array.from(sources).sort() : [],
732
+ };
733
+ }
734
+ }
328
735
  const info = {
329
736
  node_labels: Array.from(this._nodeLabels).sort(),
330
737
  relationship_types: Array.from(this._relTypes).sort(),
@@ -333,6 +740,10 @@ class StatementInfoCrawler {
333
740
  relationship_properties: {},
334
741
  nodes,
335
742
  relationships,
743
+ declared: {
744
+ nodes: declaredNodes,
745
+ relationships: declaredRelationships,
746
+ },
336
747
  };
337
748
  for (const [label, props] of this._nodeProps) {
338
749
  info.node_properties[label] = Array.from(props).sort();
@@ -346,13 +757,33 @@ class StatementInfoCrawler {
346
757
  * Returns a deep copy of a StatementInfo so callers can mutate it freely.
347
758
  */
348
759
  static clone(info) {
760
+ var _a, _b, _c, _d;
349
761
  const cloneProps = (props) => {
350
762
  const out = {};
351
763
  for (const [k, v] of Object.entries(props))
352
764
  out[k] = [...v];
353
765
  return out;
354
766
  };
767
+ const cloneLiterals = (literals) => {
768
+ const out = {};
769
+ for (const [k, v] of Object.entries(literals)) {
770
+ out[k] = v.map((item) => typeof item === "object" && item !== null ? structuredClone(item) : item);
771
+ }
772
+ return out;
773
+ };
355
774
  const cloneEntities = (entities) => {
775
+ var _a;
776
+ const out = {};
777
+ for (const [k, v] of Object.entries(entities)) {
778
+ out[k] = {
779
+ properties: [...v.properties],
780
+ sources: [...v.sources],
781
+ literal_values: cloneLiterals((_a = v.literal_values) !== null && _a !== void 0 ? _a : {}),
782
+ };
783
+ }
784
+ return out;
785
+ };
786
+ const cloneDeclared = (entities) => {
356
787
  const out = {};
357
788
  for (const [k, v] of Object.entries(entities)) {
358
789
  out[k] = {
@@ -370,6 +801,10 @@ class StatementInfoCrawler {
370
801
  relationship_properties: cloneProps(info.relationship_properties),
371
802
  nodes: cloneEntities(info.nodes),
372
803
  relationships: cloneEntities(info.relationships),
804
+ declared: {
805
+ nodes: cloneDeclared((_b = (_a = info.declared) === null || _a === void 0 ? void 0 : _a.nodes) !== null && _b !== void 0 ? _b : {}),
806
+ relationships: cloneDeclared((_d = (_c = info.declared) === null || _c === void 0 ? void 0 : _c.relationships) !== null && _d !== void 0 ? _d : {}),
807
+ },
373
808
  };
374
809
  }
375
810
  }