@aventara/core 0.1.0-pilot.4 → 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 +1478 -0
- package/README.md +275 -11
- 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 +17 -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 -25
- 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/avz/avz.js +354 -0
- package/dist/avz/index.d.ts +7 -0
- package/dist/avz/index.js +1 -0
- package/dist/avz/json-shape.registry.d.ts +22 -0
- package/dist/avz/json-shape.registry.js +16 -0
- package/dist/avz/zod-issue.translator.d.ts +57 -0
- package/dist/avz/zod-issue.translator.js +149 -0
- package/dist/compiler/contract-compiler-diagnostic.d.ts +37 -5
- package/dist/compiler/contract.compiler.d.ts +18 -18
- package/dist/compiler/contract.compiler.js +595 -421
- 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 +75 -29
- package/dist/config/config.resolver.js +109 -44
- package/dist/config/config.validator.d.ts +7 -7
- package/dist/config/config.validator.js +586 -71
- 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 +111 -37
- 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 +344 -29
- package/dist/contracts/client-contract-structure.validator.js +248 -7
- package/dist/contracts/contract-canonicalizer.d.ts +19 -3
- package/dist/contracts/contract-canonicalizer.js +172 -27
- 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 +31 -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 +61 -41
- package/dist/contracts/identifier.d.ts +4 -4
- package/dist/contracts/json-shape.d.ts +76 -0
- package/dist/contracts/json-shape.js +1 -0
- package/dist/contracts/lifecycle-trait.d.ts +2 -2
- 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 +28 -28
- package/dist/contracts/resource.d.ts +15 -9
- package/dist/contracts/scalar-value-type.d.ts +25 -14
- 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 +234 -63
- 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 +50 -24
- package/dist/framework/framework.factory.d.ts +8 -6
- package/dist/framework/framework.factory.js +95 -32
- package/dist/framework/operation-facade.d.ts +123 -52
- package/dist/framework/operation-facade.js +9 -1
- package/dist/index.d.ts +55 -82
- package/dist/index.js +6 -26
- package/dist/internal.d.ts +40 -0
- package/dist/internal.js +18 -0
- package/dist/operations/operation-arguments.d.ts +316 -201
- package/dist/operations/operation-call.d.ts +27 -28
- package/dist/operations/operation-identity.d.ts +12 -10
- package/dist/operations/operation-identity.js +3 -0
- 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 +41 -12
- package/dist/operations/operation-validator.d.ts +24 -9
- package/dist/operations/operation-validator.js +26 -10
- package/dist/operations/projection-scope.resolver.d.ts +7 -7
- package/dist/operations/structural-limits.walker.d.ts +19 -0
- package/dist/operations/structural-limits.walker.js +489 -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 -11
- package/dist/protocol/absent-route.responder.js +42 -16
- 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 +23 -13
- package/dist/protocol/http-request.decoder.js +19 -11
- 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 +52 -27
- package/dist/protocol/protocol.binder.js +14 -9
- package/dist/protocol/protocol.handler.d.ts +28 -15
- package/dist/protocol/protocol.handler.js +55 -10
- 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 +67 -0
- package/dist/runtime/computed-read-runtime.d.ts +7 -6
- package/dist/runtime/computed-read-runtime.js +61 -55
- package/dist/runtime/computed-value.error.d.ts +13 -0
- package/dist/runtime/computed-value.error.js +9 -0
- 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 +22 -57
- package/dist/runtime/create-requirement.js +140 -37
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +1 -1
- package/dist/runtime/diagnostics-reporter.d.ts +64 -22
- package/dist/runtime/diagnostics-reporter.js +35 -4
- package/dist/runtime/error-boundary.d.ts +73 -8
- package/dist/runtime/error-boundary.js +104 -48
- 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 +79 -7
- 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 +32 -0
- package/dist/runtime/misuse-reporter.js +19 -0
- package/dist/runtime/mutation-planner.d.ts +19 -10
- package/dist/runtime/mutation-planner.js +62 -36
- 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 +35 -15
- package/dist/runtime/operation-response.d.ts +17 -17
- package/dist/runtime/operation-runtime.d.ts +125 -30
- package/dist/runtime/operation-runtime.js +423 -83
- package/dist/runtime/outcome-mapper.d.ts +16 -5
- package/dist/runtime/outcome-mapper.js +35 -8
- package/dist/runtime/output-validator.d.ts +16 -6
- package/dist/runtime/output-validator.js +12 -1
- package/dist/runtime/owned-execution.registry.d.ts +13 -0
- package/dist/runtime/owned-execution.registry.js +7 -0
- 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 +202 -28
- package/dist/runtime/readonly-value.d.ts +32 -0
- package/dist/runtime/readonly-value.js +141 -0
- package/dist/runtime/referential-action.d.ts +7 -0
- package/dist/runtime/request-envelope.validator.d.ts +5 -0
- package/dist/runtime/request-envelope.validator.js +123 -0
- 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 +56 -13
- package/dist/runtime/scalar-runtime.js +167 -86
- 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 +23 -9
- package/dist/transactions/transaction-plan-assembler.js +195 -82
- package/dist/transactions/transaction-plan-validator.d.ts +53 -21
- package/dist/transactions/transaction-plan-validator.js +246 -163
- 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 +19 -2
- package/dist/transactions/transaction-reference-resolver.js +27 -10
- package/dist/transactions/transaction-reference-validator.d.ts +34 -4
- package/dist/transactions/transaction-reference-validator.js +82 -43
- package/dist/transactions/transaction-reference.d.ts +30 -58
- package/dist/transactions/transaction-request.d.ts +20 -7
- package/dist/transactions/transaction-response.d.ts +12 -7
- package/dist/transactions/transaction-response.js +24 -1
- package/dist/transactions/transaction-runtime.d.ts +11 -8
- package/dist/transactions/transaction-runtime.js +167 -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 +18 -3
- package/src/runtime/field-check.evaluator.ts +720 -0
- package/dist/config/define-fields.d.ts +0 -35
- 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 -36
- package/dist/config/framework-restrictions-config.d.ts +0 -142
- 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
- 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/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,17 +1,14 @@
|
|
|
1
1
|
import { lookupField, lookupResource } from "../contracts/contract-lookup.js";
|
|
2
|
-
import {
|
|
3
|
-
import { INTERNAL_FAILURE_MESSAGE } from "./operation-response.js";
|
|
2
|
+
import { AvComputedValueError } from "./computed-value.error.js";
|
|
4
3
|
import { normalizeScalarFieldValue } from "./scalar-runtime.js";
|
|
5
4
|
function isRecord(value) {
|
|
6
5
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7
6
|
}
|
|
8
|
-
function
|
|
7
|
+
function readCompute(value) {
|
|
9
8
|
if (typeof value === "function") {
|
|
10
9
|
return { compute: value, dependsOn: [] };
|
|
11
10
|
}
|
|
12
|
-
if (value === undefined ||
|
|
13
|
-
value === false ||
|
|
14
|
-
typeof value.compute !== "function") {
|
|
11
|
+
if (value === undefined || typeof value.compute !== "function") {
|
|
15
12
|
return undefined;
|
|
16
13
|
}
|
|
17
14
|
return {
|
|
@@ -19,6 +16,16 @@ function readBehavior(value) {
|
|
|
19
16
|
dependsOn: value.dependsOn ?? [],
|
|
20
17
|
};
|
|
21
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
|
+
}
|
|
22
29
|
function appendUnique(values, value) {
|
|
23
30
|
if (!values.includes(value)) {
|
|
24
31
|
values.push(value);
|
|
@@ -35,18 +42,17 @@ function supportsProjection(request) {
|
|
|
35
42
|
}
|
|
36
43
|
return request.variant !== "count";
|
|
37
44
|
}
|
|
38
|
-
function addScalarProjection(core, active,
|
|
45
|
+
function addScalarProjection(core, model, active, compute, resourceName, fieldName, adapterSelect, requested, computed) {
|
|
39
46
|
const activeField = lookupField(lookupResource(active, resourceName), fieldName);
|
|
40
47
|
if (activeField?.kind !== "scalar") {
|
|
41
48
|
return;
|
|
42
49
|
}
|
|
43
50
|
appendUnique(requested, fieldName);
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
51
|
+
const stage = readCompute(compute?.[resourceName]?.[fieldName]?.read);
|
|
52
|
+
if (stage !== undefined) {
|
|
46
53
|
appendUnique(computed, fieldName);
|
|
47
|
-
for (const dependency of
|
|
48
|
-
|
|
49
|
-
if (dependencyField?.kind === "scalar") {
|
|
54
|
+
for (const dependency of stage.dependsOn) {
|
|
55
|
+
if (storedScalar(model, resourceName, dependency)) {
|
|
50
56
|
appendUniqueProjection(adapterSelect, dependency);
|
|
51
57
|
}
|
|
52
58
|
}
|
|
@@ -71,7 +77,7 @@ function projectionEntry(entry) {
|
|
|
71
77
|
function withoutProjectionChoice(value) {
|
|
72
78
|
return Object.fromEntries(Object.entries(value).filter(([key]) => key !== "select" && key !== "include"));
|
|
73
79
|
}
|
|
74
|
-
function defaultProjectionLevel(core, active,
|
|
80
|
+
function defaultProjectionLevel(core, model, active, compute, resourceName) {
|
|
75
81
|
const activeResource = lookupResource(active, resourceName);
|
|
76
82
|
const adapterSelect = [];
|
|
77
83
|
const requested = [];
|
|
@@ -81,7 +87,7 @@ function defaultProjectionLevel(core, active, behaviors, resourceName) {
|
|
|
81
87
|
if (field.kind !== "scalar" || field.capabilities.select !== true) {
|
|
82
88
|
continue;
|
|
83
89
|
}
|
|
84
|
-
addScalarProjection(core, active,
|
|
90
|
+
addScalarProjection(core, model, active, compute, resourceName, fieldName, adapterSelect, requested, computed);
|
|
85
91
|
}
|
|
86
92
|
}
|
|
87
93
|
return {
|
|
@@ -95,25 +101,25 @@ function defaultProjectionLevel(core, active, behaviors, resourceName) {
|
|
|
95
101
|
},
|
|
96
102
|
};
|
|
97
103
|
}
|
|
98
|
-
function planRelationProjection(core, active,
|
|
104
|
+
function planRelationProjection(core, model, active, compute, field, value) {
|
|
99
105
|
if (!isRecord(value)) {
|
|
100
|
-
return defaultProjectionLevel(core, active,
|
|
106
|
+
return defaultProjectionLevel(core, model, active, compute, field.target);
|
|
101
107
|
}
|
|
102
108
|
if (Array.isArray(value.select)) {
|
|
103
|
-
return planSelectLevel(core, active,
|
|
109
|
+
return planSelectLevel(core, model, active, compute, field.target, value.select, true);
|
|
104
110
|
}
|
|
105
111
|
if (Array.isArray(value.include)) {
|
|
106
|
-
return planIncludeLevel(core, active,
|
|
112
|
+
return planIncludeLevel(core, model, active, compute, field.target, value.include);
|
|
107
113
|
}
|
|
108
|
-
return defaultProjectionLevel(core, active,
|
|
114
|
+
return defaultProjectionLevel(core, model, active, compute, field.target);
|
|
109
115
|
}
|
|
110
|
-
function planRelationEntry(core, active,
|
|
116
|
+
function planRelationEntry(core, model, active, compute, resourceName, fieldName, value) {
|
|
111
117
|
const activeField = lookupField(lookupResource(active, resourceName), fieldName);
|
|
112
118
|
const coreField = lookupField(lookupResource(core, resourceName), fieldName);
|
|
113
119
|
if (activeField?.kind !== "relation" || coreField?.kind !== "relation") {
|
|
114
120
|
return undefined;
|
|
115
121
|
}
|
|
116
|
-
const nested = planRelationProjection(core, active,
|
|
122
|
+
const nested = planRelationProjection(core, model, active, compute, coreField, value);
|
|
117
123
|
return {
|
|
118
124
|
adapterEntry: {
|
|
119
125
|
[fieldName]: {
|
|
@@ -128,7 +134,7 @@ function planRelationEntry(core, active, behaviors, resourceName, fieldName, val
|
|
|
128
134
|
},
|
|
129
135
|
};
|
|
130
136
|
}
|
|
131
|
-
function planSelectLevel(core, active,
|
|
137
|
+
function planSelectLevel(core, model, active, compute, resourceName, select, collectReducers = false) {
|
|
132
138
|
const activeResource = lookupResource(active, resourceName);
|
|
133
139
|
const coreResource = lookupResource(core, resourceName);
|
|
134
140
|
const adapterSelect = [];
|
|
@@ -160,11 +166,11 @@ function planSelectLevel(core, active, behaviors, resourceName, select, collectR
|
|
|
160
166
|
const activeField = lookupField(activeResource, entry);
|
|
161
167
|
const coreField = lookupField(coreResource, entry);
|
|
162
168
|
if (activeField?.kind === "scalar") {
|
|
163
|
-
addScalarProjection(core, active,
|
|
169
|
+
addScalarProjection(core, model, active, compute, resourceName, entry, adapterSelect, requested, computed);
|
|
164
170
|
continue;
|
|
165
171
|
}
|
|
166
172
|
if (activeField?.kind === "relation" && coreField?.kind === "relation") {
|
|
167
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
173
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, entry, undefined);
|
|
168
174
|
if (plannedRelation !== undefined) {
|
|
169
175
|
appendUnique(requested, entry);
|
|
170
176
|
adapterSelect.push(plannedRelation.adapterEntry);
|
|
@@ -181,7 +187,7 @@ function planSelectLevel(core, active, behaviors, resourceName, select, collectR
|
|
|
181
187
|
continue;
|
|
182
188
|
}
|
|
183
189
|
const [fieldName, nestedValue] = relationEntry;
|
|
184
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
190
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, fieldName, nestedValue);
|
|
185
191
|
if (plannedRelation === undefined) {
|
|
186
192
|
adapterSelect.push(entry);
|
|
187
193
|
continue;
|
|
@@ -195,15 +201,15 @@ function planSelectLevel(core, active, behaviors, resourceName, select, collectR
|
|
|
195
201
|
plan: { resource: resourceName, requested, computed, relations, reducers },
|
|
196
202
|
};
|
|
197
203
|
}
|
|
198
|
-
function planIncludeLevel(core, active,
|
|
199
|
-
const defaults = defaultProjectionLevel(core, active,
|
|
204
|
+
function planIncludeLevel(core, model, active, compute, resourceName, include) {
|
|
205
|
+
const defaults = defaultProjectionLevel(core, model, active, compute, resourceName);
|
|
200
206
|
const adapterSelect = [...defaults.select];
|
|
201
207
|
const requested = [...defaults.plan.requested];
|
|
202
208
|
const computed = [...defaults.plan.computed];
|
|
203
209
|
const relations = [];
|
|
204
210
|
for (const entry of include) {
|
|
205
211
|
if (typeof entry === "string") {
|
|
206
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
212
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, entry, undefined);
|
|
207
213
|
if (plannedRelation === undefined) {
|
|
208
214
|
continue;
|
|
209
215
|
}
|
|
@@ -217,7 +223,7 @@ function planIncludeLevel(core, active, behaviors, resourceName, include) {
|
|
|
217
223
|
continue;
|
|
218
224
|
}
|
|
219
225
|
const [fieldName, nestedValue] = relationEntry;
|
|
220
|
-
const plannedRelation = planRelationEntry(core, active,
|
|
226
|
+
const plannedRelation = planRelationEntry(core, model, active, compute, resourceName, fieldName, nestedValue);
|
|
221
227
|
if (plannedRelation === undefined) {
|
|
222
228
|
continue;
|
|
223
229
|
}
|
|
@@ -242,7 +248,7 @@ function replaceWithInternalSelect(args, select) {
|
|
|
242
248
|
internal.select = select;
|
|
243
249
|
return internal;
|
|
244
250
|
}
|
|
245
|
-
export function planReadOperation(core, active,
|
|
251
|
+
export function planReadOperation(core, model, active, compute, request, args) {
|
|
246
252
|
if (!isRecord(args) || !supportsProjection(request)) {
|
|
247
253
|
return { arguments: args, projection: undefined, materialize: false };
|
|
248
254
|
}
|
|
@@ -252,7 +258,7 @@ export function planReadOperation(core, active, behaviors, request, args) {
|
|
|
252
258
|
return { arguments: args, projection: undefined, materialize: false };
|
|
253
259
|
}
|
|
254
260
|
if (Array.isArray(args.select)) {
|
|
255
|
-
const planned = planSelectLevel(core, active,
|
|
261
|
+
const planned = planSelectLevel(core, model, active, compute, request.resource, args.select);
|
|
256
262
|
return {
|
|
257
263
|
arguments: replaceWithInternalSelect(args, planned.select),
|
|
258
264
|
projection: planned.plan,
|
|
@@ -260,27 +266,21 @@ export function planReadOperation(core, active, behaviors, request, args) {
|
|
|
260
266
|
};
|
|
261
267
|
}
|
|
262
268
|
if (Array.isArray(args.include)) {
|
|
263
|
-
const planned = planIncludeLevel(core, active,
|
|
269
|
+
const planned = planIncludeLevel(core, model, active, compute, request.resource, args.include);
|
|
264
270
|
return {
|
|
265
271
|
arguments: replaceWithInternalSelect(args, planned.select),
|
|
266
272
|
projection: planned.plan,
|
|
267
273
|
materialize: true,
|
|
268
274
|
};
|
|
269
275
|
}
|
|
270
|
-
const defaults = defaultProjectionLevel(core, active,
|
|
276
|
+
const defaults = defaultProjectionLevel(core, model, active, compute, request.resource);
|
|
271
277
|
return {
|
|
272
278
|
arguments: { ...args, select: defaults.select },
|
|
273
279
|
projection: defaults.plan,
|
|
274
280
|
materialize: defaults.plan.computed.length > 0,
|
|
275
281
|
};
|
|
276
282
|
}
|
|
277
|
-
function
|
|
278
|
-
throw new FrameworkError("A3000", {
|
|
279
|
-
message: INTERNAL_FAILURE_MESSAGE,
|
|
280
|
-
issues,
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
async function materializeRelation(active, behaviors, request, relation, value, contextFactory) {
|
|
283
|
+
async function materializeRelation(active, compute, request, relation, value, contextFactory) {
|
|
284
284
|
if (value === null || value === undefined) {
|
|
285
285
|
return value;
|
|
286
286
|
}
|
|
@@ -288,11 +288,11 @@ async function materializeRelation(active, behaviors, request, relation, value,
|
|
|
288
288
|
if (!Array.isArray(value)) {
|
|
289
289
|
return value;
|
|
290
290
|
}
|
|
291
|
-
return Promise.all(value.map((entry) => materializeRecord(active,
|
|
291
|
+
return Promise.all(value.map((entry) => materializeRecord(active, compute, request, relation.projection, entry, contextFactory)));
|
|
292
292
|
}
|
|
293
|
-
return materializeRecord(active,
|
|
293
|
+
return materializeRecord(active, compute, request, relation.projection, value, contextFactory);
|
|
294
294
|
}
|
|
295
|
-
async function materializeRecord(active,
|
|
295
|
+
async function materializeRecord(active, compute, request, plan, value, contextFactory) {
|
|
296
296
|
if (!isRecord(value)) {
|
|
297
297
|
return value;
|
|
298
298
|
}
|
|
@@ -303,24 +303,30 @@ async function materializeRecord(active, behaviors, request, plan, value, contex
|
|
|
303
303
|
const record = { ...value };
|
|
304
304
|
for (const fieldName of plan.computed) {
|
|
305
305
|
const field = lookupField(resource, fieldName);
|
|
306
|
-
const
|
|
307
|
-
if (field?.kind !== "scalar" ||
|
|
306
|
+
const stage = readCompute(compute?.[plan.resource]?.[fieldName]?.read);
|
|
307
|
+
if (field?.kind !== "scalar" || stage === undefined) {
|
|
308
308
|
continue;
|
|
309
309
|
}
|
|
310
|
-
const computedValue = await
|
|
311
|
-
scope: request.scope,
|
|
312
|
-
resource: plan.resource,
|
|
310
|
+
const computedValue = await stage.compute({
|
|
313
311
|
field: fieldName,
|
|
314
312
|
value: record[fieldName],
|
|
315
313
|
record,
|
|
316
|
-
|
|
314
|
+
ctx: contextFactory(),
|
|
317
315
|
});
|
|
318
316
|
if (computedValue === undefined) {
|
|
319
317
|
continue;
|
|
320
318
|
}
|
|
321
|
-
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
|
+
]);
|
|
322
328
|
if (!validation.ok) {
|
|
323
|
-
|
|
329
|
+
throw new AvComputedValueError(plan.resource, fieldName, "read", validation.issues);
|
|
324
330
|
}
|
|
325
331
|
record[fieldName] = validation.value;
|
|
326
332
|
}
|
|
@@ -328,19 +334,19 @@ async function materializeRecord(active, behaviors, request, plan, value, contex
|
|
|
328
334
|
if (!Object.hasOwn(record, relation.field)) {
|
|
329
335
|
continue;
|
|
330
336
|
}
|
|
331
|
-
record[relation.field] = await materializeRelation(active,
|
|
337
|
+
record[relation.field] = await materializeRelation(active, compute, request, relation, record[relation.field], contextFactory);
|
|
332
338
|
}
|
|
333
339
|
return Object.fromEntries(plan.requested
|
|
334
340
|
.filter((fieldName) => Object.hasOwn(record, fieldName))
|
|
335
341
|
.map((fieldName) => [fieldName, record[fieldName]]));
|
|
336
342
|
}
|
|
337
|
-
export async function materializeReadOperation(active,
|
|
343
|
+
export async function materializeReadOperation(active, compute, request, plan, value, contextFactory) {
|
|
338
344
|
const projection = plan.projection;
|
|
339
345
|
if (!plan.materialize || projection === undefined) {
|
|
340
346
|
return value;
|
|
341
347
|
}
|
|
342
348
|
if (Array.isArray(value)) {
|
|
343
|
-
return Promise.all(value.map((entry) => materializeRecord(active,
|
|
349
|
+
return Promise.all(value.map((entry) => materializeRecord(active, compute, request, projection, entry, contextFactory)));
|
|
344
350
|
}
|
|
345
|
-
return materializeRecord(active,
|
|
351
|
+
return materializeRecord(active, compute, request, projection, value, contextFactory);
|
|
346
352
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AvValidationIssue } from "../operations/validation-code.js";
|
|
2
|
+
/**
|
|
3
|
+
* What the diagnostics channel receives when a computed value — a read or write
|
|
4
|
+
* compute stage's return — is one its Contract refuses: the developer's broken code,
|
|
5
|
+
* never the caller's. It is thrown, not a deliberate outcome, so the framework's
|
|
6
|
+
* boundary answers the caller the generic `A3000` and reports this error, with the
|
|
7
|
+
* issues, exactly once.
|
|
8
|
+
*/
|
|
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
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export class AvComputedValueError extends Error {
|
|
2
|
+
issues;
|
|
3
|
+
name = "AvComputedValueError";
|
|
4
|
+
constructor(resource, field, stage, issues) {
|
|
5
|
+
const [first] = issues;
|
|
6
|
+
super(`The compute.${stage} stage of ${resource}.${field} returned a value its Contract refuses${first === undefined ? "." : ` (${first.code}: ${first.message})`}`);
|
|
7
|
+
this.issues = issues;
|
|
8
|
+
}
|
|
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
|
+
}
|