@astrale-os/kernel-protocol 0.5.0-beta.9 → 0.5.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/dist/artifact/index.d.ts +1 -1
- package/dist/artifact/index.js +1 -1
- package/dist/artifact/ref.d.ts +2 -0
- package/dist/artifact/ref.js +4 -3
- 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/errors/index.d.ts +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/errors/retry.d.ts +25 -0
- package/dist/errors/retry.js +46 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/invocation/admission.d.ts +2 -2
- package/dist/invocation/admission.js +5 -5
- package/dist/invocation/binary.d.ts +1 -0
- package/dist/invocation/binary.js +21 -4
- package/dist/invocation/binding-codecs.d.ts +3 -2
- package/dist/invocation/binding-codecs.js +1 -6
- package/dist/invocation/delegate.d.ts +0 -20
- package/dist/invocation/delegate.js +2 -34
- package/dist/invocation/http/binary.d.ts +3 -1
- package/dist/invocation/http/binary.js +6 -1
- package/dist/invocation/http/index.d.ts +2 -1
- package/dist/invocation/http/index.js +2 -1
- package/dist/invocation/http/negotiation.js +3 -10
- package/dist/invocation/http/request.d.ts +11 -0
- package/dist/invocation/http/request.js +69 -0
- package/dist/invocation/index.d.ts +5 -4
- package/dist/invocation/index.js +4 -3
- package/dist/invocation/maintenance.d.ts +14 -0
- package/dist/invocation/maintenance.js +18 -0
- package/dist/invocation/redirect.d.ts +10 -12
- package/dist/invocation/redirect.js +10 -22
- package/dist/invocation/request.d.ts +3 -0
- package/dist/invocation/request.js +9 -2
- package/dist/invocation/response.d.ts +6 -1
- package/dist/invocation/response.js +26 -15
- package/dist/invocation/routing.d.ts +14 -4
- package/dist/invocation/routing.js +35 -20
- package/dist/invocation/schema.d.ts +6 -4
- package/dist/invocation/schema.js +5 -11
- package/dist/invocation/service-reason.d.ts +20 -15
- package/dist/invocation/service-reason.js +27 -19
- package/dist/invocation/stale-route.d.ts +3 -3
- package/dist/invocation/target.d.ts +5 -2
- package/dist/invocation/target.js +25 -3
- package/dist/invocation/websocket/binary.js +1 -0
- 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/release/bindings/view.d.ts +11 -0
- package/dist/release/bindings/view.js +54 -0
- 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 +15 -20
- 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 +5 -2
- package/dist/{publication → release}/bundle.js +13 -7
- package/dist/{publication → release}/delivery/attestation.d.ts +1 -0
- package/dist/{publication → release}/delivery/claims.d.ts +7 -2
- package/dist/{publication → release}/delivery/claims.js +20 -13
- package/dist/release/delivery/delivery.d.ts +11 -0
- package/dist/{publication → release}/delivery/delivery.js +7 -12
- 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 +5 -1
- package/dist/{publication → release}/delivery/index.js +4 -0
- package/dist/{publication → release}/delivery/limits.d.ts +2 -1
- package/dist/release/delivery/limits.js +5 -0
- package/dist/{publication → release}/delivery/request.d.ts +10 -9
- package/dist/{publication → release}/delivery/request.js +9 -10
- 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 -6
- package/dist/release/error.d.ts +7 -0
- package/dist/release/error.js +11 -0
- package/dist/release/index.d.ts +17 -0
- package/dist/release/index.js +10 -0
- package/dist/release/issuer.d.ts +5 -0
- package/dist/release/issuer.js +34 -0
- package/dist/release/manifest.d.ts +6 -0
- package/dist/release/manifest.js +22 -0
- 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 +125 -0
- package/dist/release/requirements.d.ts +41 -0
- package/dist/release/requirements.js +306 -0
- package/dist/{publication → release}/route.js +24 -24
- package/dist/{publication → release}/schema.js +4 -4
- package/dist/release/served.d.ts +27 -0
- package/dist/release/served.js +56 -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 +11 -21
- package/dist/routes/compiler.d.ts +2 -2
- package/dist/routes/compiler.js +7 -3
- package/dist/routes/credential.d.ts +12 -2
- package/dist/routes/credential.js +9 -4
- package/dist/routes/decode.d.ts +5 -2
- package/dist/routes/decode.js +24 -17
- package/dist/routes/deployment-retired.d.ts +30 -0
- package/dist/routes/deployment-retired.js +96 -0
- package/dist/routes/encode.js +12 -5
- 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 -33
- package/dist/routes/header.d.ts +9 -0
- package/dist/routes/header.js +34 -0
- package/dist/routes/index.d.ts +2 -2
- package/dist/routes/index.js +2 -2
- 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 +3 -1
- package/dist/routes/reservation.d.ts +4 -2
- package/dist/routes/reservation.js +14 -8
- package/dist/routes/routing.d.ts +18 -6
- package/dist/routes/routing.js +28 -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/routes/template.d.ts +0 -1
- package/dist/routes/template.js +0 -3
- package/dist/runtime/artifact.d.ts +5 -4
- package/dist/runtime/artifact.js +7 -4
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/schema/installation/codec.d.ts +9 -0
- package/dist/schema/installation/codec.js +82 -0
- package/dist/schema/installation/index.d.ts +2 -0
- package/dist/schema/installation/index.js +1 -0
- package/dist/schema/installation/introspection/codec.d.ts +5 -2
- package/dist/schema/installation/introspection/codec.js +319 -193
- package/dist/schema/installation/introspection/index.d.ts +2 -2
- package/dist/schema/installation/introspection/index.js +1 -1
- package/dist/schema/installation/introspection/result.d.ts +84 -25
- package/dist/schema/installation/result.d.ts +15 -0
- package/dist/schema/installation/result.js +1 -0
- package/package.json +21 -75
- package/dist/codecs/msgpack.d.ts +0 -4
- package/dist/codecs/msgpack.js +0 -50
- package/dist/publication/bindings/invocation.js +0 -13
- package/dist/publication/bindings/view.d.ts +0 -7
- package/dist/publication/bindings/view.js +0 -28
- 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-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/index.js +0 -10
- package/dist/publication/manifest.d.ts +0 -6
- package/dist/publication/manifest.js +0 -30
- package/dist/publication/publication.d.ts +0 -36
- package/dist/publication/publication.js +0 -133
- package/dist/publication/reference.d.ts +0 -17
- package/dist/publication/reference.js +0 -68
- package/dist/publication/requirements.d.ts +0 -26
- package/dist/publication/requirements.js +0 -180
- 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}/route.d.ts +0 -0
- /package/dist/{publication → release}/schema.d.ts +0 -0
- /package/dist/{publication/content.js → release/shape.js} +0 -0
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
import { AuthValueError } from '@astrale-os/kernel-core/auth/error';
|
|
2
|
-
import { accept as acceptIssuerId } from '@astrale-os/kernel-core/auth/issuer';
|
|
3
|
-
import { Key, patterns } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
4
|
-
import * as portable from '@astrale-os/kernel-dsl/value';
|
|
5
|
-
import { admitCallableBinding, admitViewBinding } from './bindings/index.js';
|
|
6
|
-
import { array, deepFreeze, exact, record, unique } from './content.js';
|
|
7
|
-
import { admitEndpoint as admitDeliveryEndpoint } from './delivery-endpoint.js';
|
|
8
|
-
import { fail } from './error.js';
|
|
9
|
-
import { admitDigest, computeEtag } from './etag.js';
|
|
10
|
-
import { admitManifest } from './manifest.js';
|
|
11
|
-
import { admitRequirements } from './requirements.js';
|
|
12
|
-
import { admitRoute } from './route.js';
|
|
13
|
-
import { admitSchema } from './schema.js';
|
|
14
|
-
export const PATH = '/.well-known/astrale/domain.json';
|
|
15
|
-
export const MEDIA_TYPE = 'application/vnd.astrale.domain-publication+json;v=2';
|
|
16
|
-
/** Resolve the canonical Publication URL beneath one exact HTTP(S) serving issuer. */
|
|
17
|
-
export function url(input) {
|
|
18
|
-
const admitted = admitIssuer(input);
|
|
19
|
-
let value;
|
|
20
|
-
try {
|
|
21
|
-
value = new URL(admitted);
|
|
22
|
-
}
|
|
23
|
-
catch (cause) {
|
|
24
|
-
return fail('PUBLICATION_INVALID', 'Publication issuer is not an HTTP(S) URL.', cause);
|
|
25
|
-
}
|
|
26
|
-
if ((value.protocol !== 'https:' && value.protocol !== 'http:') ||
|
|
27
|
-
value.username !== '' ||
|
|
28
|
-
value.password !== '' ||
|
|
29
|
-
value.search !== '' ||
|
|
30
|
-
value.hash !== '') {
|
|
31
|
-
fail('PUBLICATION_INVALID', 'Publication issuer is not an HTTP(S) URL.');
|
|
32
|
-
}
|
|
33
|
-
return `${admitted.replace(/\/+$/u, '')}${PATH}`;
|
|
34
|
-
}
|
|
35
|
-
export function seal(input) {
|
|
36
|
-
return acceptPublication({ ...input, etag: computeEtag(input) }, false);
|
|
37
|
-
}
|
|
38
|
-
export function accept(input) {
|
|
39
|
-
const publication = acceptPublication(input, true);
|
|
40
|
-
if (publication.etag !== computeEtag(withoutEtag(publication))) {
|
|
41
|
-
fail('PUBLICATION_ETAG_MISMATCH', 'Publication ETag does not match its canonical content.');
|
|
42
|
-
}
|
|
43
|
-
return publication;
|
|
44
|
-
}
|
|
45
|
-
export function encode(input) {
|
|
46
|
-
return portable.json.encode(input);
|
|
47
|
-
}
|
|
48
|
-
function acceptPublication(input, requireEtag) {
|
|
49
|
-
const value = record(input, 'PUBLICATION_INVALID', 'Expected a Publication object.');
|
|
50
|
-
exact(value, [
|
|
51
|
-
'format',
|
|
52
|
-
'version',
|
|
53
|
-
'origin',
|
|
54
|
-
'identity',
|
|
55
|
-
'etag',
|
|
56
|
-
'schema',
|
|
57
|
-
'requirements',
|
|
58
|
-
'manifest',
|
|
59
|
-
'bindings',
|
|
60
|
-
'routes',
|
|
61
|
-
'delivery',
|
|
62
|
-
], 'PUBLICATION_INVALID');
|
|
63
|
-
if (value.format !== 'astrale.domain.publication' || value.version !== 2) {
|
|
64
|
-
fail('PUBLICATION_INVALID', 'Unsupported Publication format or version.');
|
|
65
|
-
}
|
|
66
|
-
if (typeof value.origin !== 'string' || !patterns.origin.test(value.origin)) {
|
|
67
|
-
fail('PUBLICATION_INVALID', 'Publication origin is invalid.');
|
|
68
|
-
}
|
|
69
|
-
const origin = value.origin;
|
|
70
|
-
const identity = record(value.identity, 'PUBLICATION_INVALID', 'Publication identity is invalid.');
|
|
71
|
-
exact(identity, ['issuer', 'subject'], 'PUBLICATION_INVALID');
|
|
72
|
-
const issuer = admitIssuer(identity.issuer);
|
|
73
|
-
if (identity.subject !== origin) {
|
|
74
|
-
fail('PUBLICATION_INVALID', 'Publication identity subject must equal its root origin.');
|
|
75
|
-
}
|
|
76
|
-
const publishedSchema = admitSchema(value.schema);
|
|
77
|
-
const requirements = admitRequirements(value.requirements);
|
|
78
|
-
const manifest = admitManifest(value.manifest);
|
|
79
|
-
const bindingSet = record(value.bindings, 'PUBLICATION_BINDING_INVALID', 'Publication bindings are invalid.');
|
|
80
|
-
exact(bindingSet, ['callables', 'views'], 'PUBLICATION_BINDING_INVALID');
|
|
81
|
-
const callables = array(bindingSet.callables, 'PUBLICATION_BINDING_INVALID').map((entry) => admitCallableBinding(entry, origin));
|
|
82
|
-
unique(callables.map(({ callable }) => callable), 'PUBLICATION_BINDING_INVALID', 'callable binding');
|
|
83
|
-
const views = array(bindingSet.views, 'PUBLICATION_VIEW_INVALID').map((entry) => admitViewBinding(entry, origin));
|
|
84
|
-
unique(views.map(({ view }) => view), 'PUBLICATION_VIEW_INVALID', 'View binding');
|
|
85
|
-
const routes = array(value.routes, 'PUBLICATION_ROUTE_INVALID').map((entry) => admitRoute(entry, origin));
|
|
86
|
-
unique(routes.map(({ name }) => name), 'PUBLICATION_ROUTE_INVALID', 'route name');
|
|
87
|
-
for (const callable of new Set(routes.map(({ callable }) => callable))) {
|
|
88
|
-
if (routes.filter((route) => route.callable === callable && route.default === true).length > 1) {
|
|
89
|
-
fail('PUBLICATION_ROUTE_INVALID', `Callable ${callable} has more than one default route.`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
if (manifest?.entrypoint !== undefined) {
|
|
93
|
-
if (manifest.entrypoint.origin !== origin) {
|
|
94
|
-
fail('PUBLICATION_VIEW_INVALID', 'Manifest entrypoint must belong to the root origin.');
|
|
95
|
-
}
|
|
96
|
-
const key = Key.of(manifest.entrypoint);
|
|
97
|
-
if (!views.some(({ view }) => view === key)) {
|
|
98
|
-
fail('PUBLICATION_VIEW_INVALID', 'Manifest entrypoint must have a published View binding.');
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
const delivery = admitDeliveryEndpoint(value.delivery, publishedSchema.bundle.href);
|
|
102
|
-
const admittedEtag = requireEtag ? admitDigest(value.etag) : value.etag;
|
|
103
|
-
if (typeof admittedEtag !== 'string')
|
|
104
|
-
fail('PUBLICATION_INVALID', 'Publication ETag is missing.');
|
|
105
|
-
return deepFreeze({
|
|
106
|
-
format: 'astrale.domain.publication',
|
|
107
|
-
version: 2,
|
|
108
|
-
origin,
|
|
109
|
-
identity: { issuer, subject: origin },
|
|
110
|
-
etag: admittedEtag,
|
|
111
|
-
schema: publishedSchema,
|
|
112
|
-
requirements,
|
|
113
|
-
...(manifest === undefined ? {} : { manifest }),
|
|
114
|
-
bindings: { callables, views },
|
|
115
|
-
routes,
|
|
116
|
-
...(delivery === undefined ? {} : { delivery }),
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
function admitIssuer(input) {
|
|
120
|
-
try {
|
|
121
|
-
return acceptIssuerId(String(input));
|
|
122
|
-
}
|
|
123
|
-
catch (cause) {
|
|
124
|
-
if (cause instanceof AuthValueError) {
|
|
125
|
-
return fail('PUBLICATION_INVALID', 'Publication issuer is invalid.', cause);
|
|
126
|
-
}
|
|
127
|
-
throw cause;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function withoutEtag(input) {
|
|
131
|
-
const { etag: _etag, ...content } = input;
|
|
132
|
-
return content;
|
|
133
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Revision } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
2
|
-
import { type Key } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
3
|
-
import type { Etag } from './etag.js';
|
|
4
|
-
import type { Publication } from './publication.js';
|
|
5
|
-
/** Compact untrusted projection that witnesses one exact Publication. */
|
|
6
|
-
export interface PublicationRef {
|
|
7
|
-
readonly origin: Key.Origin;
|
|
8
|
-
readonly identity: Publication['identity'];
|
|
9
|
-
readonly revision: Revision;
|
|
10
|
-
readonly etag: Etag;
|
|
11
|
-
}
|
|
12
|
-
export type PublicationReferenceRelation = 'exact' | 'same-schema' | 'schema-drift' | 'identity-drift' | 'origin-drift';
|
|
13
|
-
export declare function reference(input: Publication): PublicationRef;
|
|
14
|
-
export declare function acceptReference(input: unknown): PublicationRef;
|
|
15
|
-
export declare function compareReferences(input: PublicationRef, expected: PublicationRef): PublicationReferenceRelation;
|
|
16
|
-
export declare function compareReference(input: Publication, expected: PublicationRef): PublicationReferenceRelation;
|
|
17
|
-
export declare function matchesReference(input: Publication, expected: PublicationRef): boolean;
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { AuthValueError } from '@astrale-os/kernel-core/auth/error';
|
|
2
|
-
import { accept as acceptIssuerId } from '@astrale-os/kernel-core/auth/issuer';
|
|
3
|
-
import { patterns } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
4
|
-
import { deepFreeze, exact, record } from './content.js';
|
|
5
|
-
import { fail } from './error.js';
|
|
6
|
-
import { admitDigest } from './etag.js';
|
|
7
|
-
export function reference(input) {
|
|
8
|
-
return deepFreeze({
|
|
9
|
-
origin: input.origin,
|
|
10
|
-
identity: { ...input.identity },
|
|
11
|
-
revision: input.schema.revision,
|
|
12
|
-
etag: input.etag,
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
export function acceptReference(input) {
|
|
16
|
-
const value = record(input, 'PUBLICATION_INVALID', 'Publication reference is invalid.');
|
|
17
|
-
exact(value, ['origin', 'identity', 'revision', 'etag'], 'PUBLICATION_INVALID');
|
|
18
|
-
if (typeof value.origin !== 'string' || !patterns.origin.test(value.origin)) {
|
|
19
|
-
fail('PUBLICATION_INVALID', 'Publication reference origin is invalid.');
|
|
20
|
-
}
|
|
21
|
-
const origin = value.origin;
|
|
22
|
-
const identity = record(value.identity, 'PUBLICATION_INVALID', 'Publication reference identity is invalid.');
|
|
23
|
-
exact(identity, ['issuer', 'subject'], 'PUBLICATION_INVALID');
|
|
24
|
-
const issuer = acceptIssuer(identity.issuer);
|
|
25
|
-
if (identity.subject !== origin) {
|
|
26
|
-
fail('PUBLICATION_INVALID', 'Publication reference subject must equal its root origin.');
|
|
27
|
-
}
|
|
28
|
-
if (typeof value.revision !== 'string' || !patterns.revision.test(value.revision)) {
|
|
29
|
-
fail('PUBLICATION_INVALID', 'Publication reference revision is invalid.');
|
|
30
|
-
}
|
|
31
|
-
return deepFreeze({
|
|
32
|
-
origin,
|
|
33
|
-
identity: { issuer, subject: origin },
|
|
34
|
-
revision: value.revision,
|
|
35
|
-
etag: admitDigest(value.etag),
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
export function compareReferences(input, expected) {
|
|
39
|
-
if (input.origin !== expected.origin)
|
|
40
|
-
return 'origin-drift';
|
|
41
|
-
if (input.identity.issuer !== expected.identity.issuer ||
|
|
42
|
-
input.identity.subject !== expected.identity.subject) {
|
|
43
|
-
return 'identity-drift';
|
|
44
|
-
}
|
|
45
|
-
if (input.revision !== expected.revision)
|
|
46
|
-
return 'schema-drift';
|
|
47
|
-
return input.etag === expected.etag ? 'exact' : 'same-schema';
|
|
48
|
-
}
|
|
49
|
-
export function compareReference(input, expected) {
|
|
50
|
-
return compareReferences(reference(input), expected);
|
|
51
|
-
}
|
|
52
|
-
export function matchesReference(input, expected) {
|
|
53
|
-
return compareReference(input, expected) === 'exact';
|
|
54
|
-
}
|
|
55
|
-
function acceptIssuer(input) {
|
|
56
|
-
if (typeof input !== 'string') {
|
|
57
|
-
fail('PUBLICATION_INVALID', 'Publication reference issuer is invalid.');
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
return acceptIssuerId(input);
|
|
61
|
-
}
|
|
62
|
-
catch (cause) {
|
|
63
|
-
if (cause instanceof AuthValueError) {
|
|
64
|
-
return fail('PUBLICATION_INVALID', 'Publication reference issuer is invalid.', cause);
|
|
65
|
-
}
|
|
66
|
-
throw cause;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import type { AuthorityOperation } from '@astrale-os/kernel-core/schema/authority/operation';
|
|
2
|
-
import type { schema } from '@astrale-os/kernel-dsl/v1';
|
|
3
|
-
import type { ClassRef, Key as SemanticKey } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
4
|
-
export type NonEmpty<Value> = readonly [Value, ...Value[]];
|
|
5
|
-
export declare const ClassOperationOrder: readonly ["create", "read", "traverse", "update", "delete"];
|
|
6
|
-
export type ClassOperation = (typeof ClassOperationOrder)[number];
|
|
7
|
-
export interface ClassCapabilityRequirement {
|
|
8
|
-
readonly class: ClassRef;
|
|
9
|
-
readonly operations: NonEmpty<ClassOperation>;
|
|
10
|
-
}
|
|
11
|
-
export interface CapabilityAdminRequirements {
|
|
12
|
-
readonly grant?: NonEmpty<AuthorityOperation>;
|
|
13
|
-
readonly revoke?: NonEmpty<AuthorityOperation>;
|
|
14
|
-
}
|
|
15
|
-
export interface CapabilityRequirements {
|
|
16
|
-
readonly functions?: NonEmpty<SemanticKey.Callable>;
|
|
17
|
-
readonly classes?: NonEmpty<ClassCapabilityRequirement>;
|
|
18
|
-
readonly admin?: CapabilityAdminRequirements;
|
|
19
|
-
}
|
|
20
|
-
export interface PublicationRequirements {
|
|
21
|
-
readonly capabilities: CapabilityRequirements;
|
|
22
|
-
}
|
|
23
|
-
/** Admit the exact closed Publication v2 requirements object. */
|
|
24
|
-
export declare function admitRequirements(input: unknown): PublicationRequirements;
|
|
25
|
-
/** Bind explicit capability targets to exact functional dependencies in the bundle closure. */
|
|
26
|
-
export declare function validateRequirements(root: schema.DomainSchema, closure: readonly schema.DomainSchema[], requirements: PublicationRequirements): void;
|
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { AuthorityOperationOrder } from '@astrale-os/kernel-core/schema/authority/operation';
|
|
2
|
-
import { acceptDefinitionRef, callableAddress, Key } from '@astrale-os/kernel-dsl/v1/addressing';
|
|
3
|
-
import { revision } from '@astrale-os/kernel-dsl/v1/schema/revision';
|
|
4
|
-
import { array, deepFreeze, exact, record, unique } from './content.js';
|
|
5
|
-
import { fail } from './error.js';
|
|
6
|
-
export const ClassOperationOrder = Object.freeze([
|
|
7
|
-
'create',
|
|
8
|
-
'read',
|
|
9
|
-
'traverse',
|
|
10
|
-
'update',
|
|
11
|
-
'delete',
|
|
12
|
-
]);
|
|
13
|
-
/** Admit the exact closed Publication v2 requirements object. */
|
|
14
|
-
export function admitRequirements(input) {
|
|
15
|
-
const requirements = record(input, 'PUBLICATION_INVALID', 'Publication requirements are invalid.');
|
|
16
|
-
exact(requirements, ['capabilities'], 'PUBLICATION_INVALID');
|
|
17
|
-
const capabilities = record(requirements.capabilities, 'PUBLICATION_INVALID', 'Publication capability requirements are invalid.');
|
|
18
|
-
exact(capabilities, ['admin', 'classes', 'functions'], 'PUBLICATION_INVALID');
|
|
19
|
-
const functions = capabilities.functions === undefined
|
|
20
|
-
? undefined
|
|
21
|
-
: nonEmpty(capabilities.functions, 'Function capability requirements').map(admitCallable);
|
|
22
|
-
if (functions !== undefined) {
|
|
23
|
-
unique(functions, 'PUBLICATION_INVALID', 'Function capability requirement');
|
|
24
|
-
canonical(functions, compare, 'Function capability requirements');
|
|
25
|
-
}
|
|
26
|
-
const classes = capabilities.classes === undefined
|
|
27
|
-
? undefined
|
|
28
|
-
: nonEmpty(capabilities.classes, 'Class capability requirements').map(admitClassRequirement);
|
|
29
|
-
if (classes !== undefined) {
|
|
30
|
-
unique(classes.map((entry) => classKey(entry.class)), 'PUBLICATION_INVALID', 'Class capability requirement');
|
|
31
|
-
canonical(classes, (left, right) => compare(classKey(left.class), classKey(right.class)), 'Class capability requirements');
|
|
32
|
-
}
|
|
33
|
-
const admin = capabilities.admin === undefined ? undefined : admitAdminRequirements(capabilities.admin);
|
|
34
|
-
return deepFreeze({
|
|
35
|
-
capabilities: {
|
|
36
|
-
...(functions === undefined
|
|
37
|
-
? {}
|
|
38
|
-
: { functions: Object.freeze(functions) }),
|
|
39
|
-
...(classes === undefined
|
|
40
|
-
? {}
|
|
41
|
-
: { classes: Object.freeze(classes) }),
|
|
42
|
-
...(admin === undefined ? {} : { admin }),
|
|
43
|
-
},
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
/** Bind explicit capability targets to exact functional dependencies in the bundle closure. */
|
|
47
|
-
export function validateRequirements(root, closure, requirements) {
|
|
48
|
-
const dependencies = exactDependencyClosure(root, closure);
|
|
49
|
-
for (const callable of requirements.capabilities.functions ?? []) {
|
|
50
|
-
const dependency = capabilityOwner(root, dependencies, callable, 'Function');
|
|
51
|
-
if (!hasCallable(dependency, callable)) {
|
|
52
|
-
fail('PUBLICATION_INVALID', 'Function capability requirement names an unknown dependency callable.');
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
for (const requirement of requirements.capabilities.classes ?? []) {
|
|
56
|
-
const key = Key.of(requirement.class);
|
|
57
|
-
const dependency = capabilityOwner(root, dependencies, key, 'Class');
|
|
58
|
-
if (dependency.classes[requirement.class.name] === undefined) {
|
|
59
|
-
fail('PUBLICATION_INVALID', 'Class capability requirement names an unknown dependency Class.');
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function exactDependencyClosure(root, closure) {
|
|
64
|
-
const supplied = new Map(closure.map((dependency) => [dependency.origin, dependency]));
|
|
65
|
-
const reachable = new Map();
|
|
66
|
-
const visit = (owner) => {
|
|
67
|
-
for (const coordinate of owner.dependencies) {
|
|
68
|
-
const dependency = supplied.get(coordinate.origin);
|
|
69
|
-
if (dependency === undefined || revision(dependency) !== coordinate.revision) {
|
|
70
|
-
fail('PUBLICATION_INVALID', 'Publication Bundle omits an exact dependency.');
|
|
71
|
-
}
|
|
72
|
-
if (reachable.has(dependency.origin))
|
|
73
|
-
continue;
|
|
74
|
-
reachable.set(dependency.origin, dependency);
|
|
75
|
-
visit(dependency);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
visit(root);
|
|
79
|
-
return reachable;
|
|
80
|
-
}
|
|
81
|
-
function admitCallable(input) {
|
|
82
|
-
if (typeof input !== 'string' || !Key.is(input)) {
|
|
83
|
-
return fail('PUBLICATION_INVALID', 'Function capability reference is invalid.');
|
|
84
|
-
}
|
|
85
|
-
try {
|
|
86
|
-
callableAddress(input);
|
|
87
|
-
}
|
|
88
|
-
catch {
|
|
89
|
-
return fail('PUBLICATION_INVALID', 'Function capability reference must name a Function or Method.');
|
|
90
|
-
}
|
|
91
|
-
return input;
|
|
92
|
-
}
|
|
93
|
-
function admitClassRequirement(input) {
|
|
94
|
-
const requirement = record(input, 'PUBLICATION_INVALID', 'Class capability requirement is invalid.');
|
|
95
|
-
exact(requirement, ['class', 'operations'], 'PUBLICATION_INVALID');
|
|
96
|
-
const operations = nonEmpty(requirement.operations, 'Class capability operations').map((entry) => admitOperation(entry, ClassOperationOrder, 'Class capability operation'));
|
|
97
|
-
unique(operations, 'PUBLICATION_INVALID', 'Class capability operation');
|
|
98
|
-
canonical(operations, ordered(ClassOperationOrder), 'Class capability operations');
|
|
99
|
-
return Object.freeze({
|
|
100
|
-
class: admitClassRef(requirement.class),
|
|
101
|
-
operations: Object.freeze(operations),
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
function admitAdminRequirements(input) {
|
|
105
|
-
const admin = record(input, 'PUBLICATION_INVALID', 'Administrative capability requirements are invalid.');
|
|
106
|
-
exact(admin, ['grant', 'revoke'], 'PUBLICATION_INVALID');
|
|
107
|
-
if (admin.grant === undefined && admin.revoke === undefined) {
|
|
108
|
-
fail('PUBLICATION_INVALID', 'Administrative capability requirements cannot be empty.');
|
|
109
|
-
}
|
|
110
|
-
const grant = admitAuthorityOperations(admin.grant, 'grant');
|
|
111
|
-
const revoke = admitAuthorityOperations(admin.revoke, 'revoke');
|
|
112
|
-
return Object.freeze({
|
|
113
|
-
...(grant === undefined ? {} : { grant }),
|
|
114
|
-
...(revoke === undefined ? {} : { revoke }),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
function admitAuthorityOperations(input, kind) {
|
|
118
|
-
if (input === undefined)
|
|
119
|
-
return undefined;
|
|
120
|
-
const values = nonEmpty(input, `Administrative ${kind} operations`).map((entry) => admitOperation(entry, AuthorityOperationOrder, `Administrative ${kind} operation`));
|
|
121
|
-
unique(values, 'PUBLICATION_INVALID', `administrative ${kind} operation`);
|
|
122
|
-
canonical(values, ordered(AuthorityOperationOrder), `Administrative ${kind} operations`);
|
|
123
|
-
return Object.freeze(values);
|
|
124
|
-
}
|
|
125
|
-
function admitClassRef(input) {
|
|
126
|
-
let value;
|
|
127
|
-
try {
|
|
128
|
-
value = acceptDefinitionRef(input);
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
fail('PUBLICATION_INVALID', 'Class capability reference is invalid.');
|
|
132
|
-
}
|
|
133
|
-
if (value.kind !== 'class')
|
|
134
|
-
fail('PUBLICATION_INVALID', 'Class capability reference is invalid.');
|
|
135
|
-
return value;
|
|
136
|
-
}
|
|
137
|
-
function nonEmpty(input, label) {
|
|
138
|
-
const values = [...array(input, 'PUBLICATION_INVALID')];
|
|
139
|
-
if (values.length === 0)
|
|
140
|
-
fail('PUBLICATION_INVALID', `${label} cannot be empty.`);
|
|
141
|
-
return values;
|
|
142
|
-
}
|
|
143
|
-
function admitOperation(input, vocabulary, label) {
|
|
144
|
-
if (typeof input !== 'string' || !vocabulary.includes(input)) {
|
|
145
|
-
return fail('PUBLICATION_INVALID', `${label} is unknown.`);
|
|
146
|
-
}
|
|
147
|
-
return input;
|
|
148
|
-
}
|
|
149
|
-
function canonical(values, comparator, label) {
|
|
150
|
-
if (values.some((entry, index) => index > 0 && comparator(values[index - 1], entry) >= 0)) {
|
|
151
|
-
fail('PUBLICATION_INVALID', `${label} must be sorted canonically.`);
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
function ordered(order) {
|
|
155
|
-
return (left, right) => order.indexOf(left) - order.indexOf(right);
|
|
156
|
-
}
|
|
157
|
-
function compare(left, right) {
|
|
158
|
-
return left < right ? -1 : left > right ? 1 : 0;
|
|
159
|
-
}
|
|
160
|
-
function classKey(input) {
|
|
161
|
-
return String(Key.of(input));
|
|
162
|
-
}
|
|
163
|
-
function capabilityOwner(root, dependencies, key, label) {
|
|
164
|
-
const origin = Key.origin(key);
|
|
165
|
-
if (origin === root.origin)
|
|
166
|
-
fail('PUBLICATION_INVALID', `${label} capability target cannot be local.`);
|
|
167
|
-
const dependency = dependencies.get(origin);
|
|
168
|
-
if (dependency === undefined) {
|
|
169
|
-
fail('PUBLICATION_INVALID', `${label} capability target must belong to the exact dependency closure.`);
|
|
170
|
-
}
|
|
171
|
-
return dependency;
|
|
172
|
-
}
|
|
173
|
-
function hasCallable(dependency, callable) {
|
|
174
|
-
const coordinate = callableAddress(callable);
|
|
175
|
-
if (coordinate.kind === 'function')
|
|
176
|
-
return dependency.functions[coordinate.name] !== undefined;
|
|
177
|
-
const declaration = dependency.classes[coordinate.owner.name];
|
|
178
|
-
const method = declaration?.kind === 'node' ? declaration.methods[coordinate.name] : undefined;
|
|
179
|
-
return method !== undefined && method.inheritance !== 'abstract';
|
|
180
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { HttpRequest } from './request.js';
|
|
2
|
-
export interface ReleaseUnavailable {
|
|
3
|
-
readonly kind: 'release-unavailable';
|
|
4
|
-
}
|
|
5
|
-
export declare const HTTP_ROUTE_RELEASE_UNAVAILABLE: 'x-astrale-release-unavailable';
|
|
6
|
-
export declare const HTTP_ROUTE_RELEASE_UNAVAILABLE_STATUS: 410;
|
|
7
|
-
export declare function releaseUnavailableHeaders(): Readonly<Record<string, string>>;
|
|
8
|
-
export declare function decodeReleaseUnavailable(status: number, headers: HttpRequest['headers']): ReleaseUnavailable | undefined;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { fail } from './error.js';
|
|
2
|
-
export const HTTP_ROUTE_RELEASE_UNAVAILABLE = 'x-astrale-release-unavailable';
|
|
3
|
-
export const HTTP_ROUTE_RELEASE_UNAVAILABLE_STATUS = 410;
|
|
4
|
-
export function releaseUnavailableHeaders() {
|
|
5
|
-
return Object.freeze({
|
|
6
|
-
[HTTP_ROUTE_RELEASE_UNAVAILABLE]: '1',
|
|
7
|
-
'cache-control': 'no-store',
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
export function decodeReleaseUnavailable(status, headers) {
|
|
11
|
-
const encoded = header(headers, HTTP_ROUTE_RELEASE_UNAVAILABLE);
|
|
12
|
-
if (encoded === undefined)
|
|
13
|
-
return undefined;
|
|
14
|
-
if (status !== HTTP_ROUTE_RELEASE_UNAVAILABLE_STATUS || encoded !== '1') {
|
|
15
|
-
fail('NATIVE_ROUTE_ROUTING_INVALID', `${HTTP_ROUTE_RELEASE_UNAVAILABLE} is reserved for status ${HTTP_ROUTE_RELEASE_UNAVAILABLE_STATUS} with value 1.`);
|
|
16
|
-
}
|
|
17
|
-
return Object.freeze({ kind: 'release-unavailable' });
|
|
18
|
-
}
|
|
19
|
-
function header(input, name) {
|
|
20
|
-
if (input instanceof Headers)
|
|
21
|
-
return input.get(name) ?? undefined;
|
|
22
|
-
let found;
|
|
23
|
-
for (const [candidate, value] of Object.entries(input ?? {})) {
|
|
24
|
-
if (candidate.toLowerCase() !== name || value === undefined)
|
|
25
|
-
continue;
|
|
26
|
-
if (found !== undefined || (Array.isArray(value) && value.length !== 1)) {
|
|
27
|
-
fail('NATIVE_ROUTE_ROUTING_INVALID', `Header ${name} must occur at most once.`);
|
|
28
|
-
}
|
|
29
|
-
found = typeof value === 'string' ? value : value[0];
|
|
30
|
-
}
|
|
31
|
-
return found;
|
|
32
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|