@aventara/core 1.0.0-rc.1 → 1.0.0-rc.11
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 +1022 -16
- package/ERRORS.md +142 -0
- package/README.md +330 -16
- package/dist/adapters/adapter-discovery-diagnostic.guidance.d.ts +6 -0
- package/dist/adapters/adapter-discovery-diagnostic.guidance.js +7 -0
- package/dist/adapters/adapter-field.d.ts +22 -0
- package/dist/adapters/adapter-identifier.d.ts +9 -0
- package/dist/adapters/adapter-identifier.js +6 -1
- package/dist/adapters/adapter-model.d.ts +9 -0
- package/dist/adapters/adapter-operation.d.ts +18 -3
- package/dist/adapters/adapter-resource.d.ts +5 -0
- package/dist/adapters/adapter.interface.d.ts +10 -0
- package/dist/avz/avz.d.ts +1 -1
- package/dist/avz/json-shape.registry.d.ts +2 -1
- package/dist/compiler/contract-compiler-diagnostic.guidance.d.ts +6 -0
- package/dist/compiler/contract-compiler-diagnostic.guidance.js +97 -0
- package/dist/compiler/contract.compiler.d.ts +6 -0
- package/dist/compiler/contract.compiler.js +281 -31
- package/dist/compiler/create-requirements.projector.js +1 -3
- package/dist/compiler/field-validator.compiler.js +1 -3
- package/dist/compiler/managed-elsewhere.projector.d.ts +52 -0
- package/dist/compiler/managed-elsewhere.projector.js +179 -0
- package/dist/compiler/managed-elsewhere.validator.d.ts +9 -0
- package/dist/compiler/managed-elsewhere.validator.js +74 -0
- package/dist/config/cache-stage.factory.d.ts +24 -0
- package/dist/config/cache-stage.factory.js +3 -0
- package/dist/config/computed-fields.d.ts +62 -0
- package/dist/config/computed-fields.js +73 -0
- package/dist/config/config-diagnostic.guidance.d.ts +6 -0
- package/dist/config/config-diagnostic.guidance.js +32 -0
- package/dist/config/config.resolver.d.ts +24 -0
- package/dist/config/config.resolver.js +66 -7
- package/dist/config/config.validator.js +283 -21
- package/dist/config/define-resource.d.ts +1 -5
- package/dist/config/entrypoint.normalizer.d.ts +3 -2
- package/dist/config/exact-framework-config.d.ts +73 -22
- package/dist/config/field-compute.resolver.d.ts +2 -0
- package/dist/config/field-compute.resolver.js +9 -3
- package/dist/config/framework-config.d.ts +53 -15
- package/dist/config/framework-resources-config.d.ts +25 -14
- package/dist/config/pipeline-config.d.ts +20 -4
- package/dist/config/pipeline-tree.resolver.d.ts +2 -2
- package/dist/config/pipeline-tree.resolver.js +2 -3
- package/dist/contracts/access-route.rules.d.ts +10 -0
- package/dist/contracts/access-route.rules.js +45 -0
- package/dist/contracts/canonical-json.writer.d.ts +7 -0
- package/dist/contracts/canonical-json.writer.js +55 -0
- package/dist/contracts/capability.d.ts +26 -1
- package/dist/contracts/capability.js +20 -0
- package/dist/contracts/client-contract-structure.validator.d.ts +365 -89
- package/dist/contracts/client-contract-structure.validator.js +49 -2
- package/dist/contracts/contract-access.d.ts +29 -0
- package/dist/contracts/contract-access.js +1 -0
- package/dist/contracts/contract-canonicalizer.d.ts +9 -1
- package/dist/contracts/contract-canonicalizer.js +35 -4
- package/dist/contracts/contract-hash.d.ts +0 -1
- package/dist/contracts/contract-hash.js +1 -1
- package/dist/contracts/contract-limits.d.ts +6 -0
- package/dist/contracts/contract-limits.js +2 -0
- package/dist/contracts/contract.d.ts +42 -1
- package/dist/contracts/field.d.ts +58 -1
- package/dist/contracts/json-schema.projector.d.ts +11 -0
- package/dist/contracts/json-schema.projector.js +76 -0
- package/dist/contracts/scalar-value-type.d.ts +3 -1
- package/dist/framework/access-facade.d.ts +116 -0
- package/dist/framework/access-facade.js +118 -0
- package/dist/framework/contract-view.d.ts +120 -16
- package/dist/framework/framework-construction.error.d.ts +14 -4
- package/dist/framework/framework-construction.error.js +18 -2
- package/dist/framework/framework.d.ts +26 -5
- package/dist/framework/framework.factory.d.ts +1 -1
- package/dist/framework/framework.factory.js +145 -24
- package/dist/framework/operation-facade.d.ts +141 -13
- package/dist/framework/operation-facade.js +9 -1
- package/dist/framework/operation-set.runner.d.ts +47 -0
- package/dist/framework/operation-set.runner.js +168 -0
- package/dist/index.d.ts +19 -5
- package/dist/index.js +3 -0
- package/dist/internal.d.ts +9 -2
- package/dist/internal.js +8 -0
- package/dist/operations/database-refusal.d.ts +51 -0
- package/dist/operations/database-refusal.js +45 -0
- package/dist/operations/operation-arguments.d.ts +148 -60
- package/dist/operations/operation-call.d.ts +9 -5
- package/dist/operations/operation-result.d.ts +6 -4
- package/dist/operations/operation-validator-core.js +160 -39
- package/dist/operations/operation-validator.d.ts +1 -1
- package/dist/operations/structural-limits.walker.d.ts +1 -1
- package/dist/operations/structural-limits.walker.js +1 -3
- package/dist/operations/validation-code.guidance.d.ts +6 -0
- package/dist/operations/validation-code.guidance.js +90 -0
- package/dist/operations/write-scope.resolver.d.ts +4 -0
- package/dist/operations/write-scope.resolver.js +2 -0
- package/dist/policy/access-provider.interface.d.ts +91 -0
- package/dist/policy/access-provider.interface.js +1 -0
- package/dist/policy/access-provider.registry.d.ts +17 -0
- package/dist/policy/access-provider.registry.js +15 -0
- package/dist/policy/access-question.d.ts +20 -0
- package/dist/policy/access-question.js +1 -0
- package/dist/policy/access-question.registry.d.ts +5 -0
- package/dist/policy/access-question.registry.js +19 -0
- package/dist/policy/access-question.runtime.d.ts +36 -0
- package/dist/policy/access-question.runtime.js +295 -0
- package/dist/policy/access-verb.table.d.ts +56 -0
- package/dist/policy/access-verb.table.js +21 -0
- package/dist/policy/access-verbs.d.ts +46 -0
- package/dist/policy/access-verbs.js +1 -0
- package/dist/policy/policy-admission.d.ts +20 -0
- package/dist/policy/policy-admission.js +407 -0
- package/dist/policy/policy-after-update.runtime.d.ts +79 -0
- package/dist/policy/policy-after-update.runtime.js +206 -0
- package/dist/policy/policy-condition.d.ts +15 -0
- package/dist/policy/policy-condition.js +36 -0
- package/dist/policy/policy-engine.d.ts +205 -0
- package/dist/policy/policy-engine.js +1 -0
- package/dist/policy/policy-named-rows.d.ts +18 -0
- package/dist/policy/policy-named-rows.js +51 -0
- package/dist/policy/policy-nested-write.runtime.d.ts +44 -0
- package/dist/policy/policy-nested-write.runtime.js +536 -0
- package/dist/policy/policy-read.runtime.d.ts +81 -0
- package/dist/policy/policy-read.runtime.js +501 -0
- package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
- package/dist/policy/policy-reference-write.runtime.js +336 -0
- package/dist/policy/policy-request.d.ts +72 -0
- package/dist/policy/policy-request.js +115 -0
- package/dist/policy/policy-session.d.ts +106 -0
- package/dist/policy/policy-session.js +293 -0
- package/dist/policy/policy-startup.validator.d.ts +10 -0
- package/dist/policy/policy-startup.validator.js +112 -0
- package/dist/policy/policy-write.runtime.d.ts +39 -0
- package/dist/policy/policy-write.runtime.js +530 -0
- package/dist/prebuilt/core-version.generated.d.ts +2 -0
- package/dist/prebuilt/core-version.generated.js +1 -0
- package/dist/prebuilt/prebuilt-changes.d.ts +29 -0
- package/dist/prebuilt/prebuilt-changes.js +63 -0
- package/dist/prebuilt/prebuilt-contracts.d.ts +40 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.d.ts +16 -0
- package/dist/prebuilt/prebuilt-contracts.emitter.js +50 -0
- package/dist/prebuilt/prebuilt-contracts.js +1 -0
- package/dist/prebuilt/prebuilt-contracts.loader.d.ts +19 -0
- package/dist/prebuilt/prebuilt-contracts.loader.js +75 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +227 -0
- package/dist/protocol/absent-route.responder.d.ts +49 -14
- package/dist/protocol/absent-route.responder.js +92 -15
- package/dist/protocol/contract-document.responder.d.ts +9 -1
- package/dist/protocol/contract-document.responder.js +3 -2
- package/dist/protocol/http-request.decoder.d.ts +52 -3
- package/dist/protocol/http-request.decoder.js +45 -6
- package/dist/protocol/http-request.reader.d.ts +10 -3
- package/dist/protocol/http-request.reader.js +12 -2
- package/dist/protocol/http-response.encoder.d.ts +12 -0
- package/dist/protocol/http-response.encoder.js +17 -3
- package/dist/protocol/operation-response.guard.js +1 -3
- package/dist/protocol/operation-set.decoder.d.ts +48 -0
- package/dist/protocol/operation-set.decoder.js +185 -0
- package/dist/protocol/operation-set.handler.d.ts +27 -0
- package/dist/protocol/operation-set.handler.js +179 -0
- package/dist/protocol/protocol-headers.d.ts +12 -3
- package/dist/protocol/protocol-headers.js +12 -5
- package/dist/protocol/protocol-surface.d.ts +16 -3
- package/dist/protocol/protocol-surface.js +41 -1
- package/dist/protocol/protocol.binder.d.ts +52 -1
- package/dist/protocol/protocol.binder.js +6 -1
- package/dist/protocol/protocol.handler.d.ts +45 -1
- package/dist/protocol/protocol.handler.js +164 -19
- package/dist/protocol/read-cache.responder.d.ts +26 -0
- package/dist/protocol/read-cache.responder.js +47 -0
- package/dist/protocol/read-query.codec.d.ts +34 -0
- package/dist/protocol/read-query.codec.js +125 -0
- package/dist/runtime/caller-data.copier.d.ts +18 -3
- package/dist/runtime/caller-data.copier.js +21 -8
- package/dist/runtime/caller-data.door.d.ts +13 -6
- package/dist/runtime/computed-read-runtime.d.ts +6 -1
- package/dist/runtime/computed-read-runtime.js +229 -48
- package/dist/runtime/computed-value.error.d.ts +3 -2
- package/dist/runtime/computed-value.error.js +3 -2
- package/dist/runtime/context-executor.runtime.d.ts +4 -3
- package/dist/runtime/context-executor.runtime.js +4 -2
- package/dist/runtime/core-class.brand.d.ts +10 -0
- package/dist/runtime/core-class.brand.js +21 -0
- package/dist/runtime/create-requirement.d.ts +31 -5
- package/dist/runtime/create-requirement.js +130 -24
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.d.ts +49 -9
- package/dist/runtime/diagnostics-reporter.js +16 -9
- package/dist/runtime/error-boundary.d.ts +9 -2
- package/dist/runtime/error-boundary.js +12 -0
- package/dist/runtime/error-guidance.d.ts +12 -0
- package/dist/runtime/error-guidance.js +1 -0
- package/dist/runtime/error.catalog.d.ts +49 -0
- package/dist/runtime/error.catalog.js +49 -0
- package/dist/runtime/execution-options.d.ts +8 -0
- package/dist/runtime/field-check.evaluator.d.ts +0 -2
- package/dist/runtime/field-check.evaluator.js +1 -1
- package/dist/runtime/field-compute.d.ts +11 -10
- package/dist/runtime/field-stage.runtime.d.ts +3 -3
- package/dist/runtime/field-stage.runtime.js +9 -11
- package/dist/runtime/field-validator.runtime.d.ts +19 -1
- package/dist/runtime/field-validator.runtime.js +49 -9
- package/dist/runtime/framework-error.js +9 -0
- package/dist/runtime/framework-warning.emitter.d.ts +6 -0
- package/dist/runtime/framework-warning.emitter.js +8 -0
- package/dist/runtime/json-value.d.ts +10 -5
- package/dist/runtime/level-lifecycle.runtime.d.ts +4 -3
- package/dist/runtime/level-lifecycle.runtime.js +3 -5
- package/dist/runtime/mutation-planner.d.ts +1 -6
- package/dist/runtime/mutation-planner.js +4 -17
- package/dist/runtime/nested-write.walker.js +2 -1
- package/dist/runtime/operation-code.guidance.d.ts +6 -0
- package/dist/runtime/operation-code.guidance.js +167 -0
- package/dist/runtime/operation-request.d.ts +16 -9
- package/dist/runtime/operation-runtime.d.ts +35 -17
- package/dist/runtime/operation-runtime.js +220 -49
- package/dist/runtime/outcome-mapper.d.ts +2 -1
- package/dist/runtime/output-validator.d.ts +1 -1
- package/dist/runtime/output-validator.js +1 -3
- package/dist/runtime/owned-execution.registry.d.ts +34 -4
- package/dist/runtime/pipe-output.validator.d.ts +10 -0
- package/dist/runtime/pipe-output.validator.js +4 -2
- package/dist/runtime/pipeline.d.ts +52 -23
- package/dist/runtime/readonly-value.d.ts +20 -5
- package/dist/runtime/readonly-value.js +74 -26
- package/dist/runtime/record.guard.d.ts +14 -0
- package/dist/runtime/record.guard.js +10 -0
- package/dist/runtime/referential-action.d.ts +1 -1
- package/dist/runtime/request-envelope.validator.js +13 -10
- package/dist/runtime/scalar-runtime.d.ts +4 -21
- package/dist/runtime/scalar-runtime.js +91 -31
- package/dist/runtime/thenable.guard.d.ts +7 -0
- package/dist/runtime/thenable.guard.js +5 -0
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +129 -0
- package/dist/transactions/operation-arguments-encoder.js +9 -10
- package/dist/transactions/operation-fingerprint.js +2 -2
- package/dist/transactions/sql-statement.d.ts +10 -2
- package/dist/transactions/sql-statement.validator.d.ts +2 -2
- package/dist/transactions/sql-statement.validator.js +40 -6
- package/dist/transactions/transaction-envelope.d.ts +5 -4
- package/dist/transactions/transaction-integrity-error.js +2 -0
- package/dist/transactions/transaction-plan-assembler.d.ts +12 -5
- package/dist/transactions/transaction-plan-assembler.js +49 -17
- package/dist/transactions/transaction-plan-validator.d.ts +23 -7
- package/dist/transactions/transaction-plan-validator.js +31 -7
- package/dist/transactions/transaction-reference-collector.js +8 -10
- package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
- package/dist/transactions/transaction-reference-resolver.js +1 -3
- package/dist/transactions/transaction-reference-validator.d.ts +43 -3
- package/dist/transactions/transaction-reference-validator.js +83 -57
- package/dist/transactions/transaction-reference.d.ts +1 -1
- package/dist/transactions/transaction-reference.js +5 -0
- package/dist/transactions/transaction-request.d.ts +8 -8
- package/dist/transactions/transaction-runtime.d.ts +5 -2
- package/dist/transactions/transaction-runtime.js +108 -27
- package/dist/transactions/transaction-settlement.runtime.d.ts +4 -3
- package/dist/transactions/transaction-slot-path-error.js +2 -0
- package/dist/transactions/transaction-stage.runtime.d.ts +5 -3
- package/dist/transactions/transaction-stage.runtime.js +8 -7
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +1 -1
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { validationIssue, } from "../operations/validation-code.js";
|
|
2
|
+
import { operationFailure, } from "../runtime/operation-response.js";
|
|
3
|
+
import { checkPostRoute } from "./absent-route.responder.js";
|
|
4
|
+
import { isParsedByDecoder, markParsedByDecoder, readBody, transportOf, } from "./http-request.decoder.js";
|
|
5
|
+
import { ALL_ROUTE, readRequest } from "./http-request.reader.js";
|
|
6
|
+
import { encodeOperationResponse, } from "./http-response.encoder.js";
|
|
7
|
+
const BODY_KEYS = ["operations"];
|
|
8
|
+
const ENTRY_KEYS = ["resource", "family", "variant", "args"];
|
|
9
|
+
const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
10
|
+
export function decodeAllRequest(contracts, received, origin = "client") {
|
|
11
|
+
const read = readRequest(received);
|
|
12
|
+
if (!read.ok) {
|
|
13
|
+
return read;
|
|
14
|
+
}
|
|
15
|
+
const { requestId } = read;
|
|
16
|
+
const refuse = (response) => ({ ok: false, response, requestId });
|
|
17
|
+
const route = checkPostRoute(ALL_ROUTE, contracts, read, origin);
|
|
18
|
+
if (!route.ok) {
|
|
19
|
+
return refuse(route.response);
|
|
20
|
+
}
|
|
21
|
+
const body = readBody(read, contracts, origin);
|
|
22
|
+
if (!body.ok) {
|
|
23
|
+
return refuse(body.response);
|
|
24
|
+
}
|
|
25
|
+
const entries = readEntries(body.value, contracts[origin].limits.maxAll, isParsedByDecoder(body.value));
|
|
26
|
+
if (!Array.isArray(entries)) {
|
|
27
|
+
return refuse(encodeOperationResponse(entries, requestId));
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
ok: true,
|
|
31
|
+
request: {
|
|
32
|
+
origin,
|
|
33
|
+
requestId,
|
|
34
|
+
protocol: route.protocol,
|
|
35
|
+
transport: transportOf(read),
|
|
36
|
+
entries,
|
|
37
|
+
streamed: acceptsNdjson(read.headers.accept),
|
|
38
|
+
},
|
|
39
|
+
requestId,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function shapeFailure(issue) {
|
|
43
|
+
return operationFailure("A2004", {
|
|
44
|
+
message: "Operation Set failed framework validation.",
|
|
45
|
+
issues: [issue],
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function readEntries(body, maxAll, parsedHere) {
|
|
49
|
+
const members = ownMembers(body, []);
|
|
50
|
+
if (!(members instanceof Map)) {
|
|
51
|
+
return shapeFailure(members);
|
|
52
|
+
}
|
|
53
|
+
const exact = exactMembers(members, BODY_KEYS, []);
|
|
54
|
+
if (exact !== undefined) {
|
|
55
|
+
return shapeFailure(exact);
|
|
56
|
+
}
|
|
57
|
+
const operations = members.get("operations");
|
|
58
|
+
const keyed = isRecord(operations)
|
|
59
|
+
? ownMembers(operations, ["operations"])
|
|
60
|
+
: undefined;
|
|
61
|
+
if (keyed === undefined) {
|
|
62
|
+
return shapeFailure(validationIssue("V1001", ["operations"], "operations must be an object."));
|
|
63
|
+
}
|
|
64
|
+
if (!(keyed instanceof Map)) {
|
|
65
|
+
return shapeFailure(keyed);
|
|
66
|
+
}
|
|
67
|
+
if (keyed.size === 0) {
|
|
68
|
+
return shapeFailure(validationIssue("V1004", ["operations"], "Value outside accepted range."));
|
|
69
|
+
}
|
|
70
|
+
if (keyed.size > maxAll) {
|
|
71
|
+
return operationFailure("A2009", {
|
|
72
|
+
message: "Operation count exceeds the active Contract limit.",
|
|
73
|
+
issues: [
|
|
74
|
+
validationIssue("V1014", ["operations"], `Operation count exceeds maxAll (${maxAll}).`),
|
|
75
|
+
],
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
for (const key of keyed.keys()) {
|
|
79
|
+
if (!IDENTIFIER.test(key)) {
|
|
80
|
+
return shapeFailure(validationIssue("V1001", ["operations"], "Every operation key must be an identifier."));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const entries = [];
|
|
84
|
+
for (const [key, value] of keyed) {
|
|
85
|
+
const entry = readEntry(key, value);
|
|
86
|
+
if (!("key" in entry)) {
|
|
87
|
+
return shapeFailure(entry);
|
|
88
|
+
}
|
|
89
|
+
if (parsedHere) {
|
|
90
|
+
markParsedByDecoder(entry.args);
|
|
91
|
+
}
|
|
92
|
+
entries.push(entry);
|
|
93
|
+
}
|
|
94
|
+
return entries;
|
|
95
|
+
}
|
|
96
|
+
function readEntry(key, value) {
|
|
97
|
+
const at = ["operations", key];
|
|
98
|
+
const members = isRecord(value) ? ownMembers(value, at) : undefined;
|
|
99
|
+
if (members === undefined) {
|
|
100
|
+
return validationIssue("V1001", at, "Operation entry must be an object.");
|
|
101
|
+
}
|
|
102
|
+
if (!(members instanceof Map)) {
|
|
103
|
+
return members;
|
|
104
|
+
}
|
|
105
|
+
const exact = exactMembers(members, ENTRY_KEYS, at);
|
|
106
|
+
if (exact !== undefined) {
|
|
107
|
+
return exact;
|
|
108
|
+
}
|
|
109
|
+
const strings = {};
|
|
110
|
+
for (const name of ["resource", "family", "variant"]) {
|
|
111
|
+
const member = members.get(name);
|
|
112
|
+
if (typeof member !== "string") {
|
|
113
|
+
return validationIssue("V1001", [...at, name], `${name} must be a string.`);
|
|
114
|
+
}
|
|
115
|
+
strings[name] = member;
|
|
116
|
+
}
|
|
117
|
+
const args = members.get("args");
|
|
118
|
+
if (!isRecord(args)) {
|
|
119
|
+
return validationIssue("V1001", [...at, "args"], "args must be an object.");
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
key,
|
|
123
|
+
resource: strings.resource,
|
|
124
|
+
family: strings.family,
|
|
125
|
+
variant: strings.variant,
|
|
126
|
+
args,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function isRecord(value) {
|
|
130
|
+
try {
|
|
131
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function ownMembers(value, at) {
|
|
138
|
+
const refused = (message) => validationIssue("V1001", at, message);
|
|
139
|
+
try {
|
|
140
|
+
const prototype = Object.getPrototypeOf(value);
|
|
141
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
142
|
+
return refused("Value must be a plain object.");
|
|
143
|
+
}
|
|
144
|
+
const members = new Map();
|
|
145
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
146
|
+
if (typeof key === "symbol") {
|
|
147
|
+
return refused("Symbol properties are not valid here.");
|
|
148
|
+
}
|
|
149
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
150
|
+
if (descriptor === undefined ||
|
|
151
|
+
!descriptor.enumerable ||
|
|
152
|
+
!("value" in descriptor)) {
|
|
153
|
+
return refused("Every property must be an own enumerable data property.");
|
|
154
|
+
}
|
|
155
|
+
members.set(key, descriptor.value);
|
|
156
|
+
}
|
|
157
|
+
return members;
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return refused("Value could not be read.");
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function exactMembers(members, names, at) {
|
|
164
|
+
for (const name of names) {
|
|
165
|
+
if (!members.has(name)) {
|
|
166
|
+
return validationIssue("V1000", [...at, name], `Required property "${name}" is missing.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
for (const key of members.keys()) {
|
|
170
|
+
if (!names.includes(key)) {
|
|
171
|
+
return validationIssue("V1001", [...at, key], `Property "${key}" is not valid here.`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
function acceptsNdjson(accept) {
|
|
177
|
+
if (accept === undefined) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
return accept.split(",").some((range) => {
|
|
181
|
+
const [type, ...parameters] = range.split(";").map((part) => part.trim());
|
|
182
|
+
return (type?.toLowerCase() === "application/x-ndjson" &&
|
|
183
|
+
!parameters.some((parameter) => /^q\s*=\s*0(\.0{0,3})?$/i.test(parameter)));
|
|
184
|
+
});
|
|
185
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type AbortSignalShape } from "../framework/operation-set.runner.js";
|
|
2
|
+
import type { AvProtocolRequest } from "./http-request.decoder.js";
|
|
3
|
+
import { type AvProtocolResponse } from "./http-response.encoder.js";
|
|
4
|
+
import { type HandlerContext, type ProtocolCallOptions } from "./protocol.handler.js";
|
|
5
|
+
/** What a host passes beside an Operation Set request. */
|
|
6
|
+
export type OperationSetCallOptions = ProtocolCallOptions & {
|
|
7
|
+
/** Aborted when the client disconnects: entries not yet started never start, and nothing more is written. */
|
|
8
|
+
readonly signal?: AbortSignalShape;
|
|
9
|
+
};
|
|
10
|
+
/** A streamed answer: status, headers, and the body as lines produced as entries settle. */
|
|
11
|
+
type OperationSetStreamedResponse = {
|
|
12
|
+
readonly status: 200;
|
|
13
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
14
|
+
readonly body: AsyncIterable<string>;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* `protocol.handleAll(request, options?)`: the `/_all` request decoded, its state
|
|
18
|
+
* seeded once, its principal derived once, and every entry run through the bound
|
|
19
|
+
* Framework's own `execute` concurrently, at most `limits.maxAllConcurrency` at once, each
|
|
20
|
+
* answered by the envelope its single route writes. The whole request is refused
|
|
21
|
+
* only for its transport, identity, body shape or `limits.maxAll`, or when its
|
|
22
|
+
* seed or principal fails; otherwise it is `200 A1000` with `data` keyed by the
|
|
23
|
+
* caller's keys, or, under `Accept: application/x-ndjson`, one line per entry as
|
|
24
|
+
* it settles. Total over any value, as `handleOperation` is.
|
|
25
|
+
*/
|
|
26
|
+
export declare function handleAllRequest(context: HandlerContext, request: AvProtocolRequest, options?: OperationSetCallOptions): Promise<AvProtocolResponse | OperationSetStreamedResponse>;
|
|
27
|
+
export {};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { maxAllConcurrencyOf, } from "../framework/operation-set.runner.js";
|
|
2
|
+
import { createDiagnosticsReporter, } from "../runtime/diagnostics-reporter.js";
|
|
3
|
+
import { internalFailure } from "../runtime/operation-response.js";
|
|
4
|
+
import { resolveOperationRoute } from "./absent-route.responder.js";
|
|
5
|
+
import { encodeOperationResponse, encodeOperationSetResponse, operationSetLine, streamedHeaders, } from "./http-response.encoder.js";
|
|
6
|
+
import { decodeAllRequest, } from "./operation-set.decoder.js";
|
|
7
|
+
import { admitOperation, seededState, } from "./protocol.handler.js";
|
|
8
|
+
export async function handleAllRequest(context, request, options) {
|
|
9
|
+
const decoded = decodeAllRequest(context.contracts, request, context.origin);
|
|
10
|
+
if (!decoded.ok) {
|
|
11
|
+
return decoded.response;
|
|
12
|
+
}
|
|
13
|
+
const set = decoded.request;
|
|
14
|
+
const signal = signalOf(options);
|
|
15
|
+
const entries = set.entries.map((entry) => admitEntry(context, set, entry));
|
|
16
|
+
const reportEach = (error, code, marks) => {
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
if ("report" in entry) {
|
|
19
|
+
entry.report(error, code, marks);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const seeded = await seededState(options, reportEach, {
|
|
24
|
+
path: ["operations"],
|
|
25
|
+
arguments: undefined,
|
|
26
|
+
});
|
|
27
|
+
if (!seeded.ok) {
|
|
28
|
+
return encodeOperationResponse(seeded.response, set.requestId);
|
|
29
|
+
}
|
|
30
|
+
const state = seeded.state ?? {};
|
|
31
|
+
let caller;
|
|
32
|
+
try {
|
|
33
|
+
caller = await context.owned?.caller({ origin: set.origin, state });
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
reportEach(error, "A3000");
|
|
37
|
+
return encodeOperationResponse(internalFailure(), set.requestId);
|
|
38
|
+
}
|
|
39
|
+
const run = (entry) => "answered" in entry
|
|
40
|
+
? Promise.resolve(entry.answered)
|
|
41
|
+
: runEntry(entry, state, caller, set.requestId);
|
|
42
|
+
const cap = Math.min(maxAllConcurrencyOf(context.contracts[set.origin].limits), entries.length);
|
|
43
|
+
if (!set.streamed) {
|
|
44
|
+
const settled = new Map();
|
|
45
|
+
await runConcurrently(entries, cap, () => signal?.aborted === true, run, (entry, text) => {
|
|
46
|
+
settled.set(entry.key, text);
|
|
47
|
+
});
|
|
48
|
+
return encodeOperationSetResponse(entries.flatMap(({ key }) => {
|
|
49
|
+
const envelope = settled.get(key);
|
|
50
|
+
return envelope === undefined ? [] : [{ key, envelope }];
|
|
51
|
+
}), set.requestId);
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
status: 200,
|
|
55
|
+
headers: streamedHeaders(set.requestId),
|
|
56
|
+
body: streamLines(entries, cap, signal, run),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function signalOf(options) {
|
|
60
|
+
const signal = options?.signal;
|
|
61
|
+
return typeof signal === "object" &&
|
|
62
|
+
signal !== null &&
|
|
63
|
+
typeof signal.aborted === "boolean" &&
|
|
64
|
+
typeof signal.addEventListener === "function"
|
|
65
|
+
? signal
|
|
66
|
+
: undefined;
|
|
67
|
+
}
|
|
68
|
+
function admitEntry(context, set, entry) {
|
|
69
|
+
const route = resolveOperationRoute(context.contracts, set.protocol, entry, set.origin);
|
|
70
|
+
if (!route.ok) {
|
|
71
|
+
return {
|
|
72
|
+
key: entry.key,
|
|
73
|
+
answered: encodeOperationResponse(route.failure, set.requestId).body,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const request = {
|
|
77
|
+
origin: set.origin,
|
|
78
|
+
requestId: set.requestId,
|
|
79
|
+
resource: entry.resource,
|
|
80
|
+
...route.operation,
|
|
81
|
+
protocol: set.protocol,
|
|
82
|
+
transport: set.transport,
|
|
83
|
+
arguments: entry.args,
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
key: entry.key,
|
|
87
|
+
admitted: admitOperation(context, request),
|
|
88
|
+
report: createDiagnosticsReporter(context.sink, request, undefined, entry.key),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
async function runEntry(entry, state, caller, requestId) {
|
|
92
|
+
const { admitted, key, report } = entry;
|
|
93
|
+
let response;
|
|
94
|
+
try {
|
|
95
|
+
response = await admitted.run({ ...admitted.request, state }, caller === undefined ? { key } : { caller, key });
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
report(error, "A3000");
|
|
99
|
+
response = internalFailure();
|
|
100
|
+
}
|
|
101
|
+
return encodeOperationResponse(response, requestId, (error) => report(error, "A3003")).body;
|
|
102
|
+
}
|
|
103
|
+
function runConcurrently(entries, cap, stopped, run, settled) {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
let next = 0;
|
|
106
|
+
let running = 0;
|
|
107
|
+
const launch = () => {
|
|
108
|
+
while (running < cap && next < entries.length && !stopped()) {
|
|
109
|
+
const entry = entries[next];
|
|
110
|
+
next += 1;
|
|
111
|
+
running += 1;
|
|
112
|
+
run(entry).then((text) => {
|
|
113
|
+
running -= 1;
|
|
114
|
+
settled(entry, text);
|
|
115
|
+
launch();
|
|
116
|
+
}, () => {
|
|
117
|
+
running -= 1;
|
|
118
|
+
launch();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (running === 0) {
|
|
122
|
+
resolve();
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
launch();
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function streamLines(entries, cap, signal, run) {
|
|
129
|
+
const queue = [];
|
|
130
|
+
let finished = false;
|
|
131
|
+
let halted = false;
|
|
132
|
+
const waiting = [];
|
|
133
|
+
const notify = () => {
|
|
134
|
+
for (const wake of waiting.splice(0)) {
|
|
135
|
+
wake();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
const stopped = () => halted || signal?.aborted === true;
|
|
139
|
+
const halt = () => {
|
|
140
|
+
halted = true;
|
|
141
|
+
signal?.removeEventListener("abort", notify);
|
|
142
|
+
notify();
|
|
143
|
+
};
|
|
144
|
+
signal?.addEventListener("abort", notify, { once: true });
|
|
145
|
+
void runConcurrently(entries, cap, stopped, run, (entry, text) => {
|
|
146
|
+
if (!stopped()) {
|
|
147
|
+
queue.push(operationSetLine(entry.key, text));
|
|
148
|
+
}
|
|
149
|
+
notify();
|
|
150
|
+
}).then(() => {
|
|
151
|
+
finished = true;
|
|
152
|
+
signal?.removeEventListener("abort", notify);
|
|
153
|
+
notify();
|
|
154
|
+
});
|
|
155
|
+
const end = { done: true, value: undefined };
|
|
156
|
+
const iterator = {
|
|
157
|
+
async next() {
|
|
158
|
+
while (!stopped()) {
|
|
159
|
+
const line = queue.shift();
|
|
160
|
+
if (line !== undefined) {
|
|
161
|
+
return { done: false, value: line };
|
|
162
|
+
}
|
|
163
|
+
if (finished) {
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
await new Promise((resolve) => {
|
|
167
|
+
waiting.push(resolve);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
halt();
|
|
171
|
+
return end;
|
|
172
|
+
},
|
|
173
|
+
async return() {
|
|
174
|
+
halt();
|
|
175
|
+
return end;
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
179
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AvRequestProtocol } from "../runtime/operation-request.js";
|
|
2
2
|
/** `AvProtocol.headers` — the header names the protocol owns, as passive data. */
|
|
3
3
|
export declare const headers: Readonly<{
|
|
4
4
|
readonly protocolVersion: "Aventara-Protocol-Version";
|
|
@@ -22,11 +22,16 @@ export declare function normalizeHeaders(received: ReceivedHeaders): Readonly<Re
|
|
|
22
22
|
*/
|
|
23
23
|
export declare function headerValue(normalized: Readonly<Record<string, string>>, name: string): string | undefined;
|
|
24
24
|
/**
|
|
25
|
-
* The identity headers as the `
|
|
25
|
+
* The identity headers as the `AvRequestProtocol` core checks, or `undefined`
|
|
26
26
|
* when either is ABSENT — a request without them is malformed: it does not say which
|
|
27
27
|
* protocol and ClientContract it speaks, so there is nothing to compare.
|
|
28
28
|
*/
|
|
29
|
-
export declare function protocolIdentityOf(normalized: Readonly<Record<string, string>>):
|
|
29
|
+
export declare function protocolIdentityOf(normalized: Readonly<Record<string, string>>): AvRequestProtocol | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* The `Aventara-Protocol-Version` header as a number — `NaN` when it is not a
|
|
32
|
+
* decimal integer — or `undefined` when it is absent.
|
|
33
|
+
*/
|
|
34
|
+
export declare function protocolVersionOf(normalized: Readonly<Record<string, string>>): number | undefined;
|
|
30
35
|
/**
|
|
31
36
|
* The identity headers a request did not send, in the order the framework sends them —
|
|
32
37
|
* what the `A2000` answering it names. Empty exactly when {@link protocolIdentityOf}
|
|
@@ -39,4 +44,8 @@ export type IdentityHeaderName = (typeof headers)["protocolVersion"] | (typeof h
|
|
|
39
44
|
* A repeated header joins with `", "`, which holds a space, so it is never adopted.
|
|
40
45
|
*/
|
|
41
46
|
export declare function adoptedRequestId(normalized: Readonly<Record<string, string>>): string | undefined;
|
|
47
|
+
/** The header every answer to a GET carries: `X-Content-Type-Options: nosniff`. */
|
|
48
|
+
export declare const NO_SNIFF: Readonly<{
|
|
49
|
+
readonly "X-Content-Type-Options": "nosniff";
|
|
50
|
+
}>;
|
|
42
51
|
export {};
|
|
@@ -24,15 +24,19 @@ export function headerValue(normalized, name) {
|
|
|
24
24
|
}
|
|
25
25
|
const PROTOCOL_VERSION_TEXT = /^(?:0|[1-9]\d*)$/;
|
|
26
26
|
export function protocolIdentityOf(normalized) {
|
|
27
|
-
const version =
|
|
27
|
+
const version = protocolVersionOf(normalized);
|
|
28
28
|
const hash = headerValue(normalized, headers.contractHash);
|
|
29
29
|
if (version === undefined || hash === undefined) {
|
|
30
30
|
return undefined;
|
|
31
31
|
}
|
|
32
|
-
return {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
return { version, hash };
|
|
33
|
+
}
|
|
34
|
+
export function protocolVersionOf(normalized) {
|
|
35
|
+
const version = headerValue(normalized, headers.protocolVersion);
|
|
36
|
+
if (version === undefined) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return PROTOCOL_VERSION_TEXT.test(version) ? Number(version) : Number.NaN;
|
|
36
40
|
}
|
|
37
41
|
export function missingIdentityHeaders(normalized) {
|
|
38
42
|
return [headers.protocolVersion, headers.contractHash].filter((name) => headerValue(normalized, name) === undefined);
|
|
@@ -44,3 +48,6 @@ export function adoptedRequestId(normalized) {
|
|
|
44
48
|
? value
|
|
45
49
|
: undefined;
|
|
46
50
|
}
|
|
51
|
+
export const NO_SNIFF = Object.freeze({
|
|
52
|
+
"X-Content-Type-Options": "nosniff",
|
|
53
|
+
});
|
|
@@ -6,7 +6,7 @@ import { type AvOperationIdentity } from "../operations/operation-identity.js";
|
|
|
6
6
|
*/
|
|
7
7
|
export type SurfaceRoute = ({
|
|
8
8
|
readonly kind: "operation";
|
|
9
|
-
readonly method: "POST";
|
|
9
|
+
readonly method: "POST" | "GET";
|
|
10
10
|
readonly path: `/_resources/${string}`;
|
|
11
11
|
readonly resource: string;
|
|
12
12
|
} & AvOperationIdentity) | {
|
|
@@ -17,11 +17,24 @@ export type SurfaceRoute = ({
|
|
|
17
17
|
readonly kind: "transactions";
|
|
18
18
|
readonly method: "POST";
|
|
19
19
|
readonly path: "/_transactions";
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: "can";
|
|
22
|
+
readonly method: "POST";
|
|
23
|
+
readonly path: "/_access/can";
|
|
24
|
+
} | {
|
|
25
|
+
readonly kind: "all";
|
|
26
|
+
readonly method: "POST";
|
|
27
|
+
readonly path: "/_all";
|
|
20
28
|
};
|
|
29
|
+
/** Whether `client` advertises at least one operation: exactly when `/_all` is a route. */
|
|
30
|
+
export declare function advertisesAnyOperation(client: AvContractClient): boolean;
|
|
21
31
|
/**
|
|
22
32
|
* The route list of one ClientContract: every route a host mounts, from one list,
|
|
23
33
|
* deep-frozen — exactly the advertised operation triples in the Contract's own key
|
|
24
|
-
* order
|
|
25
|
-
* `
|
|
34
|
+
* order (a `find` triple the Contract marks cacheable twice: `POST`, then `GET`),
|
|
35
|
+
* then `GET /_contract`, then `POST /_transactions` when, and only when,
|
|
36
|
+
* `transactions === "interactive"`, then `POST /_all` when, and only when, it
|
|
37
|
+
* advertises an operation, then `POST /_access/can` when, and only when, the
|
|
38
|
+
* Contract has its `access` section.
|
|
26
39
|
*/
|
|
27
40
|
export declare function surfaceOf(client: AvContractClient): readonly SurfaceRoute[];
|
|
@@ -2,6 +2,21 @@ import { lookupOwn, lookupResource } from "../contracts/contract-lookup.js";
|
|
|
2
2
|
import { isOperationVariantAvailable } from "../contracts/resource-operations.js";
|
|
3
3
|
import { parseOperationIdentity, } from "../operations/operation-identity.js";
|
|
4
4
|
const ownKeys = (record) => record === undefined ? [] : Object.keys(record);
|
|
5
|
+
export function advertisesAnyOperation(client) {
|
|
6
|
+
for (const resourceName of ownKeys(client.resources)) {
|
|
7
|
+
const advertised = lookupResource(client, resourceName)?.operations;
|
|
8
|
+
for (const familyName of ownKeys(advertised)) {
|
|
9
|
+
const family = lookupOwn(advertised, familyName);
|
|
10
|
+
for (const variantName of ownKeys(family)) {
|
|
11
|
+
if (parseOperationIdentity(familyName, variantName) !== undefined &&
|
|
12
|
+
isOperationVariantAvailable(lookupOwn(family, variantName))) {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
5
20
|
export function surfaceOf(client) {
|
|
6
21
|
const routes = [];
|
|
7
22
|
for (const resourceName of ownKeys(client.resources)) {
|
|
@@ -15,13 +30,24 @@ export function surfaceOf(client) {
|
|
|
15
30
|
!isOperationVariantAvailable(lookupOwn(family, variantName))) {
|
|
16
31
|
continue;
|
|
17
32
|
}
|
|
33
|
+
const path = `/_resources/${[resourceName, identity.family, identity.variant].map(encodeURIComponent).join("/")}`;
|
|
18
34
|
routes.push(Object.freeze({
|
|
19
35
|
kind: "operation",
|
|
20
36
|
method: "POST",
|
|
21
|
-
path
|
|
37
|
+
path,
|
|
22
38
|
resource: resourceName,
|
|
23
39
|
...identity,
|
|
24
40
|
}));
|
|
41
|
+
if (identity.family === "find" &&
|
|
42
|
+
lookupOwn(resource?.cache, identity.variant) !== undefined) {
|
|
43
|
+
routes.push(Object.freeze({
|
|
44
|
+
kind: "operation",
|
|
45
|
+
method: "GET",
|
|
46
|
+
path,
|
|
47
|
+
resource: resourceName,
|
|
48
|
+
...identity,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
25
51
|
}
|
|
26
52
|
}
|
|
27
53
|
}
|
|
@@ -37,5 +63,19 @@ export function surfaceOf(client) {
|
|
|
37
63
|
path: "/_transactions",
|
|
38
64
|
}));
|
|
39
65
|
}
|
|
66
|
+
if (advertisesAnyOperation(client)) {
|
|
67
|
+
routes.push(Object.freeze({
|
|
68
|
+
kind: "all",
|
|
69
|
+
method: "POST",
|
|
70
|
+
path: "/_all",
|
|
71
|
+
}));
|
|
72
|
+
}
|
|
73
|
+
if (client.access !== undefined) {
|
|
74
|
+
routes.push(Object.freeze({
|
|
75
|
+
kind: "can",
|
|
76
|
+
method: "POST",
|
|
77
|
+
path: "/_access/can",
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
40
80
|
return Object.freeze(routes);
|
|
41
81
|
}
|
|
@@ -17,7 +17,9 @@ export type AvProtocolBinding = {
|
|
|
17
17
|
* tagged by `kind`: one `POST` `operation` route per `(resource, family, variant)`
|
|
18
18
|
* it advertises and no other, then the `contract` route `GET /_contract`
|
|
19
19
|
* (always), then the `transactions` route `POST /_transactions` exactly when its
|
|
20
|
-
* `transactions` is `"interactive"
|
|
20
|
+
* `transactions` is `"interactive"`, then the `all` route `POST /_all` exactly
|
|
21
|
+
* when it advertises an operation, then the `can` route `POST /_access/can`
|
|
22
|
+
* exactly when it has its `access` section. Each path is relative to
|
|
21
23
|
* `framework.api`. Read once at bind time: every call answers the same
|
|
22
24
|
* deep-frozen list, which is passive data a host may serialize.
|
|
23
25
|
*/
|
|
@@ -88,6 +90,55 @@ export type AvProtocolBinding = {
|
|
|
88
90
|
/** Seeds the plan's one `ctx.$state`, as for `handleOperation`. */
|
|
89
91
|
readonly state?: () => AvState | Promise<AvState>;
|
|
90
92
|
}) => Promise<AvProtocolResponse>;
|
|
93
|
+
/**
|
|
94
|
+
* The `POST /_access/can` shortcut: the request decoded, its `ctx.$state`
|
|
95
|
+
* seeded through `options.state`, its `{ questions }` answered by the bound
|
|
96
|
+
* Framework's policy engine under the principal `ctx.$state.me`, and the answer
|
|
97
|
+
* encoded: `200 A1000` with one boolean per question, in order. It only shapes
|
|
98
|
+
* a UI, so it refuses malformed input only, never `403`. Absent (`404 A2002`)
|
|
99
|
+
* unless the bound Contract has its `access` section, which it has exactly when
|
|
100
|
+
* the framework's `access` option is configured. Total over any value, as
|
|
101
|
+
* `handleOperation` is.
|
|
102
|
+
*/
|
|
103
|
+
readonly handleCan: (request: AvProtocolRequest, options?: {
|
|
104
|
+
/** Seeds the request's `ctx.$state`, as for `handleOperation`. */
|
|
105
|
+
readonly state?: () => AvState | Promise<AvState>;
|
|
106
|
+
}) => Promise<AvProtocolResponse>;
|
|
107
|
+
/**
|
|
108
|
+
* The `POST /_all` shortcut: one Operation Set — any mix of the operations the
|
|
109
|
+
* bound Contract advertises, under the caller's own identifier keys — decoded,
|
|
110
|
+
* its `ctx.$state` seeded once through `options.state` (one state every entry
|
|
111
|
+
* shares), its principal derived once, and every entry run concurrently,
|
|
112
|
+
* exactly as through its own route, at most `limits.maxAllConcurrency` at once.
|
|
113
|
+
* Answers `200 A1000` with `data` holding each key's own envelope, the one its
|
|
114
|
+
* single route writes. Under `Accept: application/x-ndjson` it answers `200`
|
|
115
|
+
* with `body` an async iterable of lines instead, one per entry as it settles:
|
|
116
|
+
* `{"key":…,"envelope":…}` and a newline. Refused whole only for its
|
|
117
|
+
* transport, identity, body shape or `limits.maxAll`, or when its seed or
|
|
118
|
+
* principal fails, as plain JSON at that code's status. When `options.signal`
|
|
119
|
+
* aborts (the client went away), entries not yet started never start and no
|
|
120
|
+
* more lines are written (a plain answer then holds only the entries that
|
|
121
|
+
* ran); so it is when the host stops reading. Absent (`404
|
|
122
|
+
* A2002`) on a Contract that advertises no operation. Total over any value, as
|
|
123
|
+
* `handleOperation` is.
|
|
124
|
+
*/
|
|
125
|
+
readonly handleAll: (request: AvProtocolRequest, options?: {
|
|
126
|
+
/** Seeds the set's one `ctx.$state`, as for `handleOperation`. */
|
|
127
|
+
readonly state?: () => AvState | Promise<AvState>;
|
|
128
|
+
/** The client's disconnect: entries not yet started never start. Any platform's `AbortSignal` is one. */
|
|
129
|
+
readonly signal?: {
|
|
130
|
+
readonly aborted: boolean;
|
|
131
|
+
readonly reason: unknown;
|
|
132
|
+
addEventListener(type: "abort", listener: () => void, options?: {
|
|
133
|
+
readonly once?: boolean;
|
|
134
|
+
}): void;
|
|
135
|
+
removeEventListener(type: "abort", listener: () => void): void;
|
|
136
|
+
};
|
|
137
|
+
}) => Promise<AvProtocolResponse | {
|
|
138
|
+
readonly status: 200;
|
|
139
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
140
|
+
readonly body: AsyncIterable<string>;
|
|
141
|
+
}>;
|
|
91
142
|
/**
|
|
92
143
|
* The `GET /_contract` responder: the bound ClientContract's JCS-canonical bytes,
|
|
93
144
|
* `ETag` its quoted hash, `Cache-Control: no-cache`, and `304` with no body when
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { accessQuestionsOf } from "../policy/access-question.registry.js";
|
|
1
2
|
import { diagnosticsSinkOf } from "../runtime/diagnostics-reporter.js";
|
|
2
3
|
import { ownedExecutionOf } from "../runtime/owned-execution.registry.js";
|
|
3
4
|
import { answerAbsentRequest } from "./absent-route.responder.js";
|
|
4
5
|
import { contractDocumentOf, encodeContractRequest, } from "./contract-document.responder.js";
|
|
5
6
|
import { decodeOperationRequest, decodeTransactionRequest, } from "./http-request.decoder.js";
|
|
6
7
|
import { encodeOperationResponse, encodeTransactionResponse, } from "./http-response.encoder.js";
|
|
7
|
-
import {
|
|
8
|
+
import { handleAllRequest, } from "./operation-set.handler.js";
|
|
9
|
+
import { handleCanRequest, handleOperationRequest, handleTransactionRequest, operationUnencodableReport, planUnencodableReport, } from "./protocol.handler.js";
|
|
8
10
|
import { transportFailure } from "./protocol-failure.js";
|
|
9
11
|
import { surfaceOf } from "./protocol-surface.js";
|
|
10
12
|
export function bind(framework, options) {
|
|
@@ -21,6 +23,7 @@ export function bind(framework, options) {
|
|
|
21
23
|
executeTransaction: (request) => framework.executeTransaction(request),
|
|
22
24
|
owned: ownedExecutionOf(framework),
|
|
23
25
|
sink,
|
|
26
|
+
can: accessQuestionsOf(framework),
|
|
24
27
|
};
|
|
25
28
|
return Object.freeze({
|
|
26
29
|
surface: () => surface,
|
|
@@ -33,6 +36,8 @@ export function bind(framework, options) {
|
|
|
33
36
|
failure: transportFailure,
|
|
34
37
|
handleOperation: (request, options) => handleOperationRequest(handler, request, options),
|
|
35
38
|
handleTransaction: (request, options) => handleTransactionRequest(handler, request, options),
|
|
39
|
+
handleCan: (request, options) => handleCanRequest(handler, request, options),
|
|
40
|
+
handleAll: (request, options) => handleAllRequest(handler, request, options),
|
|
36
41
|
encodeContract: (request) => encodeContractRequest(document, request),
|
|
37
42
|
answerAbsent: (request) => answerAbsentRequest(contracts, request, sink, origin),
|
|
38
43
|
});
|