@aventara/core 1.0.0-rc.0 → 1.0.0-rc.1
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 +1140 -0
- package/README.md +224 -57
- package/dist/adapters/adapter-discovery-diagnostic.d.ts +4 -4
- package/dist/adapters/adapter-field.d.ts +11 -11
- package/dist/adapters/adapter-identifier.d.ts +6 -6
- package/dist/adapters/adapter-model.d.ts +9 -9
- package/dist/adapters/adapter-operation-executor.interface.d.ts +4 -4
- package/dist/adapters/adapter-operation.d.ts +3 -3
- package/dist/adapters/adapter-resource.d.ts +18 -9
- package/dist/adapters/adapter-sql-statement.d.ts +19 -0
- package/dist/adapters/adapter-transaction-executor.interface.d.ts +19 -0
- package/dist/adapters/adapter.interface.d.ts +49 -29
- package/dist/avz/avz-check.factory.d.ts +83 -0
- package/dist/avz/avz-check.factory.js +116 -0
- package/dist/avz/avz-check.registry.d.ts +35 -0
- package/dist/avz/avz-check.registry.js +9 -0
- package/dist/avz/avz.d.ts +203 -0
- package/dist/{json → avz}/avz.js +150 -8
- package/dist/{json → avz}/index.d.ts +2 -1
- package/dist/{json → avz}/json-shape.registry.d.ts +7 -7
- package/dist/{json → avz}/zod-issue.translator.d.ts +8 -2
- package/dist/{json → avz}/zod-issue.translator.js +7 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +31 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +548 -441
- package/dist/compiler/create-requirements.projector.d.ts +49 -0
- package/dist/compiler/create-requirements.projector.js +423 -0
- package/dist/compiler/field-validator.compiler.d.ts +47 -0
- package/dist/compiler/field-validator.compiler.js +238 -0
- package/dist/config/any-framework-config.d.ts +19 -12
- package/dist/config/config-diagnostic.d.ts +11 -4
- package/dist/config/config-layer.d.ts +15 -6
- package/dist/config/config.resolver.d.ts +74 -31
- package/dist/config/config.resolver.js +108 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +581 -72
- package/dist/config/define-resource.d.ts +57 -0
- package/dist/config/define-resource.js +14 -0
- package/dist/config/effective-fields.d.ts +19 -23
- package/dist/config/entrypoint.normalizer.d.ts +1 -1
- package/dist/config/exact-framework-config.d.ts +280 -0
- package/dist/config/field-compute.resolver.d.ts +19 -0
- package/dist/config/field-compute.resolver.js +115 -0
- package/dist/config/field-pipeline-config.d.ts +47 -0
- package/dist/config/forced-operation.validator.d.ts +10 -0
- package/dist/config/forced-operation.validator.js +66 -0
- package/dist/config/framework-config.d.ts +110 -39
- package/dist/config/framework-exemptions-config.d.ts +91 -0
- package/dist/config/framework-exemptions-config.js +1 -0
- package/dist/config/framework-field-definition.d.ts +78 -0
- package/dist/config/framework-field-definition.js +28 -0
- package/dist/config/framework-resources-config.d.ts +116 -0
- package/dist/config/framework-resources-config.js +1 -0
- package/dist/config/pipeline-config.d.ts +56 -20
- package/dist/config/pipeline-tree.resolver.d.ts +79 -0
- package/dist/config/pipeline-tree.resolver.js +220 -0
- package/dist/config/stage-type.eraser.d.ts +65 -0
- package/dist/config/stage-type.eraser.js +1 -0
- package/dist/contracts/capability.d.ts +11 -11
- package/dist/contracts/client-contract-structure.validator.d.ts +323 -28
- package/dist/contracts/client-contract-structure.validator.js +152 -8
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +141 -28
- package/dist/contracts/contract-hash.d.ts +12 -7
- package/dist/contracts/contract-hash.js +3 -2
- package/dist/contracts/contract-limits.d.ts +4 -1
- package/dist/contracts/contract-limits.js +1 -0
- package/dist/contracts/contract-lookup.d.ts +5 -5
- package/dist/contracts/contract-protocol.d.ts +4 -4
- package/dist/contracts/contract.d.ts +24 -16
- package/dist/contracts/create-requirements.d.ts +35 -0
- package/dist/contracts/create-requirements.js +1 -0
- package/dist/contracts/enum-definition.d.ts +1 -1
- package/dist/contracts/field-check.d.ts +101 -0
- package/dist/contracts/field-check.js +1 -0
- package/dist/contracts/field.d.ts +52 -42
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +12 -1
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- package/dist/contracts/resource-operations.d.ts +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +15 -15
- package/dist/contracts/scalar.d.ts +3 -3
- package/dist/contracts/transaction-capability.d.ts +1 -1
- package/dist/contracts/wire-scalar-format.d.ts +4 -4
- package/dist/framework/contract-view.d.ts +213 -147
- package/dist/framework/framework-construction.error.d.ts +10 -10
- package/dist/framework/framework-construction.error.js +2 -2
- package/dist/framework/framework.d.ts +47 -23
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +40 -20
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -50
- package/dist/index.js +6 -6
- package/dist/internal.d.ts +15 -10
- package/dist/internal.js +3 -1
- package/dist/operations/operation-arguments.d.ts +267 -202
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +10 -10
- package/dist/operations/operation-result.d.ts +21 -21
- package/dist/operations/operation-validator-core.d.ts +6 -6
- package/dist/operations/operation-validator-core.js +13 -2
- package/dist/operations/operation-validator.d.ts +9 -9
- package/dist/operations/projection-scope.resolver.d.ts +6 -6
- package/dist/operations/structural-limits.walker.d.ts +6 -6
- package/dist/operations/structural-limits.walker.js +145 -0
- package/dist/operations/validation-code.d.ts +11 -6
- package/dist/operations/validation-code.js +4 -0
- package/dist/operations/write-scope.resolver.d.ts +6 -6
- package/dist/protocol/absent-route.responder.d.ts +13 -12
- package/dist/protocol/absent-route.responder.js +19 -19
- package/dist/protocol/contract-document.responder.d.ts +3 -3
- package/dist/protocol/contract-document.responder.js +2 -2
- package/dist/protocol/http-request.decoder.d.ts +17 -13
- package/dist/protocol/http-request.decoder.js +10 -10
- package/dist/protocol/http-request.reader.d.ts +3 -3
- package/dist/protocol/http-response.encoder.d.ts +4 -4
- package/dist/protocol/http-response.encoder.js +94 -55
- package/dist/protocol/http-status.table.d.ts +2 -2
- package/dist/protocol/operation-response.guard.d.ts +2 -2
- package/dist/protocol/protocol-surface.d.ts +4 -4
- package/dist/protocol/protocol.binder.d.ts +50 -27
- package/dist/protocol/protocol.binder.js +12 -9
- package/dist/protocol/protocol.handler.d.ts +22 -13
- package/dist/protocol/protocol.handler.js +44 -9
- package/dist/protocol/unmounted-route.error.d.ts +4 -4
- package/dist/protocol/unmounted-route.error.js +2 -2
- package/dist/runtime/caller-data.copier.d.ts +5 -5
- package/dist/runtime/caller-data.copier.js +13 -13
- package/dist/runtime/caller-data.door.d.ts +6 -6
- package/dist/runtime/caller-data.door.js +6 -4
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -48
- package/dist/runtime/computed-value.error.d.ts +6 -6
- package/dist/runtime/computed-value.error.js +3 -3
- package/dist/runtime/context-executor.runtime.d.ts +59 -0
- package/dist/runtime/context-executor.runtime.js +275 -0
- package/dist/runtime/create-requirement.d.ts +10 -81
- package/dist/runtime/create-requirement.js +80 -57
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +53 -27
- package/dist/runtime/diagnostics-reporter.js +24 -0
- package/dist/runtime/error-boundary.d.ts +65 -8
- package/dist/runtime/error-boundary.js +45 -19
- package/dist/runtime/execution-options.d.ts +10 -2
- package/dist/runtime/field-check.evaluator.d.ts +29 -0
- package/dist/runtime/field-check.evaluator.js +521 -0
- package/dist/runtime/field-compute.d.ts +64 -0
- package/dist/runtime/field-compute.js +1 -0
- package/dist/runtime/field-stage.plan.d.ts +15 -0
- package/dist/runtime/field-stage.plan.js +27 -0
- package/dist/runtime/field-stage.runtime.d.ts +35 -0
- package/dist/runtime/field-stage.runtime.js +143 -0
- package/dist/runtime/field-validator.runtime.d.ts +11 -0
- package/dist/runtime/field-validator.runtime.js +87 -0
- package/dist/runtime/framework-error-class.table.d.ts +33 -0
- package/dist/runtime/framework-error-class.table.js +25 -0
- package/dist/runtime/framework-error.d.ts +68 -8
- package/dist/runtime/framework-error.factory.d.ts +4 -0
- package/dist/runtime/framework-error.factory.js +15 -0
- package/dist/runtime/framework-error.js +23 -2
- package/dist/runtime/interceptor-next.error.d.ts +5 -0
- package/dist/runtime/interceptor-next.error.js +6 -0
- package/dist/runtime/json-value.d.ts +3 -3
- package/dist/runtime/level-lifecycle.runtime.d.ts +25 -0
- package/dist/runtime/level-lifecycle.runtime.js +42 -0
- package/dist/runtime/misuse-reporter.d.ts +12 -12
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +54 -30
- package/dist/runtime/nested-write.walker.d.ts +32 -0
- package/dist/runtime/nested-write.walker.js +73 -0
- package/dist/runtime/operation-request.d.ts +33 -6
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +113 -25
- package/dist/runtime/operation-runtime.js +395 -74
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +23 -6
- package/dist/runtime/output-validator.d.ts +10 -10
- package/dist/runtime/output-validator.js +3 -3
- package/dist/runtime/owned-execution.registry.d.ts +6 -6
- package/dist/runtime/pipe-output.validator.d.ts +33 -18
- package/dist/runtime/pipe-output.validator.js +12 -12
- package/dist/runtime/pipeline.d.ts +196 -34
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/request-envelope.validator.d.ts +3 -3
- package/dist/runtime/request-envelope.validator.js +12 -7
- package/dist/runtime/request-id.d.ts +1 -1
- package/dist/runtime/request-state.d.ts +14 -0
- package/dist/runtime/request-state.js +1 -0
- package/dist/runtime/request-state.resolver.d.ts +12 -0
- package/dist/runtime/request-state.resolver.js +20 -0
- package/dist/runtime/scalar-runtime.d.ts +31 -16
- package/dist/runtime/scalar-runtime.js +16 -18
- package/dist/transactions/deferred-operation.d.ts +35 -20
- package/dist/transactions/operation-arguments-encoder.d.ts +3 -3
- package/dist/transactions/operation-arguments-encoder.js +2 -2
- package/dist/transactions/sql-statement.d.ts +77 -0
- package/dist/transactions/sql-statement.js +47 -0
- package/dist/transactions/sql-statement.validator.d.ts +44 -0
- package/dist/transactions/sql-statement.validator.js +134 -0
- package/dist/transactions/transaction-envelope.d.ts +4 -4
- package/dist/transactions/transaction-integrity-error.d.ts +6 -6
- package/dist/transactions/transaction-integrity-error.js +5 -2
- package/dist/transactions/transaction-plan-assembler.d.ts +21 -11
- package/dist/transactions/transaction-plan-assembler.js +148 -34
- package/dist/transactions/transaction-plan-validator.d.ts +29 -20
- package/dist/transactions/transaction-plan-validator.js +70 -38
- package/dist/transactions/transaction-reference-collector.d.ts +5 -5
- package/dist/transactions/transaction-reference-resolver.d.ts +19 -2
- package/dist/transactions/transaction-reference-resolver.js +26 -14
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +58 -26
- package/dist/transactions/transaction-reference.d.ts +13 -13
- package/dist/transactions/transaction-request.d.ts +18 -5
- package/dist/transactions/transaction-response.d.ts +6 -6
- package/dist/transactions/transaction-runtime.d.ts +8 -7
- package/dist/transactions/transaction-runtime.js +166 -50
- package/dist/transactions/transaction-settlement.runtime.d.ts +51 -0
- package/dist/transactions/transaction-settlement.runtime.js +120 -0
- package/dist/transactions/transaction-slot-path-error.d.ts +4 -4
- package/dist/transactions/transaction-slot-path-error.js +2 -2
- package/dist/transactions/transaction-slot-writer.js +2 -2
- package/dist/transactions/transaction-stage.runtime.d.ts +81 -0
- package/dist/transactions/transaction-stage.runtime.js +123 -0
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -42
- package/dist/config/define-fields.js +0 -3
- package/dist/config/framework-behaviors-config.d.ts +0 -30
- package/dist/config/framework-fields-config.d.ts +0 -42
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- package/dist/json/avz.d.ts +0 -96
- package/dist/runtime/field-behavior.d.ts +0 -48
- /package/dist/{config/framework-behaviors-config.js → adapters/adapter-sql-statement.js} +0 -0
- /package/dist/{config/framework-fields-config.js → adapters/adapter-transaction-executor.interface.js} +0 -0
- /package/dist/{json → avz}/index.js +0 -0
- /package/dist/{json → avz}/json-shape.registry.js +0 -0
- /package/dist/config/{framework-restrictions-config.js → exact-framework-config.js} +0 -0
- /package/dist/{runtime/field-behavior.js → config/field-pipeline-config.js} +0 -0
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
-
import {
|
|
2
|
+
import { AvComputedValueError } from "./computed-value.error.js";
|
|
3
3
|
import { normalizeScalarFieldValue } from "./scalar-runtime.js";
|
|
4
4
|
function isRecord(value) {
|
|
5
5
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
6
6
|
}
|
|
7
|
-
function
|
|
7
|
+
function readCompute(value) {
|
|
8
8
|
if (typeof value === "function") {
|
|
9
9
|
return { compute: value, dependsOn: [] };
|
|
10
10
|
}
|
|
11
|
-
if (value === undefined ||
|
|
12
|
-
value === false ||
|
|
13
|
-
typeof value.compute !== "function") {
|
|
11
|
+
if (value === undefined || typeof value.compute !== "function") {
|
|
14
12
|
return undefined;
|
|
15
13
|
}
|
|
16
14
|
return {
|
|
@@ -18,6 +16,16 @@ function readBehavior(value) {
|
|
|
18
16
|
dependsOn: value.dependsOn ?? [],
|
|
19
17
|
};
|
|
20
18
|
}
|
|
19
|
+
function storedScalar(model, resource, field) {
|
|
20
|
+
const resources = model.resources;
|
|
21
|
+
const fields = Object.hasOwn(resources, resource)
|
|
22
|
+
? resources[resource]?.fields
|
|
23
|
+
: undefined;
|
|
24
|
+
const entry = fields !== undefined && Object.hasOwn(fields, field)
|
|
25
|
+
? fields[field]
|
|
26
|
+
: undefined;
|
|
27
|
+
return isRecord(entry) && entry.kind === "scalar";
|
|
28
|
+
}
|
|
21
29
|
function appendUnique(values, value) {
|
|
22
30
|
if (!values.includes(value)) {
|
|
23
31
|
values.push(value);
|
|
@@ -34,18 +42,17 @@ function supportsProjection(request) {
|
|
|
34
42
|
}
|
|
35
43
|
return request.variant !== "count";
|
|
36
44
|
}
|
|
37
|
-
function addScalarProjection(core, active,
|
|
45
|
+
function addScalarProjection(core, model, active, compute, resourceName, fieldName, adapterSelect, requested, computed) {
|
|
38
46
|
const activeField = lookupField(lookupResource(active, resourceName), fieldName);
|
|
39
47
|
if (activeField?.kind !== "scalar") {
|
|
40
48
|
return;
|
|
41
49
|
}
|
|
42
50
|
appendUnique(requested, fieldName);
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
51
|
+
const stage = readCompute(compute?.[resourceName]?.[fieldName]?.read);
|
|
52
|
+
if (stage !== undefined) {
|
|
45
53
|
appendUnique(computed, fieldName);
|
|
46
|
-
for (const dependency of
|
|
47
|
-
|
|
48
|
-
if (dependencyField?.kind === "scalar") {
|
|
54
|
+
for (const dependency of stage.dependsOn) {
|
|
55
|
+
if (storedScalar(model, resourceName, dependency)) {
|
|
49
56
|
appendUniqueProjection(adapterSelect, dependency);
|
|
50
57
|
}
|
|
51
58
|
}
|
|
@@ -70,7 +77,7 @@ function projectionEntry(entry) {
|
|
|
70
77
|
function withoutProjectionChoice(value) {
|
|
71
78
|
return Object.fromEntries(Object.entries(value).filter(([key]) => key !== "select" && key !== "include"));
|
|
72
79
|
}
|
|
73
|
-
function defaultProjectionLevel(core, active,
|
|
80
|
+
function defaultProjectionLevel(core, model, active, compute, resourceName) {
|
|
74
81
|
const activeResource = lookupResource(active, resourceName);
|
|
75
82
|
const adapterSelect = [];
|
|
76
83
|
const requested = [];
|
|
@@ -80,7 +87,7 @@ function defaultProjectionLevel(core, active, behaviors, resourceName) {
|
|
|
80
87
|
if (field.kind !== "scalar" || field.capabilities.select !== true) {
|
|
81
88
|
continue;
|
|
82
89
|
}
|
|
83
|
-
addScalarProjection(core, active,
|
|
90
|
+
addScalarProjection(core, model, active, compute, resourceName, fieldName, adapterSelect, requested, computed);
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
return {
|
|
@@ -94,25 +101,25 @@ function defaultProjectionLevel(core, active, behaviors, resourceName) {
|
|
|
94
101
|
},
|
|
95
102
|
};
|
|
96
103
|
}
|
|
97
|
-
function planRelationProjection(core, active,
|
|
104
|
+
function planRelationProjection(core, model, active, compute, field, value) {
|
|
98
105
|
if (!isRecord(value)) {
|
|
99
|
-
return defaultProjectionLevel(core, active,
|
|
106
|
+
return defaultProjectionLevel(core, model, active, compute, field.target);
|
|
100
107
|
}
|
|
101
108
|
if (Array.isArray(value.select)) {
|
|
102
|
-
return planSelectLevel(core, active,
|
|
109
|
+
return planSelectLevel(core, model, active, compute, field.target, value.select, true);
|
|
103
110
|
}
|
|
104
111
|
if (Array.isArray(value.include)) {
|
|
105
|
-
return planIncludeLevel(core, active,
|
|
112
|
+
return planIncludeLevel(core, model, active, compute, field.target, value.include);
|
|
106
113
|
}
|
|
107
|
-
return defaultProjectionLevel(core, active,
|
|
114
|
+
return defaultProjectionLevel(core, model, active, compute, field.target);
|
|
108
115
|
}
|
|
109
|
-
function planRelationEntry(core, active,
|
|
116
|
+
function planRelationEntry(core, model, active, compute, resourceName, fieldName, value) {
|
|
110
117
|
const activeField = lookupField(lookupResource(active, resourceName), fieldName);
|
|
111
118
|
const coreField = lookupField(lookupResource(core, resourceName), fieldName);
|
|
112
119
|
if (activeField?.kind !== "relation" || coreField?.kind !== "relation") {
|
|
113
120
|
return undefined;
|
|
114
121
|
}
|
|
115
|
-
const nested = planRelationProjection(core, active,
|
|
122
|
+
const nested = planRelationProjection(core, model, active, compute, coreField, value);
|
|
116
123
|
return {
|
|
117
124
|
adapterEntry: {
|
|
118
125
|
[fieldName]: {
|
|
@@ -127,7 +134,7 @@ function planRelationEntry(core, active, behaviors, resourceName, fieldName, val
|
|
|
127
134
|
},
|
|
128
135
|
};
|
|
129
136
|
}
|
|
130
|
-
function planSelectLevel(core, active,
|
|
137
|
+
function planSelectLevel(core, model, active, compute, resourceName, select, collectReducers = false) {
|
|
131
138
|
const activeResource = lookupResource(active, resourceName);
|
|
132
139
|
const coreResource = lookupResource(core, resourceName);
|
|
133
140
|
const adapterSelect = [];
|
|
@@ -159,11 +166,11 @@ function planSelectLevel(core, active, behaviors, resourceName, select, collectR
|
|
|
159
166
|
const activeField = lookupField(activeResource, entry);
|
|
160
167
|
const coreField = lookupField(coreResource, entry);
|
|
161
168
|
if (activeField?.kind === "scalar") {
|
|
162
|
-
addScalarProjection(core, active,
|
|
169
|
+
addScalarProjection(core, model, active, compute, resourceName, entry, adapterSelect, requested, computed);
|
|
163
170
|
continue;
|
|
164
171
|
}
|
|
165
172
|
if (activeField?.kind === "relation" && coreField?.kind === "relation") {
|
|
166
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
173
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, entry, undefined);
|
|
167
174
|
if (plannedRelation !== undefined) {
|
|
168
175
|
appendUnique(requested, entry);
|
|
169
176
|
adapterSelect.push(plannedRelation.adapterEntry);
|
|
@@ -180,7 +187,7 @@ function planSelectLevel(core, active, behaviors, resourceName, select, collectR
|
|
|
180
187
|
continue;
|
|
181
188
|
}
|
|
182
189
|
const [fieldName, nestedValue] = relationEntry;
|
|
183
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
190
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, fieldName, nestedValue);
|
|
184
191
|
if (plannedRelation === undefined) {
|
|
185
192
|
adapterSelect.push(entry);
|
|
186
193
|
continue;
|
|
@@ -194,15 +201,15 @@ function planSelectLevel(core, active, behaviors, resourceName, select, collectR
|
|
|
194
201
|
plan: { resource: resourceName, requested, computed, relations, reducers },
|
|
195
202
|
};
|
|
196
203
|
}
|
|
197
|
-
function planIncludeLevel(core, active,
|
|
198
|
-
const defaults = defaultProjectionLevel(core, active,
|
|
204
|
+
function planIncludeLevel(core, model, active, compute, resourceName, include) {
|
|
205
|
+
const defaults = defaultProjectionLevel(core, model, active, compute, resourceName);
|
|
199
206
|
const adapterSelect = [...defaults.select];
|
|
200
207
|
const requested = [...defaults.plan.requested];
|
|
201
208
|
const computed = [...defaults.plan.computed];
|
|
202
209
|
const relations = [];
|
|
203
210
|
for (const entry of include) {
|
|
204
211
|
if (typeof entry === "string") {
|
|
205
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
212
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, entry, undefined);
|
|
206
213
|
if (plannedRelation === undefined) {
|
|
207
214
|
continue;
|
|
208
215
|
}
|
|
@@ -216,7 +223,7 @@ function planIncludeLevel(core, active, behaviors, resourceName, include) {
|
|
|
216
223
|
continue;
|
|
217
224
|
}
|
|
218
225
|
const [fieldName, nestedValue] = relationEntry;
|
|
219
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
226
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, fieldName, nestedValue);
|
|
220
227
|
if (plannedRelation === undefined) {
|
|
221
228
|
continue;
|
|
222
229
|
}
|
|
@@ -241,7 +248,7 @@ function replaceWithInternalSelect(args, select) {
|
|
|
241
248
|
internal.select = select;
|
|
242
249
|
return internal;
|
|
243
250
|
}
|
|
244
|
-
export function planReadOperation(core, active,
|
|
251
|
+
export function planReadOperation(core, model, active, compute, request, args) {
|
|
245
252
|
if (!isRecord(args) || !supportsProjection(request)) {
|
|
246
253
|
return { arguments: args, projection: undefined, materialize: false };
|
|
247
254
|
}
|
|
@@ -251,7 +258,7 @@ export function planReadOperation(core, active, behaviors, request, args) {
|
|
|
251
258
|
return { arguments: args, projection: undefined, materialize: false };
|
|
252
259
|
}
|
|
253
260
|
if (Array.isArray(args.select)) {
|
|
254
|
-
const planned = planSelectLevel(core, active,
|
|
261
|
+
const planned = planSelectLevel(core, model, active, compute, request.resource, args.select);
|
|
255
262
|
return {
|
|
256
263
|
arguments: replaceWithInternalSelect(args, planned.select),
|
|
257
264
|
projection: planned.plan,
|
|
@@ -259,21 +266,21 @@ export function planReadOperation(core, active, behaviors, request, args) {
|
|
|
259
266
|
};
|
|
260
267
|
}
|
|
261
268
|
if (Array.isArray(args.include)) {
|
|
262
|
-
const planned = planIncludeLevel(core, active,
|
|
269
|
+
const planned = planIncludeLevel(core, model, active, compute, request.resource, args.include);
|
|
263
270
|
return {
|
|
264
271
|
arguments: replaceWithInternalSelect(args, planned.select),
|
|
265
272
|
projection: planned.plan,
|
|
266
273
|
materialize: true,
|
|
267
274
|
};
|
|
268
275
|
}
|
|
269
|
-
const defaults = defaultProjectionLevel(core, active,
|
|
276
|
+
const defaults = defaultProjectionLevel(core, model, active, compute, request.resource);
|
|
270
277
|
return {
|
|
271
278
|
arguments: { ...args, select: defaults.select },
|
|
272
279
|
projection: defaults.plan,
|
|
273
280
|
materialize: defaults.plan.computed.length > 0,
|
|
274
281
|
};
|
|
275
282
|
}
|
|
276
|
-
async function materializeRelation(active,
|
|
283
|
+
async function materializeRelation(active, compute, request, relation, value, contextFactory) {
|
|
277
284
|
if (value === null || value === undefined) {
|
|
278
285
|
return value;
|
|
279
286
|
}
|
|
@@ -281,11 +288,11 @@ async function materializeRelation(active, behaviors, request, relation, value,
|
|
|
281
288
|
if (!Array.isArray(value)) {
|
|
282
289
|
return value;
|
|
283
290
|
}
|
|
284
|
-
return Promise.all(value.map((entry) => materializeRecord(active,
|
|
291
|
+
return Promise.all(value.map((entry) => materializeRecord(active, compute, request, relation.projection, entry, contextFactory)));
|
|
285
292
|
}
|
|
286
|
-
return materializeRecord(active,
|
|
293
|
+
return materializeRecord(active, compute, request, relation.projection, value, contextFactory);
|
|
287
294
|
}
|
|
288
|
-
async function materializeRecord(active,
|
|
295
|
+
async function materializeRecord(active, compute, request, plan, value, contextFactory) {
|
|
289
296
|
if (!isRecord(value)) {
|
|
290
297
|
return value;
|
|
291
298
|
}
|
|
@@ -296,24 +303,30 @@ async function materializeRecord(active, behaviors, request, plan, value, contex
|
|
|
296
303
|
const record = { ...value };
|
|
297
304
|
for (const fieldName of plan.computed) {
|
|
298
305
|
const field = lookupField(resource, fieldName);
|
|
299
|
-
const
|
|
300
|
-
if (field?.kind !== "scalar" ||
|
|
306
|
+
const stage = readCompute(compute?.[plan.resource]?.[fieldName]?.read);
|
|
307
|
+
if (field?.kind !== "scalar" || stage === undefined) {
|
|
301
308
|
continue;
|
|
302
309
|
}
|
|
303
|
-
const computedValue = await
|
|
304
|
-
origin: request.origin,
|
|
305
|
-
resource: plan.resource,
|
|
310
|
+
const computedValue = await stage.compute({
|
|
306
311
|
field: fieldName,
|
|
307
312
|
value: record[fieldName],
|
|
308
313
|
record,
|
|
309
|
-
|
|
314
|
+
ctx: contextFactory(),
|
|
310
315
|
});
|
|
311
316
|
if (computedValue === undefined) {
|
|
312
317
|
continue;
|
|
313
318
|
}
|
|
314
|
-
const validation = normalizeScalarFieldValue(active, field, computedValue, "runtime", [
|
|
319
|
+
const validation = normalizeScalarFieldValue(active, field, computedValue, "runtime", [
|
|
320
|
+
"resources",
|
|
321
|
+
plan.resource,
|
|
322
|
+
"fields",
|
|
323
|
+
fieldName,
|
|
324
|
+
"pipelines",
|
|
325
|
+
"compute",
|
|
326
|
+
"read",
|
|
327
|
+
]);
|
|
315
328
|
if (!validation.ok) {
|
|
316
|
-
throw new
|
|
329
|
+
throw new AvComputedValueError(plan.resource, fieldName, "read", validation.issues);
|
|
317
330
|
}
|
|
318
331
|
record[fieldName] = validation.value;
|
|
319
332
|
}
|
|
@@ -321,19 +334,19 @@ async function materializeRecord(active, behaviors, request, plan, value, contex
|
|
|
321
334
|
if (!Object.hasOwn(record, relation.field)) {
|
|
322
335
|
continue;
|
|
323
336
|
}
|
|
324
|
-
record[relation.field] = await materializeRelation(active,
|
|
337
|
+
record[relation.field] = await materializeRelation(active, compute, request, relation, record[relation.field], contextFactory);
|
|
325
338
|
}
|
|
326
339
|
return Object.fromEntries(plan.requested
|
|
327
340
|
.filter((fieldName) => Object.hasOwn(record, fieldName))
|
|
328
341
|
.map((fieldName) => [fieldName, record[fieldName]]));
|
|
329
342
|
}
|
|
330
|
-
export async function materializeReadOperation(active,
|
|
343
|
+
export async function materializeReadOperation(active, compute, request, plan, value, contextFactory) {
|
|
331
344
|
const projection = plan.projection;
|
|
332
345
|
if (!plan.materialize || projection === undefined) {
|
|
333
346
|
return value;
|
|
334
347
|
}
|
|
335
348
|
if (Array.isArray(value)) {
|
|
336
|
-
return Promise.all(value.map((entry) => materializeRecord(active,
|
|
349
|
+
return Promise.all(value.map((entry) => materializeRecord(active, compute, request, projection, entry, contextFactory)));
|
|
337
350
|
}
|
|
338
|
-
return materializeRecord(active,
|
|
351
|
+
return materializeRecord(active, compute, request, projection, value, contextFactory);
|
|
339
352
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
2
2
|
/**
|
|
3
3
|
* What the diagnostics channel receives when a computed value — a read or write
|
|
4
|
-
*
|
|
4
|
+
* compute stage's return — is one its Contract refuses: the developer's broken code,
|
|
5
5
|
* never the caller's. It is thrown, not a deliberate outcome, so the framework's
|
|
6
6
|
* boundary answers the caller the generic `A3000` and reports this error, with the
|
|
7
7
|
* issues, exactly once.
|
|
8
8
|
*/
|
|
9
|
-
export declare class
|
|
10
|
-
readonly issues: readonly
|
|
11
|
-
readonly name = "
|
|
12
|
-
constructor(resource: string, field: string, stage: "read" | "create" | "update", issues: readonly
|
|
9
|
+
export declare class AvComputedValueError extends Error {
|
|
10
|
+
readonly issues: readonly AvValidationIssue[];
|
|
11
|
+
readonly name = "AvComputedValueError";
|
|
12
|
+
constructor(resource: string, field: string, stage: "read" | "create" | "update", issues: readonly AvValidationIssue[]);
|
|
13
13
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export class
|
|
1
|
+
export class AvComputedValueError extends Error {
|
|
2
2
|
issues;
|
|
3
|
-
name = "
|
|
3
|
+
name = "AvComputedValueError";
|
|
4
4
|
constructor(resource, field, stage, issues) {
|
|
5
5
|
const [first] = issues;
|
|
6
|
-
super(`The
|
|
6
|
+
super(`The compute.${stage} stage of ${resource}.${field} returned a value its Contract refuses${first === undefined ? "." : ` (${first.code}: ${first.message})`}`);
|
|
7
7
|
this.issues = issues;
|
|
8
8
|
}
|
|
9
9
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AvAdapter } from "../adapters/adapter.interface.js";
|
|
2
|
+
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
3
|
+
import type { AvAdapterTransactionExecutor } from "../adapters/adapter-transaction-executor.interface.js";
|
|
4
|
+
import type { AvRequestOrigin } from "../config/config-layer.js";
|
|
5
|
+
import type { AvContract } from "../contracts/contract.js";
|
|
6
|
+
import type { AvOperationFamily } from "../operations/operation-identity.js";
|
|
7
|
+
import { type AvDiagnosticsSink } from "./diagnostics-reporter.js";
|
|
8
|
+
import type { AvComputeContext } from "./field-compute.js";
|
|
9
|
+
import type { MisuseReporter } from "./misuse-reporter.js";
|
|
10
|
+
import type { ApplicationOperationRequest } from "./operation-request.js";
|
|
11
|
+
import type { AvOperationResponse } from "./operation-response.js";
|
|
12
|
+
import type { AvContext, AvTransactionContext, AvTransportContext } from "./pipeline.js";
|
|
13
|
+
import type { AvState } from "./request-state.js";
|
|
14
|
+
/** Where a ctx's calls run: inside the open transaction, or standalone. */
|
|
15
|
+
export type ExecutorScope<M extends AvAdapterModel = AvAdapterModel> = {
|
|
16
|
+
/** The request id every call of this ctx carries: its stage's request's. */
|
|
17
|
+
readonly requestId: string;
|
|
18
|
+
/** The open transaction's executor; `undefined` outside any transaction. */
|
|
19
|
+
readonly transaction: AvAdapterTransactionExecutor<M> | undefined;
|
|
20
|
+
/** The request's state, which every call of this ctx inherits. */
|
|
21
|
+
readonly state: AvState;
|
|
22
|
+
};
|
|
23
|
+
/** The `$` facts a ctx states, as own data properties. */
|
|
24
|
+
export type ContextFacts = {
|
|
25
|
+
readonly $origin: AvRequestOrigin;
|
|
26
|
+
readonly $requestId: string;
|
|
27
|
+
readonly $transport?: AvTransportContext;
|
|
28
|
+
readonly $contract: AvContract;
|
|
29
|
+
readonly $operation?: {
|
|
30
|
+
readonly resource: string;
|
|
31
|
+
readonly family: AvOperationFamily;
|
|
32
|
+
readonly variant: string;
|
|
33
|
+
readonly nested?: true;
|
|
34
|
+
};
|
|
35
|
+
readonly $args: unknown;
|
|
36
|
+
};
|
|
37
|
+
/** Runs one raw application-origin call in `transaction`, or standalone. Total. */
|
|
38
|
+
export type RawOperationRunner<M extends AvAdapterModel> = (request: ApplicationOperationRequest, transaction: AvAdapterTransactionExecutor<M> | undefined) => Promise<AvOperationResponse>;
|
|
39
|
+
export type ContextExecutorDependencies<M extends AvAdapterModel> = {
|
|
40
|
+
/** The model, as construction read it once: the ctx's Resources. */
|
|
41
|
+
readonly model: M;
|
|
42
|
+
readonly adapter: AvAdapter<M>;
|
|
43
|
+
/** The Application Contract: a `ctx.$transaction` needs its transactions. */
|
|
44
|
+
readonly application: AvContract;
|
|
45
|
+
readonly diagnostics: AvDiagnosticsSink | undefined;
|
|
46
|
+
readonly misuse: MisuseReporter;
|
|
47
|
+
readonly run: RawOperationRunner<M>;
|
|
48
|
+
};
|
|
49
|
+
/** Builds the ctx every stage is told. */
|
|
50
|
+
export type ContextExecutor<M extends AvAdapterModel = AvAdapterModel> = {
|
|
51
|
+
/** An operation stage's ctx: its facts and the full executor. */
|
|
52
|
+
readonly operation: (scope: ExecutorScope<M>, facts: ContextFacts) => AvContext;
|
|
53
|
+
/** A Resource transaction stage's ctx: the plan as `$args`, the full executor. */
|
|
54
|
+
readonly transaction: (scope: ExecutorScope<M>, facts: ContextFacts) => AvTransactionContext;
|
|
55
|
+
/** Compute's read-only ctx: the facts, and `find.*` reads only. */
|
|
56
|
+
readonly compute: (scope: ExecutorScope<M>, facts: ContextFacts) => AvComputeContext;
|
|
57
|
+
};
|
|
58
|
+
/** Builds the ctx factory of one Framework over its model. */
|
|
59
|
+
export declare function createContextExecutor<M extends AvAdapterModel>(dependencies: ContextExecutorDependencies<M>): ContextExecutor<M>;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { isOperationVariantAvailable } from "../contracts/resource-operations.js";
|
|
2
|
+
import { parseOperationIdentity } from "../operations/operation-identity.js";
|
|
3
|
+
import { validationIssue } from "../operations/validation-code.js";
|
|
4
|
+
import { readSqlTemplate } from "../transactions/sql-statement.js";
|
|
5
|
+
import { normalizeSqlValue } from "../transactions/sql-statement.validator.js";
|
|
6
|
+
import { AvTransactionIntegrityError } from "../transactions/transaction-integrity-error.js";
|
|
7
|
+
import { transactionInfrastructureFailure } from "../transactions/transaction-response.js";
|
|
8
|
+
import { copyAtDoor, refuseCallerData } from "./caller-data.door.js";
|
|
9
|
+
import { createReporter, } from "./diagnostics-reporter.js";
|
|
10
|
+
import { AdapterBoundary, sanitizeOperationFailure } from "./error-boundary.js";
|
|
11
|
+
import { thrownFailure } from "./framework-error.factory.js";
|
|
12
|
+
import { operationFailure } from "./operation-response.js";
|
|
13
|
+
import { mapValidationOutcome } from "./outcome-mapper.js";
|
|
14
|
+
import { readonlyView } from "./readonly-value.js";
|
|
15
|
+
const COMPUTE_STATE = "Compute's ctx.$state";
|
|
16
|
+
const STATE = Symbol("aventara.context");
|
|
17
|
+
const READ_FAMILY = "find";
|
|
18
|
+
function readOnlyRefusal(member) {
|
|
19
|
+
return new TypeError(`Compute's ctx is read-only: ${member} is not available in a compute stage. Put effects on other rows in an after hook.`);
|
|
20
|
+
}
|
|
21
|
+
function refusedStatement(issues) {
|
|
22
|
+
return mapValidationOutcome(issues);
|
|
23
|
+
}
|
|
24
|
+
function unavailable(message) {
|
|
25
|
+
return operationFailure("A2002", {
|
|
26
|
+
message,
|
|
27
|
+
issues: [validationIssue("V1006", [], message)],
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function createContextExecutor(dependencies) {
|
|
31
|
+
const { adapter, misuse } = dependencies;
|
|
32
|
+
const model = dependencies.model;
|
|
33
|
+
const stateOf = (context) => context[STATE];
|
|
34
|
+
const reporterOf = (scope) => createReporter(dependencies.diagnostics, {
|
|
35
|
+
kind: "transaction",
|
|
36
|
+
origin: "application",
|
|
37
|
+
requestId: scope.requestId,
|
|
38
|
+
});
|
|
39
|
+
const call = async (scope, resource, identity, args) => {
|
|
40
|
+
const response = await dependencies.run({
|
|
41
|
+
...identity,
|
|
42
|
+
origin: "application",
|
|
43
|
+
resource,
|
|
44
|
+
requestId: scope.requestId,
|
|
45
|
+
state: scope.state,
|
|
46
|
+
arguments: args === undefined ? {} : args,
|
|
47
|
+
}, scope.transaction);
|
|
48
|
+
if (response.cause !== null) {
|
|
49
|
+
throw thrownFailure(response);
|
|
50
|
+
}
|
|
51
|
+
return response.data;
|
|
52
|
+
};
|
|
53
|
+
const resourceTree = (scope, resource, readOnly) => {
|
|
54
|
+
const families = {};
|
|
55
|
+
const operations = model.resources[resource]?.operations ?? {};
|
|
56
|
+
for (const [family, variants] of Object.entries(operations)) {
|
|
57
|
+
if (readOnly && family !== READ_FAMILY) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const leaves = {};
|
|
61
|
+
for (const [variant, availability] of Object.entries(variants ?? {})) {
|
|
62
|
+
const identity = parseOperationIdentity(family, variant);
|
|
63
|
+
if (identity !== undefined &&
|
|
64
|
+
isOperationVariantAvailable(availability)) {
|
|
65
|
+
leaves[variant] = (args) => call(scope, resource, identity, args);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
families[family] = Object.freeze(leaves);
|
|
69
|
+
}
|
|
70
|
+
if (readOnly) {
|
|
71
|
+
const tree = Object.create(Object.prototype);
|
|
72
|
+
for (const [family, leaves] of Object.entries(families)) {
|
|
73
|
+
Object.defineProperty(tree, family, {
|
|
74
|
+
value: leaves,
|
|
75
|
+
enumerable: true,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
for (const family of Object.keys(operations)) {
|
|
79
|
+
if (family === READ_FAMILY) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
Object.defineProperty(tree, family, {
|
|
83
|
+
get() {
|
|
84
|
+
throw readOnlyRefusal(`ctx.${resource}.${family}`);
|
|
85
|
+
},
|
|
86
|
+
enumerable: false,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return Object.freeze(tree);
|
|
90
|
+
}
|
|
91
|
+
return Object.freeze(families);
|
|
92
|
+
};
|
|
93
|
+
const statementOf = (strings, values) => {
|
|
94
|
+
if (adapter.sqlStatements !== true) {
|
|
95
|
+
return {
|
|
96
|
+
ok: false,
|
|
97
|
+
response: unavailable("SQL statements are not available on this Adapter."),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const text = readSqlTemplate(strings, values.length);
|
|
101
|
+
if (text === undefined) {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
response: refusedStatement([
|
|
105
|
+
validationIssue("V1001", ["strings"], "SQL must be written as a tagged template: ctx.$sql`…`."),
|
|
106
|
+
]),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const copy = copyAtDoor([...values]);
|
|
110
|
+
if (!copy.ok) {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
response: refusedStatement([
|
|
114
|
+
refuseCallerData(copy.error, ["values"], { origin: "application" }, misuse),
|
|
115
|
+
]),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const bound = [];
|
|
119
|
+
const issues = [];
|
|
120
|
+
for (const [position, value] of copy.value.entries()) {
|
|
121
|
+
const normalized = normalizeSqlValue(value, ["values", position]);
|
|
122
|
+
if (normalized.ok) {
|
|
123
|
+
bound.push(normalized.value);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
issues.push(...normalized.issues);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (issues.length > 0) {
|
|
130
|
+
return { ok: false, response: refusedStatement(issues) };
|
|
131
|
+
}
|
|
132
|
+
return { ok: true, statement: { strings: text, values: bound } };
|
|
133
|
+
};
|
|
134
|
+
const executeStatement = async (runner, inTransaction, statement, report) => {
|
|
135
|
+
if (inTransaction && typeof runner.executeSql !== "function") {
|
|
136
|
+
const error = new AvTransactionIntegrityError("statement-unavailable");
|
|
137
|
+
report(error, "A3002");
|
|
138
|
+
throw thrownFailure(transactionInfrastructureFailure());
|
|
139
|
+
}
|
|
140
|
+
const boundary = new AdapterBoundary();
|
|
141
|
+
try {
|
|
142
|
+
return await boundary.execute(async () => {
|
|
143
|
+
if (typeof runner.executeSql !== "function") {
|
|
144
|
+
throw new TypeError("Adapter declares sqlStatements but has no executeSql.");
|
|
145
|
+
}
|
|
146
|
+
const rows = await runner.executeSql(statement);
|
|
147
|
+
if (!Array.isArray(rows)) {
|
|
148
|
+
throw new TypeError("Adapter executeSql must resolve the statement's rows as an array.");
|
|
149
|
+
}
|
|
150
|
+
return [...rows];
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
throw thrownFailure(sanitizeOperationFailure(error, boundary.originOf(error), report, {
|
|
155
|
+
path: ["values"],
|
|
156
|
+
arguments: statement.values,
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const openTransaction = async (scope, work) => {
|
|
161
|
+
if (typeof adapter.transaction !== "function") {
|
|
162
|
+
throw thrownFailure(unavailable("Transactions are not available on this Adapter."));
|
|
163
|
+
}
|
|
164
|
+
let thrown;
|
|
165
|
+
try {
|
|
166
|
+
return await adapter.transaction(async (executor) => {
|
|
167
|
+
try {
|
|
168
|
+
return await work(executor);
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
thrown = { error };
|
|
172
|
+
throw error;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
if (thrown !== undefined && error === thrown.error) {
|
|
178
|
+
throw error;
|
|
179
|
+
}
|
|
180
|
+
reporterOf(scope)(error, "A3002");
|
|
181
|
+
throw thrownFailure(transactionInfrastructureFailure());
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
const sql = async (scope, strings, values) => {
|
|
185
|
+
const prepared = statementOf(strings, values);
|
|
186
|
+
if (!prepared.ok) {
|
|
187
|
+
throw thrownFailure(prepared.response);
|
|
188
|
+
}
|
|
189
|
+
const report = reporterOf(scope);
|
|
190
|
+
const open = scope.transaction;
|
|
191
|
+
return open !== undefined
|
|
192
|
+
? executeStatement(open, true, prepared.statement, report)
|
|
193
|
+
: executeStatement(adapter, false, prepared.statement, report);
|
|
194
|
+
};
|
|
195
|
+
const contextOf = (prototype, scope, facts, readOnly = false) => {
|
|
196
|
+
const context = Object.create(prototype);
|
|
197
|
+
Object.defineProperty(context, STATE, {
|
|
198
|
+
value: { scope, facts, resources: new Map() },
|
|
199
|
+
});
|
|
200
|
+
for (const [key, value] of Object.entries(facts)) {
|
|
201
|
+
if (value !== undefined) {
|
|
202
|
+
Object.defineProperty(context, key, { value, enumerable: true });
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
Object.defineProperty(context, "$state", {
|
|
206
|
+
value: readOnly ? readonlyView(scope.state, COMPUTE_STATE) : scope.state,
|
|
207
|
+
enumerable: true,
|
|
208
|
+
});
|
|
209
|
+
return context;
|
|
210
|
+
};
|
|
211
|
+
const transactionOf = (prototype) => (context, work) => {
|
|
212
|
+
const { scope, facts } = stateOf(context);
|
|
213
|
+
if (typeof work !== "function") {
|
|
214
|
+
return Promise.reject(new TypeError("ctx.$transaction takes an async callback: (tx) => …"));
|
|
215
|
+
}
|
|
216
|
+
const run = work;
|
|
217
|
+
if (scope.transaction !== undefined) {
|
|
218
|
+
return (async () => run(context))();
|
|
219
|
+
}
|
|
220
|
+
if (dependencies.application.transactions === "none") {
|
|
221
|
+
return Promise.reject(thrownFailure(unavailable("Transactions are not available on the application Contract.")));
|
|
222
|
+
}
|
|
223
|
+
return openTransaction(scope, (executor) => run(contextOf(prototype, {
|
|
224
|
+
requestId: scope.requestId,
|
|
225
|
+
transaction: executor,
|
|
226
|
+
state: scope.state,
|
|
227
|
+
}, facts)));
|
|
228
|
+
};
|
|
229
|
+
const prototypeOf = (readOnly) => {
|
|
230
|
+
const prototype = {};
|
|
231
|
+
for (const resource of Object.keys(model.resources)) {
|
|
232
|
+
Object.defineProperty(prototype, resource, {
|
|
233
|
+
get() {
|
|
234
|
+
const state = stateOf(this);
|
|
235
|
+
let tree = state.resources.get(resource);
|
|
236
|
+
if (tree === undefined) {
|
|
237
|
+
tree = resourceTree(state.scope, resource, readOnly);
|
|
238
|
+
state.resources.set(resource, tree);
|
|
239
|
+
}
|
|
240
|
+
return tree;
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
if (readOnly) {
|
|
245
|
+
for (const member of ["$sql", "$transaction"]) {
|
|
246
|
+
Object.defineProperty(prototype, member, {
|
|
247
|
+
get() {
|
|
248
|
+
throw readOnlyRefusal(`ctx.${member}`);
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return Object.freeze(prototype);
|
|
253
|
+
}
|
|
254
|
+
const transaction = transactionOf(prototype);
|
|
255
|
+
Object.defineProperty(prototype, "$sql", {
|
|
256
|
+
get() {
|
|
257
|
+
const { scope } = stateOf(this);
|
|
258
|
+
return (strings, ...values) => sql(scope, strings, values);
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
Object.defineProperty(prototype, "$transaction", {
|
|
262
|
+
get() {
|
|
263
|
+
return (work) => transaction(this, work);
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
return Object.freeze(prototype);
|
|
267
|
+
};
|
|
268
|
+
const full = prototypeOf(false);
|
|
269
|
+
const reads = prototypeOf(true);
|
|
270
|
+
return {
|
|
271
|
+
operation: (scope, facts) => contextOf(full, scope, facts),
|
|
272
|
+
transaction: (scope, facts) => contextOf(full, scope, facts),
|
|
273
|
+
compute: (scope, facts) => contextOf(reads, scope, facts, true),
|
|
274
|
+
};
|
|
275
|
+
}
|