@astrale-os/kernel-protocol 0.5.0-beta.67 → 0.5.0-beta.69
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/codecs/index.d.ts +1 -2
- package/dist/codecs/index.js +1 -2
- package/dist/codecs/media-types.d.ts +0 -1
- package/dist/codecs/media-types.js +0 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/invocation/admission.d.ts +2 -2
- package/dist/invocation/admission.js +5 -5
- package/dist/invocation/binding-codecs.d.ts +1 -1
- package/dist/invocation/binding-codecs.js +1 -6
- package/dist/invocation/http/negotiation.js +3 -10
- package/dist/invocation/index.d.ts +5 -2
- package/dist/invocation/index.js +4 -2
- package/dist/invocation/legacy/index.d.ts +29 -0
- package/dist/invocation/legacy/index.js +28 -0
- package/dist/invocation/legacy/route.d.ts +14 -0
- package/dist/invocation/legacy/route.js +25 -0
- package/dist/invocation/legacy/via.d.ts +30 -0
- package/dist/invocation/legacy/via.js +15 -0
- package/dist/invocation/redirect.d.ts +15 -12
- package/dist/invocation/redirect.js +9 -21
- package/dist/invocation/request.js +2 -2
- package/dist/invocation/routing.d.ts +19 -5
- package/dist/invocation/routing.js +50 -20
- package/dist/invocation/schema.d.ts +3 -8
- package/dist/invocation/schema.js +3 -10
- package/dist/invocation/service-reason.d.ts +13 -13
- package/dist/invocation/service-reason.js +13 -12
- package/dist/invocation/stale-route.d.ts +1 -1
- package/dist/invocation/target.d.ts +3 -0
- package/dist/invocation/target.js +8 -1
- package/dist/invocation/websocket/index.d.ts +1 -1
- package/dist/invocation/websocket/index.js +1 -1
- package/dist/invocation/websocket/subprotocol.d.ts +1 -2
- package/dist/invocation/websocket/subprotocol.js +3 -4
- package/dist/issuer/claims.d.ts +7 -0
- package/dist/issuer/claims.js +7 -0
- package/dist/issuer/index.d.ts +2 -0
- package/dist/issuer/index.js +2 -0
- package/dist/issuer/paths.d.ts +2 -0
- package/dist/issuer/paths.js +1 -0
- package/dist/issuer/workflow-engine.d.ts +18 -0
- package/dist/issuer/workflow-engine.js +59 -0
- package/dist/{publication → release}/bindings/callable.js +6 -6
- package/dist/release/bindings/invocation.js +13 -0
- package/dist/{publication → release}/bindings/view.d.ts +1 -1
- package/dist/{publication → release}/bindings/view.js +10 -10
- package/dist/release/build.d.ts +6 -0
- package/dist/release/build.js +7 -0
- package/dist/release/bundle-admission.d.ts +21 -0
- package/dist/{publication → release}/bundle-admission.js +18 -18
- package/dist/{publication → release}/bundle-descriptor.js +6 -6
- package/dist/release/bundle-evidence.d.ts +3 -0
- package/dist/release/bundle-evidence.js +13 -0
- package/dist/{publication → release}/bundle.d.ts +2 -2
- package/dist/{publication → release}/bundle.js +9 -9
- package/dist/{publication → release}/delivery/attestation.d.ts +1 -0
- package/dist/release/delivery/claims.d.ts +21 -0
- package/dist/release/delivery/claims.js +68 -0
- package/dist/release/delivery/delivery.d.ts +11 -0
- package/dist/release/delivery/delivery.js +24 -0
- package/dist/{publication → release}/delivery/error.d.ts +2 -0
- package/dist/{publication → release}/delivery/error.js +1 -0
- package/dist/{publication → release}/delivery/index.d.ts +6 -1
- package/dist/{publication → release}/delivery/index.js +5 -0
- package/dist/{publication → release}/delivery/limits.d.ts +2 -1
- package/dist/release/delivery/limits.js +5 -0
- package/dist/release/delivery/request.d.ts +21 -0
- package/dist/release/delivery/request.js +60 -0
- package/dist/{publication → release}/delivery-declaration.js +4 -4
- package/dist/release/delivery-endpoint.js +22 -0
- package/dist/release/digest.d.ts +13 -0
- package/dist/{publication/etag.js → release/digest.js} +1 -10
- package/dist/release/error.d.ts +7 -0
- package/dist/release/error.js +11 -0
- package/dist/release/index.d.ts +21 -0
- package/dist/release/index.js +14 -0
- package/dist/release/issuer.d.ts +5 -0
- package/dist/release/issuer.js +34 -0
- package/dist/release/legacy/bindings.d.ts +14 -0
- package/dist/release/legacy/bindings.js +11 -0
- package/dist/release/legacy/bundle-admission.d.ts +6 -0
- package/dist/release/legacy/bundle-admission.js +10 -0
- package/dist/release/legacy/bundle-evidence.d.ts +8 -0
- package/dist/release/legacy/bundle-evidence.js +11 -0
- package/dist/release/legacy/bundle.d.ts +24 -0
- package/dist/release/legacy/bundle.js +31 -0
- package/dist/release/legacy/delivery/attestation.d.ts +6 -0
- package/dist/release/legacy/delivery/attestation.js +1 -0
- package/dist/{publication → release/legacy}/delivery/claims.d.ts +9 -0
- package/dist/{publication → release/legacy}/delivery/claims.js +7 -2
- package/dist/release/legacy/delivery/delivery.d.ts +18 -0
- package/dist/{publication → release/legacy}/delivery/delivery.js +6 -2
- package/dist/release/legacy/delivery/error.d.ts +16 -0
- package/dist/release/legacy/delivery/error.js +6 -0
- package/dist/release/legacy/delivery/index.d.ts +13 -0
- package/dist/release/legacy/delivery/index.js +13 -0
- package/dist/release/legacy/delivery/limits.d.ts +21 -0
- package/dist/release/legacy/delivery/limits.js +22 -0
- package/dist/release/legacy/delivery/request.d.ts +26 -0
- package/dist/{publication → release/legacy}/delivery/request.js +8 -3
- package/dist/release/legacy/delivery-declaration.d.ts +9 -0
- package/dist/release/legacy/delivery-declaration.js +11 -0
- package/dist/release/legacy/error.d.ts +26 -0
- package/dist/release/legacy/error.js +114 -0
- package/dist/release/legacy/etag.d.ts +12 -0
- package/dist/release/legacy/etag.js +14 -0
- package/dist/release/legacy/index.d.ts +75 -0
- package/dist/{publication → release/legacy}/index.js +7 -2
- package/dist/release/legacy/profiles.d.ts +23 -0
- package/dist/release/legacy/profiles.js +36 -0
- package/dist/release/legacy/projection.d.ts +13 -0
- package/dist/release/legacy/projection.js +25 -0
- package/dist/release/legacy/publication.d.ts +90 -0
- package/dist/release/legacy/publication.js +124 -0
- package/dist/release/legacy/reference.d.ts +47 -0
- package/dist/{publication → release/legacy}/reference.js +27 -3
- package/dist/release/legacy/requirements.d.ts +30 -0
- package/dist/release/legacy/requirements.js +20 -0
- package/dist/{publication → release}/manifest.js +6 -6
- package/dist/release/reference.d.ts +26 -0
- package/dist/release/reference.js +70 -0
- package/dist/release/release.d.ts +60 -0
- package/dist/release/release.js +123 -0
- package/dist/{publication → release}/requirements.d.ts +11 -4
- package/dist/{publication → release}/requirements.js +58 -39
- package/dist/{publication → release}/route.js +24 -24
- package/dist/{publication → release}/schema.js +4 -4
- package/dist/release/served.d.ts +26 -0
- package/dist/release/served.js +65 -0
- package/dist/release/shape.d.ts +8 -0
- package/dist/routes/admission.d.ts +1 -1
- package/dist/routes/admission.js +7 -85
- package/dist/routes/binding.d.ts +3 -3
- package/dist/routes/binding.js +10 -19
- package/dist/routes/compiler.d.ts +2 -2
- package/dist/routes/compiler.js +3 -2
- package/dist/routes/credential.d.ts +12 -1
- package/dist/routes/credential.js +11 -3
- package/dist/routes/decode.d.ts +5 -2
- package/dist/routes/decode.js +19 -6
- package/dist/routes/deployment-retired.d.ts +30 -0
- package/dist/routes/deployment-retired.js +96 -0
- package/dist/routes/encode.js +7 -6
- package/dist/routes/entries.d.ts +23 -0
- package/dist/routes/entries.js +150 -0
- package/dist/routes/evidence.d.ts +3 -3
- package/dist/routes/evidence.js +7 -34
- package/dist/routes/header.d.ts +9 -0
- package/dist/routes/header.js +34 -0
- package/dist/routes/index.d.ts +5 -2
- package/dist/routes/index.js +5 -2
- package/dist/routes/legacy/index.d.ts +29 -0
- package/dist/routes/legacy/index.js +29 -0
- package/dist/routes/legacy/routing.d.ts +24 -0
- package/dist/routes/legacy/routing.js +45 -0
- package/dist/routes/legacy/table.d.ts +34 -0
- package/dist/routes/legacy/table.js +50 -0
- package/dist/routes/mapping.d.ts +1 -1
- package/dist/routes/mapping.js +4 -4
- package/dist/routes/match.d.ts +1 -1
- package/dist/routes/placement.d.ts +2 -2
- package/dist/routes/plan.d.ts +1 -1
- package/dist/routes/request.d.ts +5 -3
- package/dist/routes/reservation.d.ts +4 -2
- package/dist/routes/reservation.js +4 -3
- package/dist/routes/routing.d.ts +30 -6
- package/dist/routes/routing.js +34 -49
- package/dist/routes/select.d.ts +1 -1
- package/dist/routes/table.d.ts +3 -2
- package/dist/routes/target.d.ts +5 -0
- package/dist/routes/target.js +16 -0
- package/dist/runtime/artifact.d.ts +2 -2
- package/dist/runtime/artifact.js +2 -2
- package/dist/schema/installation/codec.d.ts +6 -1
- package/dist/schema/installation/codec.js +10 -7
- package/dist/schema/installation/introspection/codec.d.ts +11 -2
- package/dist/schema/installation/introspection/codec.js +184 -47
- package/dist/schema/installation/introspection/index.d.ts +1 -1
- package/dist/schema/installation/introspection/result.d.ts +61 -1
- package/dist/schema/installation/legacy/index.d.ts +39 -0
- package/dist/schema/installation/legacy/index.js +39 -0
- package/dist/schema/installation/legacy/installation.d.ts +9 -0
- package/dist/schema/installation/legacy/installation.js +28 -0
- package/dist/schema/installation/legacy/introspection.d.ts +10 -0
- package/dist/schema/installation/legacy/introspection.js +70 -0
- package/dist/schema/installation/legacy/msgpack.d.ts +1 -0
- package/dist/schema/installation/legacy/msgpack.js +143 -0
- package/package.json +63 -36
- package/dist/codecs/msgpack.d.ts +0 -4
- package/dist/codecs/msgpack.js +0 -53
- package/dist/publication/bindings/invocation.js +0 -13
- package/dist/publication/bundle-admission.d.ts +0 -4
- package/dist/publication/bundle-evidence.d.ts +0 -3
- package/dist/publication/bundle-evidence.js +0 -13
- package/dist/publication/content.d.ts +0 -10
- package/dist/publication/delivery/delivery.d.ts +0 -9
- package/dist/publication/delivery/limits.js +0 -4
- package/dist/publication/delivery/request.d.ts +0 -15
- package/dist/publication/delivery-endpoint.js +0 -22
- package/dist/publication/error.d.ts +0 -7
- package/dist/publication/error.js +0 -11
- package/dist/publication/etag.d.ts +0 -6
- package/dist/publication/index.d.ts +0 -17
- package/dist/publication/legacy-requirements.d.ts +0 -12
- package/dist/publication/legacy-requirements.js +0 -23
- package/dist/publication/publication.d.ts +0 -38
- package/dist/publication/publication.js +0 -146
- package/dist/publication/reference.d.ts +0 -17
- package/dist/routes/release-unavailable.d.ts +0 -8
- package/dist/routes/release-unavailable.js +0 -32
- /package/dist/{publication → release}/bindings/callable.d.ts +0 -0
- /package/dist/{publication → release}/bindings/index.d.ts +0 -0
- /package/dist/{publication → release}/bindings/index.js +0 -0
- /package/dist/{publication → release}/bindings/invocation.d.ts +0 -0
- /package/dist/{publication → release}/bundle-descriptor.d.ts +0 -0
- /package/dist/{publication → release}/declaration.d.ts +0 -0
- /package/dist/{publication → release}/declaration.js +0 -0
- /package/dist/{publication → release}/delivery/attestation.js +0 -0
- /package/dist/{publication → release}/delivery-declaration.d.ts +0 -0
- /package/dist/{publication → release}/delivery-endpoint.d.ts +0 -0
- /package/dist/{publication → release}/manifest.d.ts +0 -0
- /package/dist/{publication → release}/route.d.ts +0 -0
- /package/dist/{publication → release}/schema.d.ts +0 -0
- /package/dist/{publication/content.js → release/shape.js} +0 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import * as portable from '@astrale-os/kernel-dsl/value';
|
|
2
|
+
import { admitBuild } from './build.js';
|
|
3
|
+
import { admitDigest, sha256Hex } from './digest.js';
|
|
4
|
+
import { fail } from './error.js';
|
|
5
|
+
import { servedUrl } from './issuer.js';
|
|
6
|
+
import { admitServedContent } from './served.js';
|
|
7
|
+
import { deepFreeze, exact, record } from './shape.js';
|
|
8
|
+
/** Path of the release a deployment serves beneath its serving issuer. */
|
|
9
|
+
export const PATH = '/.well-known/astrale/release.json';
|
|
10
|
+
/** Media type of `DomainRelease` v4. */
|
|
11
|
+
export const MEDIA_TYPE = 'application/vnd.astrale.domain-release+json;v=4';
|
|
12
|
+
/** Resolve the canonical release URL beneath one exact HTTP(S) serving issuer, keeping its path. */
|
|
13
|
+
export function url(issuer) {
|
|
14
|
+
return servedUrl(issuer, PATH);
|
|
15
|
+
}
|
|
16
|
+
const CONTENT_FIELDS = [
|
|
17
|
+
'format',
|
|
18
|
+
'version',
|
|
19
|
+
'origin',
|
|
20
|
+
'identity',
|
|
21
|
+
'build',
|
|
22
|
+
'schema',
|
|
23
|
+
'requirements',
|
|
24
|
+
'manifest',
|
|
25
|
+
'bindings',
|
|
26
|
+
'routes',
|
|
27
|
+
'delivery',
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Release digest of canonical release content: SHA-256 of its canonical JSON. The content must be
|
|
31
|
+
* exactly what `seal` produces, a sealed release without its digest; content that `seal` would
|
|
32
|
+
* canonicalize or refuse throws `ReleaseError` instead of yielding a digest no decoder accepts.
|
|
33
|
+
* Authored content gets its digest from `seal(content).digest`.
|
|
34
|
+
*/
|
|
35
|
+
export function digestOf(input) {
|
|
36
|
+
const value = record(input, 'RELEASE_INVALID', 'Expected release content.');
|
|
37
|
+
exact(value, CONTENT_FIELDS, 'RELEASE_INVALID');
|
|
38
|
+
return hash(admitCanonical(value));
|
|
39
|
+
}
|
|
40
|
+
/** Validate release content, canonicalize its View bindings, and set its release digest. */
|
|
41
|
+
export function seal(input) {
|
|
42
|
+
const authored = record(input, 'RELEASE_INVALID', 'Expected release content.');
|
|
43
|
+
exact(authored, CONTENT_FIELDS, 'RELEASE_INVALID');
|
|
44
|
+
const canonical = admitContent(admitContent(authored, 'canonicalize'), 'admit');
|
|
45
|
+
return release(canonical, hash(canonical));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Admit one wire release: exact fields at every level, every value in the canonical form `seal`
|
|
49
|
+
* produces, and a digest matching its content. One release therefore has exactly one wire
|
|
50
|
+
* document: `encode(accept(input))` is the canonical JSON of `input`.
|
|
51
|
+
*/
|
|
52
|
+
export function accept(input) {
|
|
53
|
+
const value = record(input, 'RELEASE_INVALID', 'Expected a DomainRelease object.');
|
|
54
|
+
exact(value, [...CONTENT_FIELDS, 'digest'], 'RELEASE_INVALID');
|
|
55
|
+
const { digest: received, ...wire } = value;
|
|
56
|
+
const content = admitCanonical(wire);
|
|
57
|
+
const digest = admitDigest(received);
|
|
58
|
+
if (digest !== hash(content)) {
|
|
59
|
+
fail('RELEASE_DIGEST_MISMATCH', 'Release digest does not match its canonical content.');
|
|
60
|
+
}
|
|
61
|
+
return release(content, digest);
|
|
62
|
+
}
|
|
63
|
+
/** Encode one release as its canonical JSON bytes. */
|
|
64
|
+
export function encode(input) {
|
|
65
|
+
return portable.json.encode(input);
|
|
66
|
+
}
|
|
67
|
+
function admitContent(value, views) {
|
|
68
|
+
if (value.format !== 'astrale.domain.release' || value.version !== 4) {
|
|
69
|
+
fail('RELEASE_INVALID', 'Unsupported release format or version.');
|
|
70
|
+
}
|
|
71
|
+
const served = admitServedContent(value, { views });
|
|
72
|
+
const build = admitBuild(value.build);
|
|
73
|
+
return deepFreeze({
|
|
74
|
+
format: 'astrale.domain.release',
|
|
75
|
+
version: 4,
|
|
76
|
+
origin: served.origin,
|
|
77
|
+
identity: served.identity,
|
|
78
|
+
build,
|
|
79
|
+
schema: served.schema,
|
|
80
|
+
requirements: served.requirements,
|
|
81
|
+
...(served.manifest === undefined ? {} : { manifest: served.manifest }),
|
|
82
|
+
bindings: served.bindings,
|
|
83
|
+
routes: served.routes,
|
|
84
|
+
...(served.delivery === undefined ? {} : { delivery: served.delivery }),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
/** Shared validators normalize legacy values; releases require their exact canonical spelling. */
|
|
88
|
+
function admitCanonical(value) {
|
|
89
|
+
const content = admitContent(value, 'admit');
|
|
90
|
+
const canonical = portable.json.serialize(content);
|
|
91
|
+
let received;
|
|
92
|
+
try {
|
|
93
|
+
received = portable.json.serialize(value);
|
|
94
|
+
}
|
|
95
|
+
catch (cause) {
|
|
96
|
+
if (cause instanceof portable.BoundaryError) {
|
|
97
|
+
return fail('RELEASE_INVALID', 'Release content is not in canonical form.', cause);
|
|
98
|
+
}
|
|
99
|
+
throw cause;
|
|
100
|
+
}
|
|
101
|
+
if (received !== canonical)
|
|
102
|
+
fail('RELEASE_INVALID', 'Release content is not in canonical form.');
|
|
103
|
+
return content;
|
|
104
|
+
}
|
|
105
|
+
function hash(content) {
|
|
106
|
+
return `sha256:${sha256Hex(portable.json.encode(content))}`;
|
|
107
|
+
}
|
|
108
|
+
function release(content, digest) {
|
|
109
|
+
return deepFreeze({
|
|
110
|
+
format: content.format,
|
|
111
|
+
version: content.version,
|
|
112
|
+
origin: content.origin,
|
|
113
|
+
identity: content.identity,
|
|
114
|
+
digest,
|
|
115
|
+
build: content.build,
|
|
116
|
+
schema: content.schema,
|
|
117
|
+
requirements: content.requirements,
|
|
118
|
+
...(content.manifest === undefined ? {} : { manifest: content.manifest }),
|
|
119
|
+
bindings: content.bindings,
|
|
120
|
+
routes: content.routes,
|
|
121
|
+
...(content.delivery === undefined ? {} : { delivery: content.delivery }),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -25,10 +25,17 @@ export interface CoreCapabilityRequirement {
|
|
|
25
25
|
readonly classes?: NonEmpty<ClassCapabilityRequirement>;
|
|
26
26
|
readonly authority?: CapabilityAuthorityRequirements;
|
|
27
27
|
}
|
|
28
|
-
export interface
|
|
28
|
+
export interface Requirements {
|
|
29
29
|
readonly capabilities: CapabilityRequirements;
|
|
30
30
|
}
|
|
31
|
-
/**
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Admit the exact closed canonical requirements object, the `core` form Publication v3 introduced.
|
|
33
|
+
*/
|
|
34
|
+
export declare function admitRequirements(input: unknown): Requirements;
|
|
33
35
|
/** Bind explicit capability targets to exact functional dependencies in the bundle closure. */
|
|
34
|
-
export declare function validateRequirements(root: schemaLanguage.DomainSchema, closure: readonly schemaLanguage.DomainSchema[], requirements:
|
|
36
|
+
export declare function validateRequirements(root: schemaLanguage.DomainSchema, closure: readonly schemaLanguage.DomainSchema[], requirements: Requirements): void;
|
|
37
|
+
/**
|
|
38
|
+
* Distinct Function, Method and Class Keys, including Core-scoped targets, in code-unit order.
|
|
39
|
+
* Core subjects and authority operations contribute no Keys. Results may span multiple origins.
|
|
40
|
+
*/
|
|
41
|
+
export declare function capabilityKeys(capabilities: CapabilityRequirements): readonly SemanticKey[];
|
|
@@ -2,8 +2,8 @@ import { AuthorityOperationOrder } from '@astrale-os/kernel-core/schema/authorit
|
|
|
2
2
|
import { acceptDefinitionRef, callableAddress, Key } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
3
3
|
import { isMethodExecutable } from '@astrale-os/kernel-dsl/v1/language';
|
|
4
4
|
import { revision } from '@astrale-os/kernel-dsl/v1/schema/revision';
|
|
5
|
-
import { array, deepFreeze, exact, record, unique } from './content.js';
|
|
6
5
|
import { fail } from './error.js';
|
|
6
|
+
import { array, deepFreeze, exact, record, unique } from './shape.js';
|
|
7
7
|
const KernelIdentityClass = Object.freeze({
|
|
8
8
|
origin: 'kernel.astrale.ai',
|
|
9
9
|
kind: 'class',
|
|
@@ -16,24 +16,26 @@ export const ClassOperationOrder = Object.freeze([
|
|
|
16
16
|
'update',
|
|
17
17
|
'delete',
|
|
18
18
|
]);
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Admit the exact closed canonical requirements object, the `core` form Publication v3 introduced.
|
|
21
|
+
*/
|
|
20
22
|
export function admitRequirements(input) {
|
|
21
|
-
const requirements = record(input, '
|
|
22
|
-
exact(requirements, ['capabilities'], '
|
|
23
|
-
const capabilities = record(requirements.capabilities, '
|
|
24
|
-
exact(capabilities, ['authority', 'classes', 'functions', 'core'], '
|
|
23
|
+
const requirements = record(input, 'RELEASE_INVALID', 'Release requirements are invalid.');
|
|
24
|
+
exact(requirements, ['capabilities'], 'RELEASE_INVALID');
|
|
25
|
+
const capabilities = record(requirements.capabilities, 'RELEASE_INVALID', 'Release capability requirements are invalid.');
|
|
26
|
+
exact(capabilities, ['authority', 'classes', 'functions', 'core'], 'RELEASE_INVALID');
|
|
25
27
|
const functions = capabilities.functions === undefined
|
|
26
28
|
? undefined
|
|
27
29
|
: nonEmpty(capabilities.functions, 'Function capability requirements').map(admitCallable);
|
|
28
30
|
if (functions !== undefined) {
|
|
29
|
-
unique(functions, '
|
|
31
|
+
unique(functions, 'RELEASE_INVALID', 'Function capability requirement');
|
|
30
32
|
canonical(functions, compare, 'Function capability requirements');
|
|
31
33
|
}
|
|
32
34
|
const classes = capabilities.classes === undefined
|
|
33
35
|
? undefined
|
|
34
36
|
: nonEmpty(capabilities.classes, 'Class capability requirements').map(admitClassRequirement);
|
|
35
37
|
if (classes !== undefined) {
|
|
36
|
-
unique(classes.map((entry) => classKey(entry.class)), '
|
|
38
|
+
unique(classes.map((entry) => classKey(entry.class)), 'RELEASE_INVALID', 'Class capability requirement');
|
|
37
39
|
canonical(classes, (left, right) => compare(classKey(left.class), classKey(right.class)), 'Class capability requirements');
|
|
38
40
|
}
|
|
39
41
|
const authority = capabilities.authority === undefined
|
|
@@ -43,7 +45,7 @@ export function admitRequirements(input) {
|
|
|
43
45
|
? undefined
|
|
44
46
|
: nonEmpty(capabilities.core, 'Core capability requirements').map(admitCoreCapabilityRequirement);
|
|
45
47
|
if (core !== undefined) {
|
|
46
|
-
unique(core.map(({ subject }) => Key.of(subject)), '
|
|
48
|
+
unique(core.map(({ subject }) => Key.of(subject)), 'RELEASE_INVALID', 'Core capability subject');
|
|
47
49
|
canonical(core, (left, right) => compare(Key.of(left.subject), Key.of(right.subject)), 'Core capability requirements');
|
|
48
50
|
}
|
|
49
51
|
return deepFreeze({
|
|
@@ -62,28 +64,28 @@ export function admitRequirements(input) {
|
|
|
62
64
|
});
|
|
63
65
|
}
|
|
64
66
|
function admitCoreCapabilityRequirement(input) {
|
|
65
|
-
const requirement = record(input, '
|
|
66
|
-
exact(requirement, ['authority', 'classes', 'functions', 'subject'], '
|
|
67
|
+
const requirement = record(input, 'RELEASE_INVALID', 'Core capability requirement is invalid.');
|
|
68
|
+
exact(requirement, ['authority', 'classes', 'functions', 'subject'], 'RELEASE_INVALID');
|
|
67
69
|
const subject = admitCoreRef(requirement.subject);
|
|
68
70
|
const functions = requirement.functions === undefined
|
|
69
71
|
? undefined
|
|
70
72
|
: nonEmpty(requirement.functions, 'Core capability Function requirements').map(admitCallable);
|
|
71
73
|
if (functions !== undefined) {
|
|
72
|
-
unique(functions, '
|
|
74
|
+
unique(functions, 'RELEASE_INVALID', 'Core capability Function requirement');
|
|
73
75
|
canonical(functions, compare, 'Core capability Function requirements');
|
|
74
76
|
}
|
|
75
77
|
const classes = requirement.classes === undefined
|
|
76
78
|
? undefined
|
|
77
79
|
: nonEmpty(requirement.classes, 'Core capability Class requirements').map(admitClassRequirement);
|
|
78
80
|
if (classes !== undefined) {
|
|
79
|
-
unique(classes.map((entry) => classKey(entry.class)), '
|
|
81
|
+
unique(classes.map((entry) => classKey(entry.class)), 'RELEASE_INVALID', 'Core capability Class requirement');
|
|
80
82
|
canonical(classes, (left, right) => compare(classKey(left.class), classKey(right.class)), 'Core capability Class requirements');
|
|
81
83
|
}
|
|
82
84
|
const authority = requirement.authority === undefined
|
|
83
85
|
? undefined
|
|
84
86
|
: admitAuthorityRequirements(requirement.authority);
|
|
85
87
|
if (functions === undefined && classes === undefined && authority === undefined) {
|
|
86
|
-
fail('
|
|
88
|
+
fail('RELEASE_INVALID', 'Core capability requirements cannot be empty.');
|
|
87
89
|
}
|
|
88
90
|
return Object.freeze({
|
|
89
91
|
subject,
|
|
@@ -102,14 +104,14 @@ export function validateRequirements(root, closure, requirements) {
|
|
|
102
104
|
for (const callable of requirements.capabilities.functions ?? []) {
|
|
103
105
|
const dependency = capabilityOwner(root, dependencies, callable, 'Function');
|
|
104
106
|
if (!hasCallable(dependency, callable)) {
|
|
105
|
-
fail('
|
|
107
|
+
fail('RELEASE_INVALID', 'Function capability requirement names an unknown dependency callable.');
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
for (const requirement of requirements.capabilities.classes ?? []) {
|
|
109
111
|
const key = Key.of(requirement.class);
|
|
110
112
|
const dependency = capabilityOwner(root, dependencies, key, 'Class');
|
|
111
113
|
if (dependency.classes[requirement.class.name] === undefined) {
|
|
112
|
-
fail('
|
|
114
|
+
fail('RELEASE_INVALID', 'Class capability requirement names an unknown dependency Class.');
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
for (const requirement of requirements.capabilities.core ?? []) {
|
|
@@ -117,14 +119,14 @@ export function validateRequirements(root, closure, requirements) {
|
|
|
117
119
|
if (requirement.subject.origin !== root.origin ||
|
|
118
120
|
subject === undefined ||
|
|
119
121
|
!classSatisfies(root, dependencies, subject.class, KernelIdentityClass)) {
|
|
120
|
-
fail('
|
|
122
|
+
fail('RELEASE_INVALID', 'Core capability subject must name one local Core Identity.');
|
|
121
123
|
}
|
|
122
124
|
for (const callable of requirement.functions ?? []) {
|
|
123
125
|
const owner = Key.origin(callable) === root.origin
|
|
124
126
|
? root
|
|
125
127
|
: capabilityOwner(root, dependencies, callable, 'Function');
|
|
126
128
|
if (!hasCallable(owner, callable)) {
|
|
127
|
-
fail('
|
|
129
|
+
fail('RELEASE_INVALID', 'Core capability Function requirement names an unknown or non-executable callable.');
|
|
128
130
|
}
|
|
129
131
|
}
|
|
130
132
|
for (const classRequirement of requirement.classes ?? []) {
|
|
@@ -133,11 +135,28 @@ export function validateRequirements(root, closure, requirements) {
|
|
|
133
135
|
? root
|
|
134
136
|
: capabilityOwner(root, dependencies, key, 'Class');
|
|
135
137
|
if (owner.classes[classRequirement.class.name] === undefined) {
|
|
136
|
-
fail('
|
|
138
|
+
fail('RELEASE_INVALID', 'Core capability Class requirement names an unknown dependency Class.');
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Distinct Function, Method and Class Keys, including Core-scoped targets, in code-unit order.
|
|
145
|
+
* Core subjects and authority operations contribute no Keys. Results may span multiple origins.
|
|
146
|
+
*/
|
|
147
|
+
export function capabilityKeys(capabilities) {
|
|
148
|
+
const keys = new Set(capabilities.functions);
|
|
149
|
+
for (const requirement of capabilities.classes ?? [])
|
|
150
|
+
keys.add(Key.of(requirement.class));
|
|
151
|
+
for (const requirement of capabilities.core ?? []) {
|
|
152
|
+
for (const callable of requirement.functions ?? [])
|
|
153
|
+
keys.add(callable);
|
|
154
|
+
for (const classRequirement of requirement.classes ?? []) {
|
|
155
|
+
keys.add(Key.of(classRequirement.class));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return Object.freeze([...keys].sort(compare));
|
|
159
|
+
}
|
|
141
160
|
function classSatisfies(root, dependencies, candidate, expected) {
|
|
142
161
|
const expectedKey = Key.of(expected);
|
|
143
162
|
const visited = new Set();
|
|
@@ -161,7 +180,7 @@ function exactDependencyClosure(root, closure) {
|
|
|
161
180
|
for (const coordinate of Object.values(owner.dependencies)) {
|
|
162
181
|
const dependency = supplied.get(coordinate.origin);
|
|
163
182
|
if (dependency === undefined || revision(dependency) !== coordinate.revision) {
|
|
164
|
-
fail('
|
|
183
|
+
fail('RELEASE_INVALID', 'Release bundle omits an exact dependency.');
|
|
165
184
|
}
|
|
166
185
|
if (reachable.has(dependency.origin))
|
|
167
186
|
continue;
|
|
@@ -174,21 +193,21 @@ function exactDependencyClosure(root, closure) {
|
|
|
174
193
|
}
|
|
175
194
|
function admitCallable(input) {
|
|
176
195
|
if (typeof input !== 'string' || !Key.is(input)) {
|
|
177
|
-
return fail('
|
|
196
|
+
return fail('RELEASE_INVALID', 'Function capability reference is invalid.');
|
|
178
197
|
}
|
|
179
198
|
try {
|
|
180
199
|
callableAddress(input);
|
|
181
200
|
}
|
|
182
201
|
catch {
|
|
183
|
-
return fail('
|
|
202
|
+
return fail('RELEASE_INVALID', 'Function capability reference must name a Function or Method.');
|
|
184
203
|
}
|
|
185
204
|
return input;
|
|
186
205
|
}
|
|
187
206
|
function admitClassRequirement(input) {
|
|
188
|
-
const requirement = record(input, '
|
|
189
|
-
exact(requirement, ['class', 'operations'], '
|
|
207
|
+
const requirement = record(input, 'RELEASE_INVALID', 'Class capability requirement is invalid.');
|
|
208
|
+
exact(requirement, ['class', 'operations'], 'RELEASE_INVALID');
|
|
190
209
|
const operations = nonEmpty(requirement.operations, 'Class capability operations').map((entry) => admitOperation(entry, ClassOperationOrder, 'Class capability operation'));
|
|
191
|
-
unique(operations, '
|
|
210
|
+
unique(operations, 'RELEASE_INVALID', 'Class capability operation');
|
|
192
211
|
canonical(operations, ordered(ClassOperationOrder), 'Class capability operations');
|
|
193
212
|
return Object.freeze({
|
|
194
213
|
class: admitClassRef(requirement.class),
|
|
@@ -196,10 +215,10 @@ function admitClassRequirement(input) {
|
|
|
196
215
|
});
|
|
197
216
|
}
|
|
198
217
|
function admitAuthorityRequirements(input) {
|
|
199
|
-
const authority = record(input, '
|
|
200
|
-
exact(authority, ['grant', 'revoke'], '
|
|
218
|
+
const authority = record(input, 'RELEASE_INVALID', 'Authority capability requirements are invalid.');
|
|
219
|
+
exact(authority, ['grant', 'revoke'], 'RELEASE_INVALID');
|
|
201
220
|
if (authority.grant === undefined && authority.revoke === undefined) {
|
|
202
|
-
fail('
|
|
221
|
+
fail('RELEASE_INVALID', 'Authority capability requirements cannot be empty.');
|
|
203
222
|
}
|
|
204
223
|
const grant = admitAuthorityOperations(authority.grant, 'grant');
|
|
205
224
|
const revoke = admitAuthorityOperations(authority.revoke, 'revoke');
|
|
@@ -212,7 +231,7 @@ function admitAuthorityOperations(input, kind) {
|
|
|
212
231
|
if (input === undefined)
|
|
213
232
|
return undefined;
|
|
214
233
|
const values = nonEmpty(input, `Authority ${kind} operations`).map((entry) => admitOperation(entry, AuthorityOperationOrder, `Authority ${kind} operation`));
|
|
215
|
-
unique(values, '
|
|
234
|
+
unique(values, 'RELEASE_INVALID', `authority ${kind} operation`);
|
|
216
235
|
canonical(values, ordered(AuthorityOperationOrder), `Authority ${kind} operations`);
|
|
217
236
|
return Object.freeze(values);
|
|
218
237
|
}
|
|
@@ -222,10 +241,10 @@ function admitClassRef(input) {
|
|
|
222
241
|
value = acceptDefinitionRef(input);
|
|
223
242
|
}
|
|
224
243
|
catch {
|
|
225
|
-
fail('
|
|
244
|
+
fail('RELEASE_INVALID', 'Class capability reference is invalid.');
|
|
226
245
|
}
|
|
227
246
|
if (value.kind !== 'class')
|
|
228
|
-
fail('
|
|
247
|
+
fail('RELEASE_INVALID', 'Class capability reference is invalid.');
|
|
229
248
|
return value;
|
|
230
249
|
}
|
|
231
250
|
function admitCoreRef(input) {
|
|
@@ -234,28 +253,28 @@ function admitCoreRef(input) {
|
|
|
234
253
|
value = acceptDefinitionRef(input);
|
|
235
254
|
}
|
|
236
255
|
catch {
|
|
237
|
-
fail('
|
|
256
|
+
fail('RELEASE_INVALID', 'Core capability subject is invalid.');
|
|
238
257
|
}
|
|
239
258
|
if (value.kind !== 'core') {
|
|
240
|
-
fail('
|
|
259
|
+
fail('RELEASE_INVALID', 'Core capability subject must be one local Core reference.');
|
|
241
260
|
}
|
|
242
261
|
return value;
|
|
243
262
|
}
|
|
244
263
|
function nonEmpty(input, label) {
|
|
245
|
-
const values = [...array(input, '
|
|
264
|
+
const values = [...array(input, 'RELEASE_INVALID')];
|
|
246
265
|
if (values.length === 0)
|
|
247
|
-
fail('
|
|
266
|
+
fail('RELEASE_INVALID', `${label} cannot be empty.`);
|
|
248
267
|
return values;
|
|
249
268
|
}
|
|
250
269
|
function admitOperation(input, vocabulary, label) {
|
|
251
270
|
if (typeof input !== 'string' || !vocabulary.includes(input)) {
|
|
252
|
-
return fail('
|
|
271
|
+
return fail('RELEASE_INVALID', `${label} is unknown.`);
|
|
253
272
|
}
|
|
254
273
|
return input;
|
|
255
274
|
}
|
|
256
275
|
function canonical(values, comparator, label) {
|
|
257
276
|
if (values.some((entry, index) => index > 0 && comparator(values[index - 1], entry) >= 0)) {
|
|
258
|
-
fail('
|
|
277
|
+
fail('RELEASE_INVALID', `${label} must be sorted canonically.`);
|
|
259
278
|
}
|
|
260
279
|
}
|
|
261
280
|
function ordered(order) {
|
|
@@ -270,10 +289,10 @@ function classKey(input) {
|
|
|
270
289
|
function capabilityOwner(root, dependencies, key, label) {
|
|
271
290
|
const origin = Key.origin(key);
|
|
272
291
|
if (origin === root.origin)
|
|
273
|
-
fail('
|
|
292
|
+
fail('RELEASE_INVALID', `${label} capability target cannot be local.`);
|
|
274
293
|
const dependency = dependencies.get(origin);
|
|
275
294
|
if (dependency === undefined) {
|
|
276
|
-
fail('
|
|
295
|
+
fail('RELEASE_INVALID', `${label} capability target must belong to the exact dependency closure.`);
|
|
277
296
|
}
|
|
278
297
|
return dependency;
|
|
279
298
|
}
|
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
2
|
import { admitCallableKey } from './bindings/callable.js';
|
|
3
|
-
import { absoluteHttpUrl, deepFreeze, exact, record } from './content.js';
|
|
4
3
|
import { fail } from './error.js';
|
|
4
|
+
import { absoluteHttpUrl, deepFreeze, exact, record } from './shape.js';
|
|
5
5
|
const ROUTE_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/u;
|
|
6
6
|
export function admitRoute(input, origin) {
|
|
7
|
-
const value = record(input, '
|
|
7
|
+
const value = record(input, 'RELEASE_ROUTE_INVALID', 'Release HTTP route is invalid.');
|
|
8
8
|
const common = admitRouteCommon(value, origin, 'href');
|
|
9
9
|
return deepFreeze({
|
|
10
10
|
...common,
|
|
11
|
-
href: absoluteHttpUrl(value.href, false, '
|
|
11
|
+
href: absoluteHttpUrl(value.href, false, 'RELEASE_ROUTE_INVALID', true),
|
|
12
12
|
});
|
|
13
13
|
}
|
|
14
14
|
export function admitRouteDeclaration(input, origin) {
|
|
15
|
-
const value = record(input, '
|
|
15
|
+
const value = record(input, 'RELEASE_ROUTE_INVALID', 'HTTP route declaration is invalid.');
|
|
16
16
|
const common = admitRouteCommon(value, origin, 'path');
|
|
17
17
|
if (typeof value.path !== 'string' || !value.path.startsWith('/')) {
|
|
18
|
-
fail('
|
|
18
|
+
fail('RELEASE_ROUTE_INVALID', 'Route declaration path must be relative to one issuer.');
|
|
19
19
|
}
|
|
20
20
|
let parsed;
|
|
21
21
|
try {
|
|
22
22
|
parsed = new URL(value.path, 'https://placement.invalid');
|
|
23
23
|
}
|
|
24
24
|
catch (cause) {
|
|
25
|
-
return fail('
|
|
25
|
+
return fail('RELEASE_ROUTE_INVALID', 'Route declaration path is invalid.', cause);
|
|
26
26
|
}
|
|
27
27
|
if (parsed.origin !== 'https://placement.invalid' || parsed.search !== '' || parsed.hash !== '') {
|
|
28
|
-
fail('
|
|
28
|
+
fail('RELEASE_ROUTE_INVALID', 'Route declaration path cannot select an origin, query, or fragment.');
|
|
29
29
|
}
|
|
30
30
|
return deepFreeze({ ...common, path: value.path });
|
|
31
31
|
}
|
|
@@ -42,24 +42,24 @@ function admitRouteCommon(value, origin, location) {
|
|
|
42
42
|
'headers',
|
|
43
43
|
'body',
|
|
44
44
|
'credential',
|
|
45
|
-
], '
|
|
45
|
+
], 'RELEASE_ROUTE_INVALID');
|
|
46
46
|
if (typeof value.name !== 'string' || !ROUTE_NAME.test(value.name)) {
|
|
47
|
-
fail('
|
|
47
|
+
fail('RELEASE_ROUTE_INVALID', 'Route name is invalid.');
|
|
48
48
|
}
|
|
49
49
|
const callable = admitCallableKey(value.callable, origin);
|
|
50
50
|
if (!['GET', 'POST', 'PUT', 'PATCH', 'DELETE', '*'].includes(value.method)) {
|
|
51
|
-
fail('
|
|
51
|
+
fail('RELEASE_ROUTE_INVALID', 'Route method is invalid.');
|
|
52
52
|
}
|
|
53
53
|
if (value.default !== undefined && value.default !== true) {
|
|
54
|
-
fail('
|
|
54
|
+
fail('RELEASE_ROUTE_INVALID', 'Route default may only be true.');
|
|
55
55
|
}
|
|
56
56
|
if (value.requestMethod !== undefined &&
|
|
57
57
|
(typeof value.requestMethod !== 'string' || !patterns.memberName.test(value.requestMethod))) {
|
|
58
|
-
fail('
|
|
58
|
+
fail('RELEASE_ROUTE_INVALID', 'Route request-method mapping is invalid.');
|
|
59
59
|
}
|
|
60
60
|
if (value.receiver !== undefined &&
|
|
61
61
|
(typeof value.receiver !== 'string' || !patterns.memberName.test(value.receiver))) {
|
|
62
|
-
fail('
|
|
62
|
+
fail('RELEASE_ROUTE_INVALID', 'Route receiver is invalid.');
|
|
63
63
|
}
|
|
64
64
|
const query = stringRecord(value.query);
|
|
65
65
|
const headers = headerRecord(value.headers);
|
|
@@ -83,13 +83,13 @@ function admitCredential(input) {
|
|
|
83
83
|
return undefined;
|
|
84
84
|
const values = Array.isArray(input) ? input : [input];
|
|
85
85
|
if (values.length === 0)
|
|
86
|
-
fail('
|
|
86
|
+
fail('RELEASE_ROUTE_INVALID', 'Credential source list is empty.');
|
|
87
87
|
const admitted = values.map((entry) => {
|
|
88
|
-
const value = record(entry, '
|
|
89
|
-
exact(value, ['in', 'name'], '
|
|
88
|
+
const value = record(entry, 'RELEASE_ROUTE_INVALID', 'Credential source is invalid.');
|
|
89
|
+
exact(value, ['in', 'name'], 'RELEASE_ROUTE_INVALID');
|
|
90
90
|
if (value.in === 'header') {
|
|
91
91
|
if (value.name !== undefined && (typeof value.name !== 'string' || value.name.length === 0)) {
|
|
92
|
-
fail('
|
|
92
|
+
fail('RELEASE_ROUTE_INVALID', 'Credential header name is invalid.');
|
|
93
93
|
}
|
|
94
94
|
return Object.freeze({
|
|
95
95
|
in: 'header',
|
|
@@ -101,14 +101,14 @@ function admitCredential(input) {
|
|
|
101
101
|
value.name.length > 0) {
|
|
102
102
|
return Object.freeze({ in: value.in, name: value.name });
|
|
103
103
|
}
|
|
104
|
-
return fail('
|
|
104
|
+
return fail('RELEASE_ROUTE_INVALID', 'Credential source is invalid.');
|
|
105
105
|
});
|
|
106
106
|
return Array.isArray(input) ? Object.freeze(admitted) : admitted[0];
|
|
107
107
|
}
|
|
108
108
|
function admitBody(input) {
|
|
109
109
|
if (input === undefined || input === '*')
|
|
110
110
|
return input;
|
|
111
|
-
const value = record(input, '
|
|
111
|
+
const value = record(input, 'RELEASE_ROUTE_INVALID', 'Route body mapping is invalid.');
|
|
112
112
|
const output = {};
|
|
113
113
|
for (const [field, pick] of Object.entries(value)) {
|
|
114
114
|
if (typeof pick === 'string' && pick.length > 0)
|
|
@@ -121,7 +121,7 @@ function admitBody(input) {
|
|
|
121
121
|
output[field] = { as: 'text' };
|
|
122
122
|
}
|
|
123
123
|
else {
|
|
124
|
-
fail('
|
|
124
|
+
fail('RELEASE_ROUTE_INVALID', 'Route body pick is invalid.');
|
|
125
125
|
}
|
|
126
126
|
}
|
|
127
127
|
return Object.freeze(output);
|
|
@@ -129,11 +129,11 @@ function admitBody(input) {
|
|
|
129
129
|
function headerRecord(input) {
|
|
130
130
|
if (input === undefined)
|
|
131
131
|
return undefined;
|
|
132
|
-
const value = record(input, '
|
|
132
|
+
const value = record(input, 'RELEASE_ROUTE_INVALID', 'Route header mapping is invalid.');
|
|
133
133
|
const output = {};
|
|
134
134
|
for (const [field, pick] of Object.entries(value)) {
|
|
135
135
|
if (typeof pick !== 'string' || pick.length === 0) {
|
|
136
|
-
fail('
|
|
136
|
+
fail('RELEASE_ROUTE_INVALID', 'Route header pick is invalid.');
|
|
137
137
|
}
|
|
138
138
|
output[field] = pick;
|
|
139
139
|
}
|
|
@@ -142,11 +142,11 @@ function headerRecord(input) {
|
|
|
142
142
|
function stringRecord(input) {
|
|
143
143
|
if (input === undefined)
|
|
144
144
|
return undefined;
|
|
145
|
-
const value = record(input, '
|
|
145
|
+
const value = record(input, 'RELEASE_ROUTE_INVALID', 'Route mapping is invalid.');
|
|
146
146
|
const output = {};
|
|
147
147
|
for (const [field, wire] of Object.entries(value)) {
|
|
148
148
|
if (typeof wire !== 'string' || wire.length === 0) {
|
|
149
|
-
fail('
|
|
149
|
+
fail('RELEASE_ROUTE_INVALID', 'Route wire name is invalid.');
|
|
150
150
|
}
|
|
151
151
|
output[field] = wire;
|
|
152
152
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
2
|
import { admitBundleDescriptor } from './bundle-descriptor.js';
|
|
3
|
-
import { exact, record } from './content.js';
|
|
4
3
|
import { fail } from './error.js';
|
|
4
|
+
import { exact, record } from './shape.js';
|
|
5
5
|
export function admitSchema(input) {
|
|
6
|
-
const value = record(input, '
|
|
7
|
-
exact(value, ['revision', 'bundle'], '
|
|
6
|
+
const value = record(input, 'RELEASE_INVALID', 'Release schema is invalid.');
|
|
7
|
+
exact(value, ['revision', 'bundle'], 'RELEASE_INVALID');
|
|
8
8
|
if (typeof value.revision !== 'string' || !patterns.revision.test(value.revision)) {
|
|
9
|
-
fail('
|
|
9
|
+
fail('RELEASE_INVALID', 'Release schema revision is invalid.');
|
|
10
10
|
}
|
|
11
11
|
return Object.freeze({
|
|
12
12
|
revision: value.revision,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
|
|
2
|
+
import type { ServedRelease } from './bundle-admission.js';
|
|
3
|
+
import type { Endpoint as DeliveryEndpoint } from './delivery-endpoint.js';
|
|
4
|
+
import type { Requirements } from './requirements.js';
|
|
5
|
+
/**
|
|
6
|
+
* Everything a served release document carries besides its format, version, digest and build:
|
|
7
|
+
* the content a bundle must match, the serving identity, and the optional Delivery endpoint.
|
|
8
|
+
*/
|
|
9
|
+
export interface ServedContent extends ServedRelease {
|
|
10
|
+
readonly identity: {
|
|
11
|
+
readonly issuer: IssuerId;
|
|
12
|
+
readonly subject: string;
|
|
13
|
+
};
|
|
14
|
+
readonly delivery?: DeliveryEndpoint;
|
|
15
|
+
}
|
|
16
|
+
export interface ServedOptions {
|
|
17
|
+
/** `canonicalize` normalizes authored View bindings before sealing; `admit` requires canonical ones. */
|
|
18
|
+
readonly views: 'canonicalize' | 'admit';
|
|
19
|
+
/** Reads the requirements coordinate of the document version; canonical `core` by default. */
|
|
20
|
+
readonly requirements?: (input: unknown) => Requirements;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Admit the version-neutral content of one served release document. Every document version runs
|
|
24
|
+
* these checks in this order, so a document of any version admits exactly the same served content.
|
|
25
|
+
*/
|
|
26
|
+
export declare function admitServedContent(value: Readonly<Record<string, unknown>>, options: ServedOptions): ServedContent;
|