@aventara/core 0.1.0-pilot.1 → 0.1.0-pilot.3
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/dist/adapters/adapter-field.d.ts +4 -4
- package/dist/adapters/adapter-resource.d.ts +10 -12
- package/dist/adapters/adapter.interface.d.ts +20 -10
- package/dist/compiler/contract-compiler-diagnostic.d.ts +3 -4
- package/dist/compiler/contract.compiler.d.ts +5 -4
- package/dist/compiler/contract.compiler.js +0 -132
- package/dist/config/config.resolver.d.ts +2 -2
- package/dist/config/config.resolver.js +0 -5
- package/dist/config/config.validator.d.ts +3 -3
- package/dist/config/config.validator.js +0 -8
- package/dist/config/define-fields.d.ts +2 -7
- package/dist/config/define-fields.js +0 -15
- package/dist/config/effective-fields.d.ts +4 -8
- package/dist/config/entrypoint.normalizer.d.ts +7 -7
- package/dist/config/entrypoint.normalizer.js +0 -22
- package/dist/config/framework-behaviors-config.d.ts +6 -6
- package/dist/config/framework-config.d.ts +3 -5
- package/dist/config/framework-restrictions-config.d.ts +19 -29
- package/dist/config/pipeline-config.d.ts +8 -18
- package/dist/contracts/capability.d.ts +18 -21
- package/dist/contracts/capability.js +0 -75
- package/dist/contracts/client-contract-structure.validator.d.ts +15 -22
- package/dist/contracts/client-contract-structure.validator.js +0 -27
- package/dist/contracts/contract-canonicalizer.js +0 -96
- package/dist/contracts/contract-hash.js +0 -2
- package/dist/contracts/contract-limits.js +0 -1
- package/dist/contracts/contract-lookup.js +0 -10
- package/dist/contracts/contract-protocol.js +0 -10
- package/dist/contracts/field.d.ts +23 -26
- package/dist/contracts/field.js +0 -107
- package/dist/contracts/lifecycle-trait.js +0 -5
- package/dist/contracts/resource-operations.d.ts +17 -18
- package/dist/contracts/resource-operations.js +0 -63
- package/dist/contracts/scalar-value-type.d.ts +12 -22
- package/dist/contracts/scalar.js +0 -1
- package/dist/contracts/wire-scalar-format.d.ts +20 -20
- package/dist/contracts/wire-scalar-format.js +0 -26
- package/dist/framework/contract-view.d.ts +7 -8
- package/dist/framework/framework-construction.error.js +0 -1
- package/dist/framework/framework.d.ts +26 -27
- package/dist/framework/framework.factory.js +0 -49
- package/dist/framework/operation-facade.js +0 -17
- package/dist/operations/operation-arguments.d.ts +36 -46
- package/dist/operations/operation-boolean-nodes.js +0 -16
- package/dist/operations/operation-call.d.ts +4 -9
- package/dist/operations/operation-identity.js +0 -1
- package/dist/operations/operation-nesting.js +0 -5
- package/dist/operations/operation-result.d.ts +4 -4
- package/dist/operations/operation-validator-core.d.ts +2 -2
- package/dist/operations/operation-validator-core.js +6 -71
- package/dist/operations/operation-validator.d.ts +4 -4
- package/dist/operations/operation-validator.js +0 -12
- package/dist/operations/projection-scope.resolver.d.ts +8 -10
- package/dist/operations/projection-scope.resolver.js +0 -35
- package/dist/operations/validation-code.d.ts +11 -18
- package/dist/operations/validation-code.js +0 -24
- package/dist/operations/write-scope.resolver.d.ts +4 -7
- package/dist/operations/write-scope.resolver.js +0 -37
- package/dist/protocol/absent-route.responder.d.ts +33 -48
- package/dist/protocol/absent-route.responder.js +0 -55
- package/dist/protocol/av-protocol.d.ts +6 -6
- package/dist/protocol/av-protocol.js +0 -10
- package/dist/protocol/contract-document.responder.d.ts +15 -18
- package/dist/protocol/contract-document.responder.js +0 -33
- package/dist/protocol/http-request.decoder.d.ts +55 -61
- package/dist/protocol/http-request.decoder.js +0 -62
- package/dist/protocol/http-request.reader.d.ts +12 -13
- package/dist/protocol/http-request.reader.js +0 -45
- package/dist/protocol/http-response.encoder.d.ts +17 -18
- package/dist/protocol/http-response.encoder.js +0 -59
- package/dist/protocol/http-status.table.d.ts +7 -11
- package/dist/protocol/http-status.table.js +1 -15
- package/dist/protocol/index.d.ts +5 -5
- package/dist/protocol/index.js +0 -9
- package/dist/protocol/operation-response.guard.d.ts +12 -19
- package/dist/protocol/operation-response.guard.js +0 -29
- package/dist/protocol/protocol-failure.d.ts +21 -26
- package/dist/protocol/protocol-failure.js +0 -12
- package/dist/protocol/protocol-headers.d.ts +25 -30
- package/dist/protocol/protocol-headers.js +0 -66
- package/dist/protocol/protocol-surface.d.ts +25 -29
- package/dist/protocol/protocol-surface.js +0 -19
- package/dist/protocol/protocol.binder.d.ts +58 -71
- package/dist/protocol/protocol.binder.js +0 -8
- package/dist/protocol/protocol.handler.d.ts +28 -32
- package/dist/protocol/protocol.handler.js +0 -43
- package/dist/runtime/computed-read-runtime.js +0 -29
- package/dist/runtime/create-requirement.d.ts +65 -0
- package/dist/runtime/create-requirement.js +141 -0
- package/dist/runtime/decimal.d.ts +2 -2
- package/dist/runtime/decimal.js +0 -8
- package/dist/runtime/diagnostics-reporter.d.ts +6 -6
- package/dist/runtime/diagnostics-reporter.js +0 -35
- package/dist/runtime/error-boundary.d.ts +3 -3
- package/dist/runtime/error-boundary.js +2 -167
- package/dist/runtime/execution-options.d.ts +4 -4
- package/dist/runtime/field-behavior.d.ts +6 -9
- package/dist/runtime/framework-error.js +0 -4
- package/dist/runtime/mutation-planner.d.ts +5 -6
- package/dist/runtime/mutation-planner.js +0 -23
- package/dist/runtime/operation-response.d.ts +1 -1
- package/dist/runtime/operation-response.js +1 -17
- package/dist/runtime/operation-runtime.d.ts +24 -22
- package/dist/runtime/operation-runtime.js +18 -52
- package/dist/runtime/outcome-mapper.js +0 -22
- package/dist/runtime/output-validator.js +0 -1
- package/dist/runtime/pipe-output.validator.d.ts +48 -0
- package/dist/runtime/pipe-output.validator.js +79 -0
- package/dist/runtime/pipeline.d.ts +20 -26
- package/dist/runtime/referential-action.d.ts +14 -19
- package/dist/runtime/referential-action.js +0 -23
- package/dist/runtime/request-id.js +0 -21
- package/dist/runtime/scalar-runtime.d.ts +14 -16
- package/dist/runtime/scalar-runtime.js +12 -57
- package/dist/transactions/deferred-operation.d.ts +9 -14
- package/dist/transactions/operation-arguments-encoder.d.ts +0 -1
- package/dist/transactions/operation-arguments-encoder.js +0 -14
- package/dist/transactions/operation-fingerprint.js +0 -1
- package/dist/transactions/transaction-envelope.js +0 -14
- package/dist/transactions/transaction-integrity-error.d.ts +10 -11
- package/dist/transactions/transaction-integrity-error.js +0 -5
- package/dist/transactions/transaction-plan-assembler.d.ts +0 -1
- package/dist/transactions/transaction-plan-assembler.js +0 -35
- package/dist/transactions/transaction-plan-validator.d.ts +5 -5
- package/dist/transactions/transaction-plan-validator.js +0 -63
- package/dist/transactions/transaction-reference-resolver.d.ts +12 -13
- package/dist/transactions/transaction-reference-resolver.js +0 -16
- package/dist/transactions/transaction-reference-validator.js +0 -8
- package/dist/transactions/transaction-reference.d.ts +19 -43
- package/dist/transactions/transaction-reference.js +0 -11
- package/dist/transactions/transaction-request.d.ts +4 -5
- package/dist/transactions/transaction-response.d.ts +1 -1
- package/dist/transactions/transaction-runtime.d.ts +2 -3
- package/dist/transactions/transaction-runtime.js +5 -44
- package/dist/transactions/transaction-slot-path-error.d.ts +10 -11
- package/dist/transactions/transaction-slot-path-error.js +0 -7
- package/dist/transactions/transaction-slot-writer.d.ts +12 -12
- package/dist/transactions/transaction-slot-writer.js +0 -34
- package/package.json +2 -2
|
@@ -3,20 +3,19 @@ import type { ClientOperationRequest } from "../runtime/operation-request.js";
|
|
|
3
3
|
import type { ClientTransactionRequest } from "../transactions/transaction-request.js";
|
|
4
4
|
import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
5
5
|
/**
|
|
6
|
-
* One HTTP request as a host hands it to the protocol: what the decoders
|
|
7
|
-
*
|
|
8
|
-
* row of the fixture corpus carries as its `request`.
|
|
6
|
+
* One HTTP request as a host hands it to the protocol: what the decoders read, and
|
|
7
|
+
* what each row of the fixture corpus carries as its `request`.
|
|
9
8
|
*
|
|
10
|
-
* Host-independent by construction: no host's request object, only the four
|
|
11
|
-
*
|
|
9
|
+
* Host-independent by construction: no host's request object, only the four facts
|
|
10
|
+
* every host can supply.
|
|
12
11
|
*/
|
|
13
12
|
export type AvProtocolRequest = {
|
|
14
13
|
/** The HTTP method as received. Anything but `POST` on a framework route is `A2012`. */
|
|
15
14
|
readonly method: string;
|
|
16
15
|
/**
|
|
17
|
-
* The request path RELATIVE to the configured entrypoint, as received (not
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* The request path RELATIVE to the configured entrypoint, as received (not yet
|
|
17
|
+
* URL-decoded): `/_resources/users/find/many`, `/_transactions`, `/_contract` —
|
|
18
|
+
* the space `protocol.surface()`'s paths live in.
|
|
20
19
|
*/
|
|
21
20
|
readonly path: string;
|
|
22
21
|
/**
|
|
@@ -26,16 +25,15 @@ export type AvProtocolRequest = {
|
|
|
26
25
|
*/
|
|
27
26
|
readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
28
27
|
/**
|
|
29
|
-
* The body, in one of two explicitly named arms
|
|
30
|
-
*
|
|
28
|
+
* The body, in one of two explicitly named arms — the decoder never guesses which
|
|
29
|
+
* one it was given:
|
|
31
30
|
*
|
|
32
|
-
* - `raw`: the decoded body
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* decoder ran; the decoder still refuses a non-object shape (`A2000`).
|
|
31
|
+
* - `raw`: the decoded body as received, bytes or text. The decoder enforces all
|
|
32
|
+
* of it: the size limit (`A2010`), the content type (`A2011`), JSON syntax and
|
|
33
|
+
* the body's shape (`A2000`). An absent body is zero bytes (`""`).
|
|
34
|
+
* - `parsed`: a value the host's own JSON parser already produced. Size, content
|
|
35
|
+
* type and JSON syntax were the host's to enforce before the decoder ran; the
|
|
36
|
+
* decoder still refuses a non-object shape (`A2000`).
|
|
39
37
|
*/
|
|
40
38
|
readonly body: {
|
|
41
39
|
readonly kind: "raw";
|
|
@@ -46,10 +44,10 @@ export type AvProtocolRequest = {
|
|
|
46
44
|
};
|
|
47
45
|
};
|
|
48
46
|
/**
|
|
49
|
-
* What a decoder answers
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
47
|
+
* What a decoder answers: the request core executes, or the framework response
|
|
48
|
+
* that answers the HTTP request instead — and, either way, the request id it runs
|
|
49
|
+
* or is answered under, which the host passes to the encoder:
|
|
50
|
+
* `protocol.encodeOperation(response, decoded.requestId)`.
|
|
53
51
|
*/
|
|
54
52
|
type Decoded<Request> = {
|
|
55
53
|
readonly ok: true;
|
|
@@ -69,55 +67,51 @@ export type DecodedOperationRequest = Extract<DecodedOperation, {
|
|
|
69
67
|
/** What `protocol.decodeTransaction` answers. */
|
|
70
68
|
export type DecodedTransaction = Decoded<ClientTransactionRequest>;
|
|
71
69
|
/**
|
|
72
|
-
* `protocol.decodeOperation(request)` — one operation route's HTTP request as
|
|
73
|
-
*
|
|
70
|
+
* `protocol.decodeOperation(request)` — one operation route's HTTP request as the
|
|
71
|
+
* client-origin request `framework.execute` takes, or its answer.
|
|
74
72
|
*
|
|
75
|
-
* Total over ANY value
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
73
|
+
* Total over ANY value: a value that is not an `AvProtocolRequest` at all — not an
|
|
74
|
+
* object, a missing or mistyped member, a member behind a getter or trap that
|
|
75
|
+
* throws — is malformed, `A2000` under a minted id, before anything else is read;
|
|
76
|
+
* nothing it carries is believed. Otherwise, in order, each step answering before
|
|
77
|
+
* the next runs:
|
|
80
78
|
*
|
|
81
|
-
* 1. the request id: `Aventara-Request-Id` adopted when it is 1–128 visible
|
|
82
|
-
*
|
|
83
|
-
* refusal (Q13, Q10);
|
|
79
|
+
* 1. the request id: `Aventara-Request-Id` adopted when it is 1–128 visible ASCII
|
|
80
|
+
* characters, otherwise minted by core's one helper — never a refusal;
|
|
84
81
|
* 2. the path: `/_resources/<resource>/<family>/<variant>`, each segment
|
|
85
82
|
* URL-decoded once and none empty, or `A2000`;
|
|
86
|
-
* 3. identity and absence, with no body read: a request missing either
|
|
87
|
-
*
|
|
88
|
-
* the
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* host's by construction: a parser that turns an absent body into `{}`
|
|
104
|
-
* makes absence indistinguishable from `{}`, which is valid.
|
|
83
|
+
* 3. identity and absence, with no body read: a request missing either identity
|
|
84
|
+
* header is malformed; then the protocol version (`A2006`), the ClientContract
|
|
85
|
+
* hash (`A2005`), the Resource (`A2001`) and the operation (`A2002`), each
|
|
86
|
+
* core's own envelope — so an unadvertised triple never reaches `execute`, in
|
|
87
|
+
* either route style;
|
|
88
|
+
* 4. the method: anything but `POST` is `A2012`, carrying `Allow: POST` (RFC 9110
|
|
89
|
+
* §15.5.6);
|
|
90
|
+
* 5. the body. On the RAW arm the decoder enforces all of it: the content type
|
|
91
|
+
* must be `application/json` (`A2011`; parameters are not read), the decoded
|
|
92
|
+
* byte count must not exceed the ClientContract's `limits.maxRequestBytes`, and
|
|
93
|
+
* the bytes must be UTF-8 JSON (`A2000`). On the PARSED arm the host's own
|
|
94
|
+
* parser already read the bytes, so size, content type and JSON syntax were the
|
|
95
|
+
* HOST's to enforce — answering its parser's failures with `protocol.failure` —
|
|
96
|
+
* and the decoder checks none of them. Both arms then refuse a body that is not
|
|
97
|
+
* a JSON object — an array, a scalar, `null`, or none (`A2000`). One gap is the
|
|
98
|
+
* host's by construction: a parser that turns an absent body into `{}` makes
|
|
99
|
+
* absence indistinguishable from `{}`, which is valid.
|
|
105
100
|
*
|
|
106
|
-
* The request is client origin, always
|
|
107
|
-
* application origin. `transport` carries `POST` and the request's headers
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
101
|
+
* The request is client origin, always: nothing the caller sends spells
|
|
102
|
+
* application origin. `transport` carries `POST` and the request's headers. The
|
|
103
|
+
* body is the operation's arguments verbatim: the decoder never reads into it, so
|
|
104
|
+
* routing metadata in it is core's to refuse, and a parsed body reaches core as
|
|
105
|
+
* the same object.
|
|
111
106
|
*/
|
|
112
107
|
export declare function decodeOperationRequest(contracts: FrameworkContracts, received: AvProtocolRequest): DecodedOperation;
|
|
113
108
|
/**
|
|
114
109
|
* `protocol.decodeTransaction(request)` — the `/_transactions` request as the
|
|
115
|
-
* client-origin plan request `framework.executeTransaction` takes, or its
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* origin and cannot say otherwise. The body is the plan verbatim.
|
|
110
|
+
* client-origin plan request `framework.executeTransaction` takes, or its answer.
|
|
111
|
+
* The same steps as {@link decodeOperationRequest}, with the path exactly
|
|
112
|
+
* `/_transactions` and its absence being a ClientContract without interactive
|
|
113
|
+
* transactions. Every step of the plan then runs under ClientContract rules: the
|
|
114
|
+
* plan is client origin and cannot say otherwise. The body is the plan verbatim.
|
|
121
115
|
*/
|
|
122
116
|
export declare function decodeTransactionRequest(contracts: FrameworkContracts, received: AvProtocolRequest): DecodedTransaction;
|
|
123
117
|
export {};
|
|
@@ -2,47 +2,6 @@ import { checkOperationRoute, checkTransactionsRoute, } from "./absent-route.res
|
|
|
2
2
|
import { readRequest } from "./http-request.reader.js";
|
|
3
3
|
import { transportFailure, } from "./protocol-failure.js";
|
|
4
4
|
import { headerValue } from "./protocol-headers.js";
|
|
5
|
-
/**
|
|
6
|
-
* `protocol.decodeOperation(request)` — one operation route's HTTP request as
|
|
7
|
-
* the client-origin request `framework.execute` takes, or its answer.
|
|
8
|
-
*
|
|
9
|
-
* Total over ANY value (the director, 2026-10-05): a value that is not an
|
|
10
|
-
* `AvProtocolRequest` at all — not an object, a missing or mistyped member, a
|
|
11
|
-
* member behind a getter or trap that throws — is malformed, `A2000` under a
|
|
12
|
-
* minted id, before anything else is read; nothing it carries is believed.
|
|
13
|
-
* Otherwise, in order, each step answering before the next runs:
|
|
14
|
-
*
|
|
15
|
-
* 1. the request id: `Aventara-Request-Id` adopted when it is 1–128 visible
|
|
16
|
-
* ASCII characters, otherwise minted by core's one helper — never a
|
|
17
|
-
* refusal (Q13, Q10);
|
|
18
|
-
* 2. the path: `/_resources/<resource>/<family>/<variant>`, each segment
|
|
19
|
-
* URL-decoded once and none empty, or `A2000`;
|
|
20
|
-
* 3. identity and absence, with no body read: a request missing either
|
|
21
|
-
* identity header is malformed (`A2000`; the architect, 2026-10-05); then
|
|
22
|
-
* the protocol version (`A2006`), the ClientContract hash (`A2005`), the
|
|
23
|
-
* Resource (`A2001`) and the operation (`A2002`), each core's own
|
|
24
|
-
* envelope — so an unadvertised triple never reaches `execute`, in either
|
|
25
|
-
* route style (Q12, Q2's O3);
|
|
26
|
-
* 4. the method: anything but `POST` is `A2012`, carrying `Allow: POST`
|
|
27
|
-
* (RFC 9110 §15.5.6);
|
|
28
|
-
* 5. the body (Q11). On the RAW arm the decoder enforces all of it: the
|
|
29
|
-
* content type must be `application/json` (`A2011`; parameters are not
|
|
30
|
-
* read), the decoded byte count must not exceed the ClientContract's
|
|
31
|
-
* `limits.maxRequestBytes` (`A2010`, §20.1), and the bytes must be UTF-8
|
|
32
|
-
* JSON (`A2000`). On the PARSED arm the host's own parser already read the
|
|
33
|
-
* bytes, so size, content type and JSON syntax were the HOST's to enforce —
|
|
34
|
-
* answering its parser's failures with `protocol.failure` — and the decoder
|
|
35
|
-
* checks none of them. Both arms then refuse a body that is not a JSON
|
|
36
|
-
* object — an array, a scalar, `null`, or none (`A2000`). One gap is the
|
|
37
|
-
* host's by construction: a parser that turns an absent body into `{}`
|
|
38
|
-
* makes absence indistinguishable from `{}`, which is valid.
|
|
39
|
-
*
|
|
40
|
-
* The request is client origin, always (P4): nothing the caller sends spells
|
|
41
|
-
* application origin. `transport` carries `POST` and the request's headers
|
|
42
|
-
* (Q7). The body is the operation's arguments verbatim (§12.3): the decoder
|
|
43
|
-
* never reads into it, so routing metadata in it is core's to refuse (N16),
|
|
44
|
-
* and a parsed body reaches core as the same object.
|
|
45
|
-
*/
|
|
46
5
|
export function decodeOperationRequest(contracts, received) {
|
|
47
6
|
const read = readRequest(received);
|
|
48
7
|
if (!read.ok) {
|
|
@@ -69,15 +28,6 @@ export function decodeOperationRequest(contracts, received) {
|
|
|
69
28
|
}
|
|
70
29
|
: refused(body.response, read.requestId);
|
|
71
30
|
}
|
|
72
|
-
/**
|
|
73
|
-
* `protocol.decodeTransaction(request)` — the `/_transactions` request as the
|
|
74
|
-
* client-origin plan request `framework.executeTransaction` takes, or its
|
|
75
|
-
* answer. The same steps as {@link decodeOperationRequest}, with the path
|
|
76
|
-
* exactly `/_transactions` and its absence being a ClientContract without
|
|
77
|
-
* interactive transactions (`A2002` at `["operations"]`, M17). Every step of
|
|
78
|
-
* the plan then runs under ClientContract rules (R1): the plan is client
|
|
79
|
-
* origin and cannot say otherwise. The body is the plan verbatim.
|
|
80
|
-
*/
|
|
81
31
|
export function decodeTransactionRequest(contracts, received) {
|
|
82
32
|
const read = readRequest(received);
|
|
83
33
|
if (!read.ok) {
|
|
@@ -105,21 +55,15 @@ export function decodeTransactionRequest(contracts, received) {
|
|
|
105
55
|
function refused(response, requestId) {
|
|
106
56
|
return { ok: false, response, requestId };
|
|
107
57
|
}
|
|
108
|
-
/** Q7: every client request carries `POST` and its headers, one spelling. */
|
|
109
58
|
function transportOf(read) {
|
|
110
59
|
return { method: "POST", headers: read.headers };
|
|
111
60
|
}
|
|
112
|
-
/**
|
|
113
|
-
* Step 5, after the route checks passed: the body as a JSON object, or the
|
|
114
|
-
* answer refusing it. Only the raw arm is measured, typed and parsed (Q11).
|
|
115
|
-
*/
|
|
116
61
|
function readBody(read, contracts) {
|
|
117
62
|
const code = bodyValue(read.request.body, headerValue(read.headers, "content-type"), contracts.client.limits.maxRequestBytes);
|
|
118
63
|
return typeof code === "string"
|
|
119
64
|
? { ok: false, response: transportFailure(code, read.requestId) }
|
|
120
65
|
: { ok: true, value: code.value };
|
|
121
66
|
}
|
|
122
|
-
/** The body's JSON object, or the transport code that refuses it. */
|
|
123
67
|
function bodyValue(body, contentType, maxRequestBytes) {
|
|
124
68
|
if (body.kind === "parsed") {
|
|
125
69
|
return isJsonObject(body.value) ? { value: body.value } : "A2000";
|
|
@@ -143,18 +87,12 @@ function bodyValue(body, contentType, maxRequestBytes) {
|
|
|
143
87
|
}
|
|
144
88
|
return isJsonObject(value) ? { value } : "A2000";
|
|
145
89
|
}
|
|
146
|
-
/** `application/json`, in any case; parameters (`; charset=…`) are not read. */
|
|
147
90
|
function isJsonMediaType(contentType) {
|
|
148
91
|
return (contentType
|
|
149
92
|
?.split(";")[0]
|
|
150
93
|
?.replace(/^[ \t]+|[ \t]+$/g, "")
|
|
151
94
|
.toLowerCase() === "application/json");
|
|
152
95
|
}
|
|
153
|
-
/**
|
|
154
|
-
* An object that is not a list (§12.6: a JSON array, scalar, `null` or absent
|
|
155
|
-
* body is invalid). Read without touching the value's own members, so a
|
|
156
|
-
* parsed body is never traversed; a revoked proxy is no object.
|
|
157
|
-
*/
|
|
158
96
|
function isJsonObject(value) {
|
|
159
97
|
try {
|
|
160
98
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -2,27 +2,27 @@ import type { ClientProtocolIdentity } from "../runtime/operation-request.js";
|
|
|
2
2
|
import type { OperationRouteSegments } from "./absent-route.responder.js";
|
|
3
3
|
import type { AvProtocolRequest } from "./http-request.decoder.js";
|
|
4
4
|
import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
5
|
-
/** `POST <entrypoint>/_transactions
|
|
5
|
+
/** `POST <entrypoint>/_transactions`. */
|
|
6
6
|
export declare const TRANSACTIONS_ROUTE = "/_transactions";
|
|
7
|
-
/** `GET <entrypoint>/_contract
|
|
7
|
+
/** `GET <entrypoint>/_contract`. */
|
|
8
8
|
export declare const CONTRACT_ROUTE = "/_contract";
|
|
9
|
-
/** The methods an operation or `/_transactions` route accepts: `POST`, only
|
|
9
|
+
/** The methods an operation or `/_transactions` route accepts: `POST`, only. */
|
|
10
10
|
export declare const POST_ONLY: readonly string[];
|
|
11
|
-
/** The methods the contract route accepts: `GET`, only
|
|
11
|
+
/** The methods the contract route accepts: `GET`, only. */
|
|
12
12
|
export declare const GET_ONLY: readonly string[];
|
|
13
13
|
/**
|
|
14
14
|
* Whether a path lies in the protocol's namespace: underscore-prefixed paths
|
|
15
|
-
* beneath the entrypoint are reserved for framework facilities; every other
|
|
16
|
-
*
|
|
15
|
+
* beneath the entrypoint are reserved for framework facilities; every other path
|
|
16
|
+
* is the host's.
|
|
17
17
|
*/
|
|
18
18
|
export declare function isProtocolPath(path: string): boolean;
|
|
19
19
|
/** A request the protocol could read, with what every route reads off it first. */
|
|
20
20
|
export type ReadRequest = {
|
|
21
21
|
readonly ok: true;
|
|
22
22
|
readonly request: AvProtocolRequest;
|
|
23
|
-
/** Names lower-cased, repeated values joined
|
|
23
|
+
/** Names lower-cased, repeated values joined. */
|
|
24
24
|
readonly headers: Readonly<Record<string, string>>;
|
|
25
|
-
/** Adopted from `Aventara-Request-Id
|
|
25
|
+
/** Adopted from `Aventara-Request-Id`, else minted. */
|
|
26
26
|
readonly requestId: string;
|
|
27
27
|
/** `undefined` when either identity header is absent (`A2000`). */
|
|
28
28
|
readonly protocol: ClientProtocolIdentity | undefined;
|
|
@@ -34,13 +34,12 @@ export type UnreadableRequest = {
|
|
|
34
34
|
readonly requestId: string;
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
|
-
* The host's value read as a request, or — when it is not an
|
|
38
|
-
*
|
|
39
|
-
* id (the director, 2026-10-05).
|
|
37
|
+
* The host's value read as a request, or — when it is not an `AvProtocolRequest`
|
|
38
|
+
* at all — the `400 A2000` answering it, under a minted id.
|
|
40
39
|
*/
|
|
41
40
|
export declare function readRequest(received: unknown): ReadRequest | UnreadableRequest;
|
|
42
41
|
/**
|
|
43
|
-
* The three segments of an operation route, each URL-decoded and none empty
|
|
44
|
-
*
|
|
42
|
+
* The three segments of an operation route, each URL-decoded and none empty;
|
|
43
|
+
* `undefined` when it is not one.
|
|
45
44
|
*/
|
|
46
45
|
export declare function operationRouteSegments(path: string): OperationRouteSegments | undefined;
|
|
@@ -1,36 +1,14 @@
|
|
|
1
1
|
import { requestIdOf } from "../runtime/request-id.js";
|
|
2
2
|
import { transportFailure } from "./protocol-failure.js";
|
|
3
3
|
import { adoptedRequestId, normalizeHeaders, protocolIdentityOf, } from "./protocol-headers.js";
|
|
4
|
-
/**
|
|
5
|
-
* Reading a host's request before any route answers it: the request as a
|
|
6
|
-
* value of its declared type, read once; the request id it is answered under
|
|
7
|
-
* (Q13); its headers as one spelling (Q7); its identity headers; and the
|
|
8
|
-
* route paths the protocol owns (§12.1). Shared by the decoders, the absence
|
|
9
|
-
* responder and the contract responder, so each reads a request one way.
|
|
10
|
-
*/
|
|
11
|
-
/** `<entrypoint>/_resources/:resource/:family/:variant` (§12.1). */
|
|
12
4
|
const OPERATION_ROUTE = /^\/_resources\/([^/]*)\/([^/]*)\/([^/]*)$/;
|
|
13
|
-
/** `POST <entrypoint>/_transactions` (§12.1). */
|
|
14
5
|
export const TRANSACTIONS_ROUTE = "/_transactions";
|
|
15
|
-
/** `GET <entrypoint>/_contract` (§12.1, §12.5). */
|
|
16
6
|
export const CONTRACT_ROUTE = "/_contract";
|
|
17
|
-
/** The methods an operation or `/_transactions` route accepts: `POST`, only (§12.2). */
|
|
18
7
|
export const POST_ONLY = Object.freeze(["POST"]);
|
|
19
|
-
/** The methods the contract route accepts: `GET`, only (§12.2). */
|
|
20
8
|
export const GET_ONLY = Object.freeze(["GET"]);
|
|
21
|
-
/**
|
|
22
|
-
* Whether a path lies in the protocol's namespace: underscore-prefixed paths
|
|
23
|
-
* beneath the entrypoint are reserved for framework facilities; every other
|
|
24
|
-
* path is the host's (§12.1).
|
|
25
|
-
*/
|
|
26
9
|
export function isProtocolPath(path) {
|
|
27
10
|
return path.startsWith("/_");
|
|
28
11
|
}
|
|
29
|
-
/**
|
|
30
|
-
* The host's value read as a request, or — when it is not an
|
|
31
|
-
* `AvProtocolRequest` at all — the `400 A2000` answering it, under a minted
|
|
32
|
-
* id (the director, 2026-10-05).
|
|
33
|
-
*/
|
|
34
12
|
export function readRequest(received) {
|
|
35
13
|
const request = wellFormedRequest(received);
|
|
36
14
|
if (request === undefined) {
|
|
@@ -45,10 +23,6 @@ export function readRequest(received) {
|
|
|
45
23
|
protocol: protocolIdentityOf(headers),
|
|
46
24
|
};
|
|
47
25
|
}
|
|
48
|
-
/**
|
|
49
|
-
* The three segments of an operation route, each URL-decoded and none empty
|
|
50
|
-
* (§12.6 refuses an empty-resource route); `undefined` when it is not one.
|
|
51
|
-
*/
|
|
52
26
|
export function operationRouteSegments(path) {
|
|
53
27
|
const match = OPERATION_ROUTE.exec(path);
|
|
54
28
|
if (match === null) {
|
|
@@ -61,7 +35,6 @@ export function operationRouteSegments(path) {
|
|
|
61
35
|
? { resource, family, variant }
|
|
62
36
|
: undefined;
|
|
63
37
|
}
|
|
64
|
-
/** One path segment, URL-decoded once; `undefined` when it is not decodable. */
|
|
65
38
|
function decodeSegment(segment) {
|
|
66
39
|
try {
|
|
67
40
|
return decodeURIComponent(segment);
|
|
@@ -70,11 +43,6 @@ function decodeSegment(segment) {
|
|
|
70
43
|
return undefined;
|
|
71
44
|
}
|
|
72
45
|
}
|
|
73
|
-
/**
|
|
74
|
-
* The answer to a value that is not an `AvProtocolRequest`: `A2000` under a
|
|
75
|
-
* freshly minted id — the value's own headers are not believed, so no id is
|
|
76
|
-
* adopted from them.
|
|
77
|
-
*/
|
|
78
46
|
function malformedRequest() {
|
|
79
47
|
const requestId = requestIdOf(undefined);
|
|
80
48
|
return {
|
|
@@ -83,20 +51,8 @@ function malformedRequest() {
|
|
|
83
51
|
requestId,
|
|
84
52
|
};
|
|
85
53
|
}
|
|
86
|
-
/**
|
|
87
|
-
* The host's request, read ONCE into a value of its declared type, or
|
|
88
|
-
* `undefined` when it is not one: not an object, a `method` or `path` that is
|
|
89
|
-
* not text, `headers` that are not a record of text or lists of text, or a
|
|
90
|
-
* `body` of neither arm (Q11) — raw content that is neither text nor bytes
|
|
91
|
-
* has no byte count to measure. Each member is read once, so a getter cannot
|
|
92
|
-
* answer one check and lie to the next, and one that throws — like any trap —
|
|
93
|
-
* makes the request malformed rather than the decoder throw. A parsed body's
|
|
94
|
-
* value is kept as the same object, never read into (§12.3).
|
|
95
|
-
*/
|
|
96
54
|
function wellFormedRequest(received) {
|
|
97
55
|
try {
|
|
98
|
-
// `null` and `undefined` throw here; any other non-object has no
|
|
99
|
-
// members, so its `method` is not text.
|
|
100
56
|
const { method, path, headers, body } = received;
|
|
101
57
|
const copied = headerRecord(headers);
|
|
102
58
|
const armed = armedBody(body);
|
|
@@ -130,7 +86,6 @@ function headerRecord(headers) {
|
|
|
130
86
|
return undefined;
|
|
131
87
|
}
|
|
132
88
|
}
|
|
133
|
-
// `fromEntries` defines each member, so a header named `__proto__` stays data.
|
|
134
89
|
return Object.fromEntries(copied);
|
|
135
90
|
}
|
|
136
91
|
function armedBody(body) {
|
|
@@ -7,41 +7,40 @@ import type { TransactionResponse } from "../transactions/transaction-response.j
|
|
|
7
7
|
* text, and adds nothing of the framework's.
|
|
8
8
|
*/
|
|
9
9
|
export type AvProtocolResponse = {
|
|
10
|
-
/** `AvProtocol.httpStatus` of the envelope's code
|
|
10
|
+
/** `AvProtocol.httpStatus` of the envelope's code. */
|
|
11
11
|
readonly status: number;
|
|
12
12
|
/**
|
|
13
13
|
* The headers the framework owns on this response, canonically spelled:
|
|
14
|
-
* `Content-Type: application/json` and the `Aventara-Request-Id` it echoes
|
|
15
|
-
* (Q13).
|
|
14
|
+
* `Content-Type: application/json` and the `Aventara-Request-Id` it echoes.
|
|
16
15
|
*/
|
|
17
16
|
readonly headers: Readonly<Record<string, string>>;
|
|
18
|
-
/** The JSON text of the envelope
|
|
17
|
+
/** The JSON text of the envelope, its data in wire forms. */
|
|
19
18
|
readonly body: string;
|
|
20
19
|
};
|
|
21
20
|
/**
|
|
22
21
|
* Where an encoder hands the raw failure behind an `A3003` — the binding's
|
|
23
|
-
* diagnostics channel
|
|
24
|
-
*
|
|
22
|
+
* diagnostics channel — with the plan step it blames, if any. The wire never
|
|
23
|
+
* carries it.
|
|
25
24
|
*/
|
|
26
25
|
export type UnencodableReport = (error: unknown, operation?: number) => void;
|
|
27
26
|
/**
|
|
28
27
|
* `protocol.encodeOperation(response, requestId)`: one operation's framework
|
|
29
|
-
* response as the HTTP response a host writes. Status is the code's
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* response as the HTTP response a host writes. Status is the code's; `data` is
|
|
29
|
+
* wire-encoded; `requestId` is echoed as `Aventara-Request-Id` — pass the id the
|
|
30
|
+
* request ran under, which the decoder supplies.
|
|
32
31
|
*
|
|
33
|
-
* Total: a result it cannot encode — and a response whose code is not
|
|
34
|
-
*
|
|
35
|
-
*
|
|
32
|
+
* Total: a result it cannot encode — and a response whose code is not allocated —
|
|
33
|
+
* answers `A3003` → 500 with the framework's own text, and none of the result
|
|
34
|
+
* reaches the body.
|
|
36
35
|
*/
|
|
37
36
|
export declare function encodeOperationResponse(response: OperationResponse, requestId: string, report?: UnencodableReport): AvProtocolResponse;
|
|
38
37
|
/**
|
|
39
38
|
* `protocol.encodeTransaction(response, requestId)`: the same for a plan's
|
|
40
|
-
* response
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
39
|
+
* response. A committed plan is `200 A1009` — even when it created something —
|
|
40
|
+
* with each operation's data wire-encoded in plan order by the same walker as
|
|
41
|
+
* {@link encodeOperationResponse}; a rolled-back plan answers its failing
|
|
42
|
+
* operation's own status. A step whose result cannot be encoded answers `A3003` →
|
|
43
|
+
* 500 naming that step as `cause.operation`, as every internal failure inside a
|
|
44
|
+
* plan does.
|
|
46
45
|
*/
|
|
47
46
|
export declare function encodeTransactionResponse(response: TransactionResponse, requestId: string, report?: UnencodableReport): AvProtocolResponse;
|
|
@@ -4,16 +4,6 @@ import { SERIALIZATION_FAILURE_MESSAGE } from "../runtime/error-boundary.js";
|
|
|
4
4
|
import { operationFailure, } from "../runtime/operation-response.js";
|
|
5
5
|
import { httpStatus } from "./http-status.table.js";
|
|
6
6
|
import { headers } from "./protocol-headers.js";
|
|
7
|
-
/**
|
|
8
|
-
* `protocol.encodeOperation(response, requestId)`: one operation's framework
|
|
9
|
-
* response as the HTTP response a host writes. Status is the code's (§13.2);
|
|
10
|
-
* `data` is wire-encoded (P1); `requestId` is echoed as `Aventara-Request-Id`
|
|
11
|
-
* (Q13) — pass the id the request ran under, which the decoder supplies.
|
|
12
|
-
*
|
|
13
|
-
* Total: a result it cannot encode — and a response whose code is not
|
|
14
|
-
* allocated — answers `A3003` → 500 with the framework's own text, and none of
|
|
15
|
-
* the result reaches the body.
|
|
16
|
-
*/
|
|
17
7
|
export function encodeOperationResponse(response, requestId, report) {
|
|
18
8
|
try {
|
|
19
9
|
return written(response, toWire(response.data, new Set()), requestId);
|
|
@@ -23,15 +13,6 @@ export function encodeOperationResponse(response, requestId, report) {
|
|
|
23
13
|
return written(serializationFailure(), null, requestId);
|
|
24
14
|
}
|
|
25
15
|
}
|
|
26
|
-
/**
|
|
27
|
-
* `protocol.encodeTransaction(response, requestId)`: the same for a plan's
|
|
28
|
-
* response (§14). A committed plan is `200 A1009` — even when it created
|
|
29
|
-
* something (P3) — with each operation's data wire-encoded in plan order by
|
|
30
|
-
* the same walker as {@link encodeOperationResponse}; a rolled-back plan
|
|
31
|
-
* answers its failing operation's own status (Q4). A step whose result cannot
|
|
32
|
-
* be encoded answers `A3003` → 500 naming that step as `cause.operation`, as
|
|
33
|
-
* every internal failure inside a plan does (Q5).
|
|
34
|
-
*/
|
|
35
16
|
export function encodeTransactionResponse(response, requestId, report) {
|
|
36
17
|
if (response.code !== "A1009") {
|
|
37
18
|
return encodeOperationResponse(response, requestId, report);
|
|
@@ -53,18 +34,12 @@ export function encodeTransactionResponse(response, requestId, report) {
|
|
|
53
34
|
return written(serializationFailure(step), null, requestId);
|
|
54
35
|
}
|
|
55
36
|
}
|
|
56
|
-
/** The one A3003 envelope: the framework's text, plus the step in a plan. */
|
|
57
37
|
function serializationFailure(operation) {
|
|
58
38
|
return operationFailure("A3003", {
|
|
59
39
|
message: SERIALIZATION_FAILURE_MESSAGE,
|
|
60
40
|
...(operation === undefined ? {} : { operation }),
|
|
61
41
|
});
|
|
62
42
|
}
|
|
63
|
-
/**
|
|
64
|
-
* Writes an envelope whose data is already in wire form. Throws for a code
|
|
65
|
-
* with no status (only an untyped caller can supply one), which every caller
|
|
66
|
-
* turns into `A3003`.
|
|
67
|
-
*/
|
|
68
43
|
function written(response, wireData, requestId) {
|
|
69
44
|
const { code, cause } = response;
|
|
70
45
|
if (!Object.hasOwn(httpStatus, code)) {
|
|
@@ -79,37 +54,9 @@ function written(response, wireData, requestId) {
|
|
|
79
54
|
body: JSON.stringify({ data: wireData, code, cause }),
|
|
80
55
|
};
|
|
81
56
|
}
|
|
82
|
-
/**
|
|
83
|
-
* What the walker throws for a value with no wire form — never past an
|
|
84
|
-
* encoder, which answers `A3003` and hands it to the diagnostics channel, so
|
|
85
|
-
* it names the offending value as its `cause` for the developer.
|
|
86
|
-
*/
|
|
87
57
|
function unencodable(value) {
|
|
88
58
|
return new TypeError("A result value has no wire form.", { cause: value });
|
|
89
59
|
}
|
|
90
|
-
/**
|
|
91
|
-
* The P1 wire walker: a result's runtime value as the JSON value the wire
|
|
92
|
-
* carries. Value-directed, with no Contract read: exactly four runtime classes
|
|
93
|
-
* are scalars whose wire form differs from their runtime form (A6), and a
|
|
94
|
-
* `json` value is a `JsonValue`, which contains none of them —
|
|
95
|
-
*
|
|
96
|
-
* - `bigint` → its base-10 string;
|
|
97
|
-
* - `Decimal` (core's) → its decimal string (not through `toJSON`, Q16);
|
|
98
|
-
* - `Date` → `toISOString()`, which must match the datetime wire grammar — an
|
|
99
|
-
* invalid Date, or a year the grammar's four digits cannot spell, has none;
|
|
100
|
-
* - `Uint8Array` → canonical base64.
|
|
101
|
-
*
|
|
102
|
-
* Everything else must already be JSON: `null`, a boolean, a string, a finite
|
|
103
|
-
* number, a list, or a plain record (prototype `Object.prototype` or `null`).
|
|
104
|
-
* A record member whose value is `undefined` is omitted, as JSON omits it; any
|
|
105
|
-
* other value — `undefined` in a list or a hole, a non-finite number, a
|
|
106
|
-
* function, a symbol, any other object, a cycle — has no wire form, and the
|
|
107
|
-
* walk throws. So does a getter or a proxy that throws; the encoders catch all
|
|
108
|
-
* of it.
|
|
109
|
-
*
|
|
110
|
-
* `ancestors` holds the containers on the current path only, so a shared
|
|
111
|
-
* reference is encoded twice, as JSON writes it, while a cycle is refused.
|
|
112
|
-
*/
|
|
113
60
|
function toWire(value, ancestors) {
|
|
114
61
|
switch (typeof value) {
|
|
115
62
|
case "string":
|
|
@@ -163,11 +110,6 @@ function toWireList(list, ancestors) {
|
|
|
163
110
|
}
|
|
164
111
|
return wire;
|
|
165
112
|
}
|
|
166
|
-
/**
|
|
167
|
-
* One list element. A hole has no wire form (JSON would write `null`), and is
|
|
168
|
-
* never filled from the prototype chain; an `undefined` element has none
|
|
169
|
-
* either ({@link toWire}).
|
|
170
|
-
*/
|
|
171
113
|
function toWireElement(list, index, ancestors) {
|
|
172
114
|
if (!Object.hasOwn(list, index)) {
|
|
173
115
|
throw unencodable(list);
|
|
@@ -186,6 +128,5 @@ function toWireRecord(record, ancestors) {
|
|
|
186
128
|
members.push([key, toWire(member, ancestors)]);
|
|
187
129
|
}
|
|
188
130
|
}
|
|
189
|
-
// `fromEntries` defines each member, so an own `__proto__` key stays data.
|
|
190
131
|
return Object.fromEntries(members);
|
|
191
132
|
}
|
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
import type { OperationCode } from "../runtime/operation-response.js";
|
|
2
2
|
/**
|
|
3
|
-
* `AvProtocol.httpStatus` — the HTTP status each A-code carries
|
|
4
|
-
* "default HTTP" column), as passive data.
|
|
3
|
+
* `AvProtocol.httpStatus` — the HTTP status each A-code carries, as passive data.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* plan's — and a rolled-back plan answers its failing operation's own status
|
|
10
|
-
* (Q4). The status is the coarse signal; the A-code is the exact one.
|
|
5
|
+
* So this one table serves both encoders — an operation's response and a plan's —
|
|
6
|
+
* and a rolled-back plan answers its failing operation's own status. The status is
|
|
7
|
+
* the coarse signal; the A-code is the exact one.
|
|
11
8
|
*
|
|
12
|
-
* Total by type: the literal `satisfies` the mapped type over `OperationCode`,
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* it.
|
|
9
|
+
* Total by type: the literal `satisfies` the mapped type over `OperationCode`, so
|
|
10
|
+
* a code added to core does not compile until its status is written here. Passive:
|
|
11
|
+
* numbers only, frozen, emitted by value where a consumer needs it.
|
|
16
12
|
*/
|
|
17
13
|
export declare const httpStatus: {
|
|
18
14
|
readonly [C in OperationCode]: number;
|
|
@@ -1,18 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `AvProtocol.httpStatus` — the HTTP status each A-code carries (§13.2's
|
|
3
|
-
* "default HTTP" column), as passive data.
|
|
4
|
-
*
|
|
5
|
-
* Status is a function of the code alone (N14): §13.2 gives every code exactly
|
|
6
|
-
* one status, and its success table's two 201s are exactly `A1002` and `A1003`.
|
|
7
|
-
* So this one table serves both encoders — an operation's response and a
|
|
8
|
-
* plan's — and a rolled-back plan answers its failing operation's own status
|
|
9
|
-
* (Q4). The status is the coarse signal; the A-code is the exact one.
|
|
10
|
-
*
|
|
11
|
-
* Total by type: the literal `satisfies` the mapped type over `OperationCode`,
|
|
12
|
-
* so a code added to core does not compile until its status is written here.
|
|
13
|
-
* Passive (P5): numbers only, frozen, emitted by value where a consumer needs
|
|
14
|
-
* it.
|
|
15
|
-
*/
|
|
16
1
|
export const httpStatus = Object.freeze({
|
|
17
2
|
A1000: 200,
|
|
18
3
|
A1001: 200,
|
|
@@ -43,6 +28,7 @@ export const httpStatus = Object.freeze({
|
|
|
43
28
|
A3001: 500,
|
|
44
29
|
A3002: 500,
|
|
45
30
|
A3003: 500,
|
|
31
|
+
A3004: 500,
|
|
46
32
|
A4000: 401,
|
|
47
33
|
A4001: 403,
|
|
48
34
|
A4002: 403,
|