@astrale-os/kernel-server 0.5.0-beta.73 → 0.5.0-beta.75

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.
Files changed (84) hide show
  1. package/dist/index.d.ts +1 -6
  2. package/dist/index.js +0 -5
  3. package/dist/invocation/index.d.ts +1 -1
  4. package/dist/invocation/index.js +1 -1
  5. package/dist/invocation/release-route.d.ts +3 -7
  6. package/dist/invocation/release-route.js +4 -27
  7. package/dist/invocation/types.d.ts +2 -14
  8. package/dist/issuer/routes.js +28 -47
  9. package/dist/release/delivery/handler.d.ts +1 -5
  10. package/dist/release/delivery/handler.js +8 -29
  11. package/dist/release/delivery/index.d.ts +0 -1
  12. package/dist/release/delivery/index.js +1 -1
  13. package/dist/release/delivery/types.d.ts +3 -13
  14. package/dist/release/served.d.ts +6 -15
  15. package/dist/release/served.js +3 -7
  16. package/dist/release/snapshot.d.ts +2 -6
  17. package/dist/release/snapshot.js +2 -7
  18. package/dist/release/types.d.ts +1 -11
  19. package/dist/routes/address.d.ts +2 -0
  20. package/dist/routes/address.js +6 -0
  21. package/dist/routes/binary.d.ts +9 -0
  22. package/dist/routes/binary.js +110 -0
  23. package/dist/routes/bundle.d.ts +5 -0
  24. package/dist/routes/bundle.js +50 -0
  25. package/dist/routes/csrf.d.ts +16 -0
  26. package/dist/routes/csrf.js +35 -0
  27. package/dist/routes/document.d.ts +6 -0
  28. package/dist/routes/document.js +57 -0
  29. package/dist/routes/index.d.ts +3 -1
  30. package/dist/routes/index.js +2 -1
  31. package/dist/routes/model.d.ts +15 -0
  32. package/dist/routes/native.d.ts +17 -0
  33. package/dist/routes/native.js +126 -0
  34. package/dist/routes/preflight.d.ts +4 -0
  35. package/dist/routes/preflight.js +55 -0
  36. package/dist/routes/response.d.ts +14 -0
  37. package/dist/routes/response.js +33 -0
  38. package/dist/routes/routes.d.ts +5 -24
  39. package/dist/routes/routes.js +19 -625
  40. package/dist/routes/snapshot.d.ts +13 -0
  41. package/dist/routes/snapshot.js +39 -0
  42. package/dist/routes/stream.d.ts +6 -0
  43. package/dist/routes/stream.js +122 -0
  44. package/dist/server.js +12 -30
  45. package/dist/transport/http/binary.d.ts +6 -0
  46. package/dist/transport/http/binary.js +35 -0
  47. package/dist/transport/http/byte-stream.d.ts +4 -0
  48. package/dist/transport/http/byte-stream.js +123 -0
  49. package/dist/transport/http/control.d.ts +12 -0
  50. package/dist/transport/http/control.js +24 -0
  51. package/dist/transport/http/handler.d.ts +1 -1
  52. package/dist/transport/http/handler.js +4 -2
  53. package/dist/transport/http/index.d.ts +3 -1
  54. package/dist/transport/http/index.js +2 -1
  55. package/dist/transport/http/model.d.ts +20 -0
  56. package/dist/transport/http/model.js +1 -0
  57. package/dist/transport/http/render.d.ts +3 -36
  58. package/dist/transport/http/render.js +16 -412
  59. package/dist/transport/http/response.d.ts +14 -0
  60. package/dist/transport/http/response.js +77 -0
  61. package/dist/transport/http/stream.d.ts +5 -0
  62. package/dist/transport/http/stream.js +134 -0
  63. package/dist/transport/index.d.ts +2 -1
  64. package/dist/transport/index.js +2 -1
  65. package/dist/transport/result.d.ts +13 -0
  66. package/dist/transport/result.js +35 -0
  67. package/dist/transport/websocket/session.d.ts +1 -1
  68. package/dist/transport/websocket/session.js +3 -3
  69. package/dist/types.d.ts +13 -16
  70. package/package.json +5 -13
  71. package/dist/invocation/legacy/index.d.ts +0 -20
  72. package/dist/invocation/legacy/index.js +0 -20
  73. package/dist/invocation/legacy/publication-route.d.ts +0 -16
  74. package/dist/invocation/legacy/publication-route.js +0 -39
  75. package/dist/release/legacy/delivery.d.ts +0 -18
  76. package/dist/release/legacy/delivery.js +0 -2
  77. package/dist/release/legacy/index.d.ts +0 -39
  78. package/dist/release/legacy/index.js +0 -38
  79. package/dist/release/legacy/snapshot.d.ts +0 -26
  80. package/dist/release/legacy/snapshot.js +0 -55
  81. package/dist/release/legacy/table.d.ts +0 -5
  82. package/dist/release/legacy/table.js +0 -16
  83. package/dist/release/legacy/types.d.ts +0 -31
  84. /package/dist/{release/legacy/types.js → routes/model.js} +0 -0
