@aventara/core 1.0.0-rc.1 → 1.0.0-rc.10
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 +943 -16
- package/ERRORS.md +142 -0
- package/README.md +274 -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 +227 -24
- 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/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 +65 -0
- package/dist/config/config.validator.js +222 -8
- 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 +67 -22
- package/dist/config/field-compute.resolver.d.ts +2 -0
- package/dist/config/field-compute.resolver.js +8 -0
- 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 +4 -4
- package/dist/config/pipeline-tree.resolver.d.ts +2 -2
- package/dist/contracts/access-route.rules.d.ts +10 -0
- package/dist/contracts/access-route.rules.js +45 -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 +301 -89
- package/dist/contracts/client-contract-structure.validator.js +37 -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 +30 -4
- package/dist/contracts/contract-hash.d.ts +0 -1
- package/dist/contracts/contract-hash.js +1 -1
- package/dist/contracts/contract.d.ts +27 -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 +78 -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 +120 -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 +115 -23
- package/dist/framework/operation-facade.d.ts +80 -13
- package/dist/framework/operation-facade.js +2 -1
- package/dist/index.d.ts +17 -5
- package/dist/index.js +1 -0
- package/dist/internal.d.ts +7 -2
- package/dist/internal.js +6 -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 +159 -36
- package/dist/operations/operation-validator.d.ts +1 -1
- package/dist/operations/structural-limits.walker.d.ts +1 -1
- 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 +297 -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 +409 -0
- package/dist/policy/policy-after-update.runtime.d.ts +79 -0
- package/dist/policy/policy-after-update.runtime.js +208 -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 +545 -0
- package/dist/policy/policy-read.runtime.d.ts +81 -0
- package/dist/policy/policy-read.runtime.js +503 -0
- package/dist/policy/policy-reference-write.runtime.d.ts +51 -0
- package/dist/policy/policy-reference-write.runtime.js +345 -0
- package/dist/policy/policy-request.d.ts +41 -0
- package/dist/policy/policy-request.js +63 -0
- package/dist/policy/policy-session.d.ts +106 -0
- package/dist/policy/policy-session.js +295 -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 +532 -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 +65 -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 +77 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.d.ts +16 -0
- package/dist/prebuilt/prebuilt-inputs.fingerprint.js +229 -0
- package/dist/protocol/absent-route.responder.d.ts +9 -27
- package/dist/protocol/absent-route.responder.js +36 -3
- package/dist/protocol/contract-document.responder.d.ts +2 -1
- package/dist/protocol/http-request.decoder.d.ts +19 -3
- package/dist/protocol/http-request.decoder.js +24 -1
- package/dist/protocol/http-request.reader.d.ts +6 -3
- package/dist/protocol/http-request.reader.js +1 -0
- package/dist/protocol/http-response.encoder.js +2 -2
- package/dist/protocol/protocol-headers.d.ts +3 -3
- package/dist/protocol/protocol-surface.d.ts +6 -1
- package/dist/protocol/protocol-surface.js +7 -0
- package/dist/protocol/protocol.binder.d.ts +16 -1
- package/dist/protocol/protocol.binder.js +4 -1
- package/dist/protocol/protocol.handler.d.ts +12 -1
- package/dist/protocol/protocol.handler.js +86 -17
- 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 +232 -45
- 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 +129 -21
- package/dist/runtime/decimal.d.ts +14 -0
- package/dist/runtime/decimal.js +21 -0
- package/dist/runtime/diagnostics-reporter.d.ts +44 -9
- package/dist/runtime/diagnostics-reporter.js +13 -1
- 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 -1
- package/dist/runtime/field-stage.runtime.js +8 -4
- package/dist/runtime/field-validator.runtime.d.ts +19 -1
- package/dist/runtime/field-validator.runtime.js +47 -8
- 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 +3 -14
- 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/owned-execution.registry.d.ts +13 -3
- 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 +47 -22
- package/dist/runtime/readonly-value.d.ts +20 -5
- package/dist/runtime/readonly-value.js +74 -26
- package/dist/runtime/referential-action.d.ts +1 -1
- package/dist/runtime/scalar-runtime.d.ts +4 -11
- package/dist/runtime/scalar-runtime.js +91 -22
- package/dist/runtime/unsent-value.normalizer.d.ts +15 -0
- package/dist/runtime/unsent-value.normalizer.js +128 -0
- package/dist/transactions/operation-arguments-encoder.js +8 -3
- 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 +38 -5
- 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 +48 -10
- package/dist/transactions/transaction-plan-validator.d.ts +23 -7
- package/dist/transactions/transaction-plan-validator.js +27 -1
- package/dist/transactions/transaction-reference-resolver.d.ts +3 -2
- 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 +79 -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 +7 -4
- package/package.json +6 -5
- package/src/runtime/field-check.evaluator.ts +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
2
|
+
import type { AvContracts } from "../compiler/contract.compiler.js";
|
|
3
|
+
import { type AvPrebuiltContracts } from "./prebuilt-contracts.js";
|
|
4
|
+
/**
|
|
5
|
+
* The prebuilt file's value for Contracts `compileContracts` compiled from
|
|
6
|
+
* `model` and `config` (the configuration as authored; its `adapter` and
|
|
7
|
+
* `prebuilt`, if any, are no inputs): each Contract's canonical text, and the
|
|
8
|
+
* fingerprints of the inputs.
|
|
9
|
+
*/
|
|
10
|
+
export declare function prebuiltContractsOf(model: AvAdapterModel, config: object, contracts: AvContracts): Promise<AvPrebuiltContracts>;
|
|
11
|
+
/**
|
|
12
|
+
* The generated module that exports `prebuilt` as `aventaraContracts`. Each
|
|
13
|
+
* Contract stays a string, so loading it is one `JSON.parse`; the type is
|
|
14
|
+
* imported for checking only.
|
|
15
|
+
*/
|
|
16
|
+
export declare function emitPrebuiltContracts(prebuilt: AvPrebuiltContracts): string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { canonicalContractTextOf } from "../contracts/contract-canonicalizer.js";
|
|
2
|
+
import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
|
|
3
|
+
import { CORE_VERSION } from "./core-version.generated.js";
|
|
4
|
+
import { PREBUILT_FORMAT, } from "./prebuilt-contracts.js";
|
|
5
|
+
import { prebuiltInputsFingerprint, prebuiltPartFingerprints, } from "./prebuilt-inputs.fingerprint.js";
|
|
6
|
+
export async function prebuiltContractsOf(model, config, contracts) {
|
|
7
|
+
const [fingerprint, fingerprints] = await Promise.all([
|
|
8
|
+
prebuiltInputsFingerprint(model, config),
|
|
9
|
+
prebuiltPartFingerprints(model, config),
|
|
10
|
+
]);
|
|
11
|
+
return {
|
|
12
|
+
format: PREBUILT_FORMAT,
|
|
13
|
+
core: CORE_VERSION,
|
|
14
|
+
protocol: AVENTARA_PROTOCOL_VERSION,
|
|
15
|
+
fingerprint,
|
|
16
|
+
fingerprints,
|
|
17
|
+
contracts: {
|
|
18
|
+
core: canonicalContractTextOf(contracts.core),
|
|
19
|
+
application: canonicalContractTextOf(contracts.application),
|
|
20
|
+
client: canonicalContractTextOf(contracts.client),
|
|
21
|
+
agent: canonicalContractTextOf(contracts.agent),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const literal = (value) => JSON.stringify(value);
|
|
26
|
+
const key = (name) => name === "__proto__" ? `[${literal(name)}]` : literal(name);
|
|
27
|
+
export function emitPrebuiltContracts(prebuilt) {
|
|
28
|
+
const resources = Object.entries(prebuilt.fingerprints.resources).map(([name, fingerprint]) => `\t\t\t${key(name)}: ${literal(fingerprint)},\n`);
|
|
29
|
+
return `// Generated by @aventara/core from the configuration and the adapter's model. Do not edit: regenerate it.
|
|
30
|
+
import type { AvPrebuiltContracts } from "@aventara/core";
|
|
31
|
+
|
|
32
|
+
export const aventaraContracts: AvPrebuiltContracts = {
|
|
33
|
+
\tformat: ${prebuilt.format},
|
|
34
|
+
\tcore: ${literal(prebuilt.core)},
|
|
35
|
+
\tprotocol: ${prebuilt.protocol},
|
|
36
|
+
\tfingerprint: ${literal(prebuilt.fingerprint)},
|
|
37
|
+
\tfingerprints: {
|
|
38
|
+
\t\tshared: ${literal(prebuilt.fingerprints.shared)},
|
|
39
|
+
\t\tresources: {
|
|
40
|
+
${resources.join("")}\t\t},
|
|
41
|
+
\t},
|
|
42
|
+
\tcontracts: {
|
|
43
|
+
\t\tcore: ${literal(prebuilt.contracts.core)},
|
|
44
|
+
\t\tapplication: ${literal(prebuilt.contracts.application)},
|
|
45
|
+
\t\tclient: ${literal(prebuilt.contracts.client)},
|
|
46
|
+
\t\tagent: ${literal(prebuilt.contracts.agent)},
|
|
47
|
+
\t},
|
|
48
|
+
};
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PREBUILT_FORMAT = 1;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
2
|
+
import { type AvContracts } from "../compiler/contract.compiler.js";
|
|
3
|
+
import type { ResolvedFrameworkConfig } from "../config/config.resolver.js";
|
|
4
|
+
/** The loaded Contracts, or why the file cannot be used. */
|
|
5
|
+
export type PrebuiltLoad = {
|
|
6
|
+
readonly ok: true;
|
|
7
|
+
readonly contracts: AvContracts;
|
|
8
|
+
} | {
|
|
9
|
+
readonly ok: false;
|
|
10
|
+
readonly reason: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Loads the Contracts of a prebuilt file when it was compiled by this core, for
|
|
14
|
+
* this protocol, from `config` (as authored) and `model`; binds them to the
|
|
15
|
+
* runtime pieces of the live `resolved` configuration.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadPrebuiltContracts<M extends AvAdapterModel>(prebuilt: unknown, model: M, config: object, resolved: ResolvedFrameworkConfig<M>): Promise<PrebuiltLoad>;
|
|
18
|
+
/** Warns, once for the framework being created, that its prebuilt file was not used, and why. */
|
|
19
|
+
export declare function warnStalePrebuilt(reason: string): void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { bindPrebuiltContracts, } from "../compiler/contract.compiler.js";
|
|
2
|
+
import { recordCanonicalContractText } from "../contracts/contract-canonicalizer.js";
|
|
3
|
+
import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
|
|
4
|
+
import { emitFrameworkWarning } from "../runtime/framework-warning.emitter.js";
|
|
5
|
+
import { CORE_VERSION } from "./core-version.generated.js";
|
|
6
|
+
import { listedNames, prebuiltHeaderChanges, prebuiltInputChanges, } from "./prebuilt-changes.js";
|
|
7
|
+
import { PREBUILT_FORMAT } from "./prebuilt-contracts.js";
|
|
8
|
+
import { prebuiltInputsFingerprint, prebuiltPartFingerprints, } from "./prebuilt-inputs.fingerprint.js";
|
|
9
|
+
const PREBUILT_STALE_WARNING = "AVENTARA_PREBUILT_STALE";
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
async function changedInputs(stored, model, config) {
|
|
14
|
+
const changes = prebuiltInputChanges(stored, await prebuiltPartFingerprints(model, config));
|
|
15
|
+
return changes.length === 0
|
|
16
|
+
? "its fingerprint does not match the configuration and model"
|
|
17
|
+
: `${listedNames(changes)} since it was built`;
|
|
18
|
+
}
|
|
19
|
+
function textAt(record, key) {
|
|
20
|
+
const value = Object.hasOwn(record, key) ? record[key] : undefined;
|
|
21
|
+
return typeof value === "string" ? value : undefined;
|
|
22
|
+
}
|
|
23
|
+
function parsedContracts(texts) {
|
|
24
|
+
if (!isRecord(texts)) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
const parsed = {};
|
|
28
|
+
for (const origin of ["core", "application", "client", "agent"]) {
|
|
29
|
+
const text = textAt(texts, origin);
|
|
30
|
+
if (text === undefined) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
let contract;
|
|
34
|
+
try {
|
|
35
|
+
contract = JSON.parse(text);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (!isRecord(contract)) {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
parsed[origin] = contract;
|
|
44
|
+
}
|
|
45
|
+
const contracts = parsed;
|
|
46
|
+
recordCanonicalContractText(contracts.client, textAt(texts, "client") ?? "");
|
|
47
|
+
recordCanonicalContractText(contracts.agent, textAt(texts, "agent") ?? "");
|
|
48
|
+
return contracts;
|
|
49
|
+
}
|
|
50
|
+
export async function loadPrebuiltContracts(prebuilt, model, config, resolved) {
|
|
51
|
+
if (!isRecord(prebuilt)) {
|
|
52
|
+
return { ok: false, reason: "it is not a prebuilt Contracts file" };
|
|
53
|
+
}
|
|
54
|
+
const [header] = prebuiltHeaderChanges(prebuilt, {
|
|
55
|
+
format: PREBUILT_FORMAT,
|
|
56
|
+
core: CORE_VERSION,
|
|
57
|
+
protocol: AVENTARA_PROTOCOL_VERSION,
|
|
58
|
+
});
|
|
59
|
+
if (header !== undefined) {
|
|
60
|
+
return { ok: false, reason: header };
|
|
61
|
+
}
|
|
62
|
+
if ((await prebuiltInputsFingerprint(model, config)) !== prebuilt.fingerprint) {
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
reason: await changedInputs(prebuilt.fingerprints, model, config),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const contracts = parsedContracts(prebuilt.contracts);
|
|
69
|
+
if (contracts === undefined) {
|
|
70
|
+
return { ok: false, reason: "its Contracts are not readable JSON objects" };
|
|
71
|
+
}
|
|
72
|
+
bindPrebuiltContracts(model, resolved, contracts);
|
|
73
|
+
return { ok: true, contracts };
|
|
74
|
+
}
|
|
75
|
+
export function warnStalePrebuilt(reason) {
|
|
76
|
+
emitFrameworkWarning(`prebuilt Contracts are stale and were not used: ${reason}. The Contracts were compiled at startup instead; regenerate the file with aventara-build.`, PREBUILT_STALE_WARNING);
|
|
77
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { AvAdapterModel } from "../adapters/adapter-model.js";
|
|
2
|
+
/**
|
|
3
|
+
* The fingerprint of the inputs Contracts are compiled from: `config` as
|
|
4
|
+
* authored (its `adapter` and `prebuilt` left out), `model`, this core's
|
|
5
|
+
* version, the protocol version and the file format.
|
|
6
|
+
*/
|
|
7
|
+
export declare function prebuiltInputsFingerprint(model: AvAdapterModel, config: object): Promise<string>;
|
|
8
|
+
/** The inputs' parts, each fingerprinted on its own: what a file stores to name what changed. */
|
|
9
|
+
export type PrebuiltPartFingerprints = {
|
|
10
|
+
/** The inputs that belong to no single Resource. */
|
|
11
|
+
readonly shared: string;
|
|
12
|
+
/** Each Resource's inputs (its model, and its entries in every layer), by name, sorted. */
|
|
13
|
+
readonly resources: Readonly<Record<string, string>>;
|
|
14
|
+
};
|
|
15
|
+
/** The parts of the same inputs as {@link prebuiltInputsFingerprint}'s, each fingerprinted. */
|
|
16
|
+
export declare function prebuiltPartFingerprints(model: AvAdapterModel, config: object): Promise<PrebuiltPartFingerprints>;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { avzCheckOf } from "../avz/avz-check.registry.js";
|
|
2
|
+
import { jsonShapeOf } from "../avz/json-shape.registry.js";
|
|
3
|
+
import { hashCanonicalContractText } from "../contracts/contract-hash.js";
|
|
4
|
+
import { AVENTARA_PROTOCOL_VERSION } from "../contracts/contract-protocol.js";
|
|
5
|
+
import { CORE_VERSION } from "./core-version.generated.js";
|
|
6
|
+
import { PREBUILT_FORMAT } from "./prebuilt-contracts.js";
|
|
7
|
+
const LAYERS = ["root", "application", "client", "agent"];
|
|
8
|
+
const NOT_INPUTS = new Set(["adapter", "prebuilt"]);
|
|
9
|
+
const PER_RESOURCE = "#resources";
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function sortedRecord(record) {
|
|
14
|
+
const keys = Object.keys(record);
|
|
15
|
+
const prototype = Object.getPrototypeOf(record);
|
|
16
|
+
const plain = prototype === Object.prototype || prototype === null;
|
|
17
|
+
let written = plain;
|
|
18
|
+
for (let index = 0; written && index < keys.length; index += 1) {
|
|
19
|
+
const key = keys[index];
|
|
20
|
+
written =
|
|
21
|
+
!key.startsWith("#") &&
|
|
22
|
+
(index === 0 || keys[index - 1] < key);
|
|
23
|
+
}
|
|
24
|
+
if (written) {
|
|
25
|
+
return record;
|
|
26
|
+
}
|
|
27
|
+
const sorted = {};
|
|
28
|
+
if (!plain) {
|
|
29
|
+
const name = record.constructor?.name;
|
|
30
|
+
sorted["#class"] = typeof name === "string" ? name : "";
|
|
31
|
+
}
|
|
32
|
+
for (const key of keys.sort()) {
|
|
33
|
+
sorted[key.startsWith("#") ? `#${key}` : key] = record[key];
|
|
34
|
+
}
|
|
35
|
+
return sorted;
|
|
36
|
+
}
|
|
37
|
+
function inputMember(key, value) {
|
|
38
|
+
const raw = this[key];
|
|
39
|
+
switch (typeof raw) {
|
|
40
|
+
case "string":
|
|
41
|
+
return raw.startsWith("#") ? `#${raw}` : raw;
|
|
42
|
+
case "number":
|
|
43
|
+
return Number.isFinite(raw) ? raw : `#number(${raw})`;
|
|
44
|
+
case "bigint":
|
|
45
|
+
return `#bigint(${raw})`;
|
|
46
|
+
case "function":
|
|
47
|
+
return "#function";
|
|
48
|
+
case "symbol":
|
|
49
|
+
return `#symbol(${String(raw.description)})`;
|
|
50
|
+
case "undefined":
|
|
51
|
+
return "#undefined";
|
|
52
|
+
case "boolean":
|
|
53
|
+
return raw;
|
|
54
|
+
}
|
|
55
|
+
if (raw === null) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
const check = avzCheckOf(raw);
|
|
59
|
+
if (check !== undefined) {
|
|
60
|
+
return `#avz(${inputText(check)})`;
|
|
61
|
+
}
|
|
62
|
+
const shape = jsonShapeOf(raw);
|
|
63
|
+
if (shape !== undefined) {
|
|
64
|
+
return `#shape(${inputText(shape.schema)})`;
|
|
65
|
+
}
|
|
66
|
+
if (raw instanceof Date) {
|
|
67
|
+
return `#date(${Number.isNaN(raw.getTime()) ? "invalid" : raw.toISOString()})`;
|
|
68
|
+
}
|
|
69
|
+
if (raw instanceof RegExp) {
|
|
70
|
+
return `#regexp(${JSON.stringify(raw.source)},${raw.flags})`;
|
|
71
|
+
}
|
|
72
|
+
if (raw instanceof Map || raw instanceof Set) {
|
|
73
|
+
return `#${raw instanceof Map ? "map" : "set"}(${inputText([...raw])})`;
|
|
74
|
+
}
|
|
75
|
+
if (value !== raw) {
|
|
76
|
+
return `#json(${inputText(value)})`;
|
|
77
|
+
}
|
|
78
|
+
return Array.isArray(raw) ? raw : sortedRecord(raw);
|
|
79
|
+
}
|
|
80
|
+
function inputText(value) {
|
|
81
|
+
return JSON.stringify(value, inputMember);
|
|
82
|
+
}
|
|
83
|
+
function modelText(value) {
|
|
84
|
+
try {
|
|
85
|
+
return JSON.stringify(value) ?? "#undefined";
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return inputText(value);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function recordAt(value, key) {
|
|
92
|
+
if (!isRecord(value) || !Object.hasOwn(value, key)) {
|
|
93
|
+
return undefined;
|
|
94
|
+
}
|
|
95
|
+
const member = value[key];
|
|
96
|
+
return isRecord(member) ? member : undefined;
|
|
97
|
+
}
|
|
98
|
+
function layerOf(config, layer) {
|
|
99
|
+
return layer === "root"
|
|
100
|
+
? config
|
|
101
|
+
: Object.hasOwn(config, layer)
|
|
102
|
+
? config[layer]
|
|
103
|
+
: undefined;
|
|
104
|
+
}
|
|
105
|
+
function sharedLayer(layer) {
|
|
106
|
+
if (!isRecord(layer)) {
|
|
107
|
+
return layer;
|
|
108
|
+
}
|
|
109
|
+
const shared = { ...layer };
|
|
110
|
+
if (isRecord(layer.resources)) {
|
|
111
|
+
shared.resources = PER_RESOURCE;
|
|
112
|
+
}
|
|
113
|
+
const exemptions = layer.exemptions;
|
|
114
|
+
if (isRecord(exemptions) && isRecord(exemptions.resources)) {
|
|
115
|
+
shared.exemptions = { ...exemptions, resources: PER_RESOURCE };
|
|
116
|
+
}
|
|
117
|
+
return shared;
|
|
118
|
+
}
|
|
119
|
+
function policyInput(policy) {
|
|
120
|
+
if (!isRecord(policy) || !Object.hasOwn(policy, "engine")) {
|
|
121
|
+
return policy;
|
|
122
|
+
}
|
|
123
|
+
const engine = policy.engine;
|
|
124
|
+
const governed = engine !== null && typeof engine === "object"
|
|
125
|
+
? engine.governed
|
|
126
|
+
: undefined;
|
|
127
|
+
return { ...policy, engine: { governed: governed ?? null } };
|
|
128
|
+
}
|
|
129
|
+
function accessInput(access) {
|
|
130
|
+
if (!isRecord(access)) {
|
|
131
|
+
return access;
|
|
132
|
+
}
|
|
133
|
+
const engine = access.policy;
|
|
134
|
+
const governed = engine !== null && typeof engine === "object"
|
|
135
|
+
? engine.governed
|
|
136
|
+
: undefined;
|
|
137
|
+
const policy = { policy: { governed: governed ?? null } };
|
|
138
|
+
return access.client === undefined
|
|
139
|
+
? policy
|
|
140
|
+
: { ...policy, client: accessClientInput(access.client) };
|
|
141
|
+
}
|
|
142
|
+
function accessClientInput(client) {
|
|
143
|
+
if (!isRecord(client)) {
|
|
144
|
+
return client;
|
|
145
|
+
}
|
|
146
|
+
const verbs = isRecord(client.verbs)
|
|
147
|
+
? Object.fromEntries(Object.entries(client.verbs).map(([name, verb]) => [
|
|
148
|
+
name,
|
|
149
|
+
isRecord(verb)
|
|
150
|
+
? {
|
|
151
|
+
method: verb.method,
|
|
152
|
+
path: verb.path,
|
|
153
|
+
args: verb.args,
|
|
154
|
+
result: verb.result,
|
|
155
|
+
}
|
|
156
|
+
: verb,
|
|
157
|
+
]))
|
|
158
|
+
: client.verbs;
|
|
159
|
+
return { url: client.url, verbs };
|
|
160
|
+
}
|
|
161
|
+
function authoredInputs(config) {
|
|
162
|
+
const authored = config;
|
|
163
|
+
const root = {};
|
|
164
|
+
for (const key of Object.keys(authored)) {
|
|
165
|
+
if (!NOT_INPUTS.has(key)) {
|
|
166
|
+
root[key] =
|
|
167
|
+
key === "policy"
|
|
168
|
+
? policyInput(authored[key])
|
|
169
|
+
: key === "access"
|
|
170
|
+
? accessInput(authored[key])
|
|
171
|
+
: authored[key];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return root;
|
|
175
|
+
}
|
|
176
|
+
const HEADER = `aventara-prebuilt format ${PREBUILT_FORMAT} core ${CORE_VERSION} protocol ${AVENTARA_PROTOCOL_VERSION}`;
|
|
177
|
+
export function prebuiltInputsFingerprint(model, config) {
|
|
178
|
+
return hashCanonicalContractText(`${HEADER}\n${inputText(authoredInputs(config))}\n${modelText(model)}`);
|
|
179
|
+
}
|
|
180
|
+
export async function prebuiltPartFingerprints(model, config) {
|
|
181
|
+
const root = authoredInputs(config);
|
|
182
|
+
const names = new Set(Object.keys(model.resources));
|
|
183
|
+
for (const layer of LAYERS) {
|
|
184
|
+
const value = layerOf(root, layer);
|
|
185
|
+
for (const name of Object.keys(recordAt(value, "resources") ?? {})) {
|
|
186
|
+
names.add(name);
|
|
187
|
+
}
|
|
188
|
+
const exemptions = recordAt(value, "exemptions");
|
|
189
|
+
for (const name of Object.keys(recordAt(exemptions, "resources") ?? {})) {
|
|
190
|
+
names.add(name);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const sharedRoot = sharedLayer(root);
|
|
194
|
+
for (const layer of LAYERS) {
|
|
195
|
+
if (layer !== "root" && Object.hasOwn(root, layer)) {
|
|
196
|
+
sharedRoot[layer] = sharedLayer(root[layer]);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const { resources: _resources, ...sharedModel } = model;
|
|
200
|
+
const shared = hashCanonicalContractText(`${HEADER}\n${inputText(sharedRoot)}\n${modelText(sharedModel)}`);
|
|
201
|
+
const sorted = [...names].sort();
|
|
202
|
+
const parts = sorted.map((name) => {
|
|
203
|
+
const entries = {};
|
|
204
|
+
for (const layer of LAYERS) {
|
|
205
|
+
const value = layerOf(root, layer);
|
|
206
|
+
const resources = recordAt(value, "resources");
|
|
207
|
+
const exemptions = recordAt(recordAt(value, "exemptions"), "resources");
|
|
208
|
+
const entry = {};
|
|
209
|
+
if (resources !== undefined && Object.hasOwn(resources, name)) {
|
|
210
|
+
entry.resources = resources[name];
|
|
211
|
+
}
|
|
212
|
+
if (exemptions !== undefined && Object.hasOwn(exemptions, name)) {
|
|
213
|
+
entry.exemptions = exemptions[name];
|
|
214
|
+
}
|
|
215
|
+
if (Object.keys(entry).length > 0) {
|
|
216
|
+
entries[layer] = entry;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const modelPart = Object.hasOwn(model.resources, name)
|
|
220
|
+
? modelText(model.resources[name])
|
|
221
|
+
: "#absent";
|
|
222
|
+
return hashCanonicalContractText(`${inputText(entries)}\n${modelPart}`);
|
|
223
|
+
});
|
|
224
|
+
const hashes = await Promise.all(parts);
|
|
225
|
+
return {
|
|
226
|
+
shared: await shared,
|
|
227
|
+
resources: Object.fromEntries(sorted.map((name, index) => [name, hashes[index]])),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -2,8 +2,7 @@ import type { AvContracts } from "../compiler/contract.compiler.js";
|
|
|
2
2
|
import type { RemoteRequestOrigin } from "../config/config-layer.js";
|
|
3
3
|
import { type AvOperationIdentity } from "../operations/operation-identity.js";
|
|
4
4
|
import { type AvDiagnosticsSink } from "../runtime/diagnostics-reporter.js";
|
|
5
|
-
import type {
|
|
6
|
-
import { type AvOperationFailureResponse } from "../runtime/operation-response.js";
|
|
5
|
+
import type { AvRequestProtocol } from "../runtime/operation-request.js";
|
|
7
6
|
import type { AvProtocolRequest } from "./http-request.decoder.js";
|
|
8
7
|
import { type ReadRequest } from "./http-request.reader.js";
|
|
9
8
|
import { type AvProtocolResponse } from "./http-response.encoder.js";
|
|
@@ -21,37 +20,13 @@ export type OperationRouteSegments = {
|
|
|
21
20
|
readonly family: string;
|
|
22
21
|
readonly variant: string;
|
|
23
22
|
};
|
|
24
|
-
/** What a route check resolves to: the operation it names, or the answer that refuses it. */
|
|
25
|
-
export type OperationRouteResolution = {
|
|
26
|
-
readonly ok: true;
|
|
27
|
-
readonly operation: AvOperationIdentity;
|
|
28
|
-
} | {
|
|
29
|
-
readonly ok: false;
|
|
30
|
-
readonly failure: AvOperationFailureResponse;
|
|
31
|
-
};
|
|
32
|
-
/**
|
|
33
|
-
* An operation route `/_resources/<resource>/<family>/<variant>` under one client
|
|
34
|
-
* protocol identity: version → hash → Resource → operation. A Resource key is matched
|
|
35
|
-
* exactly and only as the ClientContract's own key; a family/variant pair only when it
|
|
36
|
-
* is a standard operation the Resource advertises, read through
|
|
37
|
-
* `isOperationVariantAvailable` alone.
|
|
38
|
-
*/
|
|
39
|
-
export declare function resolveOperationRoute(contracts: AvContracts, protocol: ClientProtocolIdentity, route: OperationRouteSegments, origin?: RemoteRequestOrigin): OperationRouteResolution;
|
|
40
|
-
/**
|
|
41
|
-
* The `/_transactions` route under one client protocol identity: version → hash → the
|
|
42
|
-
* ClientContract's transaction capability. The route exists only when that capability
|
|
43
|
-
* is `"interactive"` — tested against the value itself, as `protocol.surface()` tests
|
|
44
|
-
* it — and is otherwise absent with core's own refusal of a plan. `undefined` when the
|
|
45
|
-
* route is there.
|
|
46
|
-
*/
|
|
47
|
-
export declare function transactionsRouteFailure(contracts: AvContracts, protocol: ClientProtocolIdentity, origin?: RemoteRequestOrigin): AvOperationFailureResponse | undefined;
|
|
48
23
|
/**
|
|
49
24
|
* What a route's checks before its body resolve to: the route a request
|
|
50
25
|
* reaches, or the framework response that answers it instead.
|
|
51
26
|
*/
|
|
52
27
|
type RouteCheck<Route> = ({
|
|
53
28
|
readonly ok: true;
|
|
54
|
-
readonly protocol:
|
|
29
|
+
readonly protocol: AvRequestProtocol;
|
|
55
30
|
} & Route) | {
|
|
56
31
|
readonly ok: false;
|
|
57
32
|
readonly response: AvProtocolResponse;
|
|
@@ -75,6 +50,13 @@ export declare function checkOperationRoute(contracts: AvContracts, read: ReadRe
|
|
|
75
50
|
* method (`A2012`, `Allow: POST`).
|
|
76
51
|
*/
|
|
77
52
|
export declare function checkTransactionsRoute(contracts: AvContracts, read: ReadRequest, origin?: RemoteRequestOrigin): RouteCheck<object>;
|
|
53
|
+
/**
|
|
54
|
+
* `/_access/can`'s checks before its body: the path (`A2000` unless exactly
|
|
55
|
+
* `/_access/can`), the identity headers' presence (`A2000`), then
|
|
56
|
+
* {@link accessCanRouteFailure} — version, hash, the `access` section — and the
|
|
57
|
+
* method (`A2012`, `Allow: POST`).
|
|
58
|
+
*/
|
|
59
|
+
export declare function checkAccessCanRoute(contracts: AvContracts, read: ReadRequest, origin?: RemoteRequestOrigin): RouteCheck<object>;
|
|
78
60
|
/**
|
|
79
61
|
* `protocol.answerAbsent(request)` — the fallback a host that mounts one route per
|
|
80
62
|
* `protocol.surface()` entry mounts for every path it did not. Never a catch-all into
|
|
@@ -3,17 +3,18 @@ import { isOperationVariantAvailable, } from "../contracts/resource-operations.j
|
|
|
3
3
|
import { parseOperationIdentity, } from "../operations/operation-identity.js";
|
|
4
4
|
import { operationUnavailableIssue } from "../operations/operation-validator.js";
|
|
5
5
|
import { resourceUnavailableIssue } from "../operations/operation-validator-core.js";
|
|
6
|
+
import { validationIssue } from "../operations/validation-code.js";
|
|
6
7
|
import { createReporter, } from "../runtime/diagnostics-reporter.js";
|
|
7
8
|
import { INTERNAL_FAILURE_MESSAGE, operationFailure, } from "../runtime/operation-response.js";
|
|
8
9
|
import { protocolIdentityFailure } from "../runtime/operation-runtime.js";
|
|
9
10
|
import { mapValidationOutcome } from "../runtime/outcome-mapper.js";
|
|
10
11
|
import { transactionsUnavailableFailure } from "../transactions/transaction-plan-validator.js";
|
|
11
|
-
import { CONTRACT_ROUTE, GET_ONLY, isProtocolPath, operationRouteSegments, POST_ONLY, readRequest, TRANSACTIONS_ROUTE, } from "./http-request.reader.js";
|
|
12
|
+
import { ACCESS_CAN_ROUTE, CONTRACT_ROUTE, GET_ONLY, isProtocolPath, operationRouteSegments, POST_ONLY, readRequest, TRANSACTIONS_ROUTE, } from "./http-request.reader.js";
|
|
12
13
|
import { encodeOperationResponse, } from "./http-response.encoder.js";
|
|
13
14
|
import { identityHeadersMissing, transportFailure, } from "./protocol-failure.js";
|
|
14
15
|
import { missingIdentityHeaders } from "./protocol-headers.js";
|
|
15
16
|
import { AvUnmountedRouteError } from "./unmounted-route.error.js";
|
|
16
|
-
|
|
17
|
+
function resolveOperationRoute(contracts, protocol, route, origin = "client") {
|
|
17
18
|
const identityFailure = protocolIdentityFailure(contracts, protocol, origin);
|
|
18
19
|
if (identityFailure !== undefined) {
|
|
19
20
|
return { ok: false, failure: identityFailure };
|
|
@@ -38,12 +39,25 @@ export function resolveOperationRoute(contracts, protocol, route, origin = "clie
|
|
|
38
39
|
}
|
|
39
40
|
return { ok: true, operation };
|
|
40
41
|
}
|
|
41
|
-
|
|
42
|
+
function transactionsRouteFailure(contracts, protocol, origin = "client") {
|
|
42
43
|
return (protocolIdentityFailure(contracts, protocol, origin) ??
|
|
43
44
|
(contracts[origin].transactions === "interactive"
|
|
44
45
|
? undefined
|
|
45
46
|
: transactionsUnavailableFailure()));
|
|
46
47
|
}
|
|
48
|
+
function accessCanUnavailableFailure() {
|
|
49
|
+
const message = "Access questions are not available on the active Contract.";
|
|
50
|
+
return operationFailure("A2002", {
|
|
51
|
+
message,
|
|
52
|
+
issues: [validationIssue("V1006", ["questions"], message)],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function accessCanRouteFailure(contracts, protocol, origin = "client") {
|
|
56
|
+
return (protocolIdentityFailure(contracts, protocol, origin) ??
|
|
57
|
+
(contracts[origin].access === undefined
|
|
58
|
+
? accessCanUnavailableFailure()
|
|
59
|
+
: undefined));
|
|
60
|
+
}
|
|
47
61
|
export function checkOperationRoute(contracts, read, origin = "client") {
|
|
48
62
|
const segments = operationRouteSegments(read.request.path);
|
|
49
63
|
if (segments === undefined) {
|
|
@@ -80,6 +94,21 @@ export function checkTransactionsRoute(contracts, read, origin = "client") {
|
|
|
80
94
|
? { ok: true, protocol: read.protocol }
|
|
81
95
|
: refusedWith(transportFailure("A2012", read.requestId, POST_ONLY));
|
|
82
96
|
}
|
|
97
|
+
export function checkAccessCanRoute(contracts, read, origin = "client") {
|
|
98
|
+
if (read.request.path !== ACCESS_CAN_ROUTE) {
|
|
99
|
+
return refusedWith(transportFailure("A2000", read.requestId));
|
|
100
|
+
}
|
|
101
|
+
if (read.protocol === undefined) {
|
|
102
|
+
return refusedWith(identityFailureOf(read));
|
|
103
|
+
}
|
|
104
|
+
const absent = accessCanRouteFailure(contracts, read.protocol, origin);
|
|
105
|
+
if (absent !== undefined) {
|
|
106
|
+
return refusedWith(encodeOperationResponse(absent, read.requestId));
|
|
107
|
+
}
|
|
108
|
+
return read.request.method === "POST"
|
|
109
|
+
? { ok: true, protocol: read.protocol }
|
|
110
|
+
: refusedWith(transportFailure("A2012", read.requestId, POST_ONLY));
|
|
111
|
+
}
|
|
83
112
|
function identityFailureOf(read) {
|
|
84
113
|
return identityHeadersMissing(missingIdentityHeaders(read.headers), read.requestId);
|
|
85
114
|
}
|
|
@@ -100,6 +129,10 @@ export function answerAbsentRequest(contracts, received, sink, origin = "client"
|
|
|
100
129
|
? unmounted(read.requestId)
|
|
101
130
|
: transportFailure("A2012", read.requestId, GET_ONLY);
|
|
102
131
|
}
|
|
132
|
+
if (path === ACCESS_CAN_ROUTE) {
|
|
133
|
+
const route = checkAccessCanRoute(contracts, read, origin);
|
|
134
|
+
return route.ok ? unmounted(read.requestId) : route.response;
|
|
135
|
+
}
|
|
103
136
|
if (path === TRANSACTIONS_ROUTE) {
|
|
104
137
|
const route = checkTransactionsRoute(contracts, read, origin);
|
|
105
138
|
if (!route.ok) {
|
|
@@ -6,7 +6,7 @@ import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
|
6
6
|
* entity tag, the quoted contract hash. The bytes include `protocol.hash`, although
|
|
7
7
|
* the hash's own input excludes it.
|
|
8
8
|
*/
|
|
9
|
-
|
|
9
|
+
type ContractDocument = {
|
|
10
10
|
readonly body: string;
|
|
11
11
|
readonly etag: string;
|
|
12
12
|
};
|
|
@@ -17,3 +17,4 @@ export declare function contractDocumentOf(client: AvContractClient): ContractDo
|
|
|
17
17
|
* order:
|
|
18
18
|
*/
|
|
19
19
|
export declare function encodeContractRequest(document: ContractDocument, received: AvProtocolRequest): AvProtocolResponse;
|
|
20
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AvContracts } from "../compiler/contract.compiler.js";
|
|
2
2
|
import type { RemoteRequestOrigin } from "../config/config-layer.js";
|
|
3
|
-
import type { RemoteOperationRequest } from "../runtime/operation-request.js";
|
|
4
|
-
import type {
|
|
3
|
+
import type { AvRequestProtocol, RemoteOperationRequest } from "../runtime/operation-request.js";
|
|
4
|
+
import type { AvAgentTransactionRequest, AvClientTransactionRequest } from "../transactions/transaction-request.js";
|
|
5
5
|
import type { AvProtocolResponse } from "./http-response.encoder.js";
|
|
6
6
|
/** One HTTP request as a host hands it to the protocol. */
|
|
7
7
|
export type AvProtocolRequest = {
|
|
@@ -56,7 +56,7 @@ export type DecodedOperationRequest = Extract<DecodedOperation, {
|
|
|
56
56
|
ok: true;
|
|
57
57
|
}>;
|
|
58
58
|
/** What `protocol.decodeTransaction` answers. */
|
|
59
|
-
export type DecodedTransaction = Decoded<
|
|
59
|
+
export type DecodedTransaction = Decoded<AvClientTransactionRequest | AvAgentTransactionRequest>;
|
|
60
60
|
/**
|
|
61
61
|
* `protocol.decodeOperation(request)` — one operation route's HTTP request as
|
|
62
62
|
* the client-origin request `framework.execute` takes, or its answer.
|
|
@@ -71,6 +71,22 @@ export declare function decodeOperationRequest(contracts: AvContracts, received:
|
|
|
71
71
|
* say otherwise. The body is the plan verbatim.
|
|
72
72
|
*/
|
|
73
73
|
export declare function decodeTransactionRequest(contracts: AvContracts, received: AvProtocolRequest, origin?: RemoteRequestOrigin): DecodedTransaction;
|
|
74
|
+
/** A `/_access/can` request as core answers it: its origin, identity and body. */
|
|
75
|
+
type DecodedCanRequest = {
|
|
76
|
+
readonly origin: RemoteRequestOrigin;
|
|
77
|
+
readonly requestId: string;
|
|
78
|
+
readonly protocol: AvRequestProtocol;
|
|
79
|
+
/** The body, a JSON object: `{ questions }`, read by core. */
|
|
80
|
+
readonly body: object;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* The `/_access/can` request as the request core answers, or its answer: the same
|
|
84
|
+
* steps as {@link decodeTransactionRequest}, with the path exactly `/_access/can`
|
|
85
|
+
* and its absence being a Contract without its `access` section. The body is read
|
|
86
|
+
* as a JSON object and handed to core verbatim; its `questions` are core's to
|
|
87
|
+
* judge.
|
|
88
|
+
*/
|
|
89
|
+
export declare function decodeCanRequest(contracts: AvContracts, received: AvProtocolRequest, origin?: RemoteRequestOrigin): Decoded<DecodedCanRequest>;
|
|
74
90
|
/**
|
|
75
91
|
* Whether `value` is a body this decoder parsed from raw content. Only the protocol's
|
|
76
92
|
* own shortcuts ask, about a request they have just decoded, before any host code
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { checkOperationRoute, checkTransactionsRoute, } from "./absent-route.responder.js";
|
|
1
|
+
import { checkAccessCanRoute, checkOperationRoute, checkTransactionsRoute, } from "./absent-route.responder.js";
|
|
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";
|
|
@@ -52,6 +52,29 @@ export function decodeTransactionRequest(contracts, received, origin = "client")
|
|
|
52
52
|
}
|
|
53
53
|
: refused(body.response, read.requestId);
|
|
54
54
|
}
|
|
55
|
+
export function decodeCanRequest(contracts, received, origin = "client") {
|
|
56
|
+
const read = readRequest(received);
|
|
57
|
+
if (!read.ok) {
|
|
58
|
+
return read;
|
|
59
|
+
}
|
|
60
|
+
const route = checkAccessCanRoute(contracts, read, origin);
|
|
61
|
+
if (!route.ok) {
|
|
62
|
+
return refused(route.response, read.requestId);
|
|
63
|
+
}
|
|
64
|
+
const body = readBody(read, contracts, origin);
|
|
65
|
+
return body.ok
|
|
66
|
+
? {
|
|
67
|
+
ok: true,
|
|
68
|
+
request: {
|
|
69
|
+
origin,
|
|
70
|
+
requestId: read.requestId,
|
|
71
|
+
protocol: route.protocol,
|
|
72
|
+
body: body.value,
|
|
73
|
+
},
|
|
74
|
+
requestId: read.requestId,
|
|
75
|
+
}
|
|
76
|
+
: refused(body.response, read.requestId);
|
|
77
|
+
}
|
|
55
78
|
function refused(response, requestId) {
|
|
56
79
|
return { ok: false, response, requestId };
|
|
57
80
|
}
|