@astrale-os/kernel-server 0.5.0-beta.69 → 0.5.0-beta.70
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/cors/cors.js +9 -3
- package/dist/index.d.ts +8 -3
- package/dist/index.js +7 -2
- package/dist/invocation/index.d.ts +1 -1
- package/dist/invocation/index.js +1 -1
- package/dist/invocation/legacy/index.d.ts +20 -0
- package/dist/invocation/legacy/index.js +20 -0
- package/dist/invocation/{publication-route.d.ts → legacy/publication-route.d.ts} +6 -1
- package/dist/invocation/{publication-route.js → legacy/publication-route.js} +5 -1
- package/dist/invocation/release-route.d.ts +26 -0
- package/dist/invocation/release-route.js +55 -0
- package/dist/invocation/types.d.ts +16 -4
- package/dist/issuer/routes.js +13 -0
- package/dist/issuer/types.d.ts +5 -0
- package/dist/release/delivery/handler.d.ts +13 -0
- package/dist/{publication → release}/delivery/handler.js +33 -10
- package/dist/release/delivery/types.d.ts +21 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +2 -0
- package/dist/release/legacy/delivery.d.ts +18 -0
- package/dist/release/legacy/delivery.js +2 -0
- package/dist/release/legacy/index.d.ts +39 -0
- package/dist/release/legacy/index.js +38 -0
- package/dist/{publication → release/legacy}/snapshot.d.ts +14 -4
- package/dist/{publication → release/legacy}/snapshot.js +11 -2
- package/dist/release/legacy/table.d.ts +5 -0
- package/dist/{publication → release/legacy}/table.js +2 -2
- package/dist/{publication → release/legacy}/types.d.ts +13 -4
- package/dist/release/served.d.ts +30 -0
- package/dist/release/served.js +14 -0
- package/dist/release/snapshot.d.ts +26 -0
- package/dist/release/snapshot.js +54 -0
- package/dist/release/table.d.ts +4 -0
- package/dist/release/table.js +11 -0
- package/dist/release/types.d.ts +33 -0
- package/dist/release/types.js +1 -0
- package/dist/routes/index.d.ts +1 -1
- package/dist/routes/index.js +1 -1
- package/dist/routes/routes.d.ts +9 -6
- package/dist/routes/routes.js +135 -76
- package/dist/server.js +66 -45
- package/dist/transport/codecs.d.ts +2 -2
- package/dist/transport/codecs.js +4 -3
- package/dist/transport/http/handler.d.ts +3 -2
- package/dist/transport/http/handler.js +18 -15
- package/dist/transport/http/render.d.ts +4 -0
- package/dist/transport/http/render.js +11 -3
- package/dist/transport/websocket/session.d.ts +3 -2
- package/dist/transport/websocket/session.js +15 -15
- package/dist/transport/websocket/upgrade.d.ts +2 -2
- package/dist/transport/websocket/upgrade.js +2 -4
- package/dist/types.d.ts +19 -4
- package/package.json +7 -8
- package/dist/publication/delivery/handler.d.ts +0 -9
- package/dist/publication/delivery/types.d.ts +0 -11
- package/dist/publication/index.d.ts +0 -3
- package/dist/publication/index.js +0 -2
- package/dist/publication/table.d.ts +0 -4
- /package/dist/{publication → release}/delivery/index.d.ts +0 -0
- /package/dist/{publication → release}/delivery/index.js +0 -0
- /package/dist/{publication → release}/delivery/types.js +0 -0
- /package/dist/{publication → release/legacy}/types.js +0 -0
package/dist/cors/cors.js
CHANGED
|
@@ -62,12 +62,18 @@ export function prepareCors(input) {
|
|
|
62
62
|
return new Response(null, { status: 403, headers: response(request) });
|
|
63
63
|
}
|
|
64
64
|
const allowedMethods = [...routeMethods].filter((method) => methods?.has(method) ?? true);
|
|
65
|
-
|
|
65
|
+
// Advertise the whole header allowance, not an echo of the request: Chromium and WebKit keep
|
|
66
|
+
// one preflight result per origin and URL, so an echo would preflight again whenever a later
|
|
67
|
+
// call adds a permitted header. Requested names, already admitted, add only what a
|
|
68
|
+
// wildcard-header route cannot enumerate.
|
|
69
|
+
const allowedHeaders = new Set([...routeHeaders].filter((name) => headers?.has(name) ?? true));
|
|
70
|
+
for (const name of requestedHeaders)
|
|
71
|
+
allowedHeaders.add(name);
|
|
66
72
|
const output = new Headers(response(request));
|
|
67
73
|
output.set('access-control-allow-methods', [...allowedMethods, 'OPTIONS'].join(', '));
|
|
68
74
|
output.set('access-control-max-age', String(PREFLIGHT_MAX_AGE_SECONDS));
|
|
69
|
-
if (allowedHeaders.
|
|
70
|
-
output.set('access-control-allow-headers', allowedHeaders.join(', '));
|
|
75
|
+
if (allowedHeaders.size > 0) {
|
|
76
|
+
output.set('access-control-allow-headers', [...allowedHeaders].join(', '));
|
|
71
77
|
}
|
|
72
78
|
appendVary(output, 'access-control-request-method');
|
|
73
79
|
appendVary(output, 'access-control-request-headers');
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
export { createServer, prepareServer } from './server.js';
|
|
2
2
|
export { DeliveryUnavailableError, IssuerKeysUnavailableError } from './errors/index.js';
|
|
3
|
-
export {
|
|
4
|
-
export
|
|
5
|
-
export
|
|
3
|
+
export { releaseProjectionRouteDecision, releaseRouteDecision, type RouteDecision, } from './invocation/index.js';
|
|
4
|
+
export { prepareReleaseSnapshot } from './release/index.js';
|
|
5
|
+
export * as release from './release/index.js';
|
|
6
|
+
/** @deprecated Successor: `prepareReleaseSnapshot`; serves v3-input destinations only. */
|
|
7
|
+
export { preparePublicationSnapshot } from './release/legacy/index.js';
|
|
8
|
+
/** @deprecated Successor: `release`; the Publication snapshot of a v3-input destination. */
|
|
9
|
+
export * as publication from './release/legacy/index.js';
|
|
10
|
+
export type { Application, ApplicationHost, CorsConfig, ExecutionControl, Handler, HandlerRequest, HandlerResult, HandlerStream, HealthProbe, InvocationContext, InvocationSource, RequestBackground, RequestEvidence, RequestSleep, Issuer, exchange, PreparedServer, PublicationSnapshot, PublicationSnapshotInput, PublicationSource, ReleaseSnapshot, ReleaseSnapshotInput, ReleaseSource, Server, ServerCloseOptions, ServerConfig, ServerLimits, ServerOptions, WebSocketAdapter, WebSocketHandlers, WebSocketLike, WebSocketUpgradeOptions, } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
export { createServer, prepareServer } from './server.js';
|
|
2
2
|
export { DeliveryUnavailableError, IssuerKeysUnavailableError } from './errors/index.js';
|
|
3
|
-
export {
|
|
4
|
-
export
|
|
3
|
+
export { releaseProjectionRouteDecision, releaseRouteDecision, } from './invocation/index.js';
|
|
4
|
+
export { prepareReleaseSnapshot } from './release/index.js';
|
|
5
|
+
export * as release from './release/index.js';
|
|
6
|
+
/** @deprecated Successor: `prepareReleaseSnapshot`; serves v3-input destinations only. */
|
|
7
|
+
export { preparePublicationSnapshot } from './release/legacy/index.js';
|
|
8
|
+
/** @deprecated Successor: `release`; the Publication snapshot of a v3-input destination. */
|
|
9
|
+
export * as publication from './release/legacy/index.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { defaultSleep, invocationContext } from './context.js';
|
|
2
2
|
export { captureInvocationSource, type InvocationSource, type NextInvocationId, } from './identity.js';
|
|
3
|
-
export {
|
|
3
|
+
export { admitVia, releaseProjectionRouteDecision, releaseRouteDecision, type RouteDecision, } from './release-route.js';
|
|
4
4
|
export type { ApplicationHost, ExecutionControl, Handler, HandlerRequest, HandlerResult, HandlerStream, InvocationContext, RequestBackground, RequestEvidence, RequestSleep, } from './types.js';
|
package/dist/invocation/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { defaultSleep, invocationContext } from './context.js';
|
|
2
2
|
export { captureInvocationSource, } from './identity.js';
|
|
3
|
-
export {
|
|
3
|
+
export { admitVia, releaseProjectionRouteDecision, releaseRouteDecision, } from './release-route.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routed admission at a v3-input destination: #508 same-schema and schema-drift continuity
|
|
3
|
+
* (`publicationRouteDecision`) admits a `PublicationVia` whose ETag or Schema differs from the
|
|
4
|
+
* served Publication while origin and issuer stay equal, so a legacy stable Worker keeps serving
|
|
5
|
+
* the installations made before the release snapshot with its latest code (Routage [.57929]). It
|
|
6
|
+
* applies only to a Server built from a v2/v3 Publication, never to a release snapshot.
|
|
7
|
+
*
|
|
8
|
+
* @deprecated Use `releaseRouteDecision` and `releaseProjectionRouteDecision`
|
|
9
|
+
* (`../release-route.ts`), which admit only the exact served release or its exact projection and
|
|
10
|
+
* answer every other relation with DEPLOYMENT_RETIRED.
|
|
11
|
+
*
|
|
12
|
+
* Short-term consumers: the v3-input destinations of `server/release/legacy/` (legacy direct-mode
|
|
13
|
+
* Workers, SDK Workers without a bound build digest, the Host's intrinsic publishers).
|
|
14
|
+
*
|
|
15
|
+
* Removal, in the separate breaking release that deletes `server/release/legacy/`, under its
|
|
16
|
+
* condition. Then delete this folder, its laws (SV-PUBLICATION-CONTINUITY,
|
|
17
|
+
* SV-PUBLICATION-ADMITS-LEGACY-VIA) and the `publication` branch of `admitVia`
|
|
18
|
+
* (`../release-route.ts`).
|
|
19
|
+
*/
|
|
20
|
+
export { publicationRouteDecision, type PublicationRouteDecision } from './publication-route.js';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routed admission at a v3-input destination: #508 same-schema and schema-drift continuity
|
|
3
|
+
* (`publicationRouteDecision`) admits a `PublicationVia` whose ETag or Schema differs from the
|
|
4
|
+
* served Publication while origin and issuer stay equal, so a legacy stable Worker keeps serving
|
|
5
|
+
* the installations made before the release snapshot with its latest code (Routage [.57929]). It
|
|
6
|
+
* applies only to a Server built from a v2/v3 Publication, never to a release snapshot.
|
|
7
|
+
*
|
|
8
|
+
* @deprecated Use `releaseRouteDecision` and `releaseProjectionRouteDecision`
|
|
9
|
+
* (`../release-route.ts`), which admit only the exact served release or its exact projection and
|
|
10
|
+
* answer every other relation with DEPLOYMENT_RETIRED.
|
|
11
|
+
*
|
|
12
|
+
* Short-term consumers: the v3-input destinations of `server/release/legacy/` (legacy direct-mode
|
|
13
|
+
* Workers, SDK Workers without a bound build digest, the Host's intrinsic publishers).
|
|
14
|
+
*
|
|
15
|
+
* Removal, in the separate breaking release that deletes `server/release/legacy/`, under its
|
|
16
|
+
* condition. Then delete this folder, its laws (SV-PUBLICATION-CONTINUITY,
|
|
17
|
+
* SV-PUBLICATION-ADMITS-LEGACY-VIA) and the `publication` branch of `admitVia`
|
|
18
|
+
* (`../release-route.ts`).
|
|
19
|
+
*/
|
|
20
|
+
export { publicationRouteDecision } from './publication-route.js';
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { ErrorPayload } from '@astrale-os/kernel-protocol/errors';
|
|
2
2
|
import type { Publication } from '@astrale-os/kernel-protocol/publication/publication';
|
|
3
3
|
import type { PublicationRef } from '@astrale-os/kernel-protocol/publication/reference';
|
|
4
|
+
/** @deprecated Successor: `RouteDecision`, which also answers a retired deployment. */
|
|
4
5
|
export type PublicationRouteDecision = {
|
|
5
6
|
readonly kind: 'admitted';
|
|
6
7
|
} | {
|
|
7
8
|
readonly kind: 'rejected';
|
|
8
9
|
readonly error: ErrorPayload;
|
|
9
10
|
};
|
|
10
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Accept ETag or Schema changes only while origin and issuer remain equal.
|
|
13
|
+
*
|
|
14
|
+
* @deprecated Use `releaseRouteDecision` or `releaseProjectionRouteDecision` for exact release admission.
|
|
15
|
+
*/
|
|
11
16
|
export declare function publicationRouteDecision(current: Publication, installed: PublicationRef): PublicationRouteDecision;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { CODES } from '@astrale-os/kernel-protocol/errors';
|
|
2
2
|
import { SERVICE_TRUST_REQUIRED } from '@astrale-os/kernel-protocol/invocation/service-reason';
|
|
3
3
|
import { compareReference } from '@astrale-os/kernel-protocol/publication/reference';
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Accept ETag or Schema changes only while origin and issuer remain equal.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated Use `releaseRouteDecision` or `releaseProjectionRouteDecision` for exact release admission.
|
|
8
|
+
*/
|
|
5
9
|
export function publicationRouteDecision(current, installed) {
|
|
6
10
|
switch (compareReference(current, installed)) {
|
|
7
11
|
case 'exact':
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ErrorPayload } from '@astrale-os/kernel-protocol/errors';
|
|
2
|
+
import type { PublicationVia, Via } from '@astrale-os/kernel-protocol/invocation';
|
|
3
|
+
import type { DomainRelease, ReleaseDigest, ReleaseRef } from '@astrale-os/kernel-protocol/release';
|
|
4
|
+
import type { PublicationRef } from '@astrale-os/kernel-protocol/release/legacy';
|
|
5
|
+
import type { Served } from '../release/served.js';
|
|
6
|
+
import type { ReleaseSnapshot } from '../release/types.js';
|
|
7
|
+
export type RouteDecision = {
|
|
8
|
+
readonly kind: 'admitted';
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: 'rejected';
|
|
11
|
+
readonly error: ErrorPayload;
|
|
12
|
+
}
|
|
13
|
+
/** DEPLOYMENT_RETIRED includes the requested release only when the witness identifies it. */
|
|
14
|
+
| {
|
|
15
|
+
readonly kind: 'retired';
|
|
16
|
+
readonly release?: ReleaseDigest;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Only the exact served release admits. Never report the served digest as retired when
|
|
20
|
+
* the witness pairs it with a different revision or issuer.
|
|
21
|
+
*/
|
|
22
|
+
export declare function releaseRouteDecision(current: DomainRelease, installed: ReleaseRef): RouteDecision;
|
|
23
|
+
/** Only the exact v3 projection reference admits, including its ETag, Schema and issuer. */
|
|
24
|
+
export declare function releaseProjectionRouteDecision(current: ReleaseSnapshot, installed: PublicationRef): RouteDecision;
|
|
25
|
+
/** Admit routing before credential resolution; Publication sources accept only legacy witnesses. */
|
|
26
|
+
export declare function admitVia(served: Served | undefined, via: Via | PublicationVia): RouteDecision;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { CODES } from '@astrale-os/kernel-protocol/errors';
|
|
2
|
+
import { compareReferences, reference } from '@astrale-os/kernel-protocol/release';
|
|
3
|
+
import { compareReference } from '@astrale-os/kernel-protocol/release/legacy';
|
|
4
|
+
import { publicationRouteDecision } from './legacy/index.js';
|
|
5
|
+
const ADMITTED = Object.freeze({ kind: 'admitted' });
|
|
6
|
+
const RETIRED = Object.freeze({ kind: 'retired' });
|
|
7
|
+
/**
|
|
8
|
+
* Only the exact served release admits. Never report the served digest as retired when
|
|
9
|
+
* the witness pairs it with a different revision or issuer.
|
|
10
|
+
*/
|
|
11
|
+
export function releaseRouteDecision(current, installed) {
|
|
12
|
+
switch (compareReferences(reference(current), installed)) {
|
|
13
|
+
case 'exact':
|
|
14
|
+
return ADMITTED;
|
|
15
|
+
case 'release-drift':
|
|
16
|
+
return installed.digest === current.digest
|
|
17
|
+
? RETIRED
|
|
18
|
+
: Object.freeze({ kind: 'retired', release: installed.digest });
|
|
19
|
+
case 'origin-drift':
|
|
20
|
+
return invalid('Routed release origin differs from the served Domain.');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Only the exact v3 projection reference admits, including its ETag, Schema and issuer. */
|
|
24
|
+
export function releaseProjectionRouteDecision(current, installed) {
|
|
25
|
+
switch (compareReference(current.publication, installed)) {
|
|
26
|
+
case 'exact':
|
|
27
|
+
return ADMITTED;
|
|
28
|
+
case 'same-schema':
|
|
29
|
+
case 'schema-drift':
|
|
30
|
+
case 'identity-drift':
|
|
31
|
+
return RETIRED;
|
|
32
|
+
case 'origin-drift':
|
|
33
|
+
return invalid('Routed Publication origin differs from the served Domain.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Admit routing before credential resolution; Publication sources accept only legacy witnesses. */
|
|
37
|
+
export function admitVia(served, via) {
|
|
38
|
+
if ('release' in via) {
|
|
39
|
+
return served?.kind === 'release'
|
|
40
|
+
? releaseRouteDecision(served.release, via.release)
|
|
41
|
+
: invalid('Release routing requires a release-backed destination.');
|
|
42
|
+
}
|
|
43
|
+
if (served === undefined) {
|
|
44
|
+
return invalid('Publication routing requires a Publication-backed destination.');
|
|
45
|
+
}
|
|
46
|
+
return served.kind === 'release'
|
|
47
|
+
? releaseProjectionRouteDecision(served.snapshot, via.publication)
|
|
48
|
+
: publicationRouteDecision(served.publication, via.publication);
|
|
49
|
+
}
|
|
50
|
+
function invalid(message) {
|
|
51
|
+
return Object.freeze({
|
|
52
|
+
kind: 'rejected',
|
|
53
|
+
error: Object.freeze({ code: CODES.INVALID_REQUEST, message }),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
@@ -2,7 +2,8 @@ import type { CredentialInput } from '@astrale-os/kernel-core/auth';
|
|
|
2
2
|
import type { BinaryRequestBody, CallableResult } from '@astrale-os/kernel-core/callable';
|
|
3
3
|
import type { Value } from '@astrale-os/kernel-dsl/value';
|
|
4
4
|
import type * as protocol from '@astrale-os/kernel-protocol/invocation';
|
|
5
|
-
import type {
|
|
5
|
+
import type { ReleaseSnapshot } from '../release/index.js';
|
|
6
|
+
import type { PublicationSnapshot } from '../release/legacy/index.js';
|
|
6
7
|
/** Optional platform retention and timer adaptation for one Fetch invocation. */
|
|
7
8
|
export interface ApplicationHost {
|
|
8
9
|
waitUntil(promise: Promise<unknown>): void;
|
|
@@ -51,11 +52,22 @@ export interface HandlerRequest {
|
|
|
51
52
|
readonly credential?: CredentialInput;
|
|
52
53
|
/** Source-only effective delegation forwarded unchanged to Runtime. */
|
|
53
54
|
readonly delegate?: protocol.Delegate;
|
|
54
|
-
/**
|
|
55
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Destination routing witness forwarded after Server admission: at a release-backed destination
|
|
57
|
+
* a `Via` naming its exact release or a `PublicationVia` naming its exact v3 projection; at a
|
|
58
|
+
* v3-input destination only a `PublicationVia`.
|
|
59
|
+
*/
|
|
60
|
+
readonly via?: protocol.Via | protocol.PublicationVia;
|
|
56
61
|
/** Source-only local-target or remote-revision Schema Registration precondition. */
|
|
57
62
|
readonly schema?: protocol.SchemaTarget;
|
|
58
|
-
/** Exact Server-pinned
|
|
63
|
+
/** Exact Server-pinned release for a request at a release-backed destination. */
|
|
64
|
+
readonly release?: ReleaseSnapshot;
|
|
65
|
+
/**
|
|
66
|
+
* Exact Server-pinned Publication snapshot for a request at a v3-input destination.
|
|
67
|
+
*
|
|
68
|
+
* @deprecated Successor: `release`. Present only on a Server built from a v2/v3 Publication;
|
|
69
|
+
* deleted with `server/release/legacy/`.
|
|
70
|
+
*/
|
|
59
71
|
readonly publication?: PublicationSnapshot;
|
|
60
72
|
readonly context: InvocationContext;
|
|
61
73
|
}
|
package/dist/issuer/routes.js
CHANGED
|
@@ -2,6 +2,7 @@ import { configuration } from '@astrale-os/kernel-protocol/issuer/configuration'
|
|
|
2
2
|
import { hosted } from '@astrale-os/kernel-protocol/issuer/hosted';
|
|
3
3
|
import { keySet } from '@astrale-os/kernel-protocol/issuer/key-set';
|
|
4
4
|
import { paths as issuerPaths } from '@astrale-os/kernel-protocol/issuer/paths';
|
|
5
|
+
import { workflowEngine } from '@astrale-os/kernel-protocol/issuer/workflow-engine';
|
|
5
6
|
import { admissionRefusal, IssuerKeysUnavailableError, retryAfterHeaders } from '../errors/index.js';
|
|
6
7
|
import { mount } from './exchange.js';
|
|
7
8
|
export function issuerRouteReservations(input, tokenExchange = false) {
|
|
@@ -13,6 +14,9 @@ export function issuerRouteReservations(input, tokenExchange = false) {
|
|
|
13
14
|
if (tokenExchange) {
|
|
14
15
|
reservations.push(Object.freeze({ path: paths.tokenExchange, methods: Object.freeze(['POST']) }));
|
|
15
16
|
}
|
|
17
|
+
if (input.workflowEngineKeys !== undefined) {
|
|
18
|
+
reservations.push(Object.freeze({ path: paths.workflowEngine, methods: Object.freeze(['GET']) }));
|
|
19
|
+
}
|
|
16
20
|
if (input.loadIssuerPublicKeys !== undefined) {
|
|
17
21
|
const base = new URL(input.issuer).pathname.replace(/\/+$/u, '');
|
|
18
22
|
reservations.push(Object.freeze({
|
|
@@ -50,6 +54,10 @@ export function mountIssuer(app, input, cors, active, timeoutMs, tokenExchange)
|
|
|
50
54
|
tokenExchange: tokenExchange !== undefined,
|
|
51
55
|
});
|
|
52
56
|
app.get(paths.configuration, (context) => context.json(discovery, 200, headers(context.req.raw)));
|
|
57
|
+
if (input.workflowEngineKeys !== undefined) {
|
|
58
|
+
const engine = workflowEngine(issuer, input.workflowEngineKeys);
|
|
59
|
+
app.get(paths.workflowEngine, (context) => context.json(engine, 200, headers(context.req.raw)));
|
|
60
|
+
}
|
|
53
61
|
if (cors.enabled) {
|
|
54
62
|
app.options(paths.configuration, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
|
|
55
63
|
app.options(paths.keys, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
|
|
@@ -152,9 +160,14 @@ export function admitIssuer(input) {
|
|
|
152
160
|
}
|
|
153
161
|
const loadPublicKeys = input.loadPublicKeys;
|
|
154
162
|
const loadIssuerPublicKeys = input.loadIssuerPublicKeys;
|
|
163
|
+
// A Kernel that drives an engine publishes it; invalid keys fail closed at boot.
|
|
164
|
+
const workflowEngineKeys = input.workflowEngineKeys === undefined
|
|
165
|
+
? undefined
|
|
166
|
+
: workflowEngine(issuer, input.workflowEngineKeys).identity_keys;
|
|
155
167
|
return Object.freeze({
|
|
156
168
|
issuer,
|
|
157
169
|
signingAlgorithm,
|
|
170
|
+
...(workflowEngineKeys === undefined ? {} : { workflowEngineKeys }),
|
|
158
171
|
loadPublicKeys: (signal) => loadPublicKeys.call(input, signal),
|
|
159
172
|
...(loadIssuerPublicKeys === undefined
|
|
160
173
|
? {}
|
package/dist/issuer/types.d.ts
CHANGED
|
@@ -8,4 +8,9 @@ export interface Issuer {
|
|
|
8
8
|
loadPublicKeys(signal: AbortSignal): Promise<readonly JsonWebKey[]>;
|
|
9
9
|
/** Optional hosted-key discovery for issuers below `<issuer>/iss/:thumbprint`. */
|
|
10
10
|
readonly loadIssuerPublicKeys?: (issuer: IssuerId, signal: AbortSignal) => Promise<readonly JsonWebKey[]>;
|
|
11
|
+
/**
|
|
12
|
+
* Request identity keys of the workflow engine this Kernel drives, published at
|
|
13
|
+
* `<issuer>/.well-known/astrale/workflow-engine.json` so Domains verify the engine's requests.
|
|
14
|
+
*/
|
|
15
|
+
readonly workflowEngineKeys?: readonly string[];
|
|
11
16
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ActiveOperations } from '../../lifecycle/index.js';
|
|
2
|
+
import type { Served } from '../served.js';
|
|
3
|
+
import type { Provider } from './types.js';
|
|
4
|
+
export interface Limits {
|
|
5
|
+
readonly maximumDeliveryBytes: number;
|
|
6
|
+
readonly invocationTimeoutMs: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Serve one delivery request against the exact snapshot that selected its URL, in the request's
|
|
10
|
+
* version: Delivery v2 names the served release, Delivery v1 the served Publication (a release's
|
|
11
|
+
* frozen v3 projection). A v3-input destination keeps its Delivery v1 decoder.
|
|
12
|
+
*/
|
|
13
|
+
export declare function handleDelivery(request: Request, snapshot: Served, provider: Provider | undefined, active: ActiveOperations, limits: Limits, corsFor: (exposedHeaders?: readonly string[]) => Readonly<Record<string, string>>): Promise<Response>;
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
|
-
import * as
|
|
2
|
+
import * as release from '@astrale-os/kernel-protocol/release';
|
|
3
3
|
import { admissionRefusal, DeliveryUnavailableError, retryAfterHeaders, } from '../../errors/index.js';
|
|
4
4
|
import { RequestBodyInvalidError, RequestBodyTooLargeError } from '../../errors/index.js';
|
|
5
5
|
import { readBoundedBody } from '../../transport/index.js';
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
import { servedDocument } from '../served.js';
|
|
7
|
+
const delivery = release.delivery;
|
|
8
|
+
const publicationDelivery = release.legacy.delivery;
|
|
9
|
+
/**
|
|
10
|
+
* Serve one delivery request against the exact snapshot that selected its URL, in the request's
|
|
11
|
+
* version: Delivery v2 names the served release, Delivery v1 the served Publication (a release's
|
|
12
|
+
* frozen v3 projection). A v3-input destination keeps its Delivery v1 decoder.
|
|
13
|
+
*/
|
|
8
14
|
export async function handleDelivery(request, snapshot, provider, active, limits, corsFor) {
|
|
9
15
|
const cors = corsFor();
|
|
10
16
|
if (request.method !== 'POST') {
|
|
@@ -13,7 +19,7 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
13
19
|
if (provider === undefined) {
|
|
14
20
|
return response({ error: 'Delivery is unavailable.' }, 503, cors);
|
|
15
21
|
}
|
|
16
|
-
const advertised = snapshot.
|
|
22
|
+
const advertised = servedDocument(snapshot).delivery;
|
|
17
23
|
if (advertised === undefined) {
|
|
18
24
|
return response({ error: 'Delivery is unavailable.' }, 404, cors);
|
|
19
25
|
}
|
|
@@ -60,7 +66,10 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
60
66
|
}
|
|
61
67
|
let input;
|
|
62
68
|
try {
|
|
63
|
-
input =
|
|
69
|
+
input =
|
|
70
|
+
snapshot.kind === 'release'
|
|
71
|
+
? delivery.acceptRequest(decoded)
|
|
72
|
+
: publicationDelivery.acceptRequest(decoded);
|
|
64
73
|
}
|
|
65
74
|
catch (cause) {
|
|
66
75
|
if (cause instanceof delivery.Error && cause.code === 'DELIVERY_REQUEST_INVALID') {
|
|
@@ -68,8 +77,13 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
68
77
|
}
|
|
69
78
|
throw cause;
|
|
70
79
|
}
|
|
71
|
-
|
|
72
|
-
|
|
80
|
+
const named = input.version === 2 ? input.release : input.etag;
|
|
81
|
+
const served = input.version === 2 ? servedRelease(snapshot) : snapshot.publication.etag;
|
|
82
|
+
if (named !== served) {
|
|
83
|
+
const error = input.version === 2
|
|
84
|
+
? 'Release digest differs from request.'
|
|
85
|
+
: 'Publication ETag differs from request.';
|
|
86
|
+
return response({ error }, 409, cors);
|
|
73
87
|
}
|
|
74
88
|
let candidate;
|
|
75
89
|
try {
|
|
@@ -86,7 +100,8 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
86
100
|
}
|
|
87
101
|
let created;
|
|
88
102
|
try {
|
|
89
|
-
created =
|
|
103
|
+
created =
|
|
104
|
+
input.version === 2 ? delivery.accept(candidate) : publicationDelivery.accept(candidate);
|
|
90
105
|
}
|
|
91
106
|
catch (cause) {
|
|
92
107
|
if (cause instanceof delivery.Error) {
|
|
@@ -94,8 +109,12 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
94
109
|
}
|
|
95
110
|
throw cause;
|
|
96
111
|
}
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
const delivered = created.version === 2 ? created.release.digest : created.publication.etag;
|
|
113
|
+
if (delivered !== served) {
|
|
114
|
+
const error = created.version === 2
|
|
115
|
+
? 'Delivery provider returned a different release.'
|
|
116
|
+
: 'Delivery provider returned a different Publication.';
|
|
117
|
+
return response({ error }, 500, cors);
|
|
99
118
|
}
|
|
100
119
|
const encoded = codecs.json.encode(created);
|
|
101
120
|
if (encodedSize(encoded) > limits.maximumDeliveryBytes) {
|
|
@@ -109,6 +128,10 @@ export async function handleDelivery(request, snapshot, provider, active, limits
|
|
|
109
128
|
operation.settle();
|
|
110
129
|
}
|
|
111
130
|
}
|
|
131
|
+
/** Digest a Delivery v2 must name: only a release-backed destination serves one. */
|
|
132
|
+
function servedRelease(snapshot) {
|
|
133
|
+
return snapshot.kind === 'release' ? snapshot.release.digest : undefined;
|
|
134
|
+
}
|
|
112
135
|
function bearer(input) {
|
|
113
136
|
if (input === null)
|
|
114
137
|
return undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as release from '@astrale-os/kernel-protocol/release';
|
|
2
|
+
export interface Context {
|
|
3
|
+
/** Omitted for a public endpoint; otherwise the exact admitted retrieval credential. */
|
|
4
|
+
readonly token?: release.delivery.Token;
|
|
5
|
+
readonly signal: AbortSignal;
|
|
6
|
+
}
|
|
7
|
+
/** Provider-owned retrieval, construction, and attestation signing. */
|
|
8
|
+
export interface Provider {
|
|
9
|
+
/**
|
|
10
|
+
* Answer in the request's version: a Delivery v2 carrying the served release for a v2 request,
|
|
11
|
+
* and a Delivery v1 carrying the served Publication (at a release-backed destination, its frozen
|
|
12
|
+
* v3 projection) for a deprecated v1 request. A v3-input destination passes only v1 requests.
|
|
13
|
+
* Throw DeliveryUnavailableError only for an expected dependency outage.
|
|
14
|
+
*
|
|
15
|
+
* A release-backed destination (`ServerOptions.release`) passes v2 requests, so its provider
|
|
16
|
+
* must answer both versions. The deprecated v1-only `publication.delivery.Provider` still
|
|
17
|
+
* type-checks here, because method parameters are compared bivariantly, but every v2 request
|
|
18
|
+
* it receives fails with 500 `Delivery provider returned an invalid response.`
|
|
19
|
+
*/
|
|
20
|
+
create(request: release.delivery.Request | release.legacy.delivery.Request, context: Context): Promise<release.delivery.Delivery | release.legacy.delivery.Delivery>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type * as release from '@astrale-os/kernel-protocol/release';
|
|
2
|
+
import type { Context as DeliveryContext } from '../delivery/types.js';
|
|
3
|
+
/** @deprecated Successor: `release.delivery.Context`, the same retrieval context. */
|
|
4
|
+
export type Context = DeliveryContext;
|
|
5
|
+
/**
|
|
6
|
+
* The Delivery v1 provider of a v3-input destination, which only ever receives v1 requests. It
|
|
7
|
+
* remains assignable to `release.delivery.Provider`, so existing providers keep configuring a
|
|
8
|
+
* Server unchanged. Pass it only with `ServerOptions.publication`: a release-backed destination
|
|
9
|
+
* also passes Delivery v2 requests, which this provider cannot answer.
|
|
10
|
+
*
|
|
11
|
+
* @deprecated Successor: `release.delivery.Provider`, which answers a v2 request over the release
|
|
12
|
+
* and a v1 request over its frozen v3 projection.
|
|
13
|
+
*/
|
|
14
|
+
export interface Provider {
|
|
15
|
+
create(request: release.legacy.delivery.Request, context: Context): Promise<release.legacy.delivery.Delivery>;
|
|
16
|
+
}
|
|
17
|
+
/** @deprecated Successor: `release.delivery.handleDelivery`, which serves both snapshots. */
|
|
18
|
+
export { handleDelivery, type Limits } from '../delivery/handler.js';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Publication snapshot of a v3-input destination: a Server built from a v2/v3 Publication
|
|
3
|
+
* serves `domain.json`, its bundle, Delivery v1 and its native routes exactly as before the release
|
|
4
|
+
* snapshot, and keeps the #508 same-schema and schema-drift continuity
|
|
5
|
+
* (`server/invocation/legacy/publication-route.ts`). Its answers are pinned byte for byte by the
|
|
6
|
+
* golden transcript of `server/__tests__/transcripts.test.ts`.
|
|
7
|
+
*
|
|
8
|
+
* @deprecated Use `ServerOptions.release` with `prepareReleaseSnapshot`, which serves a
|
|
9
|
+
* `DomainRelease` at `release.json` beside its frozen v3 projection at `domain.json` and admits
|
|
10
|
+
* only exact routing.
|
|
11
|
+
*
|
|
12
|
+
* Short-term consumers:
|
|
13
|
+
* - the legacy direct-mode Workers built by an SDK that seals v2/v3 only: the domains platform
|
|
14
|
+
* Domains, Shell, the Services `development` environment and `ui:domain`, until they serve
|
|
15
|
+
* releases;
|
|
16
|
+
* - an SDK Worker whose deployment host binds no build digest, which serves today's v3 document;
|
|
17
|
+
* - the Host's intrinsic Kernel and Host Domain publishers (`host/kernel/publication/`) and the
|
|
18
|
+
* FalkorDB qualification remote Domain, until the Host serves its intrinsic release.
|
|
19
|
+
*
|
|
20
|
+
* Removal, in a separate breaking release: the conditions of `protocol/release/legacy/index.ts`
|
|
21
|
+
* hold (no stable Worker serves a v2/v3 document, no installation pins one), no supported SDK
|
|
22
|
+
* builds a v3-input Worker, and the intrinsic Kernel and Host Domains serve a release. Then
|
|
23
|
+
* delete, in the same PR:
|
|
24
|
+
* - this folder with its `.spec` (its v1-only Delivery `Provider` included), the `publication`
|
|
25
|
+
* namespace, `preparePublicationSnapshot` and the Publication snapshot types of
|
|
26
|
+
* `server/index.ts` and `server/.spec/api.d.ts`, and the `./publication/snapshot` subpath
|
|
27
|
+
* (source and `dist`) of `server/package.json`;
|
|
28
|
+
* - `ServerOptions.publication` (`server/types.ts`) and `HandlerRequest.publication`
|
|
29
|
+
* (`server/invocation/types.ts`), with the `publication` branch of `Served`
|
|
30
|
+
* (`server/release/served.ts`) and every `kind === 'publication'` branch it selects in
|
|
31
|
+
* `server/routes/routes.ts`, `server/release/delivery/handler.ts` and `admitVia`;
|
|
32
|
+
* - `server/invocation/legacy/` (laws SV-PUBLICATION-CONTINUITY, SV-PUBLICATION-ADMITS-LEGACY-VIA);
|
|
33
|
+
* - the golden transcript that records a v3-input server, keeping the one of a server without a
|
|
34
|
+
* source.
|
|
35
|
+
*/
|
|
36
|
+
export { preparePublicationSnapshot, readPreparedPublicationSnapshot, type PreparedPublicationSnapshot, } from './snapshot.js';
|
|
37
|
+
export type { PublicationSnapshot, PublicationSnapshotInput, PublicationSource } from './types.js';
|
|
38
|
+
/** @deprecated The Delivery v1 boundary of a v3-input destination; use `release.delivery`. */
|
|
39
|
+
export * as delivery from './delivery.js';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Publication snapshot of a v3-input destination: a Server built from a v2/v3 Publication
|
|
3
|
+
* serves `domain.json`, its bundle, Delivery v1 and its native routes exactly as before the release
|
|
4
|
+
* snapshot, and keeps the #508 same-schema and schema-drift continuity
|
|
5
|
+
* (`server/invocation/legacy/publication-route.ts`). Its answers are pinned byte for byte by the
|
|
6
|
+
* golden transcript of `server/__tests__/transcripts.test.ts`.
|
|
7
|
+
*
|
|
8
|
+
* @deprecated Use `ServerOptions.release` with `prepareReleaseSnapshot`, which serves a
|
|
9
|
+
* `DomainRelease` at `release.json` beside its frozen v3 projection at `domain.json` and admits
|
|
10
|
+
* only exact routing.
|
|
11
|
+
*
|
|
12
|
+
* Short-term consumers:
|
|
13
|
+
* - the legacy direct-mode Workers built by an SDK that seals v2/v3 only: the domains platform
|
|
14
|
+
* Domains, Shell, the Services `development` environment and `ui:domain`, until they serve
|
|
15
|
+
* releases;
|
|
16
|
+
* - an SDK Worker whose deployment host binds no build digest, which serves today's v3 document;
|
|
17
|
+
* - the Host's intrinsic Kernel and Host Domain publishers (`host/kernel/publication/`) and the
|
|
18
|
+
* FalkorDB qualification remote Domain, until the Host serves its intrinsic release.
|
|
19
|
+
*
|
|
20
|
+
* Removal, in a separate breaking release: the conditions of `protocol/release/legacy/index.ts`
|
|
21
|
+
* hold (no stable Worker serves a v2/v3 document, no installation pins one), no supported SDK
|
|
22
|
+
* builds a v3-input Worker, and the intrinsic Kernel and Host Domains serve a release. Then
|
|
23
|
+
* delete, in the same PR:
|
|
24
|
+
* - this folder with its `.spec` (its v1-only Delivery `Provider` included), the `publication`
|
|
25
|
+
* namespace, `preparePublicationSnapshot` and the Publication snapshot types of
|
|
26
|
+
* `server/index.ts` and `server/.spec/api.d.ts`, and the `./publication/snapshot` subpath
|
|
27
|
+
* (source and `dist`) of `server/package.json`;
|
|
28
|
+
* - `ServerOptions.publication` (`server/types.ts`) and `HandlerRequest.publication`
|
|
29
|
+
* (`server/invocation/types.ts`), with the `publication` branch of `Served`
|
|
30
|
+
* (`server/release/served.ts`) and every `kind === 'publication'` branch it selects in
|
|
31
|
+
* `server/routes/routes.ts`, `server/release/delivery/handler.ts` and `admitVia`;
|
|
32
|
+
* - `server/invocation/legacy/` (laws SV-PUBLICATION-CONTINUITY, SV-PUBLICATION-ADMITS-LEGACY-VIA);
|
|
33
|
+
* - the golden transcript that records a v3-input server, keeping the one of a server without a
|
|
34
|
+
* source.
|
|
35
|
+
*/
|
|
36
|
+
export { preparePublicationSnapshot, readPreparedPublicationSnapshot, } from './snapshot.js';
|
|
37
|
+
/** @deprecated The Delivery v1 boundary of a v3-input destination; use `release.delivery`. */
|
|
38
|
+
export * as delivery from './delivery.js';
|
|
@@ -1,16 +1,26 @@
|
|
|
1
1
|
import type { Domain } from '@astrale-os/kernel-dsl/v1';
|
|
2
2
|
import type * as publication from '@astrale-os/kernel-protocol/publication';
|
|
3
|
-
import type
|
|
3
|
+
import type { Table } from '@astrale-os/kernel-protocol/routes/legacy';
|
|
4
4
|
import type { PublicationSnapshot, PublicationSnapshotInput } from './types.js';
|
|
5
|
+
/** @deprecated Successor: `PreparedReleaseSnapshot`. */
|
|
5
6
|
export interface PreparedPublicationSnapshot {
|
|
7
|
+
readonly kind: 'publication';
|
|
6
8
|
readonly snapshot: PublicationSnapshot;
|
|
7
9
|
readonly publication: publication.Publication;
|
|
8
10
|
readonly publicationBytes: Uint8Array;
|
|
9
11
|
readonly bundle: Uint8Array;
|
|
10
|
-
readonly table:
|
|
12
|
+
readonly table: Table;
|
|
11
13
|
readonly domain: Domain;
|
|
12
14
|
}
|
|
13
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Admit and detach one revision-coherent serving snapshot.
|
|
17
|
+
*
|
|
18
|
+
* @deprecated Use `prepareReleaseSnapshot` to serve a release and its v3 projection.
|
|
19
|
+
*/
|
|
14
20
|
export declare function preparePublicationSnapshot(input: PublicationSnapshotInput): PublicationSnapshot;
|
|
15
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Internal proof that a PublicationSource returned a Server-prepared snapshot without cloning it.
|
|
23
|
+
*
|
|
24
|
+
* @deprecated Successor: `readPreparedReleaseSnapshot`.
|
|
25
|
+
*/
|
|
16
26
|
export declare function readPreparedPublicationSnapshot(input: unknown): PreparedPublicationSnapshot | undefined;
|
|
@@ -3,7 +3,11 @@ import { verifyBundleBytes } from '@astrale-os/kernel-protocol/publication/bundl
|
|
|
3
3
|
import { accept as acceptPublication, encode as encodePublication, } from '@astrale-os/kernel-protocol/publication/publication';
|
|
4
4
|
import { admitTable } from './table.js';
|
|
5
5
|
const snapshots = new WeakMap();
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* Admit and detach one revision-coherent serving snapshot.
|
|
8
|
+
*
|
|
9
|
+
* @deprecated Use `prepareReleaseSnapshot` to serve a release and its v3 projection.
|
|
10
|
+
*/
|
|
7
11
|
export function preparePublicationSnapshot(input) {
|
|
8
12
|
if (input === null || typeof input !== 'object' || !(input.bundle instanceof Uint8Array)) {
|
|
9
13
|
throw new TypeError('Publication snapshot input is invalid.');
|
|
@@ -28,6 +32,7 @@ export function preparePublicationSnapshot(input) {
|
|
|
28
32
|
const publicationBytes = encodePublication(admittedPublication);
|
|
29
33
|
const snapshot = Object.freeze({ publication: admittedPublication });
|
|
30
34
|
const prepared = Object.freeze({
|
|
35
|
+
kind: 'publication',
|
|
31
36
|
snapshot,
|
|
32
37
|
publication: admittedPublication,
|
|
33
38
|
publicationBytes,
|
|
@@ -38,7 +43,11 @@ export function preparePublicationSnapshot(input) {
|
|
|
38
43
|
snapshots.set(snapshot, prepared);
|
|
39
44
|
return snapshot;
|
|
40
45
|
}
|
|
41
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Internal proof that a PublicationSource returned a Server-prepared snapshot without cloning it.
|
|
48
|
+
*
|
|
49
|
+
* @deprecated Successor: `readPreparedReleaseSnapshot`.
|
|
50
|
+
*/
|
|
42
51
|
export function readPreparedPublicationSnapshot(input) {
|
|
43
52
|
if (input === null || typeof input !== 'object')
|
|
44
53
|
return undefined;
|