package/dist/index.d.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  export { createServer, prepareServer } from './server.js';
2
2
  export { DeliveryUnavailableError, IssuerKeysUnavailableError } from './errors/index.js';
3
- export { releaseProjectionRouteDecision, releaseRouteDecision, type RouteDecision, } from './invocation/index.js';
4
3
  export { prepareReleaseSnapshot } from './release/index.js';
5
4
  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';
5
+ export type { Application, ApplicationHost, CorsConfig, ExecutionControl, Handler, HandlerRequest, HandlerResult, HandlerStream, HealthProbe, InvocationContext, InvocationSource, RequestBackground, RequestEvidence, RequestSleep, Issuer, exchange, PreparedServer, ReleaseSnapshot, ReleaseSnapshotInput, ReleaseSource, Server, ServerCloseOptions, ServerConfig, ServerLimits, ServerOptions, WebSocketAdapter, WebSocketHandlers, WebSocketLike, WebSocketUpgradeOptions, } from './types.js';
package/dist/index.js CHANGED
@@ -1,9 +1,4 @@
1
1
  export { createServer, prepareServer } from './server.js';
2
2
  export { DeliveryUnavailableError, IssuerKeysUnavailableError } from './errors/index.js';
3
- export { releaseProjectionRouteDecision, releaseRouteDecision, } from './invocation/index.js';
4
3
  export { prepareReleaseSnapshot } from './release/index.js';
5
4
  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 { admitVia, releaseProjectionRouteDecision, releaseRouteDecision, type RouteDecision, } from './release-route.js';
3
+ export { admitVia, releaseRouteDecision, type RouteDecision } from './release-route.js';
4
4
  export type { ApplicationHost, ExecutionControl, Handler, HandlerRequest, HandlerResult, HandlerStream, InvocationContext, RequestBackground, RequestEvidence, RequestSleep, } from './types.js';
@@ -1,3 +1,3 @@
1
1
  export { defaultSleep, invocationContext } from './context.js';
2
2
  export { captureInvocationSource, } from './identity.js';
3
- export { admitVia, releaseProjectionRouteDecision, releaseRouteDecision, } from './release-route.js';
3
+ export { admitVia, releaseRouteDecision } from './release-route.js';
@@ -1,9 +1,7 @@
1
1
  import type { ErrorPayload } from '@astrale-os/kernel-protocol/errors';
2
- import type { PublicationVia, Via } from '@astrale-os/kernel-protocol/invocation';
2
+ import type { Via } from '@astrale-os/kernel-protocol/invocation';
3
3
  import type { DomainRelease, ReleaseDigest, ReleaseRef } from '@astrale-os/kernel-protocol/release';
4
- import type { PublicationRef } from '@astrale-os/kernel-protocol/release/legacy';
5
4
  import type { Served } from '../release/served.js';
