eval-quality 0.1.0 → 0.3.0
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/README.md +142 -67
- package/corpus/dev/README.md +20 -13
- package/corpus/dev/compile-seal-example/brief.json +1 -1
- package/corpus/dev/compile-seal-example/contract.json +1 -1
- package/corpus/dev/contracts/absent-collection-locations.json +1 -1
- package/corpus/dev/contracts/absent-sibling-groups.json +1 -1
- package/corpus/dev/contracts/absent-success-indicator.json +1 -1
- package/corpus/dev/contracts/empty-channel-roles.json +1 -1
- package/corpus/dev/contracts/empty-collection-locations.json +1 -1
- package/corpus/dev/contracts/empty-request-shapes.json +1 -1
- package/corpus/dev/contracts/empty-sibling-groups.json +1 -1
- package/corpus/dev/contracts/fragment-selection.json +1 -0
- package/corpus/dev/contracts/no-collection-quantifier.json +1 -1
- package/corpus/dev/contracts/no-operation-inventory.json +1 -1
- package/corpus/dev/contracts/no-read-back-relation.json +1 -1
- package/corpus/dev/contracts/no-state-change-marker.json +1 -1
- package/corpus/dev/contracts/no-type-violating-step.json +1 -1
- package/corpus/dev/contracts/per-key-split-oracles.json +1 -1
- package/corpus/dev/contracts/review-corpus.json +1 -0
- package/corpus/dev/contracts/satisfied-declarations.json +1 -1
- package/corpus/dev/contracts/single-required-response-key.json +1 -1
- package/corpus/dev/contracts/split-indicator-oracle.json +1 -1
- package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -1
- package/corpus/dev/contracts/unnamed-reference-set.json +1 -1
- package/corpus/dev/contracts/wrong-cardinality-form.json +1 -1
- package/corpus/dev/index.json +1 -1
- package/dist/application/index.d.ts +2 -0
- package/dist/application/index.js +1 -0
- package/dist/application/score.d.ts +48 -0
- package/dist/application/score.js +186 -0
- package/dist/cli/arguments.d.ts +7 -3
- package/dist/cli/arguments.js +73 -5
- package/dist/cli/exit-codes.d.ts +6 -2
- package/dist/cli/exit-codes.js +8 -9
- package/dist/cli/main.js +9 -0
- package/dist/cli/render.d.ts +3 -6
- package/dist/cli/render.js +57 -5
- package/dist/cli/run.d.ts +13 -3
- package/dist/cli/run.js +136 -16
- package/dist/core/canonical/canonicalize.d.ts +12 -0
- package/dist/core/canonical/canonicalize.js +18 -4
- package/dist/core/compile/bindings.d.ts +89 -0
- package/dist/core/compile/bindings.js +334 -0
- package/dist/core/compile/compile.d.ts +17 -0
- package/dist/core/compile/compile.js +37 -1
- package/dist/core/compile/excluded-content.d.ts +11 -0
- package/dist/core/compile/excluded-content.js +42 -0
- package/dist/core/compile/expression-legality.d.ts +49 -0
- package/dist/core/compile/expression-legality.js +158 -38
- package/dist/core/compile/forbidden-inputs.d.ts +8 -1
- package/dist/core/compile/forbidden-inputs.js +16 -3
- package/dist/core/compile/interface-inventory.d.ts +57 -2
- package/dist/core/compile/interface-inventory.js +119 -13
- package/dist/core/compile/reachability.d.ts +28 -1
- package/dist/core/compile/reachability.js +167 -35
- package/dist/core/compile/schema-version.d.ts +2 -0
- package/dist/core/compile/schema-version.js +25 -0
- package/dist/core/compile/sensitivity-witness.d.ts +31 -12
- package/dist/core/compile/sensitivity-witness.js +110 -23
- package/dist/core/compile/step-reference.d.ts +2 -0
- package/dist/core/compile/step-reference.js +49 -0
- package/dist/core/coverage/operations.d.ts +62 -0
- package/dist/core/coverage/operations.js +57 -0
- package/dist/core/coverage/relevance.d.ts +4 -2
- package/dist/core/coverage/relevance.js +22 -23
- package/dist/core/coverage/satisfaction.d.ts +2 -2
- package/dist/core/coverage/satisfaction.js +73 -48
- package/dist/core/declared-inputs.d.ts +83 -4
- package/dist/core/declared-inputs.js +105 -8
- package/dist/core/emit/emit.d.ts +9 -0
- package/dist/core/emit/emit.js +214 -0
- package/dist/core/emit/private-artifact-digest.d.ts +11 -0
- package/dist/core/emit/private-artifact-digest.js +31 -0
- package/dist/core/evaluate/evidence-resolution.d.ts +10 -3
- package/dist/core/evaluate/evidence-resolution.js +74 -11
- package/dist/core/evaluate/operators.d.ts +22 -0
- package/dist/core/evaluate/operators.js +17 -1
- package/dist/core/evaluate/resolution.d.ts +14 -3
- package/dist/core/evaluate/resolution.js +63 -3
- package/dist/core/excluded-content.d.ts +65 -0
- package/dist/core/excluded-content.js +113 -0
- package/dist/core/failure-codes.d.ts +2 -2
- package/dist/core/failure-codes.js +7 -2
- package/dist/core/ingest/conditions.d.ts +201 -0
- package/dist/core/ingest/conditions.js +48 -0
- package/dist/core/ingest/index.d.ts +13 -0
- package/dist/core/ingest/index.js +11 -0
- package/dist/core/ingest/ingest.d.ts +68 -0
- package/dist/core/ingest/ingest.js +318 -0
- package/dist/core/lineage/stage-table.d.ts +21 -4
- package/dist/core/lineage/stage-table.js +35 -3
- package/dist/core/preflight/plan.d.ts +7 -5
- package/dist/core/preflight/plan.js +75 -32
- package/dist/core/preflight/projection.d.ts +6 -3
- package/dist/core/preflight/projection.js +22 -2
- package/dist/core/preflight/reduce.js +47 -7
- package/dist/core/preflight/witness-evidence.d.ts +5 -5
- package/dist/core/preflight/witness-evidence.js +73 -20
- package/dist/core/schemas/artifact.d.ts +1049 -30
- package/dist/core/schemas/constraint-ledger.js +51 -0
- package/dist/core/schemas/defect-signature.d.ts +695 -0
- package/dist/core/schemas/defect-signature.js +175 -0
- package/dist/core/schemas/eval-contract.d.ts +550 -12
- package/dist/core/schemas/eval-contract.js +32 -2
- package/dist/core/schemas/evidence-artifact.d.ts +109 -19
- package/dist/core/schemas/evidence-artifact.js +64 -8
- package/dist/core/schemas/interface.d.ts +741 -13
- package/dist/core/schemas/interface.js +120 -7
- package/dist/core/schemas/isolation-manifest.js +16 -9
- package/dist/core/schemas/plan.d.ts +329 -3
- package/dist/core/schemas/plan.js +89 -3
- package/dist/core/schemas/pointer.d.ts +47 -1
- package/dist/core/schemas/pointer.js +89 -8
- package/dist/core/schemas/port-messages.d.ts +194 -10
- package/dist/core/schemas/port-messages.js +73 -6
- package/dist/core/schemas/probe-body.d.ts +18 -0
- package/dist/core/schemas/probe-body.js +13 -0
- package/dist/core/schemas/probe-qualification.d.ts +168 -0
- package/dist/core/schemas/probe-qualification.js +106 -0
- package/dist/core/schemas/probe.d.ts +431 -0
- package/dist/core/schemas/probe.js +6 -2
- package/dist/core/schemas/scoring-policy.d.ts +1 -0
- package/dist/core/schemas/scoring-policy.js +6 -1
- package/dist/core/schemas/sealed-evaluator-brief.d.ts +1 -0
- package/dist/core/schemas/sealed-evaluator-brief.js +4 -1
- package/dist/core/schemas/sealed-run-record.d.ts +161 -12
- package/dist/core/schemas/sealed-run-record.js +92 -15
- package/dist/core/schemas/sensitivity-witness.d.ts +108 -6
- package/dist/core/schemas/sensitivity-witness.js +61 -5
- package/dist/core/score/binding-order.d.ts +31 -0
- package/dist/core/score/binding-order.js +88 -0
- package/dist/core/score/bindings.d.ts +93 -0
- package/dist/core/score/bindings.js +309 -0
- package/dist/core/score/ladder-table.d.ts +23 -0
- package/dist/core/score/ladder-table.js +109 -0
- package/dist/core/score/ladder.d.ts +229 -0
- package/dist/core/score/ladder.js +464 -0
- package/dist/core/score/mode-agreement.d.ts +30 -0
- package/dist/core/score/mode-agreement.js +16 -0
- package/dist/core/score/outcome-table.d.ts +17 -0
- package/dist/core/score/outcome-table.js +172 -0
- package/dist/core/score/outcome.d.ts +466 -0
- package/dist/core/score/outcome.js +562 -0
- package/dist/core/score/qualification.d.ts +83 -0
- package/dist/core/score/qualification.js +645 -0
- package/dist/core/score/quotation.d.ts +56 -0
- package/dist/core/score/quotation.js +226 -0
- package/dist/core/score/reduce-trials.d.ts +80 -0
- package/dist/core/score/reduce-trials.js +90 -0
- package/dist/core/score/score.d.ts +72 -0
- package/dist/core/score/score.js +607 -0
- package/dist/core/score/selection.d.ts +80 -0
- package/dist/core/score/selection.js +98 -0
- package/dist/core/score/strength.d.ts +58 -0
- package/dist/core/score/strength.js +227 -0
- package/dist/core/score/witness.d.ts +138 -0
- package/dist/core/score/witness.js +320 -0
- package/dist/core/seal/derived-reference.d.ts +2 -9
- package/dist/core/seal/derived-reference.js +293 -46
- package/dist/core/seal/plan-index.d.ts +18 -3
- package/dist/core/seal/plan-index.js +44 -8
- package/dist/core/seal/seal.js +8 -3
- package/dist/core/stage-contracts.d.ts +62 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/ports/environment-probe-port.d.ts +60 -4
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.js +1 -0
- package/dist/testing/probe-conformance.d.ts +29 -0
- package/dist/testing/probe-conformance.js +34 -5
- package/package.json +12 -3
- package/schemas/eval-contract.schema.json +1787 -797
- package/schemas/evidence-artifact.schema.json +126 -9
- package/schemas/isolation-manifest.schema.json +17 -10
- package/schemas/probe.schema.json +636 -5
- package/schemas/rubric.schema.json +1 -1
- package/schemas/scoring-policy.schema.json +8 -1
- package/schemas/sealed-evaluator-brief.schema.json +11 -1
- package/schemas/sealed-run-record.schema.json +314 -40
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One declared operation joined to the interface that declares it, plus the
|
|
3
|
+
* two facts the coverage predicates need and neither the operation nor the
|
|
4
|
+
* interface states: where a response descriptor's pointers resolve, and which
|
|
5
|
+
* channels a call's inputs may be keyed by.
|
|
6
|
+
*
|
|
7
|
+
* `relevance.ts` and `satisfaction.ts` each flattened `permittedInterfaces`
|
|
8
|
+
* with `flatMap((declared) => declared.operations)` and dropped the interface
|
|
9
|
+
* one line before every loop that ranges over it, so six of AD-20's seven
|
|
10
|
+
* rules could not see the kind that declared the operation they were grading.
|
|
11
|
+
* This is that flattening, written once and keeping the join.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here throws, which is the promise both predicate modules make in
|
|
14
|
+
* their own headers and which `evaluateCoverage`'s callers rely on. Every
|
|
15
|
+
* field is read off an already-parsed contract, so there is no precondition
|
|
16
|
+
* left to assert.
|
|
17
|
+
*/
|
|
18
|
+
import { type RequestChannel } from '../declared-inputs.ts';
|
|
19
|
+
import type { EvalContract } from '../schemas/eval-contract.ts';
|
|
20
|
+
import type { AnyOperation, InterfaceKindName, ResponseDescriptor } from '../schemas/interface.ts';
|
|
21
|
+
import type { InputChannelName } from '../schemas/pointer.ts';
|
|
22
|
+
/**
|
|
23
|
+
* RFC 6901 escaping, `~` before `/`. Lives here rather than beside the
|
|
24
|
+
* pointer-building helpers that use it, because the descriptor root is built
|
|
25
|
+
* here and both spellings have to escape the same way.
|
|
26
|
+
*/
|
|
27
|
+
export declare const encodeToken: (token: string) => string;
|
|
28
|
+
export type ResolvedOperation = {
|
|
29
|
+
readonly operation: AnyOperation;
|
|
30
|
+
/** the declaring interface's kind. An operation states no kind of its own. */
|
|
31
|
+
readonly kind: InterfaceKindName;
|
|
32
|
+
/** the declaring interface. Two interfaces may declare one `operationId`, and this is what separates them. */
|
|
33
|
+
readonly logicalId: string;
|
|
34
|
+
readonly descriptor: ResponseDescriptor;
|
|
35
|
+
/**
|
|
36
|
+
* The interaction-rooted segment a descriptor pointer hangs off, which is
|
|
37
|
+
* the channel this operation's own descriptor describes, and on the artifact
|
|
38
|
+
* channel the file as well. `/artifact` alone is not a root any real
|
|
39
|
+
* evidence pointer starts with, since an artifact pointer carries its
|
|
40
|
+
* identifier before its tail, so a root without it matched nothing and made
|
|
41
|
+
* every pointer-building rule answer against a pointer that cannot exist.
|
|
42
|
+
*/
|
|
43
|
+
readonly descriptorRoot: string;
|
|
44
|
+
/**
|
|
45
|
+
* The channels a call's inputs may be keyed by. Names only: the two sites
|
|
46
|
+
* that build candidate pointers need the names and never the shapes.
|
|
47
|
+
*/
|
|
48
|
+
readonly transportChannels: readonly InputChannelName[];
|
|
49
|
+
/**
|
|
50
|
+
* The same channels already paired with the shapes they declare. Predicates
|
|
51
|
+
* that read a declared shape take these rather than indexing the request
|
|
52
|
+
* shape with a name, because under the operation union TypeScript cannot
|
|
53
|
+
* prove a name drawn from one kind's tuple is a key of the other's shape.
|
|
54
|
+
*/
|
|
55
|
+
readonly requestChannels: readonly RequestChannel[];
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Every declared operation, in declaration order, each carrying the interface
|
|
59
|
+
* that declares it. Six of AD-20's seven rules range over this list; rule 5 is
|
|
60
|
+
* contract-level and reads the sibling groups instead.
|
|
61
|
+
*/
|
|
62
|
+
export declare function resolveOperations(contract: EvalContract): readonly ResolvedOperation[];
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One declared operation joined to the interface that declares it, plus the
|
|
3
|
+
* two facts the coverage predicates need and neither the operation nor the
|
|
4
|
+
* interface states: where a response descriptor's pointers resolve, and which
|
|
5
|
+
* channels a call's inputs may be keyed by.
|
|
6
|
+
*
|
|
7
|
+
* `relevance.ts` and `satisfaction.ts` each flattened `permittedInterfaces`
|
|
8
|
+
* with `flatMap((declared) => declared.operations)` and dropped the interface
|
|
9
|
+
* one line before every loop that ranges over it, so six of AD-20's seven
|
|
10
|
+
* rules could not see the kind that declared the operation they were grading.
|
|
11
|
+
* This is that flattening, written once and keeping the join.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here throws, which is the promise both predicate modules make in
|
|
14
|
+
* their own headers and which `evaluateCoverage`'s callers rely on. Every
|
|
15
|
+
* field is read off an already-parsed contract, so there is no precondition
|
|
16
|
+
* left to assert.
|
|
17
|
+
*/
|
|
18
|
+
import { descriptorArtifactOf, descriptorChannelOf, inputChannelsOf, requestChannelsOf, } from '../declared-inputs.js';
|
|
19
|
+
import { operationsOf } from '../schemas/interface.js';
|
|
20
|
+
/**
|
|
21
|
+
* RFC 6901 escaping, `~` before `/`. Lives here rather than beside the
|
|
22
|
+
* pointer-building helpers that use it, because the descriptor root is built
|
|
23
|
+
* here and both spellings have to escape the same way.
|
|
24
|
+
*/
|
|
25
|
+
export const encodeToken = (token) => token.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
26
|
+
/**
|
|
27
|
+
* Where a descriptor-relative pointer hangs off an interaction root for this
|
|
28
|
+
* operation. Two segments on the artifact channel, one everywhere else.
|
|
29
|
+
*/
|
|
30
|
+
const descriptorRootOf = (operation) => {
|
|
31
|
+
const channel = descriptorChannelOf(operation);
|
|
32
|
+
if (channel !== 'artifact')
|
|
33
|
+
return `/${channel}`;
|
|
34
|
+
const artifactId = descriptorArtifactOf(operation);
|
|
35
|
+
// Unreachable: `descriptorChannelOf` answers `artifact` only for the arm
|
|
36
|
+
// that carries an identifier.
|
|
37
|
+
if (artifactId === null) {
|
|
38
|
+
throw new TypeError(`operation "${operation.operationId}" describes an artifact and names none`);
|
|
39
|
+
}
|
|
40
|
+
return `/${channel}/${encodeToken(artifactId)}`;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Every declared operation, in declaration order, each carrying the interface
|
|
44
|
+
* that declares it. Six of AD-20's seven rules range over this list; rule 5 is
|
|
45
|
+
* contract-level and reads the sibling groups instead.
|
|
46
|
+
*/
|
|
47
|
+
export function resolveOperations(contract) {
|
|
48
|
+
return contract.permittedInterfaces.flatMap((declared) => operationsOf(declared).map((operation) => ({
|
|
49
|
+
operation,
|
|
50
|
+
kind: declared.kind,
|
|
51
|
+
logicalId: declared.logicalId,
|
|
52
|
+
descriptor: operation.responseDescriptor,
|
|
53
|
+
descriptorRoot: descriptorRootOf(operation),
|
|
54
|
+
transportChannels: inputChannelsOf(operation),
|
|
55
|
+
requestChannels: requestChannelsOf(operation),
|
|
56
|
+
})));
|
|
57
|
+
}
|
|
@@ -32,8 +32,10 @@ export declare function successIndicatorSeparationRelevance(contract: EvalContra
|
|
|
32
32
|
*/
|
|
33
33
|
export declare function wholeBodyRelevance(contract: EvalContract): RelevanceVerdict;
|
|
34
34
|
/**
|
|
35
|
-
* Rule 3: some operation declares a request key on any of
|
|
36
|
-
*
|
|
35
|
+
* Rule 3: some operation declares a request key on any of its own input
|
|
36
|
+
* channels, AD-19's four transport channels off an interface that speaks HTTP
|
|
37
|
+
* and the four command channels off one that does not. The site is the whole
|
|
38
|
+
* channel triple. A key with no
|
|
37
39
|
* `types` entry has an absent type, a key typed `null` has AD-31's
|
|
38
40
|
* indeterminate one, and both are relevant, so declaring an input and leaving
|
|
39
41
|
* it untyped buys no irrelevance.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveOperations } from './operations.js';
|
|
2
2
|
import { DISCIPLINE_RULES, relevancePredicateId, } from './rules.js';
|
|
3
3
|
const verdict = (rule, relevant, reason) => ({
|
|
4
4
|
rule,
|
|
@@ -8,8 +8,6 @@ const verdict = (rule, relevant, reason) => ({
|
|
|
8
8
|
});
|
|
9
9
|
/** `1 collection location`, `2 collection locations`. A reason is read by people. */
|
|
10
10
|
const plural = (count, noun) => `${count} ${noun}${count === 1 ? '' : 's'}`;
|
|
11
|
-
/** Every declared operation, flattened. Six of the seven rules range over this list. */
|
|
12
|
-
const operationsOf = (contract) => contract.permittedInterfaces.flatMap((declared) => declared.operations);
|
|
13
11
|
/** A contract declaring no operation leaves six of the rules nothing to read. */
|
|
14
12
|
export const NO_OPERATION = 'the contract declares no operation, so the declaration this rule reads is absent';
|
|
15
13
|
/**
|
|
@@ -20,11 +18,11 @@ export const NO_OPERATION = 'the contract declares no operation, so the declarat
|
|
|
20
18
|
*/
|
|
21
19
|
export function successIndicatorSeparationRelevance(contract) {
|
|
22
20
|
const rule = 'success-indicator-separation';
|
|
23
|
-
const operations =
|
|
21
|
+
const operations = resolveOperations(contract);
|
|
24
22
|
if (operations.length === 0)
|
|
25
23
|
return verdict(rule, true, NO_OPERATION);
|
|
26
|
-
for (const operation of operations) {
|
|
27
|
-
const { successIndicator, channelRoles } =
|
|
24
|
+
for (const { operation, descriptor } of operations) {
|
|
25
|
+
const { successIndicator, channelRoles } = descriptor;
|
|
28
26
|
// `=== null`: `DescriptorPointer` admits the empty string, which
|
|
29
27
|
// nominates the whole response body.
|
|
30
28
|
if (successIndicator === null) {
|
|
@@ -48,11 +46,11 @@ export function successIndicatorSeparationRelevance(contract) {
|
|
|
48
46
|
*/
|
|
49
47
|
export function wholeBodyRelevance(contract) {
|
|
50
48
|
const rule = 'whole-body';
|
|
51
|
-
const operations =
|
|
49
|
+
const operations = resolveOperations(contract);
|
|
52
50
|
if (operations.length === 0)
|
|
53
51
|
return verdict(rule, true, NO_OPERATION);
|
|
54
|
-
for (const operation of operations) {
|
|
55
|
-
const distinct = new Set(
|
|
52
|
+
for (const { operation, descriptor } of operations) {
|
|
53
|
+
const distinct = new Set(descriptor.requiredKeys);
|
|
56
54
|
if (distinct.size > 1) {
|
|
57
55
|
return verdict(rule, true, `operation ${operation.operationId} declares ${plural(distinct.size, 'distinct required response key')}`);
|
|
58
56
|
}
|
|
@@ -60,20 +58,21 @@ export function wholeBodyRelevance(contract) {
|
|
|
60
58
|
return verdict(rule, false, 'no operation declares more than one distinct required response key');
|
|
61
59
|
}
|
|
62
60
|
/**
|
|
63
|
-
* Rule 3: some operation declares a request key on any of
|
|
64
|
-
*
|
|
61
|
+
* Rule 3: some operation declares a request key on any of its own input
|
|
62
|
+
* channels, AD-19's four transport channels off an interface that speaks HTTP
|
|
63
|
+
* and the four command channels off one that does not. The site is the whole
|
|
64
|
+
* channel triple. A key with no
|
|
65
65
|
* `types` entry has an absent type, a key typed `null` has AD-31's
|
|
66
66
|
* indeterminate one, and both are relevant, so declaring an input and leaving
|
|
67
67
|
* it untyped buys no irrelevance.
|
|
68
68
|
*/
|
|
69
69
|
export function malformedInputRelevance(contract) {
|
|
70
70
|
const rule = 'malformed-input';
|
|
71
|
-
const operations =
|
|
71
|
+
const operations = resolveOperations(contract);
|
|
72
72
|
if (operations.length === 0)
|
|
73
73
|
return verdict(rule, true, NO_OPERATION);
|
|
74
|
-
for (const operation of operations) {
|
|
75
|
-
for (const channel of
|
|
76
|
-
const shape = operation.requestShape[channel];
|
|
74
|
+
for (const { operation, requestChannels } of operations) {
|
|
75
|
+
for (const { channel, shape } of requestChannels) {
|
|
77
76
|
// `Object.keys` enumerates own keys only. `KeyName` admits
|
|
78
77
|
// `constructor`, which a keyed lookup would find on the prototype.
|
|
79
78
|
const key = shape.requiredKeys[0] ??
|
|
@@ -92,11 +91,11 @@ export function malformedInputRelevance(contract) {
|
|
|
92
91
|
*/
|
|
93
92
|
export function perRecordRelevance(contract) {
|
|
94
93
|
const rule = 'per-record';
|
|
95
|
-
const operations =
|
|
94
|
+
const operations = resolveOperations(contract);
|
|
96
95
|
if (operations.length === 0)
|
|
97
96
|
return verdict(rule, true, NO_OPERATION);
|
|
98
|
-
for (const operation of operations) {
|
|
99
|
-
const { collectionLocations } =
|
|
97
|
+
for (const { operation, descriptor } of operations) {
|
|
98
|
+
const { collectionLocations } = descriptor;
|
|
100
99
|
if (collectionLocations === null) {
|
|
101
100
|
return verdict(rule, true, `operation ${operation.operationId} declares no collection-location list, so no collection is declared to range over`);
|
|
102
101
|
}
|
|
@@ -132,11 +131,11 @@ export function siblingCrossCheckRelevance(contract) {
|
|
|
132
131
|
*/
|
|
133
132
|
export function omissionAndCompletenessRelevance(contract) {
|
|
134
133
|
const rule = 'omission-and-completeness';
|
|
135
|
-
const operations =
|
|
134
|
+
const operations = resolveOperations(contract);
|
|
136
135
|
if (operations.length === 0)
|
|
137
136
|
return verdict(rule, true, NO_OPERATION);
|
|
138
|
-
for (const operation of operations) {
|
|
139
|
-
const { collectionLocations } =
|
|
137
|
+
for (const { operation, descriptor } of operations) {
|
|
138
|
+
const { collectionLocations } = descriptor;
|
|
140
139
|
if (collectionLocations === null) {
|
|
141
140
|
return verdict(rule, true, `operation ${operation.operationId} declares no collection-location list, so no location can name a reference set`);
|
|
142
141
|
}
|
|
@@ -155,10 +154,10 @@ export function omissionAndCompletenessRelevance(contract) {
|
|
|
155
154
|
*/
|
|
156
155
|
export function stateChangeReadBackRelevance(contract) {
|
|
157
156
|
const rule = 'state-change-read-back';
|
|
158
|
-
const operations =
|
|
157
|
+
const operations = resolveOperations(contract);
|
|
159
158
|
if (operations.length === 0)
|
|
160
159
|
return verdict(rule, true, NO_OPERATION);
|
|
161
|
-
for (const operation of operations) {
|
|
160
|
+
for (const { operation } of operations) {
|
|
162
161
|
if (operation.stateChangeMarker) {
|
|
163
162
|
return verdict(rule, true, `operation ${operation.operationId} declares stateChangeMarker: true`);
|
|
164
163
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type EvalContract } from '../schemas/eval-contract.ts';
|
|
2
2
|
import type { Expression } from '../schemas/expression.ts';
|
|
3
|
-
import type { Operation } from '../schemas/interface.ts';
|
|
4
3
|
import { type PlanIndex } from '../seal/plan-index.ts';
|
|
4
|
+
import { type ResolvedOperation } from './operations.ts';
|
|
5
5
|
import { type DisciplineRule } from './rules.ts';
|
|
6
6
|
export type SatisfactionVerdict = {
|
|
7
7
|
readonly rule: DisciplineRule;
|
|
@@ -40,7 +40,7 @@ type OracleView = {
|
|
|
40
40
|
* own, which is what every per-rule fixture does.
|
|
41
41
|
*/
|
|
42
42
|
export type SatisfactionContext = {
|
|
43
|
-
readonly operations: readonly
|
|
43
|
+
readonly operations: readonly ResolvedOperation[];
|
|
44
44
|
readonly index: PlanIndex;
|
|
45
45
|
readonly oracles: readonly OracleView[];
|
|
46
46
|
};
|
|
@@ -12,9 +12,11 @@
|
|
|
12
12
|
* the artifact still ships (AD-5), so there is no failure code.
|
|
13
13
|
*/
|
|
14
14
|
import { substitutePointer } from '../compile/oracle-alignment.js';
|
|
15
|
+
import { boundChannelsOf } from '../declared-inputs.js';
|
|
15
16
|
import { SIBLING_GROUP_MINIMUM, } from '../schemas/eval-contract.js';
|
|
16
|
-
import {
|
|
17
|
-
import { buildPlanIndex } from '../seal/plan-index.js';
|
|
17
|
+
import { INPUT_CHANNELS } from '../schemas/pointer.js';
|
|
18
|
+
import { anyOperationOf, buildPlanIndex, } from '../seal/plan-index.js';
|
|
19
|
+
import { encodeToken, resolveOperations, } from './operations.js';
|
|
18
20
|
import { DISCIPLINE_RULES, satisfactionPredicateId, } from './rules.js';
|
|
19
21
|
const verdict = (rule, satisfied, reason) => ({
|
|
20
22
|
rule,
|
|
@@ -26,21 +28,22 @@ const verdict = (rule, satisfied, reason) => ({
|
|
|
26
28
|
export const NO_RELEVANT_SITE = 'the rule is relevant for no site, so satisfaction holds vacuously';
|
|
27
29
|
/** A contract declaring no operation leaves six of the rules an absent site. */
|
|
28
30
|
export const NO_OPERATION_WITNESS = 'the contract declares no operation, so the site this rule fires on has no declaration to witness';
|
|
29
|
-
/** Every declared operation, flattened. `relevance.ts` keeps its own copy. */
|
|
30
|
-
const operationsOf = (contract) => contract.permittedInterfaces.flatMap((declared) => declared.operations);
|
|
31
31
|
/** `unresolved` never throws; a duplicate identifier resolves to nothing. */
|
|
32
32
|
const planIndexOf = (contract) => buildPlanIndex(contract.interactionPlan, contract.permittedInterfaces, {
|
|
33
33
|
duplicateIds: 'unresolved',
|
|
34
34
|
});
|
|
35
35
|
// ---- the join between the two pointer spellings -------------------------
|
|
36
|
-
/** RFC 6901 escaping, `~` before `/`. */
|
|
37
|
-
const encodeToken = (token) => token.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
38
36
|
/** Everything one step produced or was given. */
|
|
39
37
|
const stepRoot = (stepId) => `/interactions/${stepId}`;
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
/**
|
|
39
|
+
* A descriptor pointer read at one step, spelled interaction-rooted. The root
|
|
40
|
+
* travels on the resolved operation, so an interface kind whose response
|
|
41
|
+
* arrives on another channel is one change in `operations.ts` and none at the
|
|
42
|
+
* five call sites below.
|
|
43
|
+
*/
|
|
44
|
+
const bodyPointer = (resolved, stepId, descriptorPointer) => `${stepRoot(stepId)}${resolved.descriptorRoot}${descriptorPointer}`;
|
|
45
|
+
/** One declared response key at one step, rooted the way `bodyPointer` is. */
|
|
46
|
+
const keyPointer = (resolved, stepId, key) => `${stepRoot(stepId)}${resolved.descriptorRoot}/${encodeToken(key)}`;
|
|
44
47
|
/** One declared parameter on one transport channel at one step. */
|
|
45
48
|
const parameterPointer = (stepId, channel, key) => `/interactions/${stepId}/call-inputs/${channel}/${encodeToken(key)}`;
|
|
46
49
|
/** A pointer addresses a root when it names it or descends into it. */
|
|
@@ -113,7 +116,7 @@ const bothChannelsAddress = (oracle, root) => someAddresses(oracle.directionTarg
|
|
|
113
116
|
someAddresses(oracle.checkPointers, root);
|
|
114
117
|
const definedPointers = (node) => node.operandPointers.filter((pointer) => pointer !== null);
|
|
115
118
|
const contextOf = (contract) => ({
|
|
116
|
-
operations:
|
|
119
|
+
operations: resolveOperations(contract),
|
|
117
120
|
index: planIndexOf(contract),
|
|
118
121
|
oracles: oracleViewsOf(contract),
|
|
119
122
|
});
|
|
@@ -130,8 +133,9 @@ export function successIndicatorSeparationSatisfaction(contract, context = conte
|
|
|
130
133
|
if (operations.length === 0)
|
|
131
134
|
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
132
135
|
let sites = 0;
|
|
133
|
-
for (const
|
|
134
|
-
const {
|
|
136
|
+
for (const resolved of operations) {
|
|
137
|
+
const { operation, descriptor } = resolved;
|
|
138
|
+
const { successIndicator, channelRoles } = descriptor;
|
|
135
139
|
if (successIndicator === null) {
|
|
136
140
|
return verdict(rule, false, `operation ${operation.operationId} nominates no success indicator, so no oracle can separate one from the body`);
|
|
137
141
|
}
|
|
@@ -147,8 +151,8 @@ export function successIndicatorSeparationSatisfaction(contract, context = conte
|
|
|
147
151
|
.map(([pointer]) => pointer);
|
|
148
152
|
const witnessed = index
|
|
149
153
|
.stepsUsing(operation.operationId)
|
|
150
|
-
.some((step) => oracles.some((oracle) => bothChannelsAddress(oracle, bodyPointer(step.stepId, successIndicator)) &&
|
|
151
|
-
others.some((pointer) => bothChannelsAddress(oracle, bodyPointer(step.stepId, pointer)))));
|
|
154
|
+
.some((step) => oracles.some((oracle) => bothChannelsAddress(oracle, bodyPointer(resolved, step.stepId, successIndicator)) &&
|
|
155
|
+
others.some((pointer) => bothChannelsAddress(oracle, bodyPointer(resolved, step.stepId, pointer)))));
|
|
152
156
|
if (!witnessed) {
|
|
153
157
|
return verdict(rule, false, `no oracle addresses operation ${operation.operationId}'s success indicator beside another roled pointer at one step, in both channels`);
|
|
154
158
|
}
|
|
@@ -169,14 +173,15 @@ export function wholeBodySatisfaction(contract, context = contextOf(contract)) {
|
|
|
169
173
|
if (operations.length === 0)
|
|
170
174
|
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
171
175
|
let sites = 0;
|
|
172
|
-
for (const
|
|
173
|
-
const
|
|
176
|
+
for (const resolved of operations) {
|
|
177
|
+
const { operation, descriptor } = resolved;
|
|
178
|
+
const required = [...new Set(descriptor.requiredKeys)];
|
|
174
179
|
if (required.length <= 1)
|
|
175
180
|
continue;
|
|
176
181
|
sites += 1;
|
|
177
182
|
const witnessed = index
|
|
178
183
|
.stepsUsing(operation.operationId)
|
|
179
|
-
.some((step) => oracles.some((oracle) => required.every((key) => bothChannelsAddress(oracle, keyPointer(step.stepId, key)))));
|
|
184
|
+
.some((step) => oracles.some((oracle) => required.every((key) => bothChannelsAddress(oracle, keyPointer(resolved, step.stepId, key)))));
|
|
180
185
|
if (!witnessed) {
|
|
181
186
|
return verdict(rule, false, `no oracle covers every required response key of operation ${operation.operationId} at one addressed step, in both channels`);
|
|
182
187
|
}
|
|
@@ -185,19 +190,19 @@ export function wholeBodySatisfaction(contract, context = contextOf(contract)) {
|
|
|
185
190
|
? NO_RELEVANT_SITE
|
|
186
191
|
: 'every operation declaring more than one required response key has an oracle covering all of them at one step');
|
|
187
192
|
}
|
|
188
|
-
/** The site condition rule 3 relevance reads: a key on any
|
|
189
|
-
const declaresRequestKey = (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
if (
|
|
193
|
+
/** The site condition rule 3 relevance reads: a key on any input channel. */
|
|
194
|
+
const declaresRequestKey = (resolved) => resolved.requestChannels.some(({ shape }) => shape.requiredKeys.length > 0 ||
|
|
195
|
+
shape.permittedKeys.length > 0 ||
|
|
196
|
+
Object.keys(shape.types).length > 0);
|
|
197
|
+
/**
|
|
198
|
+
* AD-39's matcher, on any input channel of one step. The step carries no
|
|
199
|
+
* operation, so the bound channels are read off the binding itself rather than
|
|
200
|
+
* off a channel tuple that would name four channels the binding does not have.
|
|
201
|
+
*/
|
|
202
|
+
const bindsTypeViolating = (step) => boundChannelsOf(step.inputBinding).some(({ bound }) => {
|
|
203
|
+
if (bound === null)
|
|
199
204
|
return false;
|
|
200
|
-
return Object.values(
|
|
205
|
+
return Object.values(bound).some((value) => 'matcher' in value && value.matcher === 'type-violating');
|
|
201
206
|
});
|
|
202
207
|
/**
|
|
203
208
|
* Rule 3: for every operation declaring a request key, some step invoking it
|
|
@@ -211,8 +216,9 @@ export function malformedInputSatisfaction(contract, context = contextOf(contrac
|
|
|
211
216
|
if (operations.length === 0)
|
|
212
217
|
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
213
218
|
let sites = 0;
|
|
214
|
-
for (const
|
|
215
|
-
|
|
219
|
+
for (const resolved of operations) {
|
|
220
|
+
const { operation } = resolved;
|
|
221
|
+
if (!declaresRequestKey(resolved))
|
|
216
222
|
continue;
|
|
217
223
|
sites += 1;
|
|
218
224
|
const witnessed = index
|
|
@@ -240,8 +246,9 @@ export function perRecordSatisfaction(contract, context = contextOf(contract)) {
|
|
|
240
246
|
if (operations.length === 0)
|
|
241
247
|
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
242
248
|
let sites = 0;
|
|
243
|
-
for (const
|
|
244
|
-
const {
|
|
249
|
+
for (const resolved of operations) {
|
|
250
|
+
const { operation, descriptor } = resolved;
|
|
251
|
+
const { collectionLocations } = descriptor;
|
|
245
252
|
if (collectionLocations === null) {
|
|
246
253
|
return verdict(rule, false, `operation ${operation.operationId} declares no collection-location list, so no quantifier can range over a declared collection`);
|
|
247
254
|
}
|
|
@@ -249,7 +256,7 @@ export function perRecordSatisfaction(contract, context = contextOf(contract)) {
|
|
|
249
256
|
for (const location of collectionLocations) {
|
|
250
257
|
sites += 1;
|
|
251
258
|
const witnessed = steps.some((step) => {
|
|
252
|
-
const collection = bodyPointer(step.stepId, location.pointer);
|
|
259
|
+
const collection = bodyPointer(resolved, step.stepId, location.pointer);
|
|
253
260
|
return oracles.some((oracle) => oracle.nodes.some((node) => node.collection === collection));
|
|
254
261
|
});
|
|
255
262
|
if (!witnessed) {
|
|
@@ -289,7 +296,13 @@ export function siblingCrossCheckSatisfaction(contract, context = contextOf(cont
|
|
|
289
296
|
for (const group of groups.parameters) {
|
|
290
297
|
sites += 1;
|
|
291
298
|
const members = [...new Set(group)];
|
|
292
|
-
const witnessed = oracles.some((oracle) => members.filter((parameter) => contract.interactionPlan.some((step) =>
|
|
299
|
+
const witnessed = oracles.some((oracle) => members.filter((parameter) => contract.interactionPlan.some((step) =>
|
|
300
|
+
// Building candidate pointers, not reading a declared
|
|
301
|
+
// shape: `groups.parameters` carries no operation, so
|
|
302
|
+
// every input channel is tried and whichever one the
|
|
303
|
+
// oracle actually addresses is the match. It looks
|
|
304
|
+
// identical to `declaresRequestKey` above and is not.
|
|
305
|
+
INPUT_CHANNELS.some((channel) => bothChannelsAddress(oracle, parameterPointer(step.stepId, channel, parameter))))).length >= SIBLING_GROUP_MINIMUM);
|
|
293
306
|
if (!witnessed) {
|
|
294
307
|
return verdict(rule, false, `no oracle addresses two members of the parameter sibling group ${members.join(' and ')} in both channels`);
|
|
295
308
|
}
|
|
@@ -330,8 +343,9 @@ export function omissionAndCompletenessSatisfaction(contract, context = contextO
|
|
|
330
343
|
if (operations.length === 0)
|
|
331
344
|
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
332
345
|
let sites = 0;
|
|
333
|
-
for (const
|
|
334
|
-
const {
|
|
346
|
+
for (const resolved of operations) {
|
|
347
|
+
const { operation, descriptor } = resolved;
|
|
348
|
+
const { collectionLocations } = descriptor;
|
|
335
349
|
if (collectionLocations === null) {
|
|
336
350
|
return verdict(rule, false, `operation ${operation.operationId} declares no collection-location list, so no location can be reconciled against a reference set`);
|
|
337
351
|
}
|
|
@@ -342,7 +356,7 @@ export function omissionAndCompletenessSatisfaction(contract, context = contextO
|
|
|
342
356
|
continue;
|
|
343
357
|
sites += 1;
|
|
344
358
|
const witnessed = steps.some((step) => {
|
|
345
|
-
const collection = bodyPointer(step.stepId, location.pointer);
|
|
359
|
+
const collection = bodyPointer(resolved, step.stepId, location.pointer);
|
|
346
360
|
return oracles.some((oracle) => oracle.nodes.some((node) => reconciles(node, collection, location, referenceSet)));
|
|
347
361
|
});
|
|
348
362
|
if (!witnessed) {
|
|
@@ -355,17 +369,28 @@ export function omissionAndCompletenessSatisfaction(contract, context = contextO
|
|
|
355
369
|
: 'every collection location naming a reference set is reconciled against it in the declared form');
|
|
356
370
|
}
|
|
357
371
|
/** AD-39: a read-back step names the write in its temporal clause and changes no state itself. */
|
|
358
|
-
const readBackStepsFor = (contract,
|
|
372
|
+
const readBackStepsFor = (contract, context, writeStepId) => contract.interactionPlan.flatMap((step) => {
|
|
359
373
|
if (step.stepId === writeStepId || step.after !== writeStepId)
|
|
360
|
-
return
|
|
361
|
-
const operation = index
|
|
362
|
-
|
|
374
|
+
return [];
|
|
375
|
+
const operation = anyOperationOf(context.index, step.operationId);
|
|
376
|
+
if (operation === undefined || operation.stateChangeMarker)
|
|
377
|
+
return [];
|
|
378
|
+
// The index and the resolver both read `permittedInterfaces`, so the
|
|
379
|
+
// index answers with the very object the resolver carries. Matching on
|
|
380
|
+
// identity leaves the index's duplicate-identifier rule as the one
|
|
381
|
+
// thing deciding which operations resolve.
|
|
382
|
+
return context.operations
|
|
383
|
+
.filter((resolved) => resolved.operation === operation)
|
|
384
|
+
.map((resolved) => ({ step, resolved }));
|
|
363
385
|
});
|
|
364
386
|
/** One node holding a pointer into each side of the read-back relation. */
|
|
365
|
-
const relates = (node, writeStepId,
|
|
387
|
+
const relates = (node, writeStepId, readBack) => {
|
|
366
388
|
const pointers = definedPointers(node);
|
|
367
|
-
return (
|
|
368
|
-
|
|
389
|
+
return (
|
|
390
|
+
// `call-inputs` is the same segment for every interface kind, so the
|
|
391
|
+
// write side takes no root from its operation.
|
|
392
|
+
someAddresses(pointers, `${stepRoot(writeStepId)}/call-inputs`) &&
|
|
393
|
+
someAddresses(pointers, `${stepRoot(readBack.step.stepId)}${readBack.resolved.descriptorRoot}`));
|
|
369
394
|
};
|
|
370
395
|
/**
|
|
371
396
|
* Rule 7: for every operation declaring `stateChangeMarker: true`, some check
|
|
@@ -380,13 +405,13 @@ export function stateChangeReadBackSatisfaction(contract, context = contextOf(co
|
|
|
380
405
|
if (operations.length === 0)
|
|
381
406
|
return verdict(rule, false, NO_OPERATION_WITNESS);
|
|
382
407
|
let sites = 0;
|
|
383
|
-
for (const operation of operations) {
|
|
408
|
+
for (const { operation } of operations) {
|
|
384
409
|
if (!operation.stateChangeMarker)
|
|
385
410
|
continue;
|
|
386
411
|
sites += 1;
|
|
387
412
|
const witnessed = index
|
|
388
413
|
.stepsUsing(operation.operationId)
|
|
389
|
-
.some((writeStep) => readBackStepsFor(contract,
|
|
414
|
+
.some((writeStep) => readBackStepsFor(contract, context, writeStep.stepId).some((readBack) => oracles.some((oracle) => oracle.nodes.some((node) => relates(node, writeStep.stepId, readBack)))));
|
|
390
415
|
if (!witnessed) {
|
|
391
416
|
return verdict(rule, false, `no check relates operation ${operation.operationId}'s call inputs to the response body of a later step that changes no state`);
|
|
392
417
|
}
|
|
@@ -1,16 +1,95 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What an operation declares it accepts, as predicates over
|
|
2
|
+
* What an operation declares it accepts, as predicates over a request shape,
|
|
3
|
+
* plus the resolution that lets a caller read one without knowing its kind.
|
|
3
4
|
*
|
|
4
5
|
* At the `core/` root because `core/schemas/` holds Zod definitions only, and
|
|
5
6
|
* because AD-10's exemption is read by two modules: the compile check that
|
|
6
7
|
* enforces it and the reducer that records it.
|
|
7
8
|
*/
|
|
8
|
-
import type { Operation } from './schemas/interface.ts';
|
|
9
|
+
import type { AnyOperation, CommandOperation, Operation } from './schemas/interface.ts';
|
|
10
|
+
import type { BindingChannel, InputBinding } from './schemas/plan.ts';
|
|
11
|
+
import { type EvidenceChannelName, type InputChannelName } from './schemas/pointer.ts';
|
|
12
|
+
import type { KeyedShapeDescriptor } from './schemas/primitives.ts';
|
|
13
|
+
/**
|
|
14
|
+
* Which output channel the operation's one response descriptor describes.
|
|
15
|
+
*
|
|
16
|
+
* AD-19 gives every operation exactly one descriptor, and the channel that
|
|
17
|
+
* descriptor describes is what makes its keys addressable. For an interface
|
|
18
|
+
* that speaks HTTP that channel is the response body; a command operation
|
|
19
|
+
* declares its own. One rule, three roots: every consumer that used to hard-code
|
|
20
|
+
* `response-body` asks this instead.
|
|
21
|
+
*/
|
|
22
|
+
export declare const descriptorChannelOf: (operation: AnyOperation) => EvidenceChannelName;
|
|
23
|
+
/**
|
|
24
|
+
* Which artifact the descriptor describes, or `null` when it describes a
|
|
25
|
+
* stream. An artifact pointer descends through the descriptor only when it
|
|
26
|
+
* names this one; every other declared artifact is known to exist and declares
|
|
27
|
+
* no structure.
|
|
28
|
+
*/
|
|
29
|
+
export declare const descriptorArtifactOf: (operation: AnyOperation) => string | null;
|
|
30
|
+
/**
|
|
31
|
+
* Whether an evidence target addresses the channel this operation's response
|
|
32
|
+
* descriptor describes.
|
|
33
|
+
*
|
|
34
|
+
* The channel name alone is not the answer on the artifact channel: an
|
|
35
|
+
* operation may declare several files while its one descriptor describes one of
|
|
36
|
+
* them, so a pointer at a different file names a channel with no declared
|
|
37
|
+
* structure. Every consumer asks this rather than comparing the channel itself,
|
|
38
|
+
* because comparing only the channel was a defect at three separate sites and a
|
|
39
|
+
* fourth site would have made the same mistake for the same reason.
|
|
40
|
+
*/
|
|
41
|
+
export declare const targetsDescribedChannel: (operation: AnyOperation, target: {
|
|
42
|
+
readonly channel: EvidenceChannelName;
|
|
43
|
+
readonly artifactId: string | null;
|
|
44
|
+
}) => boolean;
|
|
45
|
+
/** Every artifact identifier the operation declares it writes. */
|
|
46
|
+
export declare const declaredArtifactsOf: (operation: AnyOperation) => readonly string[];
|
|
47
|
+
/** One declared input channel with the shape it declares, already paired. */
|
|
48
|
+
export type RequestChannel = {
|
|
49
|
+
readonly channel: InputChannelName;
|
|
50
|
+
readonly shape: KeyedShapeDescriptor;
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Which operation shape this is. Reads `invocation` rather than a kind field,
|
|
54
|
+
* because an operation does not carry its interface's kind; the two shapes are
|
|
55
|
+
* distinguished by a required field only one of them declares.
|
|
56
|
+
*/
|
|
57
|
+
export declare const isCommandOperation: (operation: AnyOperation) => operation is CommandOperation;
|
|
58
|
+
/** The same question the other way round, for the callers that filter. */
|
|
59
|
+
export declare const isApiOperation: (operation: AnyOperation) => operation is Operation;
|
|
60
|
+
/**
|
|
61
|
+
* The operation's input channels paired with the shapes they declare.
|
|
62
|
+
*
|
|
63
|
+
* Callers take the pairs rather than a channel list they then index the
|
|
64
|
+
* request shape with. Indexing is what breaks under the operation union:
|
|
65
|
+
* TypeScript cannot prove that a channel name drawn from one kind's tuple is a
|
|
66
|
+
* key of the other kind's shape, and both ways around that are casts. Pairing
|
|
67
|
+
* dereferences the union once, here.
|
|
68
|
+
*/
|
|
69
|
+
export declare function requestChannelsOf(operation: AnyOperation): readonly RequestChannel[];
|
|
70
|
+
/** The shape one named channel declares, or `undefined` off this kind. */
|
|
71
|
+
export declare function requestShapeOf(operation: AnyOperation, channel: InputChannelName): KeyedShapeDescriptor | undefined;
|
|
72
|
+
/** The channel names an operation of this kind may declare inputs on. */
|
|
73
|
+
export declare const inputChannelsOf: (operation: AnyOperation) => readonly InputChannelName[];
|
|
74
|
+
/**
|
|
75
|
+
* A step's bound channels paired with what each binds, for the same reason
|
|
76
|
+
* `requestChannelsOf` pairs: `InputBinding` is a union and a channel name
|
|
77
|
+
* drawn from one branch is not a key of the other.
|
|
78
|
+
*
|
|
79
|
+
* Ordered by `INPUT_CHANNELS` rather than by the parsed object's own key
|
|
80
|
+
* order, so which binding a check reports never depends on how the document
|
|
81
|
+
* happened to be written.
|
|
82
|
+
*/
|
|
83
|
+
export declare function boundChannelsOf(binding: InputBinding): readonly {
|
|
84
|
+
readonly channel: InputChannelName;
|
|
85
|
+
readonly bound: BindingChannel;
|
|
86
|
+
}[];
|
|
9
87
|
/**
|
|
10
88
|
* AD-10's exemption predicate. Any of a channel's three lists naming a key
|
|
11
89
|
* counts: a permitted-only or types-only channel is still a surface a witness
|
|
12
90
|
* can vary.
|
|
13
91
|
*/
|
|
14
|
-
export declare function declaresNoRequestKeys(operation:
|
|
92
|
+
export declare function declaresNoRequestKeys(operation: AnyOperation): boolean;
|
|
15
93
|
/** Whether no channel declares a required key. */
|
|
16
|
-
export declare function declaresNoRequiredKeys(operation:
|
|
94
|
+
export declare function declaresNoRequiredKeys(operation: AnyOperation): boolean;
|
|
95
|
+
export type { AnyOperation, CommandOperation, Operation };
|