@aventara/core 0.1.0-pilot.3 → 1.0.0-rc.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 +338 -0
- package/README.md +100 -3
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +2 -0
- package/dist/adapters/adapter-field.d.ts +3 -26
- package/dist/adapters/adapter-identifier.d.ts +1 -11
- package/dist/adapters/adapter-model.d.ts +12 -0
- package/dist/adapters/adapter-operation-executor.interface.d.ts +5 -1
- package/dist/adapters/adapter-resource.d.ts +7 -28
- package/dist/adapters/adapter.interface.d.ts +25 -26
- package/dist/compiler/contract-compiler-diagnostic.d.ts +8 -0
- package/dist/compiler/contract.compiler.d.ts +11 -22
- package/dist/compiler/contract.compiler.js +67 -0
- package/dist/config/any-framework-config.d.ts +5 -26
- package/dist/config/config-diagnostic.d.ts +2 -0
- package/dist/config/config-layer.d.ts +6 -19
- package/dist/config/config.resolver.d.ts +9 -14
- package/dist/config/config.resolver.js +1 -0
- package/dist/config/config.validator.d.ts +6 -3
- package/dist/config/config.validator.js +6 -0
- package/dist/config/define-fields.d.ts +15 -48
- package/dist/config/effective-fields.d.ts +6 -29
- package/dist/config/entrypoint.normalizer.d.ts +6 -18
- package/dist/config/framework-behaviors-config.d.ts +11 -10
- package/dist/config/framework-config.d.ts +19 -16
- package/dist/config/framework-fields-config.d.ts +20 -7
- package/dist/config/framework-restrictions-config.d.ts +41 -61
- package/dist/config/pipeline-config.d.ts +7 -17
- package/dist/contracts/capability.d.ts +13 -49
- package/dist/contracts/client-contract-structure.validator.d.ts +28 -54
- package/dist/contracts/client-contract-structure.validator.js +99 -2
- package/dist/contracts/contract-canonicalizer.d.ts +4 -6
- package/dist/contracts/contract-canonicalizer.js +32 -0
- package/dist/contracts/contract-hash.d.ts +3 -2
- package/dist/contracts/contract-limits.d.ts +7 -1
- package/dist/contracts/contract-protocol.d.ts +7 -8
- package/dist/contracts/contract.d.ts +20 -4
- package/dist/contracts/enum-definition.d.ts +2 -0
- package/dist/contracts/field.d.ts +40 -167
- package/dist/contracts/identifier.d.ts +1 -16
- package/dist/contracts/json-shape.d.ts +65 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +4 -3
- package/dist/contracts/reference-write-limit.d.ts +7 -0
- package/dist/contracts/reference-write-limit.js +1 -0
- package/dist/contracts/resource-operations.d.ts +2 -62
- package/dist/contracts/resource.d.ts +2 -0
- package/dist/contracts/scalar-value-type.d.ts +28 -48
- package/dist/contracts/scalar.d.ts +4 -3
- package/dist/contracts/transaction-capability.d.ts +2 -0
- package/dist/contracts/wire-scalar-format.d.ts +6 -21
- package/dist/framework/contract-view.d.ts +152 -130
- package/dist/framework/framework-construction.error.d.ts +6 -1
- package/dist/framework/framework.d.ts +33 -46
- package/dist/framework/framework.factory.d.ts +3 -17
- package/dist/framework/framework.factory.js +56 -13
- package/dist/framework/operation-facade.d.ts +3 -7
- package/dist/index.d.ts +8 -40
- package/dist/index.js +0 -20
- package/dist/internal.d.ts +35 -0
- package/dist/internal.js +16 -0
- package/dist/json/avz.d.ts +96 -0
- package/dist/json/avz.js +212 -0
- package/dist/json/index.d.ts +6 -0
- package/dist/json/index.js +1 -0
- package/dist/json/json-shape.registry.d.ts +22 -0
- package/dist/json/json-shape.registry.js +16 -0
- package/dist/json/zod-issue.translator.d.ts +51 -0
- package/dist/json/zod-issue.translator.js +142 -0
- package/dist/operations/operation-arguments.d.ts +198 -303
- package/dist/operations/operation-call.d.ts +13 -15
- package/dist/operations/operation-identity.d.ts +6 -2
- package/dist/operations/operation-identity.js +3 -0
- package/dist/operations/operation-result.d.ts +15 -61
- package/dist/operations/operation-validator-core.d.ts +6 -5
- package/dist/operations/operation-validator-core.js +28 -10
- package/dist/operations/operation-validator.d.ts +22 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +1 -11
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +344 -0
- package/dist/operations/validation-code.d.ts +5 -17
- package/dist/operations/write-scope.resolver.d.ts +3 -28
- package/dist/protocol/absent-route.responder.d.ts +25 -39
- package/dist/protocol/absent-route.responder.js +32 -6
- package/dist/protocol/av-protocol.d.ts +2 -8
- package/dist/protocol/contract-document.responder.d.ts +5 -18
- package/dist/protocol/http-request.decoder.d.ts +18 -59
- package/dist/protocol/http-request.decoder.js +11 -3
- package/dist/protocol/http-request.reader.d.ts +5 -5
- package/dist/protocol/http-response.encoder.d.ts +14 -20
- package/dist/protocol/http-status.table.d.ts +0 -8
- package/dist/protocol/index.d.ts +2 -7
- package/dist/protocol/operation-response.guard.d.ts +0 -15
- package/dist/protocol/protocol-failure.d.ts +8 -27
- package/dist/protocol/protocol-headers.d.ts +15 -42
- package/dist/protocol/protocol-surface.d.ts +0 -22
- package/dist/protocol/protocol.binder.d.ts +19 -27
- package/dist/protocol/protocol.binder.js +3 -1
- package/dist/protocol/protocol.handler.d.ts +19 -23
- package/dist/protocol/protocol.handler.js +14 -4
- package/dist/protocol/unmounted-route.error.d.ts +19 -0
- package/dist/protocol/unmounted-route.error.js +10 -0
- package/dist/runtime/caller-data.copier.d.ts +59 -0
- package/dist/runtime/caller-data.copier.js +470 -0
- package/dist/runtime/caller-data.door.d.ts +56 -0
- package/dist/runtime/caller-data.door.js +65 -0
- package/dist/runtime/computed-read-runtime.js +3 -10
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- package/dist/runtime/create-requirement.d.ts +43 -7
- package/dist/runtime/create-requirement.js +89 -9
- package/dist/runtime/decimal.d.ts +5 -3
- package/dist/runtime/diagnostics-reporter.d.ts +33 -37
- package/dist/runtime/diagnostics-reporter.js +11 -4
- package/dist/runtime/error-boundary.d.ts +14 -10
- package/dist/runtime/error-boundary.js +65 -35
- package/dist/runtime/execution-options.d.ts +3 -7
- package/dist/runtime/field-behavior.d.ts +14 -21
- package/dist/runtime/framework-error.d.ts +14 -2
- package/dist/runtime/json-value.d.ts +7 -15
- package/dist/runtime/misuse-reporter.d.ts +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +6 -12
- package/dist/runtime/mutation-planner.js +13 -11
- package/dist/runtime/operation-request.d.ts +11 -11
- package/dist/runtime/operation-response.d.ts +18 -23
- package/dist/runtime/operation-runtime.d.ts +25 -33
- package/dist/runtime/operation-runtime.js +37 -18
- package/dist/runtime/outcome-mapper.js +12 -2
- package/dist/runtime/output-validator.d.ts +10 -0
- package/dist/runtime/output-validator.js +11 -0
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- package/dist/runtime/pipeline.d.ts +33 -36
- package/dist/runtime/referential-action.d.ts +8 -26
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +118 -0
- package/dist/runtime/request-id.d.ts +3 -10
- package/dist/runtime/scalar-runtime.d.ts +42 -14
- package/dist/runtime/scalar-runtime.js +162 -79
- package/dist/transactions/deferred-operation.d.ts +10 -21
- package/dist/transactions/operation-arguments-encoder.d.ts +1 -0
- package/dist/transactions/operation-fingerprint.d.ts +3 -1
- package/dist/transactions/transaction-envelope.d.ts +2 -4
- package/dist/transactions/transaction-integrity-error.d.ts +4 -15
- package/dist/transactions/transaction-plan-assembler.d.ts +6 -1
- package/dist/transactions/transaction-plan-assembler.js +74 -75
- package/dist/transactions/transaction-plan-validator.d.ts +33 -24
- package/dist/transactions/transaction-plan-validator.js +204 -153
- package/dist/transactions/transaction-reference-collector.d.ts +30 -0
- package/dist/transactions/transaction-reference-collector.js +204 -0
- package/dist/transactions/transaction-reference-resolver.d.ts +9 -12
- package/dist/transactions/transaction-reference-resolver.js +10 -5
- package/dist/transactions/transaction-reference-validator.js +24 -17
- package/dist/transactions/transaction-reference.d.ts +31 -64
- package/dist/transactions/transaction-request.d.ts +6 -8
- package/dist/transactions/transaction-response.d.ts +10 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +7 -5
- package/dist/transactions/transaction-runtime.js +4 -3
- package/dist/transactions/transaction-slot-path-error.d.ts +5 -15
- package/dist/transactions/transaction-slot-writer.d.ts +3 -13
- package/package.json +17 -3
- package/dist/operations/operation-boolean-nodes.d.ts +0 -11
- package/dist/operations/operation-boolean-nodes.js +0 -283
- package/dist/operations/operation-nesting.d.ts +0 -8
- package/dist/operations/operation-nesting.js +0 -307
|
@@ -1,18 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The one owner of writing a value at a transaction slot path. Validation installs
|
|
3
|
-
* framework-owned `$ref` control snapshots through it and execution installs
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* Slot paths are collected once, from the validated semantic argument view.
|
|
8
|
-
* Validation then writes them into two views: that semantic view, where the path
|
|
9
|
-
* is known to name a member, and the raw wire view
|
|
10
|
-
* (`operation.request.arguments`), where the same path is asserted rather than
|
|
11
|
-
* established. It holds there because a slot names a scalar leaf key inside
|
|
12
|
-
* top-level mutation data and Framework Validation Layer normalization preserves
|
|
13
|
-
* that `data`-leaf key structure. A path that does not name a member of the view
|
|
14
|
-
* being written is therefore a framework defect either way, and fails loudly
|
|
15
|
-
* rather than being skipped.
|
|
3
|
+
* framework-owned `$ref` control snapshots through it and execution installs resolved
|
|
4
|
+
* final data through it, so both argument views are produced by the same ancestor-
|
|
5
|
+
* cloning walker.
|
|
16
6
|
*/
|
|
17
7
|
type Path = readonly (string | number)[];
|
|
18
8
|
/** One value to install at a full transaction slot path. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aventara/core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0-rc.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "Core runtime, contract compiler, and extension APIs for Aventara.",
|
|
6
6
|
"type": "module",
|
|
@@ -19,17 +19,31 @@
|
|
|
19
19
|
"types": "./dist/protocol/index.d.ts",
|
|
20
20
|
"import": "./dist/protocol/index.js",
|
|
21
21
|
"default": "./dist/protocol/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./internal": {
|
|
24
|
+
"types": "./dist/internal.d.ts",
|
|
25
|
+
"import": "./dist/internal.js",
|
|
26
|
+
"default": "./dist/internal.js"
|
|
27
|
+
},
|
|
28
|
+
"./json": {
|
|
29
|
+
"types": "./dist/json/index.d.ts",
|
|
30
|
+
"import": "./dist/json/index.js",
|
|
31
|
+
"default": "./dist/json/index.js"
|
|
22
32
|
}
|
|
23
33
|
},
|
|
24
34
|
"files": [
|
|
25
35
|
"dist",
|
|
26
|
-
"LICENSE-ADDITIONAL-PERMISSION.md"
|
|
36
|
+
"LICENSE-ADDITIONAL-PERMISSION.md",
|
|
37
|
+
"CHANGELOG.md"
|
|
27
38
|
],
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"zod": "^4.6.5"
|
|
41
|
+
},
|
|
28
42
|
"publishConfig": {
|
|
29
43
|
"access": "public"
|
|
30
44
|
},
|
|
31
45
|
"scripts": {
|
|
32
|
-
"build": "tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
46
|
+
"build": "node ../../scripts/build/build-output.cleaner.ts dist && tsc -p tsconfig.json --removeComments --declaration false && tsc -p tsconfig.json --emitDeclarationOnly && node ../../scripts/build/declaration-comments.sanitizer.ts dist",
|
|
33
47
|
"typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
|
|
34
48
|
"test": "vitest run --typecheck --config ../../vitest.config.mts --root .",
|
|
35
49
|
"lint": "biome lint src"
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { Contract } from "../contracts/contract.js";
|
|
2
|
-
import type { OperationIdentity } from "./operation-identity.js";
|
|
3
|
-
import type { ValidationIssue } from "./validation-code.js";
|
|
4
|
-
/**
|
|
5
|
-
* Enforces maxBooleanNodes across every general where tree in one semantic
|
|
6
|
-
* operation, including root filters, returned-relation filters, and nested
|
|
7
|
-
* relation mutation filters. Unique selectors are intentionally excluded.
|
|
8
|
-
*/
|
|
9
|
-
export declare function findOperationBooleanNodeIssue(contract: Contract, request: OperationIdentity & {
|
|
10
|
-
readonly resource: string;
|
|
11
|
-
}, args: Readonly<Record<string, unknown>>): ValidationIssue | undefined;
|
|
@@ -1,283 +0,0 @@
|
|
|
1
|
-
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
-
import { advertisedDirectives, holdsAt, } from "../contracts/field.js";
|
|
3
|
-
import { NESTED_PROJECTION_SCOPE, resolveProjectionScope, } from "./projection-scope.resolver.js";
|
|
4
|
-
function isRecord(value) {
|
|
5
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
|
-
}
|
|
7
|
-
function booleanNodeIssue(contract, path) {
|
|
8
|
-
return {
|
|
9
|
-
path,
|
|
10
|
-
code: "V1015",
|
|
11
|
-
message: `Boolean/filter node count exceeds maxBooleanNodes (${contract.limits.maxBooleanNodes}).`,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
function countNode(state, path) {
|
|
15
|
-
state.count += 1;
|
|
16
|
-
return state.count > state.contract.limits.maxBooleanNodes
|
|
17
|
-
? booleanNodeIssue(state.contract, path)
|
|
18
|
-
: undefined;
|
|
19
|
-
}
|
|
20
|
-
function walkWhere(state, resourceName, value, path) {
|
|
21
|
-
if (!isRecord(value)) {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
24
|
-
const resource = lookupResource(state.contract, resourceName);
|
|
25
|
-
if (resource === undefined) {
|
|
26
|
-
return undefined;
|
|
27
|
-
}
|
|
28
|
-
for (const [key, child] of Object.entries(value)) {
|
|
29
|
-
const exceeded = countNode(state, [...path, key]);
|
|
30
|
-
if (exceeded !== undefined) {
|
|
31
|
-
return exceeded;
|
|
32
|
-
}
|
|
33
|
-
if (key === "AND" || key === "OR") {
|
|
34
|
-
if (!Array.isArray(child)) {
|
|
35
|
-
continue;
|
|
36
|
-
}
|
|
37
|
-
for (let index = 0; index < child.length; index += 1) {
|
|
38
|
-
const issue = walkWhere(state, resourceName, child[index], [
|
|
39
|
-
...path,
|
|
40
|
-
key,
|
|
41
|
-
index,
|
|
42
|
-
]);
|
|
43
|
-
if (issue !== undefined) {
|
|
44
|
-
return issue;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
continue;
|
|
48
|
-
}
|
|
49
|
-
if (key === "NOT") {
|
|
50
|
-
const issue = walkWhere(state, resourceName, child, [...path, key]);
|
|
51
|
-
if (issue !== undefined) {
|
|
52
|
-
return issue;
|
|
53
|
-
}
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
const field = lookupField(resource, key);
|
|
57
|
-
if (field?.kind !== "relation") {
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (field.cardinality === "one") {
|
|
61
|
-
if (child === null || !isRecord(child)) {
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
const issue = walkWhere(state, field.target, child, [...path, key]);
|
|
65
|
-
if (issue !== undefined) {
|
|
66
|
-
return issue;
|
|
67
|
-
}
|
|
68
|
-
continue;
|
|
69
|
-
}
|
|
70
|
-
if (!isRecord(child)) {
|
|
71
|
-
continue;
|
|
72
|
-
}
|
|
73
|
-
for (const relationOperator of ["some", "every", "none"]) {
|
|
74
|
-
if (!(relationOperator in child)) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const issue = walkWhere(state, field.target, child[relationOperator], [
|
|
78
|
-
...path,
|
|
79
|
-
key,
|
|
80
|
-
relationOperator,
|
|
81
|
-
]);
|
|
82
|
-
if (issue !== undefined) {
|
|
83
|
-
return issue;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
function projectionRelation(contract, resourceName, entry, mode, scope) {
|
|
90
|
-
if (Object.keys(entry).length !== 1) {
|
|
91
|
-
return undefined;
|
|
92
|
-
}
|
|
93
|
-
const name = Object.keys(entry)[0];
|
|
94
|
-
const field = lookupField(lookupResource(contract, resourceName), name);
|
|
95
|
-
if (field?.kind !== "relation") {
|
|
96
|
-
return undefined;
|
|
97
|
-
}
|
|
98
|
-
if (mode === "select" && !holdsAt(field.capabilities.select, scope)) {
|
|
99
|
-
return undefined;
|
|
100
|
-
}
|
|
101
|
-
if (mode === "include" && !holdsAt(field.capabilities.include, scope)) {
|
|
102
|
-
return undefined;
|
|
103
|
-
}
|
|
104
|
-
return { name, edge: field, value: entry[name] };
|
|
105
|
-
}
|
|
106
|
-
function walkProjection(state, resourceName, value, mode, scope, path) {
|
|
107
|
-
if (!Array.isArray(value)) {
|
|
108
|
-
return undefined;
|
|
109
|
-
}
|
|
110
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
111
|
-
const entry = value[index];
|
|
112
|
-
if (!isRecord(entry)) {
|
|
113
|
-
continue;
|
|
114
|
-
}
|
|
115
|
-
const relation = projectionRelation(state.contract, resourceName, entry, mode, scope);
|
|
116
|
-
if (relation === undefined || !isRecord(relation.value)) {
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
const relationPath = [...path, index, relation.name];
|
|
120
|
-
if ("where" in relation.value &&
|
|
121
|
-
relation.edge.capabilities.filter?.includes("nested") === true) {
|
|
122
|
-
const issue = walkWhere(state, relation.edge.target, relation.value.where, [...relationPath, "where"]);
|
|
123
|
-
if (issue !== undefined) {
|
|
124
|
-
return issue;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
for (const nestedMode of ["select", "include"]) {
|
|
128
|
-
if (!(nestedMode in relation.value)) {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
const issue = walkProjection(state, relation.edge.target, relation.value[nestedMode], nestedMode, NESTED_PROJECTION_SCOPE, [...relationPath, nestedMode]);
|
|
132
|
-
if (issue !== undefined) {
|
|
133
|
-
return issue;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return undefined;
|
|
138
|
-
}
|
|
139
|
-
function relationEntries(value) {
|
|
140
|
-
return Array.isArray(value) ? value : [value];
|
|
141
|
-
}
|
|
142
|
-
function walkMutationData(state, resourceName, value, stage, path) {
|
|
143
|
-
if (!isRecord(value)) {
|
|
144
|
-
return undefined;
|
|
145
|
-
}
|
|
146
|
-
const resource = lookupResource(state.contract, resourceName);
|
|
147
|
-
if (resource === undefined) {
|
|
148
|
-
return undefined;
|
|
149
|
-
}
|
|
150
|
-
for (const [fieldName, fieldValue] of Object.entries(value)) {
|
|
151
|
-
const edge = lookupField(resource, fieldName);
|
|
152
|
-
if (edge?.kind !== "relation" || !isRecord(fieldValue)) {
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
const supported = advertisedDirectives(edge.capabilities[stage]);
|
|
156
|
-
for (const [directive, directiveValue] of Object.entries(fieldValue)) {
|
|
157
|
-
if (supported?.includes(directive) !== true) {
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
const entries = relationEntries(directiveValue);
|
|
161
|
-
for (let index = 0; index < entries.length; index += 1) {
|
|
162
|
-
const entry = entries[index];
|
|
163
|
-
const indexPath = Array.isArray(directiveValue) ? [index] : [];
|
|
164
|
-
const directivePath = [...path, fieldName, directive, ...indexPath];
|
|
165
|
-
if (directive === "$create") {
|
|
166
|
-
const issue = walkMutationData(state, edge.target, entry, "create", directivePath);
|
|
167
|
-
if (issue !== undefined) {
|
|
168
|
-
return issue;
|
|
169
|
-
}
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
if (!isRecord(entry)) {
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
if (directive === "$connectOrCreate" && "create" in entry) {
|
|
176
|
-
const issue = walkMutationData(state, edge.target, entry.create, "create", [...directivePath, "create"]);
|
|
177
|
-
if (issue !== undefined) {
|
|
178
|
-
return issue;
|
|
179
|
-
}
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (directive === "$update") {
|
|
183
|
-
if ("where" in entry) {
|
|
184
|
-
const issue = walkWhere(state, edge.target, entry.where, [
|
|
185
|
-
...directivePath,
|
|
186
|
-
"where",
|
|
187
|
-
]);
|
|
188
|
-
if (issue !== undefined) {
|
|
189
|
-
return issue;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
if ("data" in entry) {
|
|
193
|
-
const issue = walkMutationData(state, edge.target, entry.data, "update", [...directivePath, "data"]);
|
|
194
|
-
if (issue !== undefined) {
|
|
195
|
-
return issue;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
if (directive === "$delete" && "where" in entry) {
|
|
201
|
-
const issue = walkWhere(state, edge.target, entry.where, [
|
|
202
|
-
...directivePath,
|
|
203
|
-
"where",
|
|
204
|
-
]);
|
|
205
|
-
if (issue !== undefined) {
|
|
206
|
-
return issue;
|
|
207
|
-
}
|
|
208
|
-
continue;
|
|
209
|
-
}
|
|
210
|
-
if (directive === "$upsert") {
|
|
211
|
-
for (const branch of ["create", "update"]) {
|
|
212
|
-
if (!(branch in entry)) {
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
const issue = walkMutationData(state, edge.target, entry[branch], branch, [...directivePath, branch]);
|
|
216
|
-
if (issue !== undefined) {
|
|
217
|
-
return issue;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return undefined;
|
|
225
|
-
}
|
|
226
|
-
function isUniqueOperation(request) {
|
|
227
|
-
return (request.variant === "unique" &&
|
|
228
|
-
(request.family === "find" ||
|
|
229
|
-
request.family === "update" ||
|
|
230
|
-
request.family === "delete" ||
|
|
231
|
-
request.family === "upsert"));
|
|
232
|
-
}
|
|
233
|
-
export function findOperationBooleanNodeIssue(contract, request, args) {
|
|
234
|
-
const state = { contract, count: 0 };
|
|
235
|
-
const projectionScope = resolveProjectionScope(lookupResource(contract, request.resource), request);
|
|
236
|
-
if ("where" in args && !isUniqueOperation(request)) {
|
|
237
|
-
const issue = walkWhere(state, request.resource, args.where, [
|
|
238
|
-
"arguments",
|
|
239
|
-
"where",
|
|
240
|
-
]);
|
|
241
|
-
if (issue !== undefined) {
|
|
242
|
-
return issue;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
for (const mode of ["select", "include"]) {
|
|
246
|
-
if (!(mode in args)) {
|
|
247
|
-
continue;
|
|
248
|
-
}
|
|
249
|
-
const issue = walkProjection(state, request.resource, args[mode], mode, projectionScope, ["arguments", mode]);
|
|
250
|
-
if (issue !== undefined) {
|
|
251
|
-
return issue;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
if (request.family === "create" && "data" in args) {
|
|
255
|
-
const entries = Array.isArray(args.data) ? args.data : [args.data];
|
|
256
|
-
for (let index = 0; index < entries.length; index += 1) {
|
|
257
|
-
const issue = walkMutationData(state, request.resource, entries[index], "create", Array.isArray(args.data)
|
|
258
|
-
? ["arguments", "data", index]
|
|
259
|
-
: ["arguments", "data"]);
|
|
260
|
-
if (issue !== undefined) {
|
|
261
|
-
return issue;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if (request.family === "update" && "data" in args) {
|
|
266
|
-
const issue = walkMutationData(state, request.resource, args.data, "update", ["arguments", "data"]);
|
|
267
|
-
if (issue !== undefined) {
|
|
268
|
-
return issue;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
if (request.family === "upsert") {
|
|
272
|
-
for (const branch of ["create", "update"]) {
|
|
273
|
-
if (!(branch in args)) {
|
|
274
|
-
continue;
|
|
275
|
-
}
|
|
276
|
-
const issue = walkMutationData(state, request.resource, args[branch], branch, ["arguments", branch]);
|
|
277
|
-
if (issue !== undefined) {
|
|
278
|
-
return issue;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return undefined;
|
|
283
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Contract } from "../contracts/contract.js";
|
|
2
|
-
import type { ValidationIssue } from "./validation-code.js";
|
|
3
|
-
/**
|
|
4
|
-
* Enforces the Contract's semantic nesting limit across where, projection,
|
|
5
|
-
* orderBy, mutation data, and relation traversal before deeper argument
|
|
6
|
-
* validation.
|
|
7
|
-
*/
|
|
8
|
-
export declare function findOperationNestingIssue(contract: Contract, resourceName: string, args: Readonly<Record<string, unknown>>): ValidationIssue | undefined;
|
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
-
function isRecord(value) {
|
|
3
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
function nestingIssue(context) {
|
|
6
|
-
return {
|
|
7
|
-
path: context.rootPath,
|
|
8
|
-
code: "V1013",
|
|
9
|
-
message: `Request nesting exceeds maxNestingDepth (${context.limit}).`,
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
function checkDepth(context, depth) {
|
|
13
|
-
return depth > context.limit ? nestingIssue(context) : undefined;
|
|
14
|
-
}
|
|
15
|
-
function createContext(contract, rootPath) {
|
|
16
|
-
return {
|
|
17
|
-
contract,
|
|
18
|
-
limit: contract.limits.maxNestingDepth,
|
|
19
|
-
rootPath,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
function relationEntries(value) {
|
|
23
|
-
return Array.isArray(value) ? value : [value];
|
|
24
|
-
}
|
|
25
|
-
function walkWhere(context, resourceName, value, depth, path) {
|
|
26
|
-
const exceeded = checkDepth(context, depth);
|
|
27
|
-
if (exceeded !== undefined || !isRecord(value)) {
|
|
28
|
-
return exceeded;
|
|
29
|
-
}
|
|
30
|
-
const resource = lookupResource(context.contract, resourceName);
|
|
31
|
-
if (resource === undefined) {
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
for (const [key, child] of Object.entries(value)) {
|
|
35
|
-
if (key === "AND" || key === "OR") {
|
|
36
|
-
if (!Array.isArray(child)) {
|
|
37
|
-
continue;
|
|
38
|
-
}
|
|
39
|
-
for (let index = 0; index < child.length; index += 1) {
|
|
40
|
-
const issue = walkWhere(context, resourceName, child[index], depth + 1, [...path, key, index]);
|
|
41
|
-
if (issue !== undefined) {
|
|
42
|
-
return issue;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
continue;
|
|
46
|
-
}
|
|
47
|
-
if (key === "NOT") {
|
|
48
|
-
const issue = walkWhere(context, resourceName, child, depth + 1, [
|
|
49
|
-
...path,
|
|
50
|
-
key,
|
|
51
|
-
]);
|
|
52
|
-
if (issue !== undefined) {
|
|
53
|
-
return issue;
|
|
54
|
-
}
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
const field = lookupField(resource, key);
|
|
58
|
-
if (field?.kind !== "relation") {
|
|
59
|
-
continue;
|
|
60
|
-
}
|
|
61
|
-
if (field.cardinality === "one") {
|
|
62
|
-
const issue = walkWhere(context, field.target, child, depth + 1, [
|
|
63
|
-
...path,
|
|
64
|
-
key,
|
|
65
|
-
]);
|
|
66
|
-
if (issue !== undefined) {
|
|
67
|
-
return issue;
|
|
68
|
-
}
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
if (!isRecord(child)) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
for (const relationOperator of ["some", "every", "none"]) {
|
|
75
|
-
if (!(relationOperator in child)) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
const issue = walkWhere(context, field.target, child[relationOperator], depth + 1, [...path, key, relationOperator]);
|
|
79
|
-
if (issue !== undefined) {
|
|
80
|
-
return issue;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return undefined;
|
|
85
|
-
}
|
|
86
|
-
function walkOrderBy(context, resourceName, value, depth, path) {
|
|
87
|
-
const entries = Array.isArray(value) ? value : [value];
|
|
88
|
-
const resource = lookupResource(context.contract, resourceName);
|
|
89
|
-
if (resource === undefined) {
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
for (let index = 0; index < entries.length; index += 1) {
|
|
93
|
-
const entry = entries[index];
|
|
94
|
-
if (!isRecord(entry)) {
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
const entryPath = Array.isArray(value) ? [...path, index] : path;
|
|
98
|
-
for (const [fieldName, child] of Object.entries(entry)) {
|
|
99
|
-
const field = lookupField(resource, fieldName);
|
|
100
|
-
if (field?.kind !== "relation" || field.cardinality === "many") {
|
|
101
|
-
continue;
|
|
102
|
-
}
|
|
103
|
-
const relationDepth = depth + 1;
|
|
104
|
-
const exceeded = checkDepth(context, relationDepth);
|
|
105
|
-
if (exceeded !== undefined) {
|
|
106
|
-
return exceeded;
|
|
107
|
-
}
|
|
108
|
-
const issue = walkOrderBy(context, field.target, child, relationDepth, [
|
|
109
|
-
...entryPath,
|
|
110
|
-
fieldName,
|
|
111
|
-
]);
|
|
112
|
-
if (issue !== undefined) {
|
|
113
|
-
return issue;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return undefined;
|
|
118
|
-
}
|
|
119
|
-
function walkProjection(context, resourceName, value, depth, path) {
|
|
120
|
-
if (!Array.isArray(value)) {
|
|
121
|
-
return undefined;
|
|
122
|
-
}
|
|
123
|
-
const resource = lookupResource(context.contract, resourceName);
|
|
124
|
-
if (resource === undefined) {
|
|
125
|
-
return undefined;
|
|
126
|
-
}
|
|
127
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
128
|
-
const entry = value[index];
|
|
129
|
-
if (!isRecord(entry) || Object.keys(entry).length !== 1) {
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
const relationName = Object.keys(entry)[0];
|
|
133
|
-
const edge = lookupField(resource, relationName);
|
|
134
|
-
if (edge?.kind !== "relation") {
|
|
135
|
-
continue;
|
|
136
|
-
}
|
|
137
|
-
const relationPath = [...path, index, relationName];
|
|
138
|
-
const relationDepth = depth + 1;
|
|
139
|
-
const exceeded = checkDepth(context, relationDepth);
|
|
140
|
-
if (exceeded !== undefined) {
|
|
141
|
-
return exceeded;
|
|
142
|
-
}
|
|
143
|
-
const projection = entry[relationName];
|
|
144
|
-
if (!isRecord(projection)) {
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
if ("where" in projection) {
|
|
148
|
-
const issue = walkWhere(context, edge.target, projection.where, relationDepth, [...relationPath, "where"]);
|
|
149
|
-
if (issue !== undefined) {
|
|
150
|
-
return issue;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if ("orderBy" in projection) {
|
|
154
|
-
const issue = walkOrderBy(context, edge.target, projection.orderBy, relationDepth, [...relationPath, "orderBy"]);
|
|
155
|
-
if (issue !== undefined) {
|
|
156
|
-
return issue;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
for (const mode of ["select", "include"]) {
|
|
160
|
-
if (!(mode in projection)) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
const issue = walkProjection(context, edge.target, projection[mode], relationDepth, [...relationPath, mode]);
|
|
164
|
-
if (issue !== undefined) {
|
|
165
|
-
return issue;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return undefined;
|
|
170
|
-
}
|
|
171
|
-
function nestedMutationPayloads(directive, value) {
|
|
172
|
-
const values = relationEntries(value);
|
|
173
|
-
const payloads = [];
|
|
174
|
-
for (let index = 0; index < values.length; index += 1) {
|
|
175
|
-
const entry = values[index];
|
|
176
|
-
const indexPath = Array.isArray(value) ? [index] : [];
|
|
177
|
-
if (directive === "$create") {
|
|
178
|
-
payloads.push({ data: entry, suffix: indexPath });
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (!isRecord(entry)) {
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (directive === "$connectOrCreate" && "create" in entry) {
|
|
185
|
-
payloads.push({ data: entry.create, suffix: [...indexPath, "create"] });
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (directive === "$update" && "data" in entry) {
|
|
189
|
-
payloads.push({ data: entry.data, suffix: [...indexPath, "data"] });
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
if (directive === "$upsert") {
|
|
193
|
-
if ("create" in entry) {
|
|
194
|
-
payloads.push({ data: entry.create, suffix: [...indexPath, "create"] });
|
|
195
|
-
}
|
|
196
|
-
if ("update" in entry) {
|
|
197
|
-
payloads.push({ data: entry.update, suffix: [...indexPath, "update"] });
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return payloads;
|
|
202
|
-
}
|
|
203
|
-
function walkMutationDirectiveWheres(context, resourceName, directive, value, depth, path) {
|
|
204
|
-
if (directive !== "$update" && directive !== "$delete") {
|
|
205
|
-
return undefined;
|
|
206
|
-
}
|
|
207
|
-
const entries = relationEntries(value);
|
|
208
|
-
for (let index = 0; index < entries.length; index += 1) {
|
|
209
|
-
const entry = entries[index];
|
|
210
|
-
if (!isRecord(entry) || !("where" in entry)) {
|
|
211
|
-
continue;
|
|
212
|
-
}
|
|
213
|
-
const indexPath = Array.isArray(value) ? [index] : [];
|
|
214
|
-
const issue = walkWhere(context, resourceName, entry.where, depth, [
|
|
215
|
-
...path,
|
|
216
|
-
...indexPath,
|
|
217
|
-
"where",
|
|
218
|
-
]);
|
|
219
|
-
if (issue !== undefined) {
|
|
220
|
-
return issue;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return undefined;
|
|
224
|
-
}
|
|
225
|
-
function walkMutationData(context, resourceName, value, depth, path) {
|
|
226
|
-
const exceeded = checkDepth(context, depth);
|
|
227
|
-
if (exceeded !== undefined || !isRecord(value)) {
|
|
228
|
-
return exceeded;
|
|
229
|
-
}
|
|
230
|
-
const resource = lookupResource(context.contract, resourceName);
|
|
231
|
-
if (resource === undefined) {
|
|
232
|
-
return undefined;
|
|
233
|
-
}
|
|
234
|
-
for (const [fieldName, fieldValue] of Object.entries(value)) {
|
|
235
|
-
const edge = lookupField(resource, fieldName);
|
|
236
|
-
if (edge?.kind !== "relation" || !isRecord(fieldValue)) {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
for (const [directive, directiveValue] of Object.entries(fieldValue)) {
|
|
240
|
-
const relationDepth = depth + 1;
|
|
241
|
-
const relationPath = [...path, fieldName, directive];
|
|
242
|
-
const relationExceeded = checkDepth(context, relationDepth);
|
|
243
|
-
if (relationExceeded !== undefined) {
|
|
244
|
-
return relationExceeded;
|
|
245
|
-
}
|
|
246
|
-
const whereIssue = walkMutationDirectiveWheres(context, edge.target, directive, directiveValue, relationDepth, relationPath);
|
|
247
|
-
if (whereIssue !== undefined) {
|
|
248
|
-
return whereIssue;
|
|
249
|
-
}
|
|
250
|
-
for (const payload of nestedMutationPayloads(directive, directiveValue)) {
|
|
251
|
-
const issue = walkMutationData(context, edge.target, payload.data, relationDepth, [...relationPath, ...payload.suffix]);
|
|
252
|
-
if (issue !== undefined) {
|
|
253
|
-
return issue;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return undefined;
|
|
259
|
-
}
|
|
260
|
-
export function findOperationNestingIssue(contract, resourceName, args) {
|
|
261
|
-
if ("where" in args) {
|
|
262
|
-
const rootPath = ["arguments", "where"];
|
|
263
|
-
const issue = walkWhere(createContext(contract, rootPath), resourceName, args.where, 1, rootPath);
|
|
264
|
-
if (issue !== undefined) {
|
|
265
|
-
return issue;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
if ("orderBy" in args) {
|
|
269
|
-
const rootPath = ["arguments", "orderBy"];
|
|
270
|
-
const issue = walkOrderBy(createContext(contract, rootPath), resourceName, args.orderBy, 0, rootPath);
|
|
271
|
-
if (issue !== undefined) {
|
|
272
|
-
return issue;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
for (const mode of ["select", "include"]) {
|
|
276
|
-
if (!(mode in args)) {
|
|
277
|
-
continue;
|
|
278
|
-
}
|
|
279
|
-
const rootPath = ["arguments", mode];
|
|
280
|
-
const issue = walkProjection(createContext(contract, rootPath), resourceName, args[mode], 0, rootPath);
|
|
281
|
-
if (issue !== undefined) {
|
|
282
|
-
return issue;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
if ("data" in args) {
|
|
286
|
-
const rootPath = ["arguments", "data"];
|
|
287
|
-
const context = createContext(contract, rootPath);
|
|
288
|
-
const dataEntries = Array.isArray(args.data) ? args.data : [args.data];
|
|
289
|
-
for (let index = 0; index < dataEntries.length; index += 1) {
|
|
290
|
-
const issue = walkMutationData(context, resourceName, dataEntries[index], 0, Array.isArray(args.data) ? [...rootPath, index] : rootPath);
|
|
291
|
-
if (issue !== undefined) {
|
|
292
|
-
return issue;
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
for (const branch of ["create", "update"]) {
|
|
297
|
-
if (!(branch in args)) {
|
|
298
|
-
continue;
|
|
299
|
-
}
|
|
300
|
-
const rootPath = ["arguments", branch];
|
|
301
|
-
const issue = walkMutationData(createContext(contract, rootPath), resourceName, args[branch], 0, rootPath);
|
|
302
|
-
if (issue !== undefined) {
|
|
303
|
-
return issue;
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
return undefined;
|
|
307
|
-
}
|