6
- import type { ReleaseSnapshot } from '../release/types.js';
7
5
  export type RouteDecision = {
8
6
  readonly kind: 'admitted';
9
7
  } | {
@@ -20,7 +18,5 @@ export type RouteDecision = {
20
18
  * the witness pairs it with a different revision or issuer.
21
19
  */
22
20
  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;
21
+ /** Admit routing against the exact release before credential resolution. */
22
+ export declare function admitVia(served: Served | undefined, via: Via): RouteDecision;
@@ -1,7 +1,5 @@
1
1
  import { CODES } from '@astrale-os/kernel-protocol/errors';
2
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
3
  const ADMITTED = Object.freeze({ kind: 'admitted' });
6
4
  const RETIRED = Object.freeze({ kind: 'retired' });
7
5
  /**
@@ -20,32 +18,11 @@ export function releaseRouteDecision(current, installed) {
20
18
  return invalid('Routed release origin differs from the served Domain.');
21
19
  }
22
20
  }
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. */
21
+ /** Admit routing against the exact release before credential resolution. */
37
22
  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);
23
+ return served === undefined
24
+ ? invalid('Release routing requires a release-backed destination.')
25
+ : releaseRouteDecision(served.release, via.release);
49
26
  }
50
27
  function invalid(message) {
51
28
  return Object.freeze({
@@ -3,7 +3,6 @@ import type { BinaryRequestBody, CallableResult } from '@astrale-os/kernel-core/
3
3
  import type { Value } from '@astrale-os/kernel-dsl/value';
4
4
  import type * as protocol from '@astrale-os/kernel-protocol/invocation';
5
5
  import type { ReleaseSnapshot } from '../release/index.js';
6
- import type { PublicationSnapshot } from '../release/legacy/index.js';
7
6
  /** Optional platform retention and timer adaptation for one Fetch invocation. */
8
7
  export interface ApplicationHost {
9
8
  waitUntil(promise: Promise<unknown>): void;
@@ -52,23 +51,12 @@ export interface HandlerRequest {
52
51
  readonly credential?: CredentialInput;
53
52
  /** Source-only effective delegation forwarded unchanged to Runtime. */
54
53
  readonly delegate?: protocol.Delegate;
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;
54
+ /** Destination witness admitted against the exact served release. */
55
+ readonly via?: protocol.Via;
61
56
  /** Source-only local-target or remote-revision Schema Registration precondition. */
62
57
  readonly schema?: protocol.SchemaTarget;
63
58
  /** Exact Server-pinned release for a request at a release-backed destination. */
64
59
  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
- */
71
- readonly publication?: PublicationSnapshot;
72
60
  readonly context: InvocationContext;
73
61
  }
74
62
  export interface Handler {
@@ -92,56 +92,37 @@ export function mountIssuer(app, input, cors, active, timeoutMs, tokenExchange)
92
92
  app.options(`${base}/iss/:thumbprint/.well-known/openid-configuration`, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
93
93
  app.options(`${base}/iss/:thumbprint/.well-known/jwks.json`, (context) => cors.preflight(context.req.raw, { methods: ['GET'] }));
94
94
  }
95
- app.get(`${base}/iss/:thumbprint/.well-known/openid-configuration`, async (context) => {
96
- const hostedIssuer = hosted(issuer, context.req.param('thumbprint'));
97
- if (hostedIssuer === undefined)
98
- return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
99
- const operation = active.begin(timeoutMs, context.req.raw.signal);
100
- if (operation === undefined) {
101
- const refused = refusal(context.req.raw);
102
- return context.json(refused.body, 503, refused.headers);
103
- }
104
- try {
105
- const keys = await hostedKeys(loadIssuerPublicKeys, hostedIssuer, operation.signal, algorithm);
106
- if (keys.length === 0)
95
+ const mountHostedDiscovery = (path, document) => {
96
+ app.get(path, async (context) => {
97
+ const hostedIssuer = hosted(issuer, context.req.param('thumbprint'));
98
+ if (hostedIssuer === undefined)
107
99
  return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
108
- return context.json(configuration(hostedIssuer, [algorithm]), 200, headers(context.req.raw));
109
- }
110
- catch (cause) {
111
- if (operation.signal.aborted || cause instanceof IssuerKeysUnavailableError) {
112
- return context.json({ error: 'Issuer keys are unavailable.' }, 503, headers(context.req.raw));
100
+ const operation = active.begin(timeoutMs, context.req.raw.signal);
101
+ if (operation === undefined) {
102
+ const refused = refusal(context.req.raw);
103
+ return context.json(refused.body, 503, refused.headers);
113
104
  }
114
- throw cause;
115
- }
116
- finally {
117
- operation.settle();
118
- }
119
- });
120
- app.get(`${base}/iss/:thumbprint/.well-known/jwks.json`, async (context) => {
121
- const hostedIssuer = hosted(issuer, context.req.param('thumbprint'));
122
- if (hostedIssuer === undefined)
123
- return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
124
- const operation = active.begin(timeoutMs, context.req.raw.signal);
125
- if (operation === undefined) {
126
- const refused = refusal(context.req.raw);
127
- return context.json(refused.body, 503, refused.headers);
128
- }
129
- try {
130
- const keys = await hostedKeys(loadIssuerPublicKeys, hostedIssuer, operation.signal, algorithm);
131
- if (keys.length === 0)
132
- return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
133
- return context.json({ keys }, 200, headers(context.req.raw));
134
- }
135
- catch (cause) {
136
- if (operation.signal.aborted || cause instanceof IssuerKeysUnavailableError) {
137
- return context.json({ error: 'Issuer keys are unavailable.' }, 503, headers(context.req.raw));
105
+ try {
106
+ const keys = await hostedKeys(loadIssuerPublicKeys, hostedIssuer, operation.signal, algorithm);
107
+ if (keys.length === 0)
108
+ return context.json({ error: 'Issuer not found.' }, 404, headers(context.req.raw));
109
+ return context.json(document(hostedIssuer, keys), 200, headers(context.req.raw));
138
110
  }
139
- throw cause;
140
- }
141
- finally {
142
- operation.settle();
143
- }
144
- });
111
+ catch (cause) {
112
+ if (operation.signal.aborted || cause instanceof IssuerKeysUnavailableError) {
113
+ return context.json({ error: 'Issuer keys are unavailable.' }, 503, headers(context.req.raw));
114
+ }
115
+ throw cause;
116
+ }
117
+ finally {
118
+ operation.settle();
119
+ }
120
+ });
121
+ };
122
+ mountHostedDiscovery(`${base}/iss/:thumbprint/.well-known/openid-configuration`, (issuer) => configuration(issuer, [algorithm]));
123
+ mountHostedDiscovery(`${base}/iss/:thumbprint/.well-known/jwks.json`, (_issuer, keys) => ({
124
+ keys,
125
+ }));
145
126
  }
146
127
  }
147
128
  async function hostedKeys(load, issuer, signal, algorithm) {
@@ -5,9 +5,5 @@ export interface Limits {
5
5
  readonly maximumDeliveryBytes: number;
6
6
  readonly invocationTimeoutMs: number;
7
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
- */
8
+ /** Serve one version-two Delivery against the exact snapshot that selected its URL. */
13
9
  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>;
@@ -5,12 +5,7 @@ import { RequestBodyInvalidError, RequestBodyTooLargeError } from '../../errors/
5
5
  import { readBoundedBody } from '../../transport/index.js';
6
6
  import { servedDocument } from '../served.js';
7
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
+ /** Serve one version-two Delivery against the exact snapshot that selected its URL. */
14
9
  export async function handleDelivery(request, snapshot, provider, active, limits, corsFor) {
15
10
  const cors = corsFor();
16
11
  if (request.method !== 'POST') {
@@ -66,10 +61,7 @@ export async function handleDelivery(request, snapshot, provider, active, limits
66
61
  }
67
62
  let input;
68
63
  try {
69
- input =
70
- snapshot.kind === 'release'
71
- ? delivery.acceptRequest(decoded)
72
- : publicationDelivery.acceptRequest(decoded);
64
+ input = delivery.acceptRequest(decoded);
73
65
  }
74
66
  catch (cause) {
75
67
  if (cause instanceof delivery.Error && cause.code === 'DELIVERY_REQUEST_INVALID') {
@@ -77,13 +69,9 @@ export async function handleDelivery(request, snapshot, provider, active, limits
77
69
  }
78
70
  throw cause;
79
71
  }
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);
72
+ const served = snapshot.release.digest;
73
+ if (input.release !== served) {
74
+ return response({ error: 'Release digest differs from request.' }, 409, cors);
87
75
  }
88
76
  let candidate;
89
77
  try {
@@ -100,8 +88,7 @@ export async function handleDelivery(request, snapshot, provider, active, limits
100
88
  }
101
89
  let created;
102
90
  try {
103
- created =
104
- input.version === 2 ? delivery.accept(candidate) : publicationDelivery.accept(candidate);
91
+ created = delivery.accept(candidate);
105
92
  }
106
93
  catch (cause) {
107
94
  if (cause instanceof delivery.Error) {
@@ -109,12 +96,8 @@ export async function handleDelivery(request, snapshot, provider, active, limits
109
96
  }
110
97
  throw cause;
111
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
+ if (created.release.digest !== served) {
100
+ return response({ error: 'Delivery provider returned a different release.' }, 500, cors);
118
101
  }
119
102
  const encoded = codecs.json.encode(created);
120
103
  if (encodedSize(encoded) > limits.maximumDeliveryBytes) {
@@ -128,10 +111,6 @@ export async function handleDelivery(request, snapshot, provider, active, limits
128
111
  operation.settle();
129
112
  }
130
113
  }
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
- }
135
114
  function bearer(input) {
136
115
  if (input === null)
137
116
  return undefined;
@@ -1,2 +1 @@
1
- export { handleDelivery, type Limits } from './handler.js';
2
1
  export type { Context, Provider } from './types.js';
@@ -1 +1 @@
1
- export { handleDelivery } from './handler.js';
1
+ export {};
@@ -4,18 +4,8 @@ export interface Context {
4
4
  readonly token?: release.delivery.Token;
5
5
  readonly signal: AbortSignal;
6
6
  }
7
- /** Provider-owned retrieval, construction, and attestation signing. */
7
+ /** Provider-owned retrieval, construction, and attestation signing for the served release. */
8
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>;
9
+ /** Throw DeliveryUnavailableError only for an expected dependency outage. */
10
+ create(request: release.delivery.Request, context: Context): Promise<release.delivery.Delivery>;
21
11
  }
@@ -1,30 +1,21 @@
1
1
  import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
2
  import type { DomainRelease } from '@astrale-os/kernel-protocol/release';
3
- import type { Publication } from '@astrale-os/kernel-protocol/release/legacy';
4
- import type { PreparedPublicationSnapshot } from './legacy/snapshot.js';
5
- import type { PublicationSnapshot, PublicationSource } from './legacy/types.js';
6
3
  import type { PreparedReleaseSnapshot } from './snapshot.js';
7
4
  import type { ReleaseSnapshot, ReleaseSource } from './types.js';
8
- export type ServingSource = {
5
+ export interface ServingSource {
9
6
  readonly kind: 'release';
10
7
  readonly source: ReleaseSource;
11
- } | {
12
- readonly kind: 'publication';
13
- readonly source: PublicationSource;
14
- };
15
- export type Served = PreparedReleaseSnapshot | PreparedPublicationSnapshot;
8
+ }
9
+ export type Served = PreparedReleaseSnapshot;
16
10
  export interface RequestServing {
17
- readonly kind: ServingSource['kind'];
11
+ readonly kind: 'release';
18
12
  /** The snapshot the request pins; undefined while none is ready or it is withheld. */
19
13
  current(): Served | undefined;
20
- /** The deployment a DEPLOYMENT_RETIRED answer names: the Server's public serving origin. */
14
+ /** The public serving origin named by a DEPLOYMENT_RETIRED answer. */
21
15
  deployment(): IssuerId;
22
16
  }
23
17
  export declare function readServed(serving: ServingSource): Served | undefined;
24
- /** A release and its v3 projection share bundles, Delivery endpoints and native routes. */
25
- export declare function servedDocument(served: Served): DomainRelease | Publication;
18
+ export declare function servedDocument(served: Served): DomainRelease;
26
19
  export declare function pinned(served: Served): {
27
20
  readonly release: ReleaseSnapshot;
28
- } | {
29
- readonly publication: PublicationSnapshot;
30
21
  };
@@ -1,14 +1,10 @@
1
- import { readPreparedPublicationSnapshot } from './legacy/snapshot.js';
2
1
  import { readPreparedReleaseSnapshot } from './snapshot.js';
3
2
  export function readServed(serving) {
4
- return serving.kind === 'release'
5
- ? readPreparedReleaseSnapshot(serving.source.current())
6
- : readPreparedPublicationSnapshot(serving.source.current());
3
+ return readPreparedReleaseSnapshot(serving.source.current());
7
4
  }
8
- /** A release and its v3 projection share bundles, Delivery endpoints and native routes. */
9
5
  export function servedDocument(served) {
10
- return served.kind === 'release' ? served.release : served.publication;
6
+ return served.release;
11
7
  }
12
8
  export function pinned(served) {
13
- return served.kind === 'release' ? { release: served.snapshot } : { publication: served.snapshot };
9
+ return { release: served.snapshot };
14
10
  }
@@ -1,17 +1,13 @@
1
1
  import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
2
  import type { ServedReferences, Table } from '@astrale-os/kernel-protocol/routes';
3
3
  import { type DomainRelease } from '@astrale-os/kernel-protocol/release';
4
- import { type Publication } from '@astrale-os/kernel-protocol/release/legacy';
5
4
  import type { ReleaseSnapshot, ReleaseSnapshotInput } from './types.js';
6
5
  export interface PreparedReleaseSnapshot {
7
6
  readonly kind: 'release';
8
7
  readonly snapshot: ReleaseSnapshot;
9
8
  readonly release: DomainRelease;
10
9
  readonly releaseBytes: Uint8Array;
11
- /** @deprecated Use `release`. Frozen v3 projection served at `domain.json`. */
12
- readonly publication: Publication;
13
- readonly publicationBytes: Uint8Array;
14
- /** The release and the projection reference native-route If-Match may name. */
10
+ /** The exact release reference native-route If-Match names. */
15
11
  readonly references: ServedReferences;
16
12
  readonly bundle: Uint8Array;
17
13
  readonly table: Table;
@@ -19,7 +15,7 @@ export interface PreparedReleaseSnapshot {
19
15
  }
20
16
  /**
21
17
  * Admit and detach one deployment's serving snapshot before it is served: the release, its exact
22
- * bundle bytes, its resolved Domain and route Table, and its frozen v3 projection.
18
+ * bundle bytes, its resolved Domain and route Table.
23
19
  */
24
20
  export declare function prepareReleaseSnapshot(input: ReleaseSnapshotInput): ReleaseSnapshot;
25
21
  /** Internal proof that a ReleaseSource returned a Server-prepared snapshot without cloning it. */
@@ -1,10 +1,9 @@
1
1
  import { accept as acceptRelease, bundleUrl, encode as encodeRelease, verifyBundleBytes, } from '@astrale-os/kernel-protocol/release';
2
- import { encode as encodePublication, publication as project, reference as publicationReference, } from '@astrale-os/kernel-protocol/release/legacy';
3
2
  import { admitTable } from './table.js';
4
3
  const snapshots = new WeakMap();
5
4
  /**
6
5
  * Admit and detach one deployment's serving snapshot before it is served: the release, its exact
7
- * bundle bytes, its resolved Domain and route Table, and its frozen v3 projection.
6
+ * bundle bytes, its resolved Domain and route Table.
8
7
  */
9
8
  export function prepareReleaseSnapshot(input) {
10
9
  if (input === null || typeof input !== 'object' || !(input.bundle instanceof Uint8Array)) {
@@ -26,18 +25,14 @@ export function prepareReleaseSnapshot(input) {
26
25
  servingBundle.href !== addressed.href) {
27
26
  throw new TypeError('Release bundle URL differs from its artifact digest.');
28
27
  }
29
- const projection = project(release);
30
- const snapshot = Object.freeze({ release, publication: projection });
28
+ const snapshot = Object.freeze({ release });
31
29
  const prepared = Object.freeze({
32
30
  kind: 'release',
33
31
  snapshot,
34
32
  release,
35
33
  releaseBytes: encodeRelease(release),
36
- publication: projection,
37
- publicationBytes: encodePublication(projection),
38
34
  references: Object.freeze({
39
35
  release: table.release,
40
- projection: publicationReference(projection),
41
36
  }),
42
37
  bundle: bundleBytes,
43
38
  table,
@@ -10,19 +10,9 @@ export interface ReleaseSnapshotInput {
10
10
  readonly table?: Table;
11
11
  readonly domain: Domain;
12
12
  }
13
- /**
14
- * Immutable admitted serving identity of one deployment: the release it serves at `release.json`
15
- * and the frozen v3 projection it also serves at `domain.json`. Bundle and compiled routes remain
16
- * Server-private.
17
- */
13
+ /** Immutable admitted serving identity; bundle and compiled routes remain Server-private. */
18
14
  export interface ReleaseSnapshot {
19
15
  readonly release: release.DomainRelease;
20
- /**
21
- * The frozen v3 projection served at `domain.json` and named by legacy routing witnesses.
22
- *
23
- * @deprecated Use `release`.
24
- */
25
- readonly publication: release.legacy.Publication;
26
16
  readonly [RELEASE_SNAPSHOT]: true;
27
17
  }
28
18
  /** Atomically readable source swapped only after a complete snapshot is prepared. */
@@ -0,0 +1,2 @@
1
+ /** Match a resource path and query against its trusted serving origin. */
2
+ export declare function sameRoute(left: string, right: string, externalOrigin?: string): boolean;
@@ -0,0 +1,6 @@
1
+ /** Match a resource path and query against its trusted serving origin. */
2
+ export function sameRoute(left, right, externalOrigin) {
3
+ const a = new URL(left);
4
+ const b = new URL(right);
5
+ return ((externalOrigin ?? a.origin) === b.origin && a.pathname === b.pathname && a.search === b.search);
6
+ }
@@ -0,0 +1,9 @@
1
+ import type { BinaryOutput, StreamingBinaryResult } from '@astrale-os/kernel-core/callable';
2
+ import type { CorsPolicy } from '../cors/index.js';
3
+ import type { ActiveOperation } from '../lifecycle/index.js';
4
+ /** Retain the native operation until its byte producer ends or is closed. */
5
+ export declare function streamingBinaryResponse(binary: StreamingBinaryResult, operation: ActiveOperation, cors: CorsPolicy, request: Request): Response;
6
+ /** Identify a producer-backed binary result before handing off operation ownership. */
7
+ export declare function isStreamingBinary(input: BinaryOutput): input is StreamingBinaryResult;
8
+ /** Render buffered application bytes with the native binary header manifest. */
9
+ export declare function binaryResponse(binary: Exclude<BinaryOutput, StreamingBinaryResult>, cors: CorsPolicy, request: Request): Response;
@@ -0,0 +1,110 @@
1
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
2
+ import * as routes from '@astrale-os/kernel-protocol/routes';
3
+ import { toBody } from './response.js';
4
+ /** Retain the native operation until its byte producer ends or is closed. */
5
+ export function streamingBinaryResponse(binary, operation, cors, request) {
6
+ // Admit the reserved/invalid headers before acquiring the producer, so a refused render never
7
+ // captures the stream it would then have to abandon.
8
+ const applicationHeaders = applicationBinaryHeaders(binary.headers);
9
+ const iterator = binary.body[Symbol.asyncIterator]();
10
+ let terminal = false;
11
+ let controller;
12
+ let stopping;
13
+ const stop = (reason = 'Binary response was interrupted.') => {
14
+ if (terminal)
15
+ return stopping ?? Promise.resolve();
16
+ terminal = true;
17
+ controller?.error(new Error(reason));
18
+ stopping = Promise.resolve().then(async () => {
19
+ try {
20
+ await iterator.return?.();
21
+ }
22
+ finally {
23
+ operation.settle();
24
+ }
25
+ });
26
+ return stopping;
27
+ };
28
+ const body = new ReadableStream({
29
+ start(output) {
30
+ controller = output;
31
+ },
32
+ async pull(controller) {
33
+ if (terminal)
34
+ return;
35
+ try {
36
+ const next = await iterator.next();
37
+ if (terminal)
38
+ return;
39
+ if (next.done) {
40
+ terminal = true;
41
+ controller.close();
42
+ operation.settle();
43
+ return;
44
+ }
45
+ controller.enqueue(next.value);
46
+ }
47
+ catch (cause) {
48
+ if (terminal)
49
+ return;
50
+ terminal = true;
51
+ controller.error(cause);
52
+ operation.settle();
53
+ }
54
+ },
55
+ async cancel(reason) {
56
+ const message = typeof reason === 'string' ? reason : 'native binary response cancelled';
57
+ operation.cancel(message);
58
+ await stop();
59
+ },
60
+ });
61
+ operation.onCancel(stop);
62
+ try {
63
+ return new Response(body, {
64
+ status: binary.status ?? 200,
65
+ headers: {
66
+ ...cors.response(request, Object.keys(applicationHeaders)),
67
+ ...applicationHeaders,
68
+ 'content-type': binary.mediaType,
69
+ },
70
+ });
71
+ }
72
+ catch (cause) {
73
+ // A body-forbidding status (e.g. 204) rejects the Response after the producer was wired:
74
+ // close it and let the caller settle the operation rather than leaking its slot.
75
+ void stop('Binary response was refused.');
76
+ throw cause;
77
+ }
78
+ }
79
+ /** Identify a producer-backed binary result before handing off operation ownership. */
80
+ export function isStreamingBinary(input) {
81
+ return !(input.body instanceof Uint8Array);
82
+ }
83
+ function applicationBinaryHeaders(input) {
84
+ const output = {};
85
+ for (const [name, value] of Object.entries(input ?? {})) {
86
+ if (name.toLowerCase() === routes.HTTP_ROUTE_STALE ||
87
+ name.toLowerCase() === routes.HTTP_ROUTE_MISS) {
88
+ throw new TypeError(`${name.toLowerCase()} is reserved for native routing control.`);
89
+ }
90
+ if (protocol.http.isApplicationBinaryHeader(name))
91
+ output[name] = value;
92
+ }
93
+ return output;
94
+ }
95
+ /** Render buffered application bytes with the native binary header manifest. */
96
+ export function binaryResponse(binary, cors, request) {
97
+ const applicationHeaders = applicationBinaryHeaders(binary.headers);
98
+ return new Response(toBody(binary.body), {
99
+ status: binary.status ?? 200,
100
+ headers: {
101
+ ...cors.response(request, [
102
+ protocol.http.BINARY_HEADERS_FIELD,
103
+ ...Object.keys(applicationHeaders),
104
+ ]),
105
+ ...applicationHeaders,
106
+ 'content-type': binary.mediaType,
107
+ [protocol.http.BINARY_HEADERS_FIELD]: protocol.http.encodeBinaryHeaderNames(applicationHeaders),
108
+ },
109
+ });
110
+ }
@@ -0,0 +1,5 @@
1
+ import type { ActiveOperations } from '../lifecycle/index.js';
2
+ import type { Served } from '../release/served.js';
3
+ import type { CorsHeaders } from './model.js';
4
+ /** Serve exact bundle bytes or an uncached miss inside its immutable artifact directory. */
5
+ export declare function bundleResponse(request: Request, snapshot: Served, active: ActiveOperations, responseCors: Readonly<Record<string, string>>, corsFor: CorsHeaders, externalOrigin?: string): Response | undefined;