grafeo-ogm 2.5.2 → 2.5.4

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 (118) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/cjs/compilers/mutation.compiler.d.ts.map +1 -1
  3. package/dist/cjs/compilers/mutation.compiler.js +8 -3
  4. package/dist/cjs/compilers/mutation.compiler.js.map +1 -1
  5. package/dist/cjs/compilers/select-normalizer.d.ts +5 -0
  6. package/dist/cjs/compilers/select-normalizer.d.ts.map +1 -1
  7. package/dist/cjs/compilers/select-normalizer.js +54 -3
  8. package/dist/cjs/compilers/select-normalizer.js.map +1 -1
  9. package/dist/cjs/compilers/selection.compiler.d.ts +47 -0
  10. package/dist/cjs/compilers/selection.compiler.d.ts.map +1 -1
  11. package/dist/cjs/compilers/selection.compiler.js +248 -56
  12. package/dist/cjs/compilers/selection.compiler.js.map +1 -1
  13. package/dist/cjs/compilers/where.compiler.d.ts +17 -7
  14. package/dist/cjs/compilers/where.compiler.d.ts.map +1 -1
  15. package/dist/cjs/compilers/where.compiler.js +45 -13
  16. package/dist/cjs/compilers/where.compiler.js.map +1 -1
  17. package/dist/cjs/execution/executor.d.ts +7 -2
  18. package/dist/cjs/execution/executor.d.ts.map +1 -1
  19. package/dist/cjs/execution/executor.js +22 -5
  20. package/dist/cjs/execution/executor.js.map +1 -1
  21. package/dist/cjs/generator/generate-types.d.ts.map +1 -1
  22. package/dist/cjs/generator/generate-types.js +19 -2
  23. package/dist/cjs/generator/generate-types.js.map +1 -1
  24. package/dist/cjs/generator/type-emitters/model-map-emitter.d.ts +2 -1
  25. package/dist/cjs/generator/type-emitters/model-map-emitter.d.ts.map +1 -1
  26. package/dist/cjs/generator/type-emitters/model-map-emitter.js +9 -7
  27. package/dist/cjs/generator/type-emitters/model-map-emitter.js.map +1 -1
  28. package/dist/cjs/generator/type-emitters/utility-types.d.ts +13 -1
  29. package/dist/cjs/generator/type-emitters/utility-types.d.ts.map +1 -1
  30. package/dist/cjs/generator/type-emitters/utility-types.js +21 -3
  31. package/dist/cjs/generator/type-emitters/utility-types.js.map +1 -1
  32. package/dist/cjs/interface-model.d.ts +14 -10
  33. package/dist/cjs/interface-model.d.ts.map +1 -1
  34. package/dist/cjs/interface-model.js +22 -14
  35. package/dist/cjs/interface-model.js.map +1 -1
  36. package/dist/cjs/ogm.js +1 -1
  37. package/dist/cjs/ogm.js.map +1 -1
  38. package/dist/cjs/schema/parser.d.ts.map +1 -1
  39. package/dist/cjs/schema/parser.js +7 -2
  40. package/dist/cjs/schema/parser.js.map +1 -1
  41. package/dist/cjs/testing/cypher-assert.d.ts +2 -2
  42. package/dist/cjs/testing/cypher-assert.d.ts.map +1 -1
  43. package/dist/cjs/testing/cypher-assert.js +54 -6
  44. package/dist/cjs/testing/cypher-assert.js.map +1 -1
  45. package/dist/cjs/testing/neo4j-record-factory.d.ts +5 -0
  46. package/dist/cjs/testing/neo4j-record-factory.d.ts.map +1 -1
  47. package/dist/cjs/testing/neo4j-record-factory.js +82 -27
  48. package/dist/cjs/testing/neo4j-record-factory.js.map +1 -1
  49. package/dist/cjs/utils/cypher-field-projection.d.ts +9 -1
  50. package/dist/cjs/utils/cypher-field-projection.d.ts.map +1 -1
  51. package/dist/cjs/utils/cypher-field-projection.js +39 -9
  52. package/dist/cjs/utils/cypher-field-projection.js.map +1 -1
  53. package/dist/cjs/utils/cypher-sort-projection.d.ts +6 -4
  54. package/dist/cjs/utils/cypher-sort-projection.d.ts.map +1 -1
  55. package/dist/cjs/utils/cypher-sort-projection.js +9 -7
  56. package/dist/cjs/utils/cypher-sort-projection.js.map +1 -1
  57. package/dist/cjs/utils/write-coercion.d.ts.map +1 -1
  58. package/dist/cjs/utils/write-coercion.js +12 -1
  59. package/dist/cjs/utils/write-coercion.js.map +1 -1
  60. package/dist/esm/compilers/mutation.compiler.d.ts.map +1 -1
  61. package/dist/esm/compilers/mutation.compiler.js +8 -3
  62. package/dist/esm/compilers/mutation.compiler.js.map +1 -1
  63. package/dist/esm/compilers/select-normalizer.d.ts +5 -0
  64. package/dist/esm/compilers/select-normalizer.d.ts.map +1 -1
  65. package/dist/esm/compilers/select-normalizer.js +54 -3
  66. package/dist/esm/compilers/select-normalizer.js.map +1 -1
  67. package/dist/esm/compilers/selection.compiler.d.ts +47 -0
  68. package/dist/esm/compilers/selection.compiler.d.ts.map +1 -1
  69. package/dist/esm/compilers/selection.compiler.js +249 -57
  70. package/dist/esm/compilers/selection.compiler.js.map +1 -1
  71. package/dist/esm/compilers/where.compiler.d.ts +17 -7
  72. package/dist/esm/compilers/where.compiler.d.ts.map +1 -1
  73. package/dist/esm/compilers/where.compiler.js +45 -13
  74. package/dist/esm/compilers/where.compiler.js.map +1 -1
  75. package/dist/esm/execution/executor.d.ts +7 -2
  76. package/dist/esm/execution/executor.d.ts.map +1 -1
  77. package/dist/esm/execution/executor.js +22 -5
  78. package/dist/esm/execution/executor.js.map +1 -1
  79. package/dist/esm/generator/generate-types.d.ts.map +1 -1
  80. package/dist/esm/generator/generate-types.js +20 -3
  81. package/dist/esm/generator/generate-types.js.map +1 -1
  82. package/dist/esm/generator/type-emitters/model-map-emitter.d.ts +2 -1
  83. package/dist/esm/generator/type-emitters/model-map-emitter.d.ts.map +1 -1
  84. package/dist/esm/generator/type-emitters/model-map-emitter.js +9 -7
  85. package/dist/esm/generator/type-emitters/model-map-emitter.js.map +1 -1
  86. package/dist/esm/generator/type-emitters/utility-types.d.ts +13 -1
  87. package/dist/esm/generator/type-emitters/utility-types.d.ts.map +1 -1
  88. package/dist/esm/generator/type-emitters/utility-types.js +19 -3
  89. package/dist/esm/generator/type-emitters/utility-types.js.map +1 -1
  90. package/dist/esm/interface-model.d.ts +14 -10
  91. package/dist/esm/interface-model.d.ts.map +1 -1
  92. package/dist/esm/interface-model.js +22 -14
  93. package/dist/esm/interface-model.js.map +1 -1
  94. package/dist/esm/ogm.js +1 -1
  95. package/dist/esm/ogm.js.map +1 -1
  96. package/dist/esm/schema/parser.d.ts.map +1 -1
  97. package/dist/esm/schema/parser.js +7 -2
  98. package/dist/esm/schema/parser.js.map +1 -1
  99. package/dist/esm/testing/cypher-assert.d.ts +2 -2
  100. package/dist/esm/testing/cypher-assert.d.ts.map +1 -1
  101. package/dist/esm/testing/cypher-assert.js +54 -6
  102. package/dist/esm/testing/cypher-assert.js.map +1 -1
  103. package/dist/esm/testing/neo4j-record-factory.d.ts +5 -0
  104. package/dist/esm/testing/neo4j-record-factory.d.ts.map +1 -1
  105. package/dist/esm/testing/neo4j-record-factory.js +82 -27
  106. package/dist/esm/testing/neo4j-record-factory.js.map +1 -1
  107. package/dist/esm/utils/cypher-field-projection.d.ts +9 -1
  108. package/dist/esm/utils/cypher-field-projection.d.ts.map +1 -1
  109. package/dist/esm/utils/cypher-field-projection.js +39 -9
  110. package/dist/esm/utils/cypher-field-projection.js.map +1 -1
  111. package/dist/esm/utils/cypher-sort-projection.d.ts +6 -4
  112. package/dist/esm/utils/cypher-sort-projection.d.ts.map +1 -1
  113. package/dist/esm/utils/cypher-sort-projection.js +9 -7
  114. package/dist/esm/utils/cypher-sort-projection.js.map +1 -1
  115. package/dist/esm/utils/write-coercion.d.ts.map +1 -1
  116. package/dist/esm/utils/write-coercion.js +12 -1
  117. package/dist/esm/utils/write-coercion.js.map +1 -1
  118. package/package.json +1 -1
