@astrale-os/kernel-protocol 0.5.0-beta.45 → 0.5.0-beta.46
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/publication/etag.js +5 -1
- package/dist/publication/index.d.ts +2 -2
- package/dist/publication/index.js +1 -1
- package/dist/publication/legacy-requirements.d.ts +12 -0
- package/dist/publication/legacy-requirements.js +23 -0
- package/dist/publication/publication.d.ts +4 -2
- package/dist/publication/publication.js +22 -11
- package/dist/publication/requirements.d.ts +3 -3
- package/dist/publication/requirements.js +38 -36
- package/dist/runtime/artifact.d.ts +4 -2
- package/dist/runtime/artifact.js +19 -6
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/package.json +5 -1
package/dist/publication/etag.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as portable from '@astrale-os/kernel-dsl/value';
|
|
2
2
|
import { fail } from './error.js';
|
|
3
|
+
import { encodeLegacyRequirements } from './legacy-requirements.js';
|
|
3
4
|
const DIGEST = /^sha256:[0-9a-f]{64}$/u;
|
|
4
5
|
export function admitDigest(input) {
|
|
5
6
|
if (typeof input !== 'string' || !DIGEST.test(input)) {
|
|
@@ -8,7 +9,10 @@ export function admitDigest(input) {
|
|
|
8
9
|
return input;
|
|
9
10
|
}
|
|
10
11
|
export function computeEtag(input) {
|
|
11
|
-
const
|
|
12
|
+
const wire = input.version === 2
|
|
13
|
+
? { ...input, requirements: encodeLegacyRequirements(input.requirements) }
|
|
14
|
+
: input;
|
|
15
|
+
const bytes = portable.json.encode(wire);
|
|
12
16
|
return `sha256:${sha256Hex(bytes)}`;
|
|
13
17
|
}
|
|
14
18
|
export function sha256Hex(input) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MEDIA_TYPE, PATH, url, seal, accept, encode, type Publication } from './publication.js';
|
|
1
|
+
export { MEDIA_TYPE, LEGACY_MEDIA_TYPE, mediaType, PATH, url, seal, accept, encode, type Publication, } from './publication.js';
|
|
2
2
|
export { acceptBundle, decodeBundle } from './bundle-admission.js';
|
|
3
3
|
export { BUNDLE_MEDIA_TYPE, describeBundle, type BundleDescriptor } from './bundle.js';
|
|
4
4
|
export { verifyBundleBytes } from './bundle-evidence.js';
|
|
@@ -10,7 +10,7 @@ export { PublicationError, type PublicationErrorCode } from './error.js';
|
|
|
10
10
|
export * as delivery from './delivery/index.js';
|
|
11
11
|
export type { Authentication as DeliveryAuthentication, Endpoint as DeliveryEndpoint, } from './delivery-endpoint.js';
|
|
12
12
|
export type { Manifest } from './manifest.js';
|
|
13
|
-
export { ClassOperationOrder, admitRequirements, validateRequirements, type CapabilityAuthorityRequirements, type
|
|
13
|
+
export { ClassOperationOrder, admitRequirements, validateRequirements, type CapabilityAuthorityRequirements, type CoreCapabilityRequirement, type CapabilityRequirements, type ClassCapabilityRequirement, type ClassOperation, type NonEmpty, type PublicationRequirements, } from './requirements.js';
|
|
14
14
|
export { acceptReference, compareReference, compareReferences, matchesReference, reference, type PublicationRef, type PublicationReferenceRelation, } from './reference.js';
|
|
15
15
|
export type { BodyPick, CredentialSource, HttpMethod, HttpRoute, HttpRouteDeclaration, RouteBody, RouteHeaders, SingleCredentialSource, } from './route.js';
|
|
16
16
|
export type { Schema } from './schema.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { MEDIA_TYPE, PATH, url, seal, accept, encode } from './publication.js';
|
|
1
|
+
export { MEDIA_TYPE, LEGACY_MEDIA_TYPE, mediaType, PATH, url, seal, accept, encode, } from './publication.js';
|
|
2
2
|
export { acceptBundle, decodeBundle } from './bundle-admission.js';
|
|
3
3
|
export { BUNDLE_MEDIA_TYPE, describeBundle } from './bundle.js';
|
|
4
4
|
export { verifyBundleBytes } from './bundle-evidence.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PublicationRequirements } from './requirements.js';
|
|
2
|
+
/** Read the retired v2 wire coordinate without exposing it in canonical requirements. */
|
|
3
|
+
export declare function decodeLegacyRequirements(input: unknown): PublicationRequirements;
|
|
4
|
+
/** Preserve immutable v2 bytes and the historical installation-target digest vocabulary. */
|
|
5
|
+
export declare function encodeLegacyRequirements(input: PublicationRequirements): Readonly<{
|
|
6
|
+
capabilities: Readonly<{
|
|
7
|
+
functions?: import("./requirements.js").NonEmpty<import("@astrale-os/kernel-dsl/v1").Key.Callable>;
|
|
8
|
+
classes?: import("./requirements.js").NonEmpty<import("./requirements.js").ClassCapabilityRequirement>;
|
|
9
|
+
authority?: import("./requirements.js").CapabilityAuthorityRequirements;
|
|
10
|
+
profiles?: import("./requirements.js").NonEmpty<import("./requirements.js").CoreCapabilityRequirement> | undefined;
|
|
11
|
+
}>;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { exact, record } from './content.js';
|
|
2
|
+
import { admitRequirements } from './requirements.js';
|
|
3
|
+
/** Read the retired v2 wire coordinate without exposing it in canonical requirements. */
|
|
4
|
+
export function decodeLegacyRequirements(input) {
|
|
5
|
+
const requirements = record(input, 'PUBLICATION_INVALID', 'Publication requirements are invalid.');
|
|
6
|
+
exact(requirements, ['capabilities'], 'PUBLICATION_INVALID');
|
|
7
|
+
const capabilities = record(requirements.capabilities, 'PUBLICATION_INVALID', 'Publication capability requirements are invalid.');
|
|
8
|
+
exact(capabilities, ['authority', 'classes', 'functions', 'profiles'], 'PUBLICATION_INVALID');
|
|
9
|
+
const { profiles, ...principal } = capabilities;
|
|
10
|
+
return admitRequirements({
|
|
11
|
+
capabilities: { ...principal, ...(profiles === undefined ? {} : { core: profiles }) },
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
/** Preserve immutable v2 bytes and the historical installation-target digest vocabulary. */
|
|
15
|
+
export function encodeLegacyRequirements(input) {
|
|
16
|
+
const { core, ...principal } = input.capabilities;
|
|
17
|
+
return Object.freeze({
|
|
18
|
+
capabilities: Object.freeze({
|
|
19
|
+
...principal,
|
|
20
|
+
...(core === undefined ? {} : { profiles: core }),
|
|
21
|
+
}),
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -9,12 +9,14 @@ import type { PublicationRequirements } from './requirements.js';
|
|
|
9
9
|
import type { HttpRoute } from './route.js';
|
|
10
10
|
import type { Schema } from './schema.js';
|
|
11
11
|
export declare const PATH: '/.well-known/astrale/domain.json';
|
|
12
|
-
export declare const MEDIA_TYPE: 'application/vnd.astrale.domain-publication+json;v=
|
|
12
|
+
export declare const MEDIA_TYPE: 'application/vnd.astrale.domain-publication+json;v=3';
|
|
13
|
+
export declare const LEGACY_MEDIA_TYPE: 'application/vnd.astrale.domain-publication+json;v=2';
|
|
14
|
+
export declare function mediaType(input: Pick<Publication, 'version'>): typeof MEDIA_TYPE | typeof LEGACY_MEDIA_TYPE;
|
|
13
15
|
/** Resolve the canonical Publication URL beneath one exact HTTP(S) serving issuer. */
|
|
14
16
|
export declare function url(input: string): string;
|
|
15
17
|
export interface Publication {
|
|
16
18
|
readonly format: 'astrale.domain.publication';
|
|
17
|
-
readonly version: 2;
|
|
19
|
+
readonly version: 2 | 3;
|
|
18
20
|
readonly origin: Key.Origin;
|
|
19
21
|
readonly identity: {
|
|
20
22
|
readonly issuer: IssuerId;
|
|
@@ -8,12 +8,17 @@ import { array, deepFreeze, exact, record, unique } from './content.js';
|
|
|
8
8
|
import { admitEndpoint as admitDeliveryEndpoint } from './delivery-endpoint.js';
|
|
9
9
|
import { fail } from './error.js';
|
|
10
10
|
import { admitDigest, computeEtag } from './etag.js';
|
|
11
|
+
import { decodeLegacyRequirements, encodeLegacyRequirements } from './legacy-requirements.js';
|
|
11
12
|
import { admitManifest } from './manifest.js';
|
|
12
13
|
import { admitRequirements } from './requirements.js';
|
|
13
14
|
import { admitRoute } from './route.js';
|
|
14
15
|
import { admitSchema } from './schema.js';
|
|
15
16
|
export const PATH = '/.well-known/astrale/domain.json';
|
|
16
|
-
export const MEDIA_TYPE = 'application/vnd.astrale.domain-publication+json;v=
|
|
17
|
+
export const MEDIA_TYPE = 'application/vnd.astrale.domain-publication+json;v=3';
|
|
18
|
+
export const LEGACY_MEDIA_TYPE = 'application/vnd.astrale.domain-publication+json;v=2';
|
|
19
|
+
export function mediaType(input) {
|
|
20
|
+
return input.version === 2 ? LEGACY_MEDIA_TYPE : MEDIA_TYPE;
|
|
21
|
+
}
|
|
17
22
|
/** Resolve the canonical Publication URL beneath one exact HTTP(S) serving issuer. */
|
|
18
23
|
export function url(input) {
|
|
19
24
|
const admitted = admitIssuer(input);
|
|
@@ -34,20 +39,23 @@ export function url(input) {
|
|
|
34
39
|
return `${admitted.replace(/\/+$/u, '')}${PATH}`;
|
|
35
40
|
}
|
|
36
41
|
export function seal(input) {
|
|
37
|
-
const canonical = withoutEtag(acceptPublication({ ...input, etag: 'canonicalizing' },
|
|
38
|
-
return acceptPublication({ ...canonical, etag: computeEtag(canonical) },
|
|
42
|
+
const canonical = withoutEtag(acceptPublication({ ...input, etag: 'canonicalizing' }, 'seal'));
|
|
43
|
+
return acceptPublication({ ...canonical, etag: computeEtag(canonical) }, 'canonical');
|
|
39
44
|
}
|
|
40
45
|
export function accept(input) {
|
|
41
|
-
const publication = acceptPublication(input,
|
|
46
|
+
const publication = acceptPublication(input, 'wire');
|
|
42
47
|
if (publication.etag !== computeEtag(withoutEtag(publication))) {
|
|
43
48
|
fail('PUBLICATION_ETAG_MISMATCH', 'Publication ETag does not match its canonical content.');
|
|
44
49
|
}
|
|
45
50
|
return publication;
|
|
46
51
|
}
|
|
47
52
|
export function encode(input) {
|
|
48
|
-
|
|
53
|
+
const wire = input.version === 2
|
|
54
|
+
? { ...input, requirements: encodeLegacyRequirements(input.requirements) }
|
|
55
|
+
: input;
|
|
56
|
+
return portable.json.encode(wire);
|
|
49
57
|
}
|
|
50
|
-
function acceptPublication(input,
|
|
58
|
+
function acceptPublication(input, mode) {
|
|
51
59
|
const value = record(input, 'PUBLICATION_INVALID', 'Expected a Publication object.');
|
|
52
60
|
exact(value, [
|
|
53
61
|
'format',
|
|
@@ -62,7 +70,8 @@ function acceptPublication(input, requireEtag, canonicalizeViews = false) {
|
|
|
62
70
|
'routes',
|
|
63
71
|
'delivery',
|
|
64
72
|
], 'PUBLICATION_INVALID');
|
|
65
|
-
if (value.format !== 'astrale.domain.publication' ||
|
|
73
|
+
if (value.format !== 'astrale.domain.publication' ||
|
|
74
|
+
(value.version !== 2 && value.version !== 3)) {
|
|
66
75
|
fail('PUBLICATION_INVALID', 'Unsupported Publication format or version.');
|
|
67
76
|
}
|
|
68
77
|
if (typeof value.origin !== 'string' || !patterns.origin.test(value.origin)) {
|
|
@@ -76,13 +85,15 @@ function acceptPublication(input, requireEtag, canonicalizeViews = false) {
|
|
|
76
85
|
fail('PUBLICATION_INVALID', 'Publication identity subject must equal its root origin.');
|
|
77
86
|
}
|
|
78
87
|
const publishedSchema = admitSchema(value.schema);
|
|
79
|
-
const requirements =
|
|
88
|
+
const requirements = value.version === 2 && mode === 'wire'
|
|
89
|
+
? decodeLegacyRequirements(value.requirements)
|
|
90
|
+
: admitRequirements(value.requirements);
|
|
80
91
|
const manifest = admitManifest(value.manifest);
|
|
81
92
|
const bindingSet = record(value.bindings, 'PUBLICATION_BINDING_INVALID', 'Publication bindings are invalid.');
|
|
82
93
|
exact(bindingSet, ['callables', 'views'], 'PUBLICATION_BINDING_INVALID');
|
|
83
94
|
const callables = array(bindingSet.callables, 'PUBLICATION_BINDING_INVALID').map((entry) => admitCallableBinding(entry, origin));
|
|
84
95
|
unique(callables.map(({ callable }) => callable), 'PUBLICATION_BINDING_INVALID', 'callable binding');
|
|
85
|
-
const views = array(bindingSet.views, 'PUBLICATION_VIEW_INVALID').map((entry) =>
|
|
96
|
+
const views = array(bindingSet.views, 'PUBLICATION_VIEW_INVALID').map((entry) => mode === 'seal' ? canonicalizeViewBinding(entry, origin) : admitViewBinding(entry, origin));
|
|
86
97
|
unique(views.map(({ view }) => view), 'PUBLICATION_VIEW_INVALID', 'View binding');
|
|
87
98
|
const routes = array(value.routes, 'PUBLICATION_ROUTE_INVALID').map((entry) => admitRoute(entry, origin));
|
|
88
99
|
unique(routes.map(({ name }) => name), 'PUBLICATION_ROUTE_INVALID', 'route name');
|
|
@@ -101,12 +112,12 @@ function acceptPublication(input, requireEtag, canonicalizeViews = false) {
|
|
|
101
112
|
}
|
|
102
113
|
}
|
|
103
114
|
const delivery = admitDeliveryEndpoint(value.delivery, publishedSchema.bundle.href);
|
|
104
|
-
const admittedEtag =
|
|
115
|
+
const admittedEtag = mode === 'wire' ? admitDigest(value.etag) : value.etag;
|
|
105
116
|
if (typeof admittedEtag !== 'string')
|
|
106
117
|
fail('PUBLICATION_INVALID', 'Publication ETag is missing.');
|
|
107
118
|
return deepFreeze({
|
|
108
119
|
format: 'astrale.domain.publication',
|
|
109
|
-
version:
|
|
120
|
+
version: value.version,
|
|
110
121
|
origin,
|
|
111
122
|
identity: { issuer, subject: origin },
|
|
112
123
|
etag: admittedEtag,
|
|
@@ -16,10 +16,10 @@ export interface CapabilityRequirements {
|
|
|
16
16
|
readonly functions?: NonEmpty<SemanticKey.Callable>;
|
|
17
17
|
readonly classes?: NonEmpty<ClassCapabilityRequirement>;
|
|
18
18
|
readonly authority?: CapabilityAuthorityRequirements;
|
|
19
|
-
readonly
|
|
19
|
+
readonly core?: NonEmpty<CoreCapabilityRequirement>;
|
|
20
20
|
}
|
|
21
21
|
/** Installation-owned authority materialized onto one local Core Identity. */
|
|
22
|
-
export interface
|
|
22
|
+
export interface CoreCapabilityRequirement {
|
|
23
23
|
readonly subject: CoreRef;
|
|
24
24
|
readonly functions?: NonEmpty<SemanticKey.Callable>;
|
|
25
25
|
readonly classes?: NonEmpty<ClassCapabilityRequirement>;
|
|
@@ -28,7 +28,7 @@ export interface CapabilityProfileRequirement {
|
|
|
28
28
|
export interface PublicationRequirements {
|
|
29
29
|
readonly capabilities: CapabilityRequirements;
|
|
30
30
|
}
|
|
31
|
-
/** Admit the exact closed
|
|
31
|
+
/** Admit the exact closed canonical requirements object used by Publication v3. */
|
|
32
32
|
export declare function admitRequirements(input: unknown): PublicationRequirements;
|
|
33
33
|
/** Bind explicit capability targets to exact functional dependencies in the bundle closure. */
|
|
34
34
|
export declare function validateRequirements(root: schemaLanguage.DomainSchema, closure: readonly schemaLanguage.DomainSchema[], requirements: PublicationRequirements): void;
|
|
@@ -15,12 +15,12 @@ export const ClassOperationOrder = Object.freeze([
|
|
|
15
15
|
'update',
|
|
16
16
|
'delete',
|
|
17
17
|
]);
|
|
18
|
-
/** Admit the exact closed
|
|
18
|
+
/** Admit the exact closed canonical requirements object used by Publication v3. */
|
|
19
19
|
export function admitRequirements(input) {
|
|
20
20
|
const requirements = record(input, 'PUBLICATION_INVALID', 'Publication requirements are invalid.');
|
|
21
21
|
exact(requirements, ['capabilities'], 'PUBLICATION_INVALID');
|
|
22
22
|
const capabilities = record(requirements.capabilities, 'PUBLICATION_INVALID', 'Publication capability requirements are invalid.');
|
|
23
|
-
exact(capabilities, ['authority', 'classes', 'functions', '
|
|
23
|
+
exact(capabilities, ['authority', 'classes', 'functions', 'core'], 'PUBLICATION_INVALID');
|
|
24
24
|
const functions = capabilities.functions === undefined
|
|
25
25
|
? undefined
|
|
26
26
|
: nonEmpty(capabilities.functions, 'Function capability requirements').map(admitCallable);
|
|
@@ -38,12 +38,12 @@ export function admitRequirements(input) {
|
|
|
38
38
|
const authority = capabilities.authority === undefined
|
|
39
39
|
? undefined
|
|
40
40
|
: admitAuthorityRequirements(capabilities.authority);
|
|
41
|
-
const
|
|
41
|
+
const core = capabilities.core === undefined
|
|
42
42
|
? undefined
|
|
43
|
-
: nonEmpty(capabilities.
|
|
44
|
-
if (
|
|
45
|
-
unique(
|
|
46
|
-
canonical(
|
|
43
|
+
: nonEmpty(capabilities.core, 'Core capability requirements').map(admitCoreCapabilityRequirement);
|
|
44
|
+
if (core !== undefined) {
|
|
45
|
+
unique(core.map(({ subject }) => Key.of(subject)), 'PUBLICATION_INVALID', 'Core capability subject');
|
|
46
|
+
canonical(core, (left, right) => compare(Key.of(left.subject), Key.of(right.subject)), 'Core capability requirements');
|
|
47
47
|
}
|
|
48
48
|
return deepFreeze({
|
|
49
49
|
capabilities: {
|
|
@@ -54,33 +54,35 @@ export function admitRequirements(input) {
|
|
|
54
54
|
? {}
|
|
55
55
|
: { classes: Object.freeze(classes) }),
|
|
56
56
|
...(authority === undefined ? {} : { authority }),
|
|
57
|
-
...(
|
|
57
|
+
...(core === undefined
|
|
58
58
|
? {}
|
|
59
|
-
: {
|
|
59
|
+
: { core: Object.freeze(core) }),
|
|
60
60
|
},
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
function
|
|
64
|
-
const
|
|
65
|
-
exact(
|
|
66
|
-
const subject = admitCoreRef(
|
|
67
|
-
const functions =
|
|
63
|
+
function admitCoreCapabilityRequirement(input) {
|
|
64
|
+
const requirement = record(input, 'PUBLICATION_INVALID', 'Core capability requirement is invalid.');
|
|
65
|
+
exact(requirement, ['authority', 'classes', 'functions', 'subject'], 'PUBLICATION_INVALID');
|
|
66
|
+
const subject = admitCoreRef(requirement.subject);
|
|
67
|
+
const functions = requirement.functions === undefined
|
|
68
68
|
? undefined
|
|
69
|
-
: nonEmpty(
|
|
69
|
+
: nonEmpty(requirement.functions, 'Core capability Function requirements').map(admitCallable);
|
|
70
70
|
if (functions !== undefined) {
|
|
71
|
-
unique(functions, 'PUBLICATION_INVALID', '
|
|
72
|
-
canonical(functions, compare, '
|
|
71
|
+
unique(functions, 'PUBLICATION_INVALID', 'Core capability Function requirement');
|
|
72
|
+
canonical(functions, compare, 'Core capability Function requirements');
|
|
73
73
|
}
|
|
74
|
-
const classes =
|
|
74
|
+
const classes = requirement.classes === undefined
|
|
75
75
|
? undefined
|
|
76
|
-
: nonEmpty(
|
|
76
|
+
: nonEmpty(requirement.classes, 'Core capability Class requirements').map(admitClassRequirement);
|
|
77
77
|
if (classes !== undefined) {
|
|
78
|
-
unique(classes.map((entry) => classKey(entry.class)), 'PUBLICATION_INVALID', '
|
|
79
|
-
canonical(classes, (left, right) => compare(classKey(left.class), classKey(right.class)), '
|
|
78
|
+
unique(classes.map((entry) => classKey(entry.class)), 'PUBLICATION_INVALID', 'Core capability Class requirement');
|
|
79
|
+
canonical(classes, (left, right) => compare(classKey(left.class), classKey(right.class)), 'Core capability Class requirements');
|
|
80
80
|
}
|
|
81
|
-
const authority =
|
|
81
|
+
const authority = requirement.authority === undefined
|
|
82
|
+
? undefined
|
|
83
|
+
: admitAuthorityRequirements(requirement.authority);
|
|
82
84
|
if (functions === undefined && classes === undefined && authority === undefined) {
|
|
83
|
-
fail('PUBLICATION_INVALID', '
|
|
85
|
+
fail('PUBLICATION_INVALID', 'Core capability requirements cannot be empty.');
|
|
84
86
|
}
|
|
85
87
|
return Object.freeze({
|
|
86
88
|
subject,
|
|
@@ -109,26 +111,26 @@ export function validateRequirements(root, closure, requirements) {
|
|
|
109
111
|
fail('PUBLICATION_INVALID', 'Class capability requirement names an unknown dependency Class.');
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
|
-
for (const
|
|
113
|
-
const subject = root.core.nodes[
|
|
114
|
-
if (
|
|
114
|
+
for (const requirement of requirements.capabilities.core ?? []) {
|
|
115
|
+
const subject = root.core.nodes[requirement.subject.name];
|
|
116
|
+
if (requirement.subject.origin !== root.origin ||
|
|
115
117
|
subject === undefined ||
|
|
116
118
|
!classSatisfies(root, dependencies, subject.class, KernelIdentityClass)) {
|
|
117
|
-
fail('PUBLICATION_INVALID', '
|
|
119
|
+
fail('PUBLICATION_INVALID', 'Core capability subject must name one local Core Identity.');
|
|
118
120
|
}
|
|
119
|
-
for (const callable of
|
|
121
|
+
for (const callable of requirement.functions ?? []) {
|
|
120
122
|
const dependency = capabilityOwner(root, dependencies, callable, 'Function');
|
|
121
123
|
if (!hasCallable(dependency, callable)) {
|
|
122
|
-
fail('PUBLICATION_INVALID', '
|
|
124
|
+
fail('PUBLICATION_INVALID', 'Core capability Function requirement names an unknown dependency callable.');
|
|
123
125
|
}
|
|
124
126
|
}
|
|
125
|
-
for (const
|
|
126
|
-
const key = Key.of(
|
|
127
|
-
const owner =
|
|
127
|
+
for (const classRequirement of requirement.classes ?? []) {
|
|
128
|
+
const key = Key.of(classRequirement.class);
|
|
129
|
+
const owner = classRequirement.class.origin === root.origin
|
|
128
130
|
? root
|
|
129
131
|
: capabilityOwner(root, dependencies, key, 'Class');
|
|
130
|
-
if (owner.classes[
|
|
131
|
-
fail('PUBLICATION_INVALID', '
|
|
132
|
+
if (owner.classes[classRequirement.class.name] === undefined) {
|
|
133
|
+
fail('PUBLICATION_INVALID', 'Core capability Class requirement names an unknown dependency Class.');
|
|
132
134
|
}
|
|
133
135
|
}
|
|
134
136
|
}
|
|
@@ -229,10 +231,10 @@ function admitCoreRef(input) {
|
|
|
229
231
|
value = acceptDefinitionRef(input);
|
|
230
232
|
}
|
|
231
233
|
catch {
|
|
232
|
-
fail('PUBLICATION_INVALID', '
|
|
234
|
+
fail('PUBLICATION_INVALID', 'Core capability subject is invalid.');
|
|
233
235
|
}
|
|
234
236
|
if (value.kind !== 'core') {
|
|
235
|
-
fail('PUBLICATION_INVALID', '
|
|
237
|
+
fail('PUBLICATION_INVALID', 'Core capability subject must be one local Core reference.');
|
|
236
238
|
}
|
|
237
239
|
return value;
|
|
238
240
|
}
|
|
@@ -31,13 +31,15 @@ export interface Module {
|
|
|
31
31
|
/** Immutable portable closure selected by one local Schema installation source. */
|
|
32
32
|
export interface RuntimeArtifact {
|
|
33
33
|
readonly format: 'astrale.domain-runtime-artifact';
|
|
34
|
-
readonly version: 2;
|
|
34
|
+
readonly version: 2 | 3;
|
|
35
35
|
readonly issuer: IssuerId;
|
|
36
36
|
readonly bundle: Ref;
|
|
37
37
|
readonly requirements: PublicationRequirements;
|
|
38
38
|
readonly modules: readonly [Module, ...Module[]];
|
|
39
39
|
}
|
|
40
|
-
export declare const MEDIA_TYPE: 'application/vnd.astrale.domain-runtime+json;v=
|
|
40
|
+
export declare const MEDIA_TYPE: 'application/vnd.astrale.domain-runtime+json;v=3';
|
|
41
|
+
export declare const LEGACY_MEDIA_TYPE: 'application/vnd.astrale.domain-runtime+json;v=2';
|
|
42
|
+
export declare function mediaType(input: Pick<RuntimeArtifact, 'version'>): typeof MEDIA_TYPE | typeof LEGACY_MEDIA_TYPE;
|
|
41
43
|
export declare function accept(input: unknown): RuntimeArtifact;
|
|
42
44
|
export declare function encode(input: RuntimeArtifact): Uint8Array;
|
|
43
45
|
export declare function runtimeDigest(input: RuntimeArtifact): Digest;
|
package/dist/runtime/artifact.js
CHANGED
|
@@ -4,18 +4,26 @@ import * as portable from '@astrale-os/kernel-dsl/value';
|
|
|
4
4
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
5
5
|
import * as artifact from '../artifact/index.js';
|
|
6
6
|
import { admitRequirements } from '../publication/index.js';
|
|
7
|
-
|
|
7
|
+
import { decodeLegacyRequirements, encodeLegacyRequirements, } from '../publication/legacy-requirements.js';
|
|
8
|
+
export const MEDIA_TYPE = 'application/vnd.astrale.domain-runtime+json;v=3';
|
|
9
|
+
export const LEGACY_MEDIA_TYPE = 'application/vnd.astrale.domain-runtime+json;v=2';
|
|
10
|
+
export function mediaType(input) {
|
|
11
|
+
return input.version === 2 ? LEGACY_MEDIA_TYPE : MEDIA_TYPE;
|
|
12
|
+
}
|
|
8
13
|
export function accept(input) {
|
|
9
14
|
const root = record(input, 'Domain Runtime artifact');
|
|
10
15
|
exact(root, ['bundle', 'format', 'issuer', 'modules', 'requirements', 'version']);
|
|
11
|
-
if (root.format !== 'astrale.domain-runtime-artifact' ||
|
|
16
|
+
if (root.format !== 'astrale.domain-runtime-artifact' ||
|
|
17
|
+
(root.version !== 2 && root.version !== 3)) {
|
|
12
18
|
invalid('Domain Runtime artifact format or version is unsupported.');
|
|
13
19
|
}
|
|
14
20
|
if (typeof root.issuer !== 'string')
|
|
15
21
|
invalid('Domain Runtime artifact issuer is invalid.');
|
|
16
22
|
const admittedIssuer = acceptIssuer(root.issuer);
|
|
17
23
|
const bundle = artifact.accept(root.bundle);
|
|
18
|
-
const requirements =
|
|
24
|
+
const requirements = root.version === 2
|
|
25
|
+
? decodeLegacyRequirements(root.requirements)
|
|
26
|
+
: admitRequirements(root.requirements);
|
|
19
27
|
if (!Array.isArray(root.modules) || root.modules.length === 0) {
|
|
20
28
|
invalid('Domain Runtime artifact requires at least one Module.');
|
|
21
29
|
}
|
|
@@ -26,7 +34,7 @@ export function accept(input) {
|
|
|
26
34
|
}
|
|
27
35
|
return Object.freeze({
|
|
28
36
|
format: 'astrale.domain-runtime-artifact',
|
|
29
|
-
version:
|
|
37
|
+
version: root.version,
|
|
30
38
|
issuer: admittedIssuer,
|
|
31
39
|
bundle,
|
|
32
40
|
requirements,
|
|
@@ -34,10 +42,15 @@ export function accept(input) {
|
|
|
34
42
|
});
|
|
35
43
|
}
|
|
36
44
|
export function encode(input) {
|
|
37
|
-
return portable.json.encode(accept(input));
|
|
45
|
+
return portable.json.encode(wire(accept(wire(input))));
|
|
38
46
|
}
|
|
39
47
|
export function runtimeDigest(input) {
|
|
40
|
-
return digestBytes(portable.json.encode(input));
|
|
48
|
+
return digestBytes(portable.json.encode(wire(input)));
|
|
49
|
+
}
|
|
50
|
+
function wire(input) {
|
|
51
|
+
return input.version === 2
|
|
52
|
+
? { ...input, requirements: encodeLegacyRequirements(input.requirements) }
|
|
53
|
+
: input;
|
|
41
54
|
}
|
|
42
55
|
export function acceptExecutable(input) {
|
|
43
56
|
return executable(input, 0);
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { MEDIA_TYPE, accept, acceptExecutable, encode, executableClosure, executableDigest, runtimeDigest, type Digest, type Executable, type ExecutableDependency, type Export, type Module, type RuntimeArtifact, type Ref, } from './artifact.js';
|
|
1
|
+
export { MEDIA_TYPE, LEGACY_MEDIA_TYPE, mediaType, accept, acceptExecutable, encode, executableClosure, executableDigest, runtimeDigest, type Digest, type Executable, type ExecutableDependency, type Export, type Module, type RuntimeArtifact, type Ref, } from './artifact.js';
|
package/dist/runtime/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { MEDIA_TYPE, accept, acceptExecutable, encode, executableClosure, executableDigest, runtimeDigest, } from './artifact.js';
|
|
1
|
+
export { MEDIA_TYPE, LEGACY_MEDIA_TYPE, mediaType, accept, acceptExecutable, encode, executableClosure, executableDigest, runtimeDigest, } from './artifact.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrale-os/kernel-protocol",
|
|
3
|
-
"version": "0.5.0-beta.
|
|
3
|
+
"version": "0.5.0-beta.46",
|
|
4
4
|
"description": "Portable Astrale invocation, publication, installation, routing, and issuer protocols",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -75,6 +75,10 @@
|
|
|
75
75
|
"types": "./dist/publication/requirements.d.ts",
|
|
76
76
|
"import": "./dist/publication/requirements.js"
|
|
77
77
|
},
|
|
78
|
+
"./publication/legacy-requirements": {
|
|
79
|
+
"types": "./dist/publication/legacy-requirements.d.ts",
|
|
80
|
+
"import": "./dist/publication/legacy-requirements.js"
|
|
81
|
+
},
|
|
78
82
|
"./publication/delivery": {
|
|
79
83
|
"types": "./dist/publication/delivery/index.d.ts",
|
|
80
84
|
"import": "./dist/publication/delivery/index.js"
|