grafeo-ogm 1.13.0 → 2.0.0
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.
- package/CHANGELOG.md +221 -0
- package/README.md +70 -0
- package/dist/cjs/compilers/fulltext.compiler.d.ts.map +1 -1
- package/dist/cjs/compilers/fulltext.compiler.js +30 -14
- package/dist/cjs/compilers/fulltext.compiler.js.map +1 -1
- package/dist/cjs/compilers/mutation.compiler.d.ts +54 -1
- package/dist/cjs/compilers/mutation.compiler.d.ts.map +1 -1
- package/dist/cjs/compilers/mutation.compiler.js +134 -32
- package/dist/cjs/compilers/mutation.compiler.js.map +1 -1
- package/dist/cjs/compilers/where.compiler.d.ts.map +1 -1
- package/dist/cjs/compilers/where.compiler.js +7 -0
- package/dist/cjs/compilers/where.compiler.js.map +1 -1
- package/dist/cjs/generator/type-emitters/fulltext-emitter.js +24 -8
- package/dist/cjs/generator/type-emitters/fulltext-emitter.js.map +1 -1
- package/dist/cjs/generator/type-emitters/sort-options-emitter.d.ts +2 -1
- package/dist/cjs/generator/type-emitters/sort-options-emitter.d.ts.map +1 -1
- package/dist/cjs/generator/type-emitters/sort-options-emitter.js +12 -5
- package/dist/cjs/generator/type-emitters/sort-options-emitter.js.map +1 -1
- package/dist/cjs/generator/type-emitters/utility-types.d.ts.map +1 -1
- package/dist/cjs/generator/type-emitters/utility-types.js +20 -0
- package/dist/cjs/generator/type-emitters/utility-types.js.map +1 -1
- package/dist/cjs/interface-model.d.ts.map +1 -1
- package/dist/cjs/interface-model.js +9 -1
- package/dist/cjs/interface-model.js.map +1 -1
- package/dist/cjs/model.d.ts.map +1 -1
- package/dist/cjs/model.js +12 -3
- package/dist/cjs/model.js.map +1 -1
- package/dist/cjs/ogm.d.ts.map +1 -1
- package/dist/cjs/ogm.js +6 -1
- package/dist/cjs/ogm.js.map +1 -1
- package/dist/cjs/utils/cypher-sort-projection.d.ts +3 -0
- package/dist/cjs/utils/cypher-sort-projection.d.ts.map +1 -1
- package/dist/cjs/utils/cypher-sort-projection.js +13 -1
- package/dist/cjs/utils/cypher-sort-projection.js.map +1 -1
- package/dist/cjs/utils/validation.d.ts +79 -0
- package/dist/cjs/utils/validation.d.ts.map +1 -1
- package/dist/cjs/utils/validation.js +554 -0
- package/dist/cjs/utils/validation.js.map +1 -1
- package/dist/esm/compilers/fulltext.compiler.d.ts.map +1 -1
- package/dist/esm/compilers/fulltext.compiler.js +31 -15
- package/dist/esm/compilers/fulltext.compiler.js.map +1 -1
- package/dist/esm/compilers/mutation.compiler.d.ts +54 -1
- package/dist/esm/compilers/mutation.compiler.d.ts.map +1 -1
- package/dist/esm/compilers/mutation.compiler.js +134 -32
- package/dist/esm/compilers/mutation.compiler.js.map +1 -1
- package/dist/esm/compilers/where.compiler.d.ts.map +1 -1
- package/dist/esm/compilers/where.compiler.js +8 -1
- package/dist/esm/compilers/where.compiler.js.map +1 -1
- package/dist/esm/generator/type-emitters/fulltext-emitter.js +24 -8
- package/dist/esm/generator/type-emitters/fulltext-emitter.js.map +1 -1
- package/dist/esm/generator/type-emitters/sort-options-emitter.d.ts +2 -1
- package/dist/esm/generator/type-emitters/sort-options-emitter.d.ts.map +1 -1
- package/dist/esm/generator/type-emitters/sort-options-emitter.js +12 -5
- package/dist/esm/generator/type-emitters/sort-options-emitter.js.map +1 -1
- package/dist/esm/generator/type-emitters/utility-types.d.ts.map +1 -1
- package/dist/esm/generator/type-emitters/utility-types.js +20 -0
- package/dist/esm/generator/type-emitters/utility-types.js.map +1 -1
- package/dist/esm/interface-model.d.ts.map +1 -1
- package/dist/esm/interface-model.js +9 -1
- package/dist/esm/interface-model.js.map +1 -1
- package/dist/esm/model.d.ts.map +1 -1
- package/dist/esm/model.js +12 -3
- package/dist/esm/model.js.map +1 -1
- package/dist/esm/ogm.d.ts.map +1 -1
- package/dist/esm/ogm.js +6 -1
- package/dist/esm/ogm.js.map +1 -1
- package/dist/esm/utils/cypher-sort-projection.d.ts +3 -0
- package/dist/esm/utils/cypher-sort-projection.d.ts.map +1 -1
- package/dist/esm/utils/cypher-sort-projection.js +14 -2
- package/dist/esm/utils/cypher-sort-projection.js.map +1 -1
- package/dist/esm/utils/validation.d.ts +79 -0
- package/dist/esm/utils/validation.d.ts.map +1 -1
- package/dist/esm/utils/validation.js +549 -0
- package/dist/esm/utils/validation.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OGMError } from '../errors';
|
|
2
2
|
import { isFulltextLeaf, isFulltextIndexEntry, } from '../model';
|
|
3
|
-
import { assertSafeIdentifier, escapeIdentifier, mergeParams, } from '../utils/validation';
|
|
3
|
+
import { assertPlainObject, assertSafeIdentifier, assertSingleFulltextKey, assertSingleFulltextOperator, escapeIdentifier, mergeParams, } from '../utils/validation';
|
|
4
4
|
/** Maximum recursion depth for nested fulltext logical operators */
|
|
5
5
|
const MAX_DEPTH = 10;
|
|
6
6
|
/**
|
|
@@ -64,12 +64,16 @@ export class FulltextCompiler {
|
|
|
64
64
|
*/
|
|
65
65
|
compileRelationship(fulltext, relIndex, relVar = 'r') {
|
|
66
66
|
assertSafeIdentifier(relVar, 'relationship variable');
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
assertPlainObject(fulltext, 'Relationship fulltext input');
|
|
68
|
+
const entries = Object.entries(fulltext);
|
|
69
|
+
if (entries.length === 0)
|
|
69
70
|
throw new OGMError('Fulltext input must contain at least one index entry');
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
const
|
|
71
|
+
// Validate every key, not just the surviving one, so the arity error only
|
|
72
|
+
// ever interpolates identifier-shaped strings.
|
|
73
|
+
for (const [key] of entries)
|
|
74
|
+
assertSafeIdentifier(key, 'fulltext index name');
|
|
75
|
+
assertSingleFulltextKey(entries, 'Relationship fulltext input');
|
|
76
|
+
const [indexName, input] = entries[0];
|
|
73
77
|
assertValidFulltextPhrase(input.phrase);
|
|
74
78
|
if (relIndex.name !== indexName)
|
|
75
79
|
throw new OGMError(`Unknown relationship fulltext index "${indexName}". Expected "${relIndex.name}".`);
|
|
@@ -94,6 +98,12 @@ export class FulltextCompiler {
|
|
|
94
98
|
compileNode(input, nodeDef, nodeVar, paramCounter, depth = 0) {
|
|
95
99
|
if (depth > MAX_DEPTH)
|
|
96
100
|
throw new OGMError(`Fulltext logical nesting exceeds maximum depth of ${MAX_DEPTH}`);
|
|
101
|
+
// Guarded here rather than in `compile()` so it also covers every branch
|
|
102
|
+
// reached through OR / AND / NOT recursion, e.g. `{ OR: [null] }`.
|
|
103
|
+
assertPlainObject(input, 'Fulltext input');
|
|
104
|
+
// The operator dispatch below returns on its first hit, so anything
|
|
105
|
+
// alongside OR / AND / NOT would be dropped without a word.
|
|
106
|
+
assertSingleFulltextOperator(Object.keys(input));
|
|
97
107
|
if (isFulltextLeaf(input))
|
|
98
108
|
return this.compileLeaf(input, nodeDef, nodeVar, paramCounter);
|
|
99
109
|
if ('OR' in input)
|
|
@@ -110,11 +120,16 @@ export class FulltextCompiler {
|
|
|
110
120
|
* - Relationship index: `{ relField: { IndexName: { phrase, score? } } }` — value is nested
|
|
111
121
|
*/
|
|
112
122
|
compileLeaf(leaf, nodeDef, nodeVar, paramCounter) {
|
|
113
|
-
const
|
|
114
|
-
if (
|
|
123
|
+
const entries = Object.entries(leaf);
|
|
124
|
+
if (entries.length === 0)
|
|
115
125
|
throw new OGMError('Fulltext input must contain at least one index entry');
|
|
116
|
-
const
|
|
117
|
-
|
|
126
|
+
for (const [k] of entries)
|
|
127
|
+
assertSafeIdentifier(k, 'fulltext index or relationship name');
|
|
128
|
+
assertSingleFulltextKey(entries, 'Fulltext leaf');
|
|
129
|
+
const key = entries[0][0];
|
|
130
|
+
// `isFulltextIndexEntry` uses the `in` operator, which throws a bare
|
|
131
|
+
// TypeError on a non-object (e.g. `{ BookSearch: null }`).
|
|
132
|
+
assertPlainObject(leaf[key], `Fulltext entry "${key}"`);
|
|
118
133
|
const value = leaf[key];
|
|
119
134
|
// If value has `phrase`, it's a direct node index entry
|
|
120
135
|
if (isFulltextIndexEntry(value))
|
|
@@ -160,12 +175,13 @@ export class FulltextCompiler {
|
|
|
160
175
|
const relProps = this.schema.relationshipProperties.get(relDef.properties);
|
|
161
176
|
if (!relProps)
|
|
162
177
|
throw new OGMError(`@relationshipProperties type "${relDef.properties}" not found`);
|
|
163
|
-
const
|
|
164
|
-
if (
|
|
178
|
+
const innerEntries = Object.entries(inner);
|
|
179
|
+
if (innerEntries.length === 0)
|
|
165
180
|
throw new OGMError('Relationship fulltext entry must contain an index name');
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
181
|
+
for (const [key] of innerEntries)
|
|
182
|
+
assertSafeIdentifier(key, 'fulltext index name');
|
|
183
|
+
assertSingleFulltextKey(innerEntries, `Relationship fulltext entry for "${relFieldName}"`);
|
|
184
|
+
const [indexName, input] = innerEntries[0];
|
|
169
185
|
const ftIndex = (relProps.fulltextIndexes ?? []).find((idx) => idx.name === indexName);
|
|
170
186
|
if (!ftIndex)
|
|
171
187
|
throw new OGMError(`Unknown fulltext index "${indexName}" on @relationshipProperties "${relDef.properties}"`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fulltext.compiler.js","sourceRoot":"","sources":["../../../src/compilers/fulltext.compiler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EACL,cAAc,EACd,oBAAoB,GAGrB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAE7B,oEAAoE;AACpE,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB;;;;;;;;GAQG;AACH,MAAM,0BAA0B,GAAG,CAAC,GAAG,IAAI,CAAC;AAE5C,SAAS,yBAAyB,CAAC,MAAe;IAChD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAC1D,MAAM,IAAI,QAAQ,CAAC,mCAAmC,CAAC,CAAC;IAC1D,IAAI,MAAM,CAAC,MAAM,GAAG,0BAA0B;QAC5C,MAAM,IAAI,QAAQ,CAChB,iDAAiD,0BAA0B,cAAc;YACvF,QAAQ,MAAM,CAAC,MAAM,kDAAkD,CAC1E,CAAC;AACN,CAAC;AAQD;;;;;;;;;;GAUG;AACH,MAAM,OAAO,gBAAgB;IAC3B,YAAoB,MAAsB;QAAtB,WAAM,GAAN,MAAM,CAAgB;IAAG,CAAC;IAE9C;;;OAGG;IACH,OAAO,CACL,QAAuB,EACvB,OAAuB,EACvB,OAAO,GAAG,GAAG;QAEb,oBAAoB,CAAC,OAAO,EAAE,eAAe,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;QAE1E,oEAAoE;QACpE,wEAAwE;QACxE,IAAI,cAAc,CAAC,QAAQ,CAAC;YAC1B,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;gBAC1C,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;oBACjC,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC7C,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC1B,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,GAAG,EAAE,EAAE,YAAY,CAAC,CAAC;gBACjE,CAAC;qBAAM,IAAI,GAAG,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;oBACvC,MAAM,CAAC,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC5C,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBAC5B,CAAC;QAEL,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;OAGG;IACH,mBAAmB,CACjB,QAA4D,EAC5D,QAAuB,EACvB,MAAM,GAAG,GAAG;QAEZ,oBAAoB,CAAC,MAAM,EAAE,uBAAuB,CAAC,CAAC;QAEtD,MAAM,
|
|
1
|
+
{"version":3,"file":"fulltext.compiler.js","sourceRoot":"","sources":["../../../src/compilers/fulltext.compiler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { OGMLogger } from '../execution/executor';
|
|
2
|
+
import type { PolicyContextBundle } from '../policy/types';
|
|
2
3
|
import { NodeDefinition, SchemaMetadata } from '../schema/types';
|
|
4
|
+
import { WhereCompiler } from './where.compiler';
|
|
3
5
|
export interface MutationResult {
|
|
4
6
|
cypher: string;
|
|
5
7
|
params: Record<string, unknown>;
|
|
@@ -15,9 +17,44 @@ export declare class MutationCompiler {
|
|
|
15
17
|
*/
|
|
16
18
|
private labelCache;
|
|
17
19
|
private logger?;
|
|
20
|
+
/**
|
|
21
|
+
* WhereCompiler used ONLY to compile a relationship TARGET type's
|
|
22
|
+
* `read` policy predicate for connect/disconnect target MATCHes,
|
|
23
|
+
* mirroring the read paths (`WhereCompiler`/`SelectionCompiler`).
|
|
24
|
+
* Injected by the OGM so it shares the user's compiler options;
|
|
25
|
+
* lazily constructed with default options otherwise.
|
|
26
|
+
*/
|
|
27
|
+
private policyWhereCompiler?;
|
|
18
28
|
constructor(schema: SchemaMetadata, options?: {
|
|
19
29
|
logger?: OGMLogger;
|
|
30
|
+
whereCompiler?: WhereCompiler;
|
|
20
31
|
});
|
|
32
|
+
/**
|
|
33
|
+
* Resolve and compile the TARGET type's `read` policy predicate for a
|
|
34
|
+
* connect/disconnect target MATCH, mirroring the read path
|
|
35
|
+
* (`WhereCompiler.buildTargetBundle` + `WhereCompiler.compile`). You
|
|
36
|
+
* must be allowed to SEE a node to link/unlink it, so linking to an
|
|
37
|
+
* unreadable node is the IDOR this closes; the codebase models the
|
|
38
|
+
* row-level write gate for `update`/`delete` targets as `read`
|
|
39
|
+
* ReadRestrictive policies (see policy/types.ts), so `read` is both
|
|
40
|
+
* necessary and the semantically correct operation here.
|
|
41
|
+
*
|
|
42
|
+
* Returns `null` — leaving the emitted Cypher BYTE-IDENTICAL — when:
|
|
43
|
+
* - no policy context is threaded (no policy bound / bypass active), OR
|
|
44
|
+
* - no `paramCounter` is available to allocate policy params, OR
|
|
45
|
+
* - the target type has no policy applicable to `read`, OR
|
|
46
|
+
* - the resolved policy is `overridden` (compiles to nothing).
|
|
47
|
+
*
|
|
48
|
+
* When a target policy resolves, the compiled predicate is returned and
|
|
49
|
+
* its bound params are merged into `params`. Policy params use the
|
|
50
|
+
* shared `paramCounter` (`param<N>`), continuing the sequence started by
|
|
51
|
+
* the top-level WHERE, so they can never collide with the
|
|
52
|
+
* connect/disconnect `where` params (prefix-named, e.g.
|
|
53
|
+
* `connect_<field>_<prop>`) or the selection's params (allocated later
|
|
54
|
+
* from the same counter).
|
|
55
|
+
*/
|
|
56
|
+
private buildTargetPolicyPredicate;
|
|
57
|
+
private getPolicyWhereCompiler;
|
|
21
58
|
/**
|
|
22
59
|
* Surface a logical operator that contributed zero effective conditions.
|
|
23
60
|
* Mirrors WhereCompiler.warnEmptyLogical — fires only on the anomalous
|
|
@@ -39,7 +76,23 @@ export declare class MutationCompiler {
|
|
|
39
76
|
cypher: string;
|
|
40
77
|
params: Record<string, unknown>;
|
|
41
78
|
preludes?: string[];
|
|
42
|
-
}, labels?: string[], returnMode?: 'node' | 'count'
|
|
79
|
+
}, labels?: string[], returnMode?: 'node' | 'count',
|
|
80
|
+
/**
|
|
81
|
+
* The caller's resolved policy bundle for this mutation (the SOURCE
|
|
82
|
+
* type's `update` bundle). Its `resolveForType` is used to resolve
|
|
83
|
+
* each connect/disconnect TARGET type's `read` policy. `undefined`
|
|
84
|
+
* when no policy is bound or a bypass is active — in which case the
|
|
85
|
+
* emitted Cypher is byte-identical to before this enforcement.
|
|
86
|
+
*/
|
|
87
|
+
policyContext?: PolicyContextBundle,
|
|
88
|
+
/**
|
|
89
|
+
* Shared `param<N>` counter, continued from the top-level WHERE
|
|
90
|
+
* compile so target-policy params never collide with connect/
|
|
91
|
+
* disconnect `where` params or the selection's params.
|
|
92
|
+
*/
|
|
93
|
+
paramCounter?: {
|
|
94
|
+
count: number;
|
|
95
|
+
}): MutationResult;
|
|
43
96
|
/**
|
|
44
97
|
* Generate DELETE Cypher with optional cascade.
|
|
45
98
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation.compiler.d.ts","sourceRoot":"","sources":["../../../src/compilers/mutation.compiler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EACL,cAAc,EAEd,cAAc,EACf,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"mutation.compiler.d.ts","sourceRoot":"","sources":["../../../src/compilers/mutation.compiler.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OGMError } from '../errors';
|
|
2
2
|
import { buildRelPattern, getTargetLabelString, resolveTargetDef, } from '../schema/utils';
|
|
3
3
|
import { assertSafeIdentifier, assertSafeLabel, assertSafePropertyName, escapeIdentifier, mergeParams, } from '../utils/validation';
|
|
4
|
+
import { WhereCompiler } from './where.compiler';
|
|
4
5
|
/**
|
|
5
6
|
* Relationship filter suffixes that require EXISTS subquery patterns.
|
|
6
7
|
* These match related nodes via relationship traversal rather than scalar property comparison.
|
|
@@ -20,6 +21,59 @@ export class MutationCompiler {
|
|
|
20
21
|
/** Counter for unique relationship filter variables within a single WHERE clause. */
|
|
21
22
|
this.relFilterCounter = 0;
|
|
22
23
|
this.logger = options?.logger;
|
|
24
|
+
this.policyWhereCompiler = options?.whereCompiler;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Resolve and compile the TARGET type's `read` policy predicate for a
|
|
28
|
+
* connect/disconnect target MATCH, mirroring the read path
|
|
29
|
+
* (`WhereCompiler.buildTargetBundle` + `WhereCompiler.compile`). You
|
|
30
|
+
* must be allowed to SEE a node to link/unlink it, so linking to an
|
|
31
|
+
* unreadable node is the IDOR this closes; the codebase models the
|
|
32
|
+
* row-level write gate for `update`/`delete` targets as `read`
|
|
33
|
+
* ReadRestrictive policies (see policy/types.ts), so `read` is both
|
|
34
|
+
* necessary and the semantically correct operation here.
|
|
35
|
+
*
|
|
36
|
+
* Returns `null` — leaving the emitted Cypher BYTE-IDENTICAL — when:
|
|
37
|
+
* - no policy context is threaded (no policy bound / bypass active), OR
|
|
38
|
+
* - no `paramCounter` is available to allocate policy params, OR
|
|
39
|
+
* - the target type has no policy applicable to `read`, OR
|
|
40
|
+
* - the resolved policy is `overridden` (compiles to nothing).
|
|
41
|
+
*
|
|
42
|
+
* When a target policy resolves, the compiled predicate is returned and
|
|
43
|
+
* its bound params are merged into `params`. Policy params use the
|
|
44
|
+
* shared `paramCounter` (`param<N>`), continuing the sequence started by
|
|
45
|
+
* the top-level WHERE, so they can never collide with the
|
|
46
|
+
* connect/disconnect `where` params (prefix-named, e.g.
|
|
47
|
+
* `connect_<field>_<prop>`) or the selection's params (allocated later
|
|
48
|
+
* from the same counter).
|
|
49
|
+
*/
|
|
50
|
+
buildTargetPolicyPredicate(targetNodeDef, targetVar, params, policyContext, paramCounter) {
|
|
51
|
+
if (!policyContext || !paramCounter)
|
|
52
|
+
return null;
|
|
53
|
+
const targetPolicy = policyContext.resolveForType(targetNodeDef.typeName, 'read');
|
|
54
|
+
if (!targetPolicy)
|
|
55
|
+
return null;
|
|
56
|
+
const targetBundle = {
|
|
57
|
+
ctx: policyContext.ctx,
|
|
58
|
+
operation: 'read',
|
|
59
|
+
resolved: targetPolicy,
|
|
60
|
+
resolveForType: policyContext.resolveForType,
|
|
61
|
+
defaults: policyContext.defaults,
|
|
62
|
+
};
|
|
63
|
+
const compiled = this.getPolicyWhereCompiler().compile(undefined, targetVar, targetNodeDef, paramCounter, { policyContext: targetBundle });
|
|
64
|
+
if (compiled.preludes && compiled.preludes.length > 0)
|
|
65
|
+
throw new OGMError(`Policy on "${targetNodeDef.typeName}" requires @cypher field ` +
|
|
66
|
+
`projection, which is not supported inside connect/disconnect ` +
|
|
67
|
+
`target filters. Refactor the policy to use stored properties.`);
|
|
68
|
+
if (!compiled.cypher)
|
|
69
|
+
return null;
|
|
70
|
+
mergeParams(params, compiled.params);
|
|
71
|
+
return compiled.cypher;
|
|
72
|
+
}
|
|
73
|
+
getPolicyWhereCompiler() {
|
|
74
|
+
if (!this.policyWhereCompiler)
|
|
75
|
+
this.policyWhereCompiler = new WhereCompiler(this.schema);
|
|
76
|
+
return this.policyWhereCompiler;
|
|
23
77
|
}
|
|
24
78
|
/**
|
|
25
79
|
* Surface a logical operator that contributed zero effective conditions.
|
|
@@ -64,7 +118,11 @@ export class MutationCompiler {
|
|
|
64
118
|
// Process relationship operations (nested create / connect)
|
|
65
119
|
// Pass all previously created node vars as ancestors so WITH clauses
|
|
66
120
|
// don't drop them from scope (needed for RETURN n at the end)
|
|
67
|
-
const relLines = this.buildCreateRelationships(input, nodeDef, nodeVar, prefix, params, createdVars.slice(0, -1)
|
|
121
|
+
const relLines = this.buildCreateRelationships(input, nodeDef, nodeVar, prefix, params, createdVars.slice(0, -1), // ancestors = all vars before current
|
|
122
|
+
// No policy context is threaded on the create path: `create`'s
|
|
123
|
+
// nested connect enforcement is out of scope for this fix, so
|
|
124
|
+
// the emitted create Cypher stays byte-identical.
|
|
125
|
+
undefined, undefined);
|
|
68
126
|
lines.push(...relLines);
|
|
69
127
|
}
|
|
70
128
|
// Add extra labels to all created nodes
|
|
@@ -79,7 +137,21 @@ export class MutationCompiler {
|
|
|
79
137
|
/**
|
|
80
138
|
* Generate UPDATE Cypher (SET properties + connect/disconnect).
|
|
81
139
|
*/
|
|
82
|
-
compileUpdate(_where, update, connect, disconnect, nodeDef, whereResult, labels, returnMode = 'node'
|
|
140
|
+
compileUpdate(_where, update, connect, disconnect, nodeDef, whereResult, labels, returnMode = 'node',
|
|
141
|
+
/**
|
|
142
|
+
* The caller's resolved policy bundle for this mutation (the SOURCE
|
|
143
|
+
* type's `update` bundle). Its `resolveForType` is used to resolve
|
|
144
|
+
* each connect/disconnect TARGET type's `read` policy. `undefined`
|
|
145
|
+
* when no policy is bound or a bypass is active — in which case the
|
|
146
|
+
* emitted Cypher is byte-identical to before this enforcement.
|
|
147
|
+
*/
|
|
148
|
+
policyContext,
|
|
149
|
+
/**
|
|
150
|
+
* Shared `param<N>` counter, continued from the top-level WHERE
|
|
151
|
+
* compile so target-policy params never collide with connect/
|
|
152
|
+
* disconnect `where` params or the selection's params.
|
|
153
|
+
*/
|
|
154
|
+
paramCounter) {
|
|
83
155
|
this.relFilterCounter = 0;
|
|
84
156
|
const labelStr = this.getCachedLabelString(nodeDef);
|
|
85
157
|
const lines = [];
|
|
@@ -114,18 +186,18 @@ export class MutationCompiler {
|
|
|
114
186
|
if (setClauses.length > 0)
|
|
115
187
|
lines.push(`SET ${setClauses.join(', ')}`);
|
|
116
188
|
// Process nested relationship operations within update body
|
|
117
|
-
const relLines = this.buildUpdateRelationships(update, nodeDef, 'n', 'update', params);
|
|
189
|
+
const relLines = this.buildUpdateRelationships(update, nodeDef, 'n', 'update', params, 0, [], policyContext, paramCounter);
|
|
118
190
|
lines.push(...relLines);
|
|
119
191
|
}
|
|
120
192
|
// Disconnect relationships (top-level) — must run before connects
|
|
121
193
|
// so that blanket disconnects don't remove newly-connected relationships.
|
|
122
194
|
if (disconnect) {
|
|
123
|
-
const disconnectLines = this.buildDisconnects(disconnect, nodeDef, params);
|
|
195
|
+
const disconnectLines = this.buildDisconnects(disconnect, nodeDef, params, policyContext, paramCounter);
|
|
124
196
|
lines.push(...disconnectLines);
|
|
125
197
|
}
|
|
126
198
|
// Connect relationships (top-level)
|
|
127
199
|
if (connect) {
|
|
128
|
-
const connectLines = this.buildConnects(connect, nodeDef, params);
|
|
200
|
+
const connectLines = this.buildConnects(connect, nodeDef, params, policyContext, paramCounter);
|
|
129
201
|
lines.push(...connectLines);
|
|
130
202
|
}
|
|
131
203
|
lines.push(returnMode === 'count' ? 'RETURN count(n) AS count' : 'RETURN n');
|
|
@@ -387,7 +459,7 @@ export class MutationCompiler {
|
|
|
387
459
|
}
|
|
388
460
|
return { propString: parts.join(', '), propParams };
|
|
389
461
|
}
|
|
390
|
-
buildCreateRelationships(input, nodeDef, nodeVar, prefix, params, ancestorVars = []) {
|
|
462
|
+
buildCreateRelationships(input, nodeDef, nodeVar, prefix, params, ancestorVars = [], policyContext, paramCounter) {
|
|
391
463
|
const lines = [];
|
|
392
464
|
let nestedCounter = 0;
|
|
393
465
|
// WITH clause must carry all ancestors + the node being created on
|
|
@@ -412,7 +484,7 @@ export class MutationCompiler {
|
|
|
412
484
|
continue;
|
|
413
485
|
const memberInput = memberValue;
|
|
414
486
|
// Process creates/connects for this union member
|
|
415
|
-
const memberLines = this.buildCreateRelationshipsForTarget(memberInput, memberNodeDef, relDef, nodeVar, `${prefix}_${key}_${memberKey}`, params, allVars, withClause, nestedCounter);
|
|
487
|
+
const memberLines = this.buildCreateRelationshipsForTarget(memberInput, memberNodeDef, relDef, nodeVar, `${prefix}_${key}_${memberKey}`, params, allVars, withClause, nestedCounter, policyContext, paramCounter);
|
|
416
488
|
nestedCounter += memberLines.counterUsed;
|
|
417
489
|
lines.push(...memberLines.lines);
|
|
418
490
|
}
|
|
@@ -421,7 +493,7 @@ export class MutationCompiler {
|
|
|
421
493
|
const targetNodeDef = this.schema.nodes.get(relDef.target);
|
|
422
494
|
if (!targetNodeDef)
|
|
423
495
|
continue;
|
|
424
|
-
const resultLines = this.buildCreateRelationshipsForTarget(relInput, targetNodeDef, relDef, nodeVar, `${prefix}_${key}`, params, allVars, withClause, nestedCounter);
|
|
496
|
+
const resultLines = this.buildCreateRelationshipsForTarget(relInput, targetNodeDef, relDef, nodeVar, `${prefix}_${key}`, params, allVars, withClause, nestedCounter, policyContext, paramCounter);
|
|
425
497
|
nestedCounter += resultLines.counterUsed;
|
|
426
498
|
lines.push(...resultLines.lines);
|
|
427
499
|
}
|
|
@@ -431,7 +503,7 @@ export class MutationCompiler {
|
|
|
431
503
|
* Process create/connect operations for a specific target node type.
|
|
432
504
|
* Used by buildCreateRelationships for both union-member and non-union targets.
|
|
433
505
|
*/
|
|
434
|
-
buildCreateRelationshipsForTarget(relInput, targetNodeDef, relDef, nodeVar, prefix, params, allVars, withClause, startCounter) {
|
|
506
|
+
buildCreateRelationshipsForTarget(relInput, targetNodeDef, relDef, nodeVar, prefix, params, allVars, withClause, startCounter, policyContext, paramCounter) {
|
|
435
507
|
const lines = [];
|
|
436
508
|
let counter = 0;
|
|
437
509
|
const targetLabelStr = this.getCachedLabelString(targetNodeDef);
|
|
@@ -447,7 +519,7 @@ export class MutationCompiler {
|
|
|
447
519
|
counter++;
|
|
448
520
|
const { propString, propParams } = this.buildCreateProperties(nodeSpec, targetNodeDef, `${prefix}_create${ci}`);
|
|
449
521
|
// Handle nested relationship creates within the created node
|
|
450
|
-
const nestedRelLines = this.buildCreateRelationships(nodeSpec, targetNodeDef, createVar, `${prefix}_create${ci}`, params, allVars);
|
|
522
|
+
const nestedRelLines = this.buildCreateRelationships(nodeSpec, targetNodeDef, createVar, `${prefix}_create${ci}`, params, allVars, policyContext, paramCounter);
|
|
451
523
|
const propsClause = propString.length > 0
|
|
452
524
|
? ` { ${propString} }`
|
|
453
525
|
: ` { ${this.buildGeneratedIdClause(targetNodeDef)} }`;
|
|
@@ -519,8 +591,12 @@ export class MutationCompiler {
|
|
|
519
591
|
counter++;
|
|
520
592
|
lines.push(`MATCH (${connectVar}:${targetLabelStr})`);
|
|
521
593
|
const conditions = this.buildConnectionWhereConditions(whereSpec, connectVar, `${prefix}_conn${ci}`, params, targetNodeDef);
|
|
522
|
-
|
|
523
|
-
|
|
594
|
+
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, connectVar, params, policyContext, paramCounter);
|
|
595
|
+
const connectConditions = policyPredicate
|
|
596
|
+
? [...conditions, policyPredicate]
|
|
597
|
+
: conditions;
|
|
598
|
+
if (connectConditions.length > 0)
|
|
599
|
+
lines.push(`WHERE ${connectConditions.join(' AND ')}`);
|
|
524
600
|
if (edgeInput && Object.keys(edgeInput).length > 0) {
|
|
525
601
|
// Use bare target var (no label) since connectVar is already bound by MATCH.
|
|
526
602
|
// Including labels on a bound variable in MERGE causes Neo4j to reject it.
|
|
@@ -557,7 +633,7 @@ export class MutationCompiler {
|
|
|
557
633
|
}
|
|
558
634
|
return { lines, counterUsed: counter };
|
|
559
635
|
}
|
|
560
|
-
buildConnects(connect, nodeDef, params) {
|
|
636
|
+
buildConnects(connect, nodeDef, params, policyContext, paramCounter) {
|
|
561
637
|
const lines = [];
|
|
562
638
|
for (const [fieldName, spec] of Object.entries(connect)) {
|
|
563
639
|
const relDef = nodeDef.relationships.get(fieldName);
|
|
@@ -588,8 +664,12 @@ export class MutationCompiler {
|
|
|
588
664
|
const connectVar = `target_${fieldName}_${ci}`;
|
|
589
665
|
lines.push(`MATCH (${connectVar}:${targetLabelStr})`);
|
|
590
666
|
const conditions = this.buildConnectionWhereConditions(whereSpec, connectVar, `connect_${fieldName}_${ci}`, params, targetNodeDef);
|
|
591
|
-
|
|
592
|
-
|
|
667
|
+
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, connectVar, params, policyContext, paramCounter);
|
|
668
|
+
const connectConditions = policyPredicate
|
|
669
|
+
? [...conditions, policyPredicate]
|
|
670
|
+
: conditions;
|
|
671
|
+
if (connectConditions.length > 0)
|
|
672
|
+
lines.push(`WHERE ${connectConditions.join(' AND ')}`);
|
|
593
673
|
if (edgeInput && Object.keys(edgeInput).length > 0) {
|
|
594
674
|
assertSafeIdentifier(relDef.type, 'relationship type');
|
|
595
675
|
const escapedRelType = escapeIdentifier(relDef.type);
|
|
@@ -658,8 +738,12 @@ export class MutationCompiler {
|
|
|
658
738
|
`to share the same shape.`);
|
|
659
739
|
}
|
|
660
740
|
const whereConditions = this.extractConnectWhereConditions(firstItem, 'target', 'connItem');
|
|
661
|
-
|
|
662
|
-
|
|
741
|
+
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, 'target', params, policyContext, paramCounter);
|
|
742
|
+
const unwindConditions = policyPredicate
|
|
743
|
+
? [...whereConditions, policyPredicate]
|
|
744
|
+
: whereConditions;
|
|
745
|
+
if (unwindConditions.length > 0)
|
|
746
|
+
lines.push(`WHERE ${unwindConditions.join(' AND ')}`);
|
|
663
747
|
const mergePattern = buildRelPattern({
|
|
664
748
|
sourceVar: 'n',
|
|
665
749
|
relDef,
|
|
@@ -691,8 +775,12 @@ export class MutationCompiler {
|
|
|
691
775
|
lines.push('WITH n');
|
|
692
776
|
lines.push(`MATCH (target:${targetLabelStr})`);
|
|
693
777
|
const matchConditions = this.buildConnectionWhereConditions(whereSpec, 'target', `connect_${fieldName}`, params, targetNodeDef);
|
|
694
|
-
|
|
695
|
-
|
|
778
|
+
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, 'target', params, policyContext, paramCounter);
|
|
779
|
+
const singleConnectConditions = policyPredicate
|
|
780
|
+
? [...matchConditions, policyPredicate]
|
|
781
|
+
: matchConditions;
|
|
782
|
+
if (singleConnectConditions.length > 0)
|
|
783
|
+
lines.push(`WHERE ${singleConnectConditions.join(' AND ')}`);
|
|
696
784
|
if (edgeInput && Object.keys(edgeInput).length > 0) {
|
|
697
785
|
// Use bare target var (no label) since target is already bound by MATCH
|
|
698
786
|
assertSafeIdentifier(relDef.type, 'relationship type');
|
|
@@ -724,7 +812,7 @@ export class MutationCompiler {
|
|
|
724
812
|
}
|
|
725
813
|
return lines;
|
|
726
814
|
}
|
|
727
|
-
buildDisconnects(disconnect, nodeDef, params) {
|
|
815
|
+
buildDisconnects(disconnect, nodeDef, params, policyContext, paramCounter) {
|
|
728
816
|
const lines = [];
|
|
729
817
|
for (const [fieldName, spec] of Object.entries(disconnect)) {
|
|
730
818
|
const relDef = nodeDef.relationships.get(fieldName);
|
|
@@ -763,8 +851,14 @@ export class MutationCompiler {
|
|
|
763
851
|
});
|
|
764
852
|
lines.push(`OPTIONAL MATCH ${pattern}`);
|
|
765
853
|
const conditions = this.buildConnectionWhereConditions(whereSpec, targetVar, `disconnect_${fieldName}_${si}`, params, targetNodeDef);
|
|
766
|
-
|
|
767
|
-
|
|
854
|
+
const policyPredicate = targetNodeDef
|
|
855
|
+
? this.buildTargetPolicyPredicate(targetNodeDef, targetVar, params, policyContext, paramCounter)
|
|
856
|
+
: null;
|
|
857
|
+
const disconnectConditions = policyPredicate
|
|
858
|
+
? [...conditions, policyPredicate]
|
|
859
|
+
: conditions;
|
|
860
|
+
if (disconnectConditions.length > 0)
|
|
861
|
+
lines.push(`WHERE ${disconnectConditions.join(' AND ')}`);
|
|
768
862
|
lines.push(`DELETE ${relVar}`);
|
|
769
863
|
}
|
|
770
864
|
}
|
|
@@ -776,7 +870,7 @@ export class MutationCompiler {
|
|
|
776
870
|
* Each member key in the input is recursed into buildUpdateRelationships
|
|
777
871
|
* with a narrowed relDef pointing at the concrete member type.
|
|
778
872
|
*/
|
|
779
|
-
dispatchUnionUpdateOps(key, memberEntries, relDef, nodeDef, sourceVar, prefix, params, depth, ancestorVars) {
|
|
873
|
+
dispatchUnionUpdateOps(key, memberEntries, relDef, nodeDef, sourceVar, prefix, params, depth, ancestorVars, policyContext, paramCounter) {
|
|
780
874
|
const unionMembers = this.schema.unions.get(relDef.target);
|
|
781
875
|
const lines = [];
|
|
782
876
|
for (const [memberKey, memberValue] of Object.entries(memberEntries)) {
|
|
@@ -796,7 +890,7 @@ export class MutationCompiler {
|
|
|
796
890
|
...nodeDef,
|
|
797
891
|
relationships: new Map([[key, memberRelDef]]),
|
|
798
892
|
};
|
|
799
|
-
lines.push(...this.buildUpdateRelationships({ [key]: memberItems }, memberNodeDef2, sourceVar, `${prefix}_${key}_${memberKey}`, params, depth + 1, ancestorVars));
|
|
893
|
+
lines.push(...this.buildUpdateRelationships({ [key]: memberItems }, memberNodeDef2, sourceVar, `${prefix}_${key}_${memberKey}`, params, depth + 1, ancestorVars, policyContext, paramCounter));
|
|
800
894
|
}
|
|
801
895
|
return lines;
|
|
802
896
|
}
|
|
@@ -804,7 +898,7 @@ export class MutationCompiler {
|
|
|
804
898
|
* Process nested relationship operations within an update input.
|
|
805
899
|
* Handles: create, connect, disconnect, update, and arrays of these.
|
|
806
900
|
*/
|
|
807
|
-
buildUpdateRelationships(update, nodeDef, sourceVar, prefix, params, depth = 0, ancestorVars = []) {
|
|
901
|
+
buildUpdateRelationships(update, nodeDef, sourceVar, prefix, params, depth = 0, ancestorVars = [], policyContext, paramCounter) {
|
|
808
902
|
if (depth > 5)
|
|
809
903
|
throw new OGMError(`[neo4j-ogm] buildUpdateRelationships: max nesting depth (5) exceeded for prefix "${prefix}". ` +
|
|
810
904
|
'Nested relationship mutations beyond depth 5 are not supported.');
|
|
@@ -821,7 +915,7 @@ export class MutationCompiler {
|
|
|
821
915
|
const isUnionTarget = !this.schema.nodes.has(relDef.target) &&
|
|
822
916
|
this.schema.unions?.has(relDef.target);
|
|
823
917
|
if (isUnionTarget) {
|
|
824
|
-
lines.push(...this.dispatchUnionUpdateOps(key, value, relDef, nodeDef, sourceVar, prefix, params, depth, ancestorVars));
|
|
918
|
+
lines.push(...this.dispatchUnionUpdateOps(key, value, relDef, nodeDef, sourceVar, prefix, params, depth, ancestorVars, policyContext, paramCounter));
|
|
825
919
|
continue;
|
|
826
920
|
}
|
|
827
921
|
const targetNodeDef = this.schema.nodes.get(relDef.target);
|
|
@@ -891,7 +985,7 @@ export class MutationCompiler {
|
|
|
891
985
|
varCounter++;
|
|
892
986
|
const { propString, propParams } = this.buildCreateProperties(nodeSpec, targetNodeDef, `${itemPrefix}_create${ci}`);
|
|
893
987
|
// Handle nested relationship creates within the created node
|
|
894
|
-
const nestedRelLines = this.buildCreateRelationships(nodeSpec, targetNodeDef, createVar, `${itemPrefix}_create${ci}`, params, allVars);
|
|
988
|
+
const nestedRelLines = this.buildCreateRelationships(nodeSpec, targetNodeDef, createVar, `${itemPrefix}_create${ci}`, params, allVars, policyContext, paramCounter);
|
|
895
989
|
const propsClause = propString.length > 0
|
|
896
990
|
? ` { ${propString} }`
|
|
897
991
|
: ` { ${this.buildGeneratedIdClause(targetNodeDef)} }`;
|
|
@@ -960,8 +1054,12 @@ export class MutationCompiler {
|
|
|
960
1054
|
// path (`buildDisconnects`). Restores backwards-compat with
|
|
961
1055
|
// @neo4j/graphql-ogm's nested update.disconnect[].where shape.
|
|
962
1056
|
const conditions = this.buildConnectionWhereConditions(whereSpec, discTarget, `${itemPrefix}_disc_${di}`, params, targetNodeDef);
|
|
963
|
-
|
|
964
|
-
|
|
1057
|
+
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, discTarget, params, policyContext, paramCounter);
|
|
1058
|
+
const nestedDiscConditions = policyPredicate
|
|
1059
|
+
? [...conditions, policyPredicate]
|
|
1060
|
+
: conditions;
|
|
1061
|
+
if (nestedDiscConditions.length > 0)
|
|
1062
|
+
lines.push(`WHERE ${nestedDiscConditions.join(' AND ')}`);
|
|
965
1063
|
lines.push(`DELETE ${relVar}`);
|
|
966
1064
|
}
|
|
967
1065
|
}
|
|
@@ -990,8 +1088,12 @@ export class MutationCompiler {
|
|
|
990
1088
|
// top-level paths. Empty/undefined `whereSpec` short-circuits
|
|
991
1089
|
// to `[]` inside the helper, so the no-where case still works.
|
|
992
1090
|
const conditions = this.buildConnectionWhereConditions(whereSpec, connectVar, `${itemPrefix}_conn${ci}`, params, targetNodeDef);
|
|
993
|
-
|
|
994
|
-
|
|
1091
|
+
const policyPredicate = this.buildTargetPolicyPredicate(targetNodeDef, connectVar, params, policyContext, paramCounter);
|
|
1092
|
+
const nestedConnConditions = policyPredicate
|
|
1093
|
+
? [...conditions, policyPredicate]
|
|
1094
|
+
: conditions;
|
|
1095
|
+
if (nestedConnConditions.length > 0)
|
|
1096
|
+
lines.push(`WHERE ${nestedConnConditions.join(' AND ')}`);
|
|
995
1097
|
if (edgeInput && Object.keys(edgeInput).length > 0) {
|
|
996
1098
|
// Use bare target var (no label) since connectVar is already bound by MATCH
|
|
997
1099
|
const relVar = `r_conn_${key}_${ci}`;
|
|
@@ -1062,7 +1164,7 @@ export class MutationCompiler {
|
|
|
1062
1164
|
}
|
|
1063
1165
|
// Pre-compute nested relationship operations
|
|
1064
1166
|
const nestedParams = {};
|
|
1065
|
-
const nestedRelLines = this.buildUpdateRelationships(nodeUpdate, targetNodeDef, updateVar, itemPrefix, nestedParams, depth + 1, allVars);
|
|
1167
|
+
const nestedRelLines = this.buildUpdateRelationships(nodeUpdate, targetNodeDef, updateVar, itemPrefix, nestedParams, depth + 1, allVars, policyContext, paramCounter);
|
|
1066
1168
|
// Only emit the MATCH block if there's actual work to do
|
|
1067
1169
|
const hasWork = setClauses.length > 0 ||
|
|
1068
1170
|
edgeClauses.length > 0 ||
|