@@ -58,6 +58,23 @@ export interface SelectionNode {
58
58
  * can't honour them throw instead of dropping them.
59
59
  */
60
60
  hasArguments?: boolean;
61
+ /**
62
+ * v2.5.3 — the same response key selected on DIFFERENT fields by sibling
63
+ * inline fragments (`... on A { x: title } ... on B { x: body }`): the
64
+ * field each fragment type selected. Projected as a per-type CASE.
65
+ */
66
+ fieldByType?: Record<string, string>;
67
+ /**
68
+ * Connection only (v2.5.3): selectionSet aliases of the connection's own
69
+ * keys. Pre-2.5.3 they were ignored and the keys always came back as
70
+ * `edges`, `node`, `properties` and `totalCount`.
71
+ */
72
+ connectionAliases?: {
73
+ totalCount?: string;
74
+ edges?: string;
75
+ node?: string;
76
+ properties?: string;
77
+ };
61
78
  }
62
79
  /**
63
80
  * Compiles a selection set (parsed SelectionNode[]) into a Cypher RETURN map projection.
@@ -132,8 +149,22 @@ export declare class SelectionCompiler {
132
149
  * when the abstract type declares the field as `@cypher` itself, when a
133
150
  * condition isn't a concrete type, or when no fragment type declares it
134
151
  * as `@cypher`.
152
+ *
153
+ * v2.5.4 — under a policy context, each `@cypher` branch is also guarded
154
+ * by that member's own `read` clause (`WHEN n:B AND <B read> THEN …`),
155
+ * so a member's statement only projects for a node that passes the
156
+ * member's policy. The node filter (`compileTargetPolicyClause`) already
157
+ * requires every carried member's clause; this is defense in depth.
158
+ * Without a policy context the branches are unchanged.
135
159
  */
