@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
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { AvContracts } from "../compiler/contract.compiler.js";
|
|
2
2
|
import type { RemoteRequestOrigin } from "../config/config-layer.js";
|
|
3
3
|
import type { AvFramework } from "../framework/framework.js";
|
|
4
|
-
import {
|
|
4
|
+
import type { AccessQuestionsAnswerer } from "../policy/access-question.runtime.js";
|
|
5
|
+
import { type AvDiagnosticsSink, createReporter } from "../runtime/diagnostics-reporter.js";
|
|
5
6
|
import type { RemoteOperationRequest } from "../runtime/operation-request.js";
|
|
7
|
+
import { type AvOperationFailureResponse, type AvOperationResponse } from "../runtime/operation-response.js";
|
|
6
8
|
import type { OwnedExecution } from "../runtime/owned-execution.registry.js";
|
|
7
9
|
import type { AvState } from "../runtime/request-state.js";
|
|
8
10
|
import { type AvProtocolRequest } from "./http-request.decoder.js";
|
|
@@ -21,12 +23,46 @@ export type HandlerContext = {
|
|
|
21
23
|
readonly executeTransaction: AvFramework["executeTransaction"];
|
|
22
24
|
readonly owned: OwnedExecution | undefined;
|
|
23
25
|
readonly sink: AvDiagnosticsSink | undefined;
|
|
26
|
+
/** The Framework's `can` answerer: absent for one without `access`, or anything `createFramework` did not return. */
|
|
27
|
+
readonly can: AccessQuestionsAnswerer | undefined;
|
|
24
28
|
};
|
|
29
|
+
/** A decoded operation as the shortcut runs it: the arguments it reads, and its entry. */
|
|
30
|
+
export type AdmittedOperation = {
|
|
31
|
+
readonly request: RemoteOperationRequest;
|
|
32
|
+
/**
|
|
33
|
+
* `member` is an Operation Set entry's (its principal, derived once for the
|
|
34
|
+
* set, and its key), which only a constructed Framework's owned entry reads.
|
|
35
|
+
*/
|
|
36
|
+
readonly run: (request: RemoteOperationRequest, member?: OperationSetMember) => Promise<AvOperationResponse>;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The entry a decoded operation runs through, with the arguments it reads, settled
|
|
40
|
+
* at the call, before the shortcut's first await. A body parsed here from raw
|
|
41
|
+
* content is fresh and runs uncopied. A host's `{ kind: "parsed" }` body is the
|
|
42
|
+
* host's object, so it is copied here, as `framework.execute` copies a server-side
|
|
43
|
+
* caller's arguments when called: a change the host makes after the call changes
|
|
44
|
+
* nothing, and a body that cannot be copied is answered as that door answers it
|
|
45
|
+
* (`A2004`/`V1001` at its path), never read again.
|
|
46
|
+
*/
|
|
47
|
+
export declare function admitOperation(context: HandlerContext, request: RemoteOperationRequest): AdmittedOperation;
|
|
48
|
+
/** What an Operation Set entry runs under besides its request: the set's principal, and its key. */
|
|
49
|
+
type OperationSetMember = NonNullable<Parameters<OwnedExecution["execute"]>[2]>;
|
|
25
50
|
/** What a host passes beside one protocol request. */
|
|
26
51
|
export type ProtocolCallOptions = {
|
|
27
52
|
/** Seeds the request's `ctx.$state`; called once, after decoding. Omitted, the state is `{}`. */
|
|
28
53
|
readonly state?: () => AvState | Promise<AvState>;
|
|
29
54
|
};
|
|
55
|
+
/** The request's seeded state, or the failure the seed's throw answers. */
|
|
56
|
+
export declare function seededState(options: ProtocolCallOptions | undefined, report: ReturnType<typeof createReporter>, view: {
|
|
57
|
+
readonly path: readonly (string | number)[];
|
|
58
|
+
readonly arguments: unknown;
|
|
59
|
+
}): Promise<{
|
|
60
|
+
readonly ok: true;
|
|
61
|
+
readonly state: AvState | undefined;
|
|
62
|
+
} | {
|
|
63
|
+
readonly ok: false;
|
|
64
|
+
readonly response: AvOperationFailureResponse;
|
|
65
|
+
}>;
|
|
30
66
|
/**
|
|
31
67
|
* Where a plan's `A3003` goes: the Framework's diagnostics sink, as a `"transaction"`
|
|
32
68
|
* diagnostic — client origin, the request id the plan ran under, and the step the
|
|
@@ -62,3 +98,11 @@ export declare function handleOperationRequest(context: HandlerContext, request:
|
|
|
62
98
|
* planUnencodableReport}.
|
|
63
99
|
*/
|
|
64
100
|
export declare function handleTransactionRequest(context: HandlerContext, request: AvProtocolRequest, options?: ProtocolCallOptions): Promise<AvProtocolResponse>;
|
|
101
|
+
/**
|
|
102
|
+
* `protocol.handleCan(request)`: the `/_access/can` request decoded, its state
|
|
103
|
+
* seeded, its questions answered by the bound Framework, and the answer encoded —
|
|
104
|
+
* `200 A1000` with one boolean per question, in order, or the refusal. Total over
|
|
105
|
+
* any value, as {@link handleOperationRequest} is.
|
|
106
|
+
*/
|
|
107
|
+
export declare function handleCanRequest(context: HandlerContext, request: AvProtocolRequest, options?: ProtocolCallOptions): Promise<AvProtocolResponse>;
|
|
108
|
+
export {};
|
|
@@ -1,18 +1,67 @@
|
|
|
1
|
+
import { copyAtDoor } from "../runtime/caller-data.door.js";
|
|
1
2
|
import { createDiagnosticsReporter, createReporter, } from "../runtime/diagnostics-reporter.js";
|
|
2
3
|
import { sanitizeOperationFailure } from "../runtime/error-boundary.js";
|
|
3
|
-
import {
|
|
4
|
+
import { internalFailure, } from "../runtime/operation-response.js";
|
|
5
|
+
import { capturePlanAtCall } from "../transactions/transaction-plan-validator.js";
|
|
6
|
+
import { checkOperationRoute } from "./absent-route.responder.js";
|
|
7
|
+
import { adoptParsedBody, decodeCanRequest, decodeOperationRead, decodeTransactionRequest, isParsedByDecoder, transportOf, } from "./http-request.decoder.js";
|
|
8
|
+
import { readRequest } from "./http-request.reader.js";
|
|
4
9
|
import { encodeOperationResponse, encodeTransactionResponse, } from "./http-response.encoder.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
10
|
+
import { transportFailure } from "./protocol-failure.js";
|
|
11
|
+
import { headerValue } from "./protocol-headers.js";
|
|
12
|
+
import { cachedReadAnswer, uncachedAnswer } from "./read-cache.responder.js";
|
|
13
|
+
import { readArgumentsOf } from "./read-query.codec.js";
|
|
14
|
+
export function admitOperation(context, request) {
|
|
15
|
+
const owned = context.owned;
|
|
16
|
+
if (isParsedByDecoder(request.arguments)) {
|
|
17
|
+
return {
|
|
18
|
+
request,
|
|
19
|
+
run: owned === undefined
|
|
20
|
+
? (admitted) => context.execute(admitted)
|
|
21
|
+
: (admitted, member) => owned.execute(admitted, undefined, member),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const copy = copyAtDoor(request.arguments);
|
|
25
|
+
if (owned === undefined) {
|
|
26
|
+
return {
|
|
27
|
+
request: copy.ok ? { ...request, arguments: copy.value } : request,
|
|
28
|
+
run: (admitted) => context.execute(admitted),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (copy.ok) {
|
|
32
|
+
return {
|
|
33
|
+
request: { ...request, arguments: copy.value },
|
|
34
|
+
run: (admitted, member) => owned.execute(admitted, undefined, member),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
request: { ...request, arguments: {} },
|
|
39
|
+
run: (admitted, member) => owned.execute(admitted, copy.error, member),
|
|
40
|
+
};
|
|
9
41
|
}
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
42
|
+
function admitTransaction(context, request) {
|
|
43
|
+
const owned = context.owned;
|
|
44
|
+
if (isParsedByDecoder(request.plan)) {
|
|
45
|
+
return {
|
|
46
|
+
request,
|
|
47
|
+
run: owned?.executeTransaction ?? context.executeTransaction,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const captured = capturePlanAtCall(request.plan);
|
|
51
|
+
if (owned === undefined) {
|
|
52
|
+
return {
|
|
53
|
+
request: captured.refused.size === 0
|
|
54
|
+
? { ...request, plan: captured.plan }
|
|
55
|
+
: request,
|
|
56
|
+
run: context.executeTransaction,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
request: { ...request, plan: captured.plan },
|
|
61
|
+
run: (admitted) => owned.executeTransaction(admitted, captured.refused.size === 0 ? undefined : captured.refused),
|
|
62
|
+
};
|
|
14
63
|
}
|
|
15
|
-
async function seededState(options, report, view) {
|
|
64
|
+
export async function seededState(options, report, view) {
|
|
16
65
|
if (options?.state === undefined) {
|
|
17
66
|
return { ok: true, state: undefined };
|
|
18
67
|
}
|
|
@@ -44,32 +93,128 @@ export function operationUnencodableReport(sink, request) {
|
|
|
44
93
|
return (error) => createDiagnosticsReporter(sink, request)(error, "A3003");
|
|
45
94
|
}
|
|
46
95
|
export async function handleOperationRequest(context, request, options) {
|
|
47
|
-
const
|
|
96
|
+
const read = readRequest(request);
|
|
97
|
+
if (!read.ok) {
|
|
98
|
+
return read.response;
|
|
99
|
+
}
|
|
100
|
+
if (read.request.method === "GET") {
|
|
101
|
+
return handleCachedRead(context, read, options);
|
|
102
|
+
}
|
|
103
|
+
const decoded = decodeOperationRead(context.contracts, read, context.origin);
|
|
48
104
|
if (!decoded.ok) {
|
|
49
105
|
return decoded.response;
|
|
50
106
|
}
|
|
51
|
-
|
|
107
|
+
return runOperation(context, decoded, options);
|
|
108
|
+
}
|
|
109
|
+
async function runOperation(context, decoded, options) {
|
|
110
|
+
const admitted = admitOperation(context, decoded.request);
|
|
111
|
+
const seeded = await seededState(options, createDiagnosticsReporter(context.sink, admitted.request), { path: ["arguments"], arguments: admitted.request.arguments });
|
|
52
112
|
const response = seeded.ok
|
|
53
|
-
? await
|
|
54
|
-
?
|
|
55
|
-
: { ...
|
|
113
|
+
? await admitted.run(seeded.state === undefined
|
|
114
|
+
? admitted.request
|
|
115
|
+
: { ...admitted.request, state: seeded.state })
|
|
56
116
|
: seeded.response;
|
|
57
117
|
return encodeOperationResponse(response, decoded.requestId, operationUnencodableReport(context.sink, decoded.request));
|
|
58
118
|
}
|
|
119
|
+
async function handleCachedRead(context, read, options) {
|
|
120
|
+
const route = checkOperationRoute(context.contracts, read, context.origin);
|
|
121
|
+
if (!route.ok) {
|
|
122
|
+
return uncachedAnswer(route.response);
|
|
123
|
+
}
|
|
124
|
+
const { cache, query } = route;
|
|
125
|
+
if (cache === undefined || query === undefined) {
|
|
126
|
+
return uncachedAnswer(transportFailure("A2000", read.requestId));
|
|
127
|
+
}
|
|
128
|
+
const args = await readArgumentsOf(query.q, context.contracts[context.origin].limits.maxRequestBytes);
|
|
129
|
+
if (!args.ok) {
|
|
130
|
+
return uncachedAnswer(transportFailure(args.code, read.requestId));
|
|
131
|
+
}
|
|
132
|
+
adoptParsedBody(args.value);
|
|
133
|
+
const request = {
|
|
134
|
+
origin: context.origin,
|
|
135
|
+
requestId: read.requestId,
|
|
136
|
+
resource: route.resource,
|
|
137
|
+
...route.operation,
|
|
138
|
+
protocol: route.protocol,
|
|
139
|
+
transport: transportOf(read),
|
|
140
|
+
arguments: args.value,
|
|
141
|
+
};
|
|
142
|
+
const ifNoneMatch = headerValue(read.headers, "if-none-match");
|
|
143
|
+
const owned = context.owned;
|
|
144
|
+
if (cache.for !== "anonymous" || owned === undefined) {
|
|
145
|
+
const answer = await runOperation(context, { ok: true, request, requestId: read.requestId }, options);
|
|
146
|
+
return cachedReadAnswer(answer, cache, ifNoneMatch, false);
|
|
147
|
+
}
|
|
148
|
+
const report = createDiagnosticsReporter(context.sink, request);
|
|
149
|
+
const seeded = await seededState(options, report, {
|
|
150
|
+
path: ["arguments"],
|
|
151
|
+
arguments: request.arguments,
|
|
152
|
+
});
|
|
153
|
+
if (!seeded.ok) {
|
|
154
|
+
return uncachedAnswer(encodeOperationResponse(seeded.response, read.requestId));
|
|
155
|
+
}
|
|
156
|
+
const state = seeded.state ?? {};
|
|
157
|
+
let caller;
|
|
158
|
+
try {
|
|
159
|
+
caller = await owned.caller({ origin: context.origin, state });
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
report(error, "A3000");
|
|
163
|
+
return uncachedAnswer(encodeOperationResponse(internalFailure(), read.requestId));
|
|
164
|
+
}
|
|
165
|
+
const response = await owned.execute({ ...request, state }, undefined, caller === undefined ? undefined : { caller });
|
|
166
|
+
return cachedReadAnswer(encodeOperationResponse(response, read.requestId, operationUnencodableReport(context.sink, request)), cache, ifNoneMatch, isAnonymous(caller, read.headers));
|
|
167
|
+
}
|
|
168
|
+
function isAnonymous(caller, headers) {
|
|
169
|
+
return caller === undefined
|
|
170
|
+
? headerValue(headers, "cookie") === undefined &&
|
|
171
|
+
headerValue(headers, "authorization") === undefined
|
|
172
|
+
: caller.principal === null || caller.principal === undefined;
|
|
173
|
+
}
|
|
59
174
|
export async function handleTransactionRequest(context, request, options) {
|
|
60
175
|
const decoded = decodeTransactionRequest(context.contracts, request, context.origin);
|
|
61
176
|
if (!decoded.ok) {
|
|
62
177
|
return decoded.response;
|
|
63
178
|
}
|
|
179
|
+
const admitted = admitTransaction(context, decoded.request);
|
|
64
180
|
const seeded = await seededState(options, createReporter(context.sink, {
|
|
65
181
|
kind: "transaction",
|
|
66
182
|
origin: context.origin,
|
|
67
183
|
requestId: decoded.requestId,
|
|
68
|
-
}), { path: ["plan"], arguments:
|
|
184
|
+
}), { path: ["plan"], arguments: admitted.request.plan });
|
|
69
185
|
const response = seeded.ok
|
|
70
|
-
? await
|
|
71
|
-
?
|
|
72
|
-
: { ...
|
|
186
|
+
? await admitted.run(seeded.state === undefined
|
|
187
|
+
? admitted.request
|
|
188
|
+
: { ...admitted.request, state: seeded.state })
|
|
73
189
|
: seeded.response;
|
|
74
190
|
return encodeTransactionResponse(response, decoded.requestId, planUnencodableReport(context.sink, decoded.requestId, context.origin));
|
|
75
191
|
}
|
|
192
|
+
export async function handleCanRequest(context, request, options) {
|
|
193
|
+
const decoded = decodeCanRequest(context.contracts, request, context.origin);
|
|
194
|
+
if (!decoded.ok) {
|
|
195
|
+
return decoded.response;
|
|
196
|
+
}
|
|
197
|
+
const { requestId, body } = decoded.request;
|
|
198
|
+
const report = createReporter(context.sink, {
|
|
199
|
+
kind: "can",
|
|
200
|
+
origin: "client",
|
|
201
|
+
requestId,
|
|
202
|
+
});
|
|
203
|
+
if (context.can === undefined) {
|
|
204
|
+
report(new Error("The bound Framework has no answerer for /_access/can."), "A3000");
|
|
205
|
+
return encodeOperationResponse(internalFailure(), requestId);
|
|
206
|
+
}
|
|
207
|
+
const seeded = await seededState(options, report, {
|
|
208
|
+
path: ["questions"],
|
|
209
|
+
arguments: body,
|
|
210
|
+
});
|
|
211
|
+
const response = seeded.ok
|
|
212
|
+
? await context.can({
|
|
213
|
+
origin: context.origin,
|
|
214
|
+
requestId,
|
|
215
|
+
...(seeded.state === undefined ? {} : { state: seeded.state }),
|
|
216
|
+
body,
|
|
217
|
+
})
|
|
218
|
+
: seeded.response;
|
|
219
|
+
return encodeOperationResponse(response, requestId);
|
|
220
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AvContractClient } from "../contracts/contract.js";
|
|
2
|
+
import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
3
|
+
/** A cached read's mark, as the Client Contract states it. */
|
|
4
|
+
type ReadCacheMark = NonNullable<NonNullable<AvContractClient["resources"][string]["cache"]>["first" | "unique" | "many" | "count"]>;
|
|
5
|
+
/**
|
|
6
|
+
* A `GET`'s answer that must not be kept: `Cache-Control: no-store` (with
|
|
7
|
+
* `Referrer-Policy: no-referrer` and `X-Content-Type-Options: nosniff`). Every
|
|
8
|
+
* refusal, a stale client's `A2005` included, and every answer but `200`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function uncachedAnswer(response: AvProtocolResponse): AvProtocolResponse;
|
|
11
|
+
/**
|
|
12
|
+
* A cached read's answer. A `200` gets, by the mark's `for`:
|
|
13
|
+
*
|
|
14
|
+
* - `"everyone"`: `Cache-Control: public, max-age=<n>`;
|
|
15
|
+
* - `"anonymous"`: `public` to an `anonymous` caller, `private` otherwise, both
|
|
16
|
+
* with `Vary: Authorization, Cookie`;
|
|
17
|
+
* - `"user"`: `Cache-Control: private, max-age=<n>`, `Vary: Authorization,
|
|
18
|
+
* Cookie`;
|
|
19
|
+
*
|
|
20
|
+
* and `Referrer-Policy: no-referrer`, `X-Content-Type-Options: nosniff` and an
|
|
21
|
+
* `ETag` over its body; it is `304` with no body when `ifNoneMatch` names that
|
|
22
|
+
* tag. A public answer echoes no `Aventara-Request-Id`. Any other status is
|
|
23
|
+
* {@link uncachedAnswer}.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cachedReadAnswer(response: AvProtocolResponse, cache: ReadCacheMark, ifNoneMatch: string | undefined, anonymous: boolean): Promise<AvProtocolResponse>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { noneMatchHolds } from "./contract-document.responder.js";
|
|
2
|
+
import { headers, NO_SNIFF } from "./protocol-headers.js";
|
|
3
|
+
const CALLER_VARY = "Authorization, Cookie";
|
|
4
|
+
export function uncachedAnswer(response) {
|
|
5
|
+
return {
|
|
6
|
+
...response,
|
|
7
|
+
headers: {
|
|
8
|
+
...response.headers,
|
|
9
|
+
"Cache-Control": "no-store",
|
|
10
|
+
"Referrer-Policy": "no-referrer",
|
|
11
|
+
...NO_SNIFF,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export async function cachedReadAnswer(response, cache, ifNoneMatch, anonymous) {
|
|
16
|
+
if (response.status !== 200) {
|
|
17
|
+
return uncachedAnswer(response);
|
|
18
|
+
}
|
|
19
|
+
const shared = cache.for === "everyone" || (cache.for === "anonymous" && anonymous);
|
|
20
|
+
const validators = {
|
|
21
|
+
"Cache-Control": `${shared ? "public" : "private"}, max-age=${cache.maxAge}`,
|
|
22
|
+
...(cache.for === "everyone" ? {} : { Vary: CALLER_VARY }),
|
|
23
|
+
"Referrer-Policy": "no-referrer",
|
|
24
|
+
...NO_SNIFF,
|
|
25
|
+
ETag: `"${await bodyDigest(response.body)}"`,
|
|
26
|
+
};
|
|
27
|
+
const kept = { ...response.headers };
|
|
28
|
+
if (shared) {
|
|
29
|
+
delete kept[headers.requestId];
|
|
30
|
+
}
|
|
31
|
+
if (noneMatchHolds(ifNoneMatch, validators.ETag)) {
|
|
32
|
+
delete kept["Content-Type"];
|
|
33
|
+
return { status: 304, headers: { ...kept, ...validators }, body: "" };
|
|
34
|
+
}
|
|
35
|
+
return { ...response, headers: { ...kept, ...validators } };
|
|
36
|
+
}
|
|
37
|
+
async function bodyDigest(body) {
|
|
38
|
+
const digest = new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(body)));
|
|
39
|
+
let binary = "";
|
|
40
|
+
for (const byte of digest) {
|
|
41
|
+
binary += String.fromCharCode(byte);
|
|
42
|
+
}
|
|
43
|
+
return btoa(binary)
|
|
44
|
+
.replace(/\+/g, "-")
|
|
45
|
+
.replace(/\//g, "_")
|
|
46
|
+
.replace(/=+$/, "");
|
|
47
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** A cached read's query: its two parameters, each percent-decoded once. */
|
|
2
|
+
export type ReadQuery = {
|
|
3
|
+
/** The arguments, in either form. */
|
|
4
|
+
readonly q: string;
|
|
5
|
+
/** The Client Contract hash the client was generated against. */
|
|
6
|
+
readonly v: string;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The query's `q` and `v`, or `undefined` when it is anything else: absent,
|
|
10
|
+
* either parameter missing or repeated, another parameter, or a percent-escape
|
|
11
|
+
* that does not decode.
|
|
12
|
+
*/
|
|
13
|
+
export declare function readQueryOf(query: string | undefined): ReadQuery | undefined;
|
|
14
|
+
/** What reading `q` answers: the arguments object, or the transport code refusing it. */
|
|
15
|
+
type ReadArguments = {
|
|
16
|
+
readonly ok: true;
|
|
17
|
+
readonly value: object;
|
|
18
|
+
} | {
|
|
19
|
+
readonly ok: false;
|
|
20
|
+
readonly code: "A2000" | "A2010";
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* `q` as the arguments object: plain JSON when it starts with `{`, otherwise
|
|
24
|
+
* base64url(deflate-raw(JSON)). More than `maxBytes` bytes of JSON, or of
|
|
25
|
+
* compressed input, is `A2010`; anything that is not base64url, deflate-raw,
|
|
26
|
+
* UTF-8 or a JSON object is `A2000`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function readArgumentsOf(q: string, maxBytes: number): Promise<ReadArguments>;
|
|
29
|
+
/**
|
|
30
|
+
* A read's `q`: its arguments' canonical JSON, percent-encoded, or that JSON
|
|
31
|
+
* deflate-raw'd and base64url'd when that is shorter.
|
|
32
|
+
*/
|
|
33
|
+
export declare function readQueryArgumentOf(args: object): Promise<string>;
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { canonicalJson } from "../contracts/canonical-json.writer.js";
|
|
2
|
+
const BASE64URL = /^[A-Za-z0-9_-]*$/;
|
|
3
|
+
export function readQueryOf(query) {
|
|
4
|
+
if (query === undefined || query === "") {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
const found = new Map();
|
|
8
|
+
for (const pair of query.split("&")) {
|
|
9
|
+
const equals = pair.indexOf("=");
|
|
10
|
+
const name = equals === -1 ? pair : pair.slice(0, equals);
|
|
11
|
+
if ((name !== "q" && name !== "v") || found.has(name) || equals === -1) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const value = percentDecoded(pair.slice(equals + 1));
|
|
15
|
+
if (value === undefined) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
found.set(name, value);
|
|
19
|
+
}
|
|
20
|
+
const q = found.get("q");
|
|
21
|
+
const v = found.get("v");
|
|
22
|
+
return q === undefined || v === undefined ? undefined : { q, v };
|
|
23
|
+
}
|
|
24
|
+
function percentDecoded(text) {
|
|
25
|
+
try {
|
|
26
|
+
return decodeURIComponent(text);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function readArgumentsOf(q, maxBytes) {
|
|
33
|
+
if (q.startsWith("{")) {
|
|
34
|
+
const bytes = new TextEncoder().encode(q);
|
|
35
|
+
return bytes.byteLength > maxBytes
|
|
36
|
+
? { ok: false, code: "A2010" }
|
|
37
|
+
: parsedObject(q);
|
|
38
|
+
}
|
|
39
|
+
if (!BASE64URL.test(q) || q.length % 4 === 1) {
|
|
40
|
+
return { ok: false, code: "A2000" };
|
|
41
|
+
}
|
|
42
|
+
if (Math.floor((q.length * 3) / 4) > maxBytes) {
|
|
43
|
+
return { ok: false, code: "A2010" };
|
|
44
|
+
}
|
|
45
|
+
const compressed = base64urlBytes(q);
|
|
46
|
+
const inflated = await inflatedWithin(compressed, maxBytes);
|
|
47
|
+
if (inflated.ok === false) {
|
|
48
|
+
return inflated;
|
|
49
|
+
}
|
|
50
|
+
let text;
|
|
51
|
+
try {
|
|
52
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(inflated.bytes);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return { ok: false, code: "A2000" };
|
|
56
|
+
}
|
|
57
|
+
return parsedObject(text);
|
|
58
|
+
}
|
|
59
|
+
function parsedObject(text) {
|
|
60
|
+
let value;
|
|
61
|
+
try {
|
|
62
|
+
value = JSON.parse(text);
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { ok: false, code: "A2000" };
|
|
66
|
+
}
|
|
67
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
68
|
+
? { ok: true, value }
|
|
69
|
+
: { ok: false, code: "A2000" };
|
|
70
|
+
}
|
|
71
|
+
function base64urlBytes(text) {
|
|
72
|
+
const binary = atob(text.replace(/-/g, "+").replace(/_/g, "/") +
|
|
73
|
+
"=".repeat((4 - (text.length % 4)) % 4));
|
|
74
|
+
const bytes = new Uint8Array(binary.length);
|
|
75
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
76
|
+
bytes[index] = binary.charCodeAt(index);
|
|
77
|
+
}
|
|
78
|
+
return bytes;
|
|
79
|
+
}
|
|
80
|
+
async function inflatedWithin(compressed, maxBytes) {
|
|
81
|
+
const stream = new Blob([compressed])
|
|
82
|
+
.stream()
|
|
83
|
+
.pipeThrough(new DecompressionStream("deflate-raw"));
|
|
84
|
+
const reader = stream.getReader();
|
|
85
|
+
const chunks = [];
|
|
86
|
+
let total = 0;
|
|
87
|
+
try {
|
|
88
|
+
for (;;) {
|
|
89
|
+
const { done, value } = await reader.read();
|
|
90
|
+
if (done) {
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
total += value.byteLength;
|
|
94
|
+
if (total > maxBytes) {
|
|
95
|
+
await reader.cancel().catch(() => undefined);
|
|
96
|
+
return { ok: false, code: "A2010" };
|
|
97
|
+
}
|
|
98
|
+
chunks.push(value);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { ok: false, code: "A2000" };
|
|
103
|
+
}
|
|
104
|
+
const bytes = new Uint8Array(total);
|
|
105
|
+
let offset = 0;
|
|
106
|
+
for (const chunk of chunks) {
|
|
107
|
+
bytes.set(chunk, offset);
|
|
108
|
+
offset += chunk.byteLength;
|
|
109
|
+
}
|
|
110
|
+
return { ok: true, bytes };
|
|
111
|
+
}
|
|
112
|
+
export async function readQueryArgumentOf(args) {
|
|
113
|
+
const json = canonicalJson(args);
|
|
114
|
+
const plain = encodeURIComponent(json);
|
|
115
|
+
const deflated = await new Response(new Blob([json]).stream().pipeThrough(new CompressionStream("deflate-raw"))).arrayBuffer();
|
|
116
|
+
let binary = "";
|
|
117
|
+
for (const byte of new Uint8Array(deflated)) {
|
|
118
|
+
binary += String.fromCharCode(byte);
|
|
119
|
+
}
|
|
120
|
+
const compressed = btoa(binary)
|
|
121
|
+
.replace(/\+/g, "-")
|
|
122
|
+
.replace(/\//g, "_")
|
|
123
|
+
.replace(/=+$/, "");
|
|
124
|
+
return compressed.length < plain.length ? compressed : plain;
|
|
125
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Why a caller value could not be copied. */
|
|
2
|
-
|
|
2
|
+
type CallerDataFailureKind = "unreadable-value" | "non-plain-value";
|
|
3
3
|
/** A data location inside the copied value, from its root. */
|
|
4
4
|
export type CallerDataPath = readonly (string | number)[];
|
|
5
5
|
/**
|
|
@@ -41,6 +41,22 @@ export type CallerDataPolicy = {
|
|
|
41
41
|
readonly keep?: CallerDataKeep;
|
|
42
42
|
readonly mirror?: CallerDataMirror;
|
|
43
43
|
};
|
|
44
|
+
/** The members a structural mirror copies by value rather than mirroring. */
|
|
45
|
+
type MirrorMembers = {
|
|
46
|
+
/** The policy a member at `path` (from the mirror's root) is copied by, or `undefined`. */
|
|
47
|
+
readonly copyAt: (path: CallerDataPath) => CallerDataPolicy | undefined;
|
|
48
|
+
/** Receives a member whose copy failed; the mirror holds `undefined` for it. */
|
|
49
|
+
readonly refused: (path: CallerDataPath, error: AvCallerDataError) => void;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Mirrors `value` by its descriptors, as a `$ref` control object is mirrored (no
|
|
53
|
+
* getter runs, an accessor stays a tripwire), except the members `members.copyAt`
|
|
54
|
+
* names by their path: each is copied by value under the policy it returns, and one
|
|
55
|
+
* whose copy fails is reported to `members.refused` and held as `undefined`.
|
|
56
|
+
*
|
|
57
|
+
* @throws {@link AvCallerDataError} at `[]` when `value` itself cannot be reflected.
|
|
58
|
+
*/
|
|
59
|
+
export declare function mirrorCallerData(value: object, members: MirrorMembers): unknown;
|
|
44
60
|
/**
|
|
45
61
|
* Places a mirror tripwire's failure in the value the door copied: its path from the
|
|
46
62
|
* copy's root, where `error.path` started from the mirror. A failure that is not a
|
|
@@ -55,5 +71,4 @@ export declare function locateCallerDataError(error: AvCallerDataError, copy: un
|
|
|
55
71
|
* naming its path from `value`.
|
|
56
72
|
*/
|
|
57
73
|
export declare function copyCallerData(value: unknown, policy?: CallerDataPolicy): unknown;
|
|
58
|
-
|
|
59
|
-
export declare function callerDataFailureMessage(kind: CallerDataFailureKind): string;
|
|
74
|
+
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AvDecimal } from "./decimal.js";
|
|
1
|
+
import { AvDecimal, decimalDigitsOf, isAvDecimal } from "./decimal.js";
|
|
2
2
|
const FAILURE_MESSAGES = {
|
|
3
3
|
"unreadable-value": "Value could not be read.",
|
|
4
4
|
"non-plain-value": "Value must be plain data.",
|
|
@@ -19,7 +19,6 @@ const NO_POLICY = {};
|
|
|
19
19
|
const OBJECT_PROTOTYPE = Object.prototype;
|
|
20
20
|
const DATE_PROTOTYPE = Date.prototype;
|
|
21
21
|
const dateValueOf = Date.prototype.getTime;
|
|
22
|
-
const decimalValueOf = AvDecimal.prototype.toString;
|
|
23
22
|
const FAST_WALK_DEPTH = 256;
|
|
24
23
|
const SLOW_WALK = Symbol("slow walk");
|
|
25
24
|
class FastWalkFailure {
|
|
@@ -33,8 +32,8 @@ function copyValueType(value, prototype) {
|
|
|
33
32
|
if (prototype === DATE_PROTOTYPE || value instanceof Date) {
|
|
34
33
|
return new Date(dateValueOf.call(value));
|
|
35
34
|
}
|
|
36
|
-
if (value
|
|
37
|
-
return new AvDecimal(
|
|
35
|
+
if (isAvDecimal(value)) {
|
|
36
|
+
return new AvDecimal(decimalDigitsOf(value));
|
|
38
37
|
}
|
|
39
38
|
if (value instanceof Uint8Array) {
|
|
40
39
|
return new Uint8Array(value);
|
|
@@ -268,7 +267,7 @@ function pathSegment(array, key) {
|
|
|
268
267
|
}
|
|
269
268
|
return key;
|
|
270
269
|
}
|
|
271
|
-
function mirrorControlData(value) {
|
|
270
|
+
function mirrorControlData(value, members) {
|
|
272
271
|
const shape = reflectNode(value);
|
|
273
272
|
if (shape === undefined) {
|
|
274
273
|
throw new AvCallerDataError("unreadable-value", []);
|
|
@@ -375,6 +374,20 @@ function mirrorControlData(value) {
|
|
|
375
374
|
continue;
|
|
376
375
|
}
|
|
377
376
|
const memberLink = childLink(link, pathSegment(shape.array, key));
|
|
377
|
+
const byValue = members?.copyAt(pathOf(memberLink));
|
|
378
|
+
if (byValue !== undefined) {
|
|
379
|
+
try {
|
|
380
|
+
store(copyCallerData(member, byValue));
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
if (!(error instanceof AvCallerDataError)) {
|
|
384
|
+
throw error;
|
|
385
|
+
}
|
|
386
|
+
members?.refused(pathOf(memberLink), error);
|
|
387
|
+
store(undefined);
|
|
388
|
+
}
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
378
391
|
const memberShape = reflectNode(member);
|
|
379
392
|
if (memberShape === undefined) {
|
|
380
393
|
store(UNREADABLE);
|
|
@@ -408,6 +421,9 @@ function mirrorControlData(value) {
|
|
|
408
421
|
}
|
|
409
422
|
return root;
|
|
410
423
|
}
|
|
424
|
+
export function mirrorCallerData(value, members) {
|
|
425
|
+
return mirrorControlData(value, members);
|
|
426
|
+
}
|
|
411
427
|
export function locateCallerDataError(error, copy) {
|
|
412
428
|
const within = error.within;
|
|
413
429
|
if (within === undefined) {
|
|
@@ -465,6 +481,3 @@ export function copyCallerData(value, policy = NO_POLICY) {
|
|
|
465
481
|
}
|
|
466
482
|
return guardNative(() => copyFast(value, 0, policy), (failure) => copySlow(value, policy, failure instanceof FastWalkFailure ? failure.at : undefined));
|
|
467
483
|
}
|
|
468
|
-
export function callerDataFailureMessage(kind) {
|
|
469
|
-
return FAILURE_MESSAGES[kind];
|
|
470
|
-
}
|
|
@@ -6,15 +6,21 @@ import type { AvMisuseEvent, MisuseReporter } from "./misuse-reporter.js";
|
|
|
6
6
|
*
|
|
7
7
|
* - `"caller"`: a server-side caller's own objects — copied at the door.
|
|
8
8
|
* - `"owned"`: data no caller holds a reference to — a body the protocol decoder
|
|
9
|
-
* parsed with `JSON.parse` for this request alone,
|
|
10
|
-
*
|
|
9
|
+
* parsed with `JSON.parse` for this request alone, a host's parsed body the
|
|
10
|
+
* protocol's shortcut copied at the call, or a plan the typed runner assembled
|
|
11
|
+
* from copies its builders took. It is never copied again.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
13
|
+
* - `{ refused }`: a body the protocol's shortcut copied at the call, whose copy
|
|
14
|
+
* failed — answered at the door as `"caller"` would answer it, never read again.
|
|
15
|
+
*
|
|
16
|
+
* Only core's own modules choose anything but `"caller"`: the public
|
|
17
|
+
* `framework.execute` and `executeTransaction` always pass `"caller"`.
|
|
14
18
|
*/
|
|
15
|
-
export type ArgumentsSource = "caller" | "owned"
|
|
19
|
+
export type ArgumentsSource = "caller" | "owned" | {
|
|
20
|
+
readonly refused: AvCallerDataError;
|
|
21
|
+
};
|
|
16
22
|
/** The operation identity a misuse event may name, as far as it could be read. */
|
|
17
|
-
|
|
23
|
+
type MisuseIdentity = Omit<AvMisuseEvent, "kind" | "path">;
|
|
18
24
|
/**
|
|
19
25
|
* The identity of a request the framework holds a copy of, keeping only what is
|
|
20
26
|
* identity: an origin, a Resource name and a standard operation, each present only
|
|
@@ -54,3 +60,4 @@ export declare function copyAtDoor(value: unknown, policy?: CallerDataPolicy): C
|
|
|
54
60
|
* plan validator inspects and copies step by step.
|
|
55
61
|
*/
|
|
56
62
|
export declare function copyEnvelopeAtDoor(request: unknown, members: readonly string[]): CallerDataCopy;
|
|
63
|
+
export {};
|