136
160
  private compileFragmentScalar;
161
+ /**
162
+ * ` AND <member read clause>` guarding a fragment `@cypher` branch
163
+ * (v2.5.4); `''` without a policy context, when the member has no read
164
+ * clause, or when that clause needs `@cypher` preludes a projection
165
+ * cannot host (the node filter still enforces it).
166
+ */
167
+ private memberReadGuard;
137
168
  /**
138
169
  * Parse a GraphQL selection set string into SelectionNode[].
139
170
  *
@@ -149,6 +180,22 @@ export declare class SelectionCompiler {
149
180
  * Parse the children of a connection field, extracting node and edge children.
150
181
  */
151
182
  private parseConnectionChildren;
183
+ /**
184
+ * Keep the selectionSet alias of a connection key (v2.5.3). The first
185
+ * alias given for a key wins.
186
+ */
187
+ private recordConnectionAlias;
188
+ /**
189
+ * Compile a relationship or connection field selected through inline
190
+ * fragments on an abstract type that does not declare it (v2.5.3):
191
+ * `CASE WHEN n:D THEN <D's projection> … END`, each branch compiled
192
+ * against the implementer — with its arguments and the target type's
193
+ * read policy. Pre-2.5.3 the field was looked up on the abstract type,
194
+ * not found, and silently dropped together with its arguments.
195
+ *
196
+ * Returns `null` (unchanged behaviour) when no fragment type declares it.
197
+ */
198
+ private compileFragmentRelationship;
152
199
  /**
153
200
  * Compile a relationship field into a Cypher pattern comprehension.
154
201
  */
@@ -1 +1 @@
1
- {"version":3,"file":"selection.compiler.d.ts","sourceRoot":"","sources":["../../../src/compilers/selection.compiler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"selection.compiler.d.ts","sourceRoot":"","sources":["../../../src/compilers/selection.compiler.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,92 @@ const utils_1 = require("../schema/utils");
8
8
  const cypher_field_projection_1 = require("../utils/cypher-field-projection");
9
9
  const validation_1 = require("../utils/validation");
10
10
  const where_compiler_1 = require("./where.compiler");
11
+ /**
12
+ * v2.5.3 — the printed arguments of a parsed selectionSet field, so a
13
+ * response key selected twice merges its sub-selections only when both
14
+ * selections carry the same arguments.
15
+ */
16
+ const argumentKeys = new WeakMap();
17
+ function sameArguments(a, b) {
18
+ return (argumentKeys.get(a) ?? '') === (argumentKeys.get(b) ?? '');
19
+ }
20
+ /**
21
+ * Merge `incoming` into `list` by response key (v2.5.3), as GraphQL merges
22
+ * a field selected more than once: sub-selections are unioned. Pre-2.5.3
23
+ * the second selection of a key was dropped with its sub-fields.
24
+ *
25
+ * - Same field, same arguments: children, edge children and `totalCount`
26
+ * merge recursively.
27
+ * - Same field, different arguments: the first selection wins (unchanged).
28
+ * - Different fields under one key, each selected by inline fragments on
29
+ * different types: recorded in `fieldByType` for a per-type projection.
30
+ * - A selection outside any fragment makes the field unconditional.
31
+ */
32
+ function mergeSelection(list, seen, incoming) {
33
+ const key = incoming.alias ?? incoming.fieldName;
34
+ const existing = seen.get(key);
35
+ if (!existing) {
36
+ seen.set(key, incoming);
37
+ list.push(incoming);
38
+ return;
39
+ }
40
+ if (existing.fieldName !== incoming.fieldName || existing.fieldByType) {
41
+ mergeFragmentField(existing, incoming);
42
+ return;
43
+ }
44
+ if (existing.typeConditions && incoming.typeConditions)
45
+ existing.typeConditions = [
46
+ ...new Set([...existing.typeConditions, ...incoming.typeConditions]),
47
+ ];
48
+ // Also selected outside any fragment → unconditional.
49
+ else
50
+ delete existing.typeConditions;
51
+ if (!sameArguments(existing, incoming))
52
+ return;
53
+ if (incoming.children)
54
+ existing.children = mergeSelectionLists(existing.children, incoming.children);
55
+ if (incoming.edgeChildren)
56
+ existing.edgeChildren = mergeSelectionLists(existing.edgeChildren, incoming.edgeChildren);
57
+ if (existing.isConnection) {
58
+ if (incoming.totalCount)
59
+ existing.totalCount = true;
60
+ if (existing.totalCountOnly && !incoming.totalCountOnly)
61
+ delete existing.totalCountOnly;
62
+ if (incoming.connectionAliases)
63
+ existing.connectionAliases = {
64
+ ...incoming.connectionAliases,
65
+ ...existing.connectionAliases,
66
+ };
67
+ }
68
+ }
69
+ /** Two scalars under one key, from fragments on different types (v2.5.3). */
70
+ function mergeFragmentField(existing, incoming) {
71
+ // Only scalars selected by inline fragments can differ per type; any
72
+ // other clash keeps the first selection (unchanged).
73
+ if (!existing.isScalar ||
74
+ !incoming.isScalar ||
75
+ !existing.typeConditions ||
76
+ !incoming.typeConditions)
77
+ return;
78
+ const byType = existing.fieldByType ?? {};
79
+ if (!existing.fieldByType)
80
+ for (const t of existing.typeConditions)
81
+ byType[t] = existing.fieldName;
82
+ for (const t of incoming.typeConditions)
83
+ if (byType[t] === undefined)
84
+ byType[t] = incoming.fieldName;
85
+ existing.fieldByType = byType;
86
+ existing.typeConditions = [
87
+ ...new Set([...existing.typeConditions, ...incoming.typeConditions]),
88
+ ];
89
+ }
90
+ function mergeSelectionLists(base, extra) {
91
+ const list = [];
92
+ const seen = new Map();
93
+ for (const node of [...(base ?? []), ...extra])
94
+ mergeSelection(list, seen, node);
95
+ return list;
96
+ }
11
97
  /** selectionSet field arguments the OGM honours, by field kind (v2.5.0). */
12
98
  const RELATIONSHIP_ARGUMENTS = new Set(['where', 'options']);
13
99
  const CONNECTION_ARGUMENTS = new Set(['where']);
@@ -202,7 +288,7 @@ class SelectionCompiler {
202
288
  }
203
289
  (0, validation_1.assertSafeIdentifier)(node.fieldName, 'selection field');
204
290
  if (isAbstract && node.typeConditions) {
205
- const conditional = this.compileFragmentScalar(node, key, nodeVar, nodeDef, cypherScope);
291
+ const conditional = this.compileFragmentScalar(node, key, nodeVar, nodeDef, cypherScope, params, paramCounter, policyContext);
206
292
  if (conditional) {
207
293
  parts.push(conditional);
208
294
  continue;
@@ -229,6 +315,21 @@ class SelectionCompiler {
229
315
  : `.${(0, validation_1.escapeIdentifier)(node.fieldName)}`);
230
316
  continue;
231
317
  }
318
+ // v2.5.3 — a relationship / connection field selected through an
319
+ // inline fragment on an abstract type that doesn't declare it.
320
+ // (Union relationship fields keep their per-member CASE below.)
321
+ if (isAbstract &&
322
+ node.typeConditions &&
323
+ (node.isConnection || !(unionMembers && unionMembers.length > 0)) &&
324
+ !nodeDef.relationships.has(node.isConnection
325
+ ? node.fieldName.replace(/Connection$/, '')
326
+ : node.fieldName)) {
327
+ const fragmentCypher = this.compileFragmentRelationship(node, nodeVar, maxDepth, currentDepth, params, paramCounter, policyContext);
328
+ if (fragmentCypher) {
329
+ parts.push(`${key}: ${fragmentCypher}`);
330
+ continue;
331
+ }
332
+ }
232
333
  if (node.isConnection) {
233
334
  const connectionCypher = this.compileConnection(node, nodeVar, nodeDef, maxDepth, currentDepth, params, paramCounter, policyContext);
234
335
  if (connectionCypher)
@@ -263,33 +364,64 @@ class SelectionCompiler {
263
364
  * when the abstract type declares the field as `@cypher` itself, when a
264
365
  * condition isn't a concrete type, or when no fragment type declares it
265
366
  * as `@cypher`.
367
+ *
368
+ * v2.5.4 — under a policy context, each `@cypher` branch is also guarded
369
+ * by that member's own `read` clause (`WHEN n:B AND <B read> THEN …`),
370
+ * so a member's statement only projects for a node that passes the
371
+ * member's policy. The node filter (`compileTargetPolicyClause`) already
372
+ * requires every carried member's clause; this is defense in depth.
373
+ * Without a policy context the branches are unchanged.
266
374
  */
267
- compileFragmentScalar(node, key, nodeVar, nodeDef, cypherScope) {
268
- if (nodeDef.properties.get(node.fieldName)?.isCypher)
375
+ compileFragmentScalar(node, key, nodeVar, nodeDef, cypherScope, params, paramCounter, policyContext = null) {
376
+ // v2.5.3 — `fieldByType`: sibling fragments selected different fields
377
+ // under this key, so each type projects its own field.
378
+ const perType = node.fieldByType;
379
+ if (!perType && nodeDef.properties.get(node.fieldName)?.isCypher)
269
380
  return null;
270
381
  const members = [];
271
382
  for (const typeName of node.typeConditions ?? []) {
272
383
  const def = this.schema.nodes.get(typeName);
273
384
  if (!def)
274
385
  return null;
275
- const prop = def.properties.get(node.fieldName);
386
+ const field = perType?.[typeName] ?? node.fieldName;
387
+ (0, validation_1.assertSafeIdentifier)(field, 'selection field');
388
+ const prop = def.properties.get(field);
276
389
  if (prop)
277
- members.push({ def, prop });
390
+ members.push({ def, prop, field });
278
391
  }
279
- if (!members.some((m) => m.prop.isCypher && m.prop.cypherStatement))
392
+ if (!perType &&
393
+ !members.some((m) => m.prop.isCypher && m.prop.cypherStatement))
280
394
  return null;
281
- const branches = members.map(({ def, prop }) => {
395
+ const branches = members.map(({ def, prop, field }) => {
282
396
  let expr;
283
397
  if (prop.isCypher && prop.cypherStatement)
284
398
  expr = cypherScope
285
- ? cypherScope.register(node.fieldName, prop, def.label)
286
- : (0, cypher_field_projection_1.buildInlineCypherFieldExpr)(prop.cypherStatement, nodeVar, `${def.typeName}.${node.fieldName}`);
399
+ ? cypherScope.register(field, prop, def.label)
400
+ : (0, cypher_field_projection_1.buildInlineCypherFieldExpr)(prop.cypherStatement, nodeVar, `${def.typeName}.${field}`);
287
401
  else
288
- expr = `${nodeVar}.${(0, validation_1.escapeIdentifier)(node.fieldName)}`;
289
- return `WHEN ${nodeVar}:${(0, validation_1.escapeIdentifier)(def.label)} THEN ${expr}`;
402
+ expr = `${nodeVar}.${(0, validation_1.escapeIdentifier)(field)}`;
403
+ const guard = prop.isCypher && prop.cypherStatement
404
+ ? this.memberReadGuard(def, nodeVar, params, paramCounter, policyContext)
405
+ : '';
406
+ return `WHEN ${nodeVar}:${(0, validation_1.escapeIdentifier)(def.label)}${guard} THEN ${expr}`;
290
407
  });
291
408
  return `${(0, validation_1.escapeIdentifier)(key)}: CASE ${branches.join(' ')} END`;
292
409
  }
410
+ /**
411
+ * ` AND <member read clause>` guarding a fragment `@cypher` branch
412
+ * (v2.5.4); `''` without a policy context, when the member has no read
413
+ * clause, or when that clause needs `@cypher` preludes a projection
414
+ * cannot host (the node filter still enforces it).
415
+ */
416
+ memberReadGuard(def, nodeVar, params, paramCounter, policyContext) {
417
+ if (!policyContext || !this.whereCompiler || !params || !paramCounter)
418
+ return '';
419
+ const clause = this.whereCompiler.compileTargetPolicyClause(def.typeName, nodeVar, 'read', policyContext, paramCounter);
420
+ if (clause.cypher === null || clause.preludes.length > 0)
421
+ return '';
422
+ (0, validation_1.mergeParams)(params, clause.params);
423
+ return ` AND ${clause.cypher}`;
424
+ }
293
425
  /**
294
426
  * Parse a GraphQL selection set string into SelectionNode[].
295
427
  *
@@ -351,23 +483,8 @@ class SelectionCompiler {
351
483
  const fragment = sel;
352
484
  const condition = fragment.typeCondition?.name.value ?? typeCondition;
353
485
  if (fragment.selectionSet)
354
- for (const merged of this.convertSelectionSet(fragment.selectionSet, condition)) {
355
- const mergedKey = merged.alias ?? merged.fieldName;
356
- const existing = seen.get(mergedKey);
357
- if (!existing) {
358
- seen.set(mergedKey, merged);
359
- nodes.push(merged);
360
- }
361
- else if (existing.typeConditions)
362
- existing.typeConditions = merged.typeConditions
363
- ? [
364
- ...new Set([
365
- ...existing.typeConditions,
366
- ...merged.typeConditions,
367
- ]),
368
- ]
369
- : undefined;
370
- }
486
+ for (const merged of this.convertSelectionSet(fragment.selectionSet, condition))
487
+ mergeSelection(nodes, seen, merged);
371
488
  continue;
372
489
  }
373
490
  if (sel.kind !== graphql_1.Kind.FIELD)
@@ -377,14 +494,9 @@ class SelectionCompiler {
377
494
  // v2.5.0 — fields are keyed by their OUTPUT name, so the same field
378
495
  // under two aliases (`a: kids { .. } b: kids { .. }`) stays two
379
496
  // selections. Pre-2.5.0 they collapsed into one.
380
- const outputKey = field.alias?.value ?? fieldName;
381
- const existing = seen.get(outputKey);
382
- if (existing) {
383
- // Also selected outside any fragment → unconditional.
384
- if (typeCondition === undefined)
385
- delete existing.typeConditions;
386
- continue;
387
- }
497
+ // v2.5.3 — a key selected twice is merged (see `mergeSelection`);
498
+ // the repeat's arguments are compared, not re-validated.
499
+ const existing = seen.get(field.alias?.value ?? fieldName);
388
500
  const hasChildren = !!field.selectionSet;
389
501
  const isConnection = fieldName.endsWith('Connection');
390
502
  const node = {
@@ -396,16 +508,18 @@ class SelectionCompiler {
396
508
  };
397
509
  if (typeCondition !== undefined)
398
510
  node.typeConditions = [typeCondition];
399
- seen.set(outputKey, node);
400
- if (field.arguments && field.arguments.length > 0)
401
- applyFieldArguments(field, node);
511
+ if (field.arguments && field.arguments.length > 0) {
512
+ argumentKeys.set(node, field.arguments.map((a) => (0, graphql_1.print)(a)).join(','));
513
+ if (!existing)
514
+ applyFieldArguments(field, node);
515
+ }
402
516
  if (hasChildren && field.selectionSet)
403
517
  if (isConnection)
404
518
  // For connections, parse edges -> node and edges -> properties
405
519
  this.parseConnectionChildren(field.selectionSet, node);
406
520
  else
407
521
  node.children = this.convertSelectionSet(field.selectionSet);
408
- nodes.push(node);
522
+ mergeSelection(nodes, seen, node);
409
523
  }
410
524
  return nodes;
411
525
  }
@@ -419,25 +533,81 @@ class SelectionCompiler {
419
533
  continue;
420
534
  const field = sel;
421
535
  assertNoArguments(field, `${parentNode.fieldName}.${field.name.value}`);
422
- if (field.name.value === 'totalCount')
536
+ if (field.name.value === 'totalCount') {
423
537
  parentNode.totalCount = true;
424
- if (field.name.value === 'edges')
538
+ this.recordConnectionAlias(parentNode, 'totalCount', field);
539
+ }
540
+ if (field.name.value === 'edges') {
425
541
  selectsEdges = true;
542
+ this.recordConnectionAlias(parentNode, 'edges', field);
543
+ }
426
544
  if (field.name.value === 'edges' && field.selectionSet)
427
545
  for (const edgeSel of field.selectionSet.selections) {
428
546
  if (edgeSel.kind !== graphql_1.Kind.FIELD)
429
547
  continue;
430
548
  const edgeField = edgeSel;
431
549
  assertNoArguments(edgeField, `${parentNode.fieldName}.edges.${edgeField.name.value}`);
432
- if (edgeField.name.value === 'node' && edgeField.selectionSet)
433
- parentNode.children = this.convertSelectionSet(edgeField.selectionSet);
434
- if (edgeField.name.value === 'properties' && edgeField.selectionSet)
435
- parentNode.edgeChildren = this.convertSelectionSet(edgeField.selectionSet);
550
+ // v2.5.3 — a repeated `edges { node { … } }` merges its
551
+ // sub-selections; pre-2.5.3 the last one replaced the others.
552
+ if (edgeField.name.value === 'node' && edgeField.selectionSet) {
553
+ this.recordConnectionAlias(parentNode, 'node', edgeField);
554
+ parentNode.children = mergeSelectionLists(parentNode.children, this.convertSelectionSet(edgeField.selectionSet));
555
+ }
556
+ if (edgeField.name.value === 'properties' && edgeField.selectionSet) {
557
+ this.recordConnectionAlias(parentNode, 'properties', edgeField);
558
+ parentNode.edgeChildren = mergeSelectionLists(parentNode.edgeChildren, this.convertSelectionSet(edgeField.selectionSet));
559
+ }
436
560
  }
437
561
  }
438
562
  if (parentNode.totalCount && !selectsEdges)
439
563
  parentNode.totalCountOnly = true;
440
564
  }
565
+ /**
566
+ * Keep the selectionSet alias of a connection key (v2.5.3). The first
567
+ * alias given for a key wins.
568
+ */
569
+ recordConnectionAlias(node, key, field) {
570
+ var _a;
571
+ const alias = field.alias?.value;
572
+ if (alias === undefined || alias === key)
573
+ return;
574
+ (0, validation_1.assertSafeIdentifier)(alias, 'selection alias');
575
+ node.connectionAliases ?? (node.connectionAliases = {});
576
+ (_a = node.connectionAliases)[key] ?? (_a[key] = alias);
577
+ }
578
+ /**
579
+ * Compile a relationship or connection field selected through inline
580
+ * fragments on an abstract type that does not declare it (v2.5.3):
581
+ * `CASE WHEN n:D THEN <D's projection> … END`, each branch compiled
582
+ * against the implementer — with its arguments and the target type's
583
+ * read policy. Pre-2.5.3 the field was looked up on the abstract type,
584
+ * not found, and silently dropped together with its arguments.
585
+ *
586
+ * Returns `null` (unchanged behaviour) when no fragment type declares it.
587
+ */
588
+ compileFragmentRelationship(node, nodeVar, maxDepth, currentDepth, params, paramCounter, policyContext = null) {
589
+ const relField = node.isConnection
590
+ ? node.fieldName.replace(/Connection$/, '')
591
+ : node.fieldName;
592
+ const branches = [];
593
+ let singular = node.isConnection;
594
+ for (const typeName of node.typeConditions ?? []) {
595
+ const def = this.schema.nodes.get(typeName);
596
+ const relDef = def?.relationships.get(relField);
597
+ if (!def || !relDef)
598
+ continue;
599
+ if (!relDef.isArray)
600
+ singular = true;
601
+ const expr = node.isConnection
602
+ ? this.compileConnection(node, nodeVar, def, maxDepth, currentDepth, params, paramCounter, policyContext)
603
+ : this.compileRelationship(node, nodeVar, def, maxDepth, currentDepth, params, paramCounter, policyContext);
604
+ if (expr)
605
+ branches.push(`WHEN ${nodeVar}:${(0, validation_1.escapeIdentifier)(def.label)} THEN ${expr}`);
606
+ }
607
+ if (branches.length === 0)
608
+ return null;
609
+ return `CASE ${branches.join(' ')} ELSE ${singular ? 'null' : '[]'} END`;
610
+ }
441
611
  /**
442
612
  * Compile a relationship field into a Cypher pattern comprehension.
443
613
  */
@@ -502,9 +672,15 @@ class SelectionCompiler {
502
672
  params[name] = (0, neo4j_driver_1.int)(value);
503
673
  return `$${name}`;
504
674
  };
505
- const start = node.offset ?? 0;
675
+ // v2.5.3 — a list slice bound must fit in 32 bits (and a JS number
676
+ // past 2^63 overflows the driver's Integer). No list is that long, so
677
+ // clamping keeps the result; pre-2.5.3 the query failed.
678
+ const INT32_MAX = 2147483647;
679
+ const start = Math.min(node.offset ?? 0, INT32_MAX);
506
680
  const startRef = node.offset !== undefined ? bind(start) : '';
507
- const endRef = node.limit !== undefined ? bind(start + node.limit) : '';
681
+ const endRef = node.limit !== undefined
682
+ ? bind(Math.min(start + node.limit, INT32_MAX))
683
+ : '';
508
684
  return `[${startRef}..${endRef}]`;
509
685
  }
510
686
  /**
@@ -632,20 +808,36 @@ class SelectionCompiler {
632
808
  });
633
809
  const mapParts = [];
634
810
  const projectionKeys = [];
811
+ // v2.5.3 — selectionSet aliases of the connection keys (validated when
812
+ // parsed); unaliased output is byte-identical.
813
+ const aliases = node.connectionAliases ?? {};
814
+ const nodeKey = aliases.node ? (0, validation_1.escapeIdentifier)(aliases.node) : 'node';
815
+ const propertiesKey = aliases.properties
816
+ ? (0, validation_1.escapeIdentifier)(aliases.properties)
817
+ : 'properties';
818
+ const edgesKey = aliases.edges ? (0, validation_1.escapeIdentifier)(aliases.edges) : 'edges';
819
+ const totalCountKey = aliases.totalCount
820
+ ? (0, validation_1.escapeIdentifier)(aliases.totalCount)
821
+ : 'totalCount';
635
822
  // Compile node projection
636
823
  if (node.children && node.children.length > 0) {
637
824
  const nodeProjection = this.compile(node.children, childVar, targetDef, maxDepth, currentDepth + 1, params, paramCounter, null, policyContext);
638
- mapParts.push(`node: ${nodeProjection}`);
639
- projectionKeys.push('node');
825
+ mapParts.push(`${nodeKey}: ${nodeProjection}`);
826
+ projectionKeys.push(nodeKey);
640
827
  }
641
828
  // Compile edge properties projection
642
829
  if (node.edgeChildren && node.edgeChildren.length > 0) {
643
830
  const edgeFields = node.edgeChildren
644
831
  .filter((c) => c.isScalar)
645
- .map((c) => `.${(0, validation_1.escapeIdentifier)(c.fieldName)}`)
832
+ .map((c) => {
833
+ if (c.alias === undefined || c.alias === c.fieldName)
834
+ return `.${(0, validation_1.escapeIdentifier)(c.fieldName)}`;
835
+ (0, validation_1.assertSafeIdentifier)(c.alias, 'selection alias');
836
+ return `${(0, validation_1.escapeIdentifier)(c.alias)}: ${edgeVar}.${(0, validation_1.escapeIdentifier)(c.fieldName)}`;
837
+ })
646
838
  .join(', ');
647
- mapParts.push(`properties: ${edgeVar} { ${edgeFields} }`);
648
- projectionKeys.push('properties');
839
+ mapParts.push(`${propertiesKey}: ${edgeVar} { ${edgeFields} }`);
840
+ projectionKeys.push(propertiesKey);
649
841
  }
650
842
  // Inject synthetic sort scalars at the top level of the edge map so
651
843
  // apoc.coll.sortMulti (which only accepts top-level keys) can order the
@@ -789,19 +981,19 @@ class SelectionCompiler {
789
981
  // (same pattern, same `where`, same target `read` policy), so it can
790
982
  // never count a node the caller cannot see. Pre-2.4.8 it was dropped.
791
983
  const totalCount = node.totalCount
792
- ? `totalCount: size([${pattern}${whereClause} | 1])`
984
+ ? `${totalCountKey}: size([${pattern}${whereClause} | 1])`
793
985
  : '';
794
986
  if (node.totalCountOnly)
795
987
  return `{ ${totalCount} }`;
796
988
  const withTotal = totalCount ? `, ${totalCount}` : '';
797
989
  if (sortKeyTokens.length === 0)
798
- return `{ edges: [${pattern}${whereClause} | ${innerMap}]${withTotal} }`;
990
+ return `{ ${edgesKey}: [${pattern}${whereClause} | ${innerMap}]${withTotal} }`;
799
991
  // Re-project only the user-facing keys so synthetic __skN scalars don't
800
992
  // leak out of the projection.
801
993
  const outerProjection = projectionKeys.length > 0
802
994
  ? `{ ${projectionKeys.map((k) => `${k}: x.${k}`).join(', ')} }`
803
995
  : `{}`;
804
- return `{ edges: [x IN apoc.coll.sortMulti([${pattern}${whereClause} | ${innerMap}], [${sortKeyTokens.join(', ')}]) | ${outerProjection}]${withTotal} }`;
996
+ return `{ ${edgesKey}: [x IN apoc.coll.sortMulti([${pattern}${whereClause} | ${innerMap}], [${sortKeyTokens.join(', ')}]) | ${outerProjection}]${withTotal} }`;
805
997
  }
806
998
  /**
807
999
  * AND-combine an optional user where (`select.where` / `connectionWhere`)