@astrale-os/kernel-server 0.5.0-beta.4 → 0.5.0-beta.40

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 (179) hide show
  1. package/dist/capture.d.ts +0 -1
  2. package/dist/capture.js +0 -1
  3. package/dist/cors/cors.d.ts +0 -1
  4. package/dist/cors/cors.js +2 -1
  5. package/dist/cors/index.d.ts +0 -1
  6. package/dist/cors/index.js +0 -1
  7. package/dist/cors/types.d.ts +0 -1
  8. package/dist/cors/types.js +0 -1
  9. package/dist/errors/availability.d.ts +0 -1
  10. package/dist/errors/availability.js +0 -1
  11. package/dist/errors/classify.d.ts +1 -2
  12. package/dist/errors/classify.js +1 -2
  13. package/dist/errors/index.d.ts +0 -1
  14. package/dist/errors/index.js +0 -1
  15. package/dist/errors/request-body.d.ts +0 -1
  16. package/dist/errors/request-body.js +0 -1
  17. package/dist/errors/response-body.d.ts +0 -1
  18. package/dist/errors/response-body.js +0 -1
  19. package/dist/index.d.ts +0 -1
  20. package/dist/index.js +0 -1
  21. package/dist/invocation/context.d.ts +1 -2
  22. package/dist/invocation/context.js +0 -1
  23. package/dist/invocation/identity.d.ts +1 -2
  24. package/dist/invocation/identity.js +1 -2
  25. package/dist/invocation/index.d.ts +0 -1
  26. package/dist/invocation/index.js +0 -1
  27. package/dist/invocation/publication-route.d.ts +6 -7
  28. package/dist/invocation/publication-route.js +8 -16
  29. package/dist/invocation/types.d.ts +3 -4
  30. package/dist/invocation/types.js +0 -1
  31. package/dist/issuer/exchange.d.ts +2 -3
  32. package/dist/issuer/exchange.js +22 -12
  33. package/dist/issuer/index.d.ts +0 -1
  34. package/dist/issuer/index.js +0 -1
  35. package/dist/issuer/routes.d.ts +0 -1
  36. package/dist/issuer/routes.js +14 -12
  37. package/dist/issuer/types.d.ts +2 -2
  38. package/dist/issuer/types.js +0 -1
  39. package/dist/lifecycle/active.d.ts +2 -1
  40. package/dist/lifecycle/active.js +5 -3
  41. package/dist/lifecycle/index.d.ts +0 -1
  42. package/dist/lifecycle/index.js +0 -1
  43. package/dist/publication/delivery/handler.d.ts +0 -1
  44. package/dist/publication/delivery/handler.js +2 -2
  45. package/dist/publication/delivery/index.d.ts +0 -1
  46. package/dist/publication/delivery/index.js +0 -1
  47. package/dist/publication/delivery/types.d.ts +1 -2
  48. package/dist/publication/delivery/types.js +0 -1
  49. package/dist/publication/index.d.ts +0 -1
  50. package/dist/publication/index.js +0 -1
  51. package/dist/publication/snapshot.d.ts +5 -3
  52. package/dist/publication/snapshot.js +14 -7
  53. package/dist/publication/table.d.ts +4 -0
  54. package/dist/publication/table.js +16 -0
  55. package/dist/publication/types.d.ts +5 -2
  56. package/dist/publication/types.js +0 -1
  57. package/dist/routes/index.d.ts +0 -1
  58. package/dist/routes/index.js +0 -1
  59. package/dist/routes/routes.d.ts +1 -2
  60. package/dist/routes/routes.js +88 -24
  61. package/dist/server.d.ts +0 -1
  62. package/dist/server.js +7 -6
  63. package/dist/transport/body.d.ts +0 -1
  64. package/dist/transport/body.js +0 -1
  65. package/dist/transport/codecs.d.ts +1 -2
  66. package/dist/transport/codecs.js +1 -2
  67. package/dist/transport/credential.d.ts +1 -2
  68. package/dist/transport/credential.js +1 -2
  69. package/dist/transport/http/handler.d.ts +0 -1
  70. package/dist/transport/http/handler.js +3 -2
  71. package/dist/transport/http/index.d.ts +0 -1
  72. package/dist/transport/http/index.js +0 -1
  73. package/dist/transport/http/render.d.ts +12 -9
  74. package/dist/transport/http/render.js +75 -5
  75. package/dist/transport/index.d.ts +0 -1
  76. package/dist/transport/index.js +0 -1
  77. package/dist/transport/router.d.ts +0 -1
  78. package/dist/transport/router.js +0 -1
  79. package/dist/transport/stream-error.d.ts +2 -3
  80. package/dist/transport/stream-error.js +3 -4
  81. package/dist/transport/types.d.ts +1 -2
  82. package/dist/transport/types.js +0 -1
  83. package/dist/transport/websocket/index.d.ts +0 -1
  84. package/dist/transport/websocket/index.js +0 -1
  85. package/dist/transport/websocket/session.d.ts +2 -3
  86. package/dist/transport/websocket/session.js +14 -3
  87. package/dist/transport/websocket/upgrade.d.ts +0 -1
  88. package/dist/transport/websocket/upgrade.js +1 -2
  89. package/dist/types.d.ts +1 -2
  90. package/dist/types.js +0 -1
  91. package/package.json +23 -12
  92. package/dist/capture.d.ts.map +0 -1
  93. package/dist/capture.js.map +0 -1
  94. package/dist/cors/cors.d.ts.map +0 -1
  95. package/dist/cors/cors.js.map +0 -1
  96. package/dist/cors/index.d.ts.map +0 -1
  97. package/dist/cors/index.js.map +0 -1
  98. package/dist/cors/types.d.ts.map +0 -1
  99. package/dist/cors/types.js.map +0 -1
  100. package/dist/errors/availability.d.ts.map +0 -1
  101. package/dist/errors/availability.js.map +0 -1
  102. package/dist/errors/classify.d.ts.map +0 -1
  103. package/dist/errors/classify.js.map +0 -1
  104. package/dist/errors/index.d.ts.map +0 -1
  105. package/dist/errors/index.js.map +0 -1
  106. package/dist/errors/request-body.d.ts.map +0 -1
  107. package/dist/errors/request-body.js.map +0 -1
  108. package/dist/errors/response-body.d.ts.map +0 -1
  109. package/dist/errors/response-body.js.map +0 -1
  110. package/dist/index.d.ts.map +0 -1
  111. package/dist/index.js.map +0 -1
  112. package/dist/invocation/context.d.ts.map +0 -1
  113. package/dist/invocation/context.js.map +0 -1
  114. package/dist/invocation/identity.d.ts.map +0 -1
  115. package/dist/invocation/identity.js.map +0 -1
  116. package/dist/invocation/index.d.ts.map +0 -1
  117. package/dist/invocation/index.js.map +0 -1
  118. package/dist/invocation/publication-route.d.ts.map +0 -1
  119. package/dist/invocation/publication-route.js.map +0 -1
  120. package/dist/invocation/types.d.ts.map +0 -1
  121. package/dist/invocation/types.js.map +0 -1
  122. package/dist/issuer/exchange.d.ts.map +0 -1
  123. package/dist/issuer/exchange.js.map +0 -1
  124. package/dist/issuer/index.d.ts.map +0 -1
  125. package/dist/issuer/index.js.map +0 -1
  126. package/dist/issuer/routes.d.ts.map +0 -1
  127. package/dist/issuer/routes.js.map +0 -1
  128. package/dist/issuer/types.d.ts.map +0 -1
  129. package/dist/issuer/types.js.map +0 -1
  130. package/dist/lifecycle/active.d.ts.map +0 -1
  131. package/dist/lifecycle/active.js.map +0 -1
  132. package/dist/lifecycle/index.d.ts.map +0 -1
  133. package/dist/lifecycle/index.js.map +0 -1
  134. package/dist/publication/delivery/handler.d.ts.map +0 -1
  135. package/dist/publication/delivery/handler.js.map +0 -1
  136. package/dist/publication/delivery/index.d.ts.map +0 -1
  137. package/dist/publication/delivery/index.js.map +0 -1
  138. package/dist/publication/delivery/types.d.ts.map +0 -1
  139. package/dist/publication/delivery/types.js.map +0 -1
  140. package/dist/publication/index.d.ts.map +0 -1
  141. package/dist/publication/index.js.map +0 -1
  142. package/dist/publication/snapshot.d.ts.map +0 -1
  143. package/dist/publication/snapshot.js.map +0 -1
  144. package/dist/publication/types.d.ts.map +0 -1
  145. package/dist/publication/types.js.map +0 -1
  146. package/dist/routes/index.d.ts.map +0 -1
  147. package/dist/routes/index.js.map +0 -1
  148. package/dist/routes/routes.d.ts.map +0 -1
  149. package/dist/routes/routes.js.map +0 -1
  150. package/dist/server.d.ts.map +0 -1
  151. package/dist/server.js.map +0 -1
  152. package/dist/transport/body.d.ts.map +0 -1
  153. package/dist/transport/body.js.map +0 -1
  154. package/dist/transport/codecs.d.ts.map +0 -1
  155. package/dist/transport/codecs.js.map +0 -1
  156. package/dist/transport/credential.d.ts.map +0 -1
  157. package/dist/transport/credential.js.map +0 -1
  158. package/dist/transport/http/handler.d.ts.map +0 -1
  159. package/dist/transport/http/handler.js.map +0 -1
  160. package/dist/transport/http/index.d.ts.map +0 -1
  161. package/dist/transport/http/index.js.map +0 -1
  162. package/dist/transport/http/render.d.ts.map +0 -1
  163. package/dist/transport/http/render.js.map +0 -1
  164. package/dist/transport/index.d.ts.map +0 -1
  165. package/dist/transport/index.js.map +0 -1
  166. package/dist/transport/router.d.ts.map +0 -1
  167. package/dist/transport/router.js.map +0 -1
  168. package/dist/transport/stream-error.d.ts.map +0 -1
  169. package/dist/transport/stream-error.js.map +0 -1
  170. package/dist/transport/types.d.ts.map +0 -1
  171. package/dist/transport/types.js.map +0 -1
  172. package/dist/transport/websocket/index.d.ts.map +0 -1
  173. package/dist/transport/websocket/index.js.map +0 -1
  174. package/dist/transport/websocket/session.d.ts.map +0 -1
  175. package/dist/transport/websocket/session.js.map +0 -1
  176. package/dist/transport/websocket/upgrade.d.ts.map +0 -1
  177. package/dist/transport/websocket/upgrade.js.map +0 -1
  178. package/dist/types.d.ts.map +0 -1
  179. package/dist/types.js.map +0 -1
@@ -1,4 +1,3 @@
1
1
  export { preparePublicationSnapshot, readPreparedPublicationSnapshot, type PreparedPublicationSnapshot, } from './snapshot.js';
2
2
  export type { PublicationSnapshot, PublicationSnapshotInput, PublicationSource } from './types.js';
3
3
  export * as delivery from './delivery/index.js';
4
- //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,2 @@
1
1
  export { preparePublicationSnapshot, readPreparedPublicationSnapshot, } from './snapshot.js';
2
2
  export * as delivery from './delivery/index.js';
3
- //# sourceMappingURL=index.js.map
@@ -1,14 +1,16 @@
1
- import { publication, routes } from '@astrale-os/kernel-protocol';
1
+ import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
+ import type * as publication from '@astrale-os/kernel-protocol/publication';
3
+ import type * as routes from '@astrale-os/kernel-protocol/routes';
2
4
  import type { PublicationSnapshot, PublicationSnapshotInput } from './types.js';
3
5
  export interface PreparedPublicationSnapshot {
4
6
  readonly snapshot: PublicationSnapshot;
5
7
  readonly publication: publication.Publication;
6
8
  readonly publicationBytes: Uint8Array;
7
9
  readonly bundle: Uint8Array;
8
- readonly routes: readonly routes.CompiledRoute[];
10
+ readonly table: routes.Table;
11
+ readonly domain: Domain;
9
12
  }
10
13
  /** Admit and detach one revision-coherent serving snapshot before publication. */
11
14
  export declare function preparePublicationSnapshot(input: PublicationSnapshotInput): PublicationSnapshot;
12
15
  /** Internal proof that a PublicationSource returned a Server-prepared snapshot without cloning it. */
13
16
  export declare function readPreparedPublicationSnapshot(input: unknown): PreparedPublicationSnapshot | undefined;
14
- //# sourceMappingURL=snapshot.d.ts.map
@@ -1,22 +1,30 @@
1
- import { publication, routes } from '@astrale-os/kernel-protocol';
1
+ import { verifyBundleBytes } from '@astrale-os/kernel-protocol/publication/bundle-evidence';
2
+ import { accept as acceptPublication, encode as encodePublication, } from '@astrale-os/kernel-protocol/publication/publication';
3
+ import { admitTable } from './table.js';
2
4
  const snapshots = new WeakMap();
3
5
  /** Admit and detach one revision-coherent serving snapshot before publication. */
4
6
  export function preparePublicationSnapshot(input) {
5
7
  if (input === null || typeof input !== 'object' || !(input.bundle instanceof Uint8Array)) {
6
8
  throw new TypeError('Publication snapshot input is invalid.');
7
9
  }
8
- const admittedPublication = publication.accept(input.publication);
10
+ const admittedPublication = acceptPublication(input.publication);
9
11
  const bundleBytes = new Uint8Array(input.bundle);
10
- const artifact = publication.decodeBundle(admittedPublication, bundleBytes);
11
- const compiledRoutes = routes.compile(admittedPublication, artifact);
12
- const publicationBytes = publication.encode(admittedPublication);
12
+ verifyBundleBytes(admittedPublication, bundleBytes);
13
+ const domain = input.domain;
14
+ if (domain.origin !== admittedPublication.origin ||
15
+ domain.revision !== admittedPublication.schema.revision) {
16
+ throw new TypeError('Publication snapshot Domain identity differs from its Publication.');
17
+ }
18
+ const table = admitTable(admittedPublication, input.table, domain);
19
+ const publicationBytes = encodePublication(admittedPublication);
13
20
  const snapshot = Object.freeze({ publication: admittedPublication });
14
21
  const prepared = Object.freeze({
15
22
  snapshot,
16
23
  publication: admittedPublication,
17
24
  publicationBytes,
18
25
  bundle: bundleBytes,
19
- routes: compiledRoutes,
26
+ table,
27
+ domain,
20
28
  });
21
29
  snapshots.set(snapshot, prepared);
22
30
  return snapshot;
@@ -27,4 +35,3 @@ export function readPreparedPublicationSnapshot(input) {
27
35
  return undefined;
28
36
  return snapshots.get(input);
29
37
  }
30
- //# sourceMappingURL=snapshot.js.map
@@ -0,0 +1,4 @@
1
+ import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
+ import type * as publication from '@astrale-os/kernel-protocol/publication';
3
+ import type * as routes from '@astrale-os/kernel-protocol/routes';
4
+ export declare function admitTable(deployed: publication.Publication, input: routes.Table | undefined, domain: Domain): routes.Table;
@@ -0,0 +1,16 @@
1
+ import { reference } from '@astrale-os/kernel-protocol/publication/reference';
2
+ import { acceptTable } from '@astrale-os/kernel-protocol/routes/admission';
3
+ import { verifyTable } from '@astrale-os/kernel-protocol/routes/evidence';
4
+ export function admitTable(deployed, input, domain) {
5
+ if (input === undefined) {
6
+ if (deployed.routes.length !== 0) {
7
+ throw new TypeError('A Publication with native routes requires a Route Table.');
8
+ }
9
+ return verifyTable(Object.freeze({
10
+ publication: reference(deployed),
11
+ entries: Object.freeze([]),
12
+ }), deployed, domain);
13
+ }
14
+ const table = acceptTable(input);
15
+ return verifyTable(table, deployed, domain);
16
+ }
@@ -1,9 +1,13 @@
1
- import type { publication } from '@astrale-os/kernel-protocol';
1
+ import type { Domain } from '@astrale-os/kernel-dsl/v1';
2
+ import type * as publication from '@astrale-os/kernel-protocol/publication';
3
+ import type * as routes from '@astrale-os/kernel-protocol/routes';
2
4
  declare const PUBLICATION_SNAPSHOT: unique symbol;
3
5
  export interface PublicationSnapshotInput {
4
6
  readonly publication: publication.Publication;
5
7
  /** Exact artifact served at Publication.schema.bundle.href. */
6
8
  readonly bundle: Uint8Array;
9
+ readonly table?: routes.Table;
10
+ readonly domain: Domain;
7
11
  }
8
12
  /** Immutable admitted serving identity; bundle and compiled routes remain Server-private. */
9
13
  export interface PublicationSnapshot {
@@ -16,4 +20,3 @@ export interface PublicationSource {
16
20
  current(): PublicationSnapshot | undefined;
17
21
  }
18
22
  export {};
19
- //# sourceMappingURL=types.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=types.js.map
@@ -1,2 +1 @@
1
1
  export { currentPublicationSnapshot, mountRoutes, type RouteLimits, type RouteReservation, } from './routes.js';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1 @@
1
1
  export { currentPublicationSnapshot, mountRoutes, } from './routes.js';
2
- //# sourceMappingURL=index.js.map
@@ -5,7 +5,7 @@ import type { Provider as DeliveryProvider } from '../publication/delivery/index
5
5
  import type { PublicationSource } from '../publication/index.js';
6
6
  import { type ApplicationHost, type Handler } from '../invocation/index.js';
7
7
  import { type PreparedPublicationSnapshot } from '../publication/index.js';
8
- import { type ServerRouter } from '../transport/index.js';
8
+ import { type ServerRouter } from '../transport/router.js';
9
9
  export interface RouteLimits {
10
10
  readonly maximumRequestBytes: number;
11
11
  readonly maximumPublicationBytes: number;
@@ -23,4 +23,3 @@ export declare function currentPublicationSnapshot(source: PublicationSource, li
23
23
  readonly issuer?: string;
24
24
  readonly reservations: readonly RouteReservation[];
25
25
  }): PreparedPublicationSnapshot | undefined;
26
- //# sourceMappingURL=routes.d.ts.map
@@ -1,13 +1,20 @@
1
- import { codecs, errors, invocation as protocol, publication, routes, } from '@astrale-os/kernel-protocol';
1
+ import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
+ import * as errors from '@astrale-os/kernel-protocol/errors';
3
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
4
+ import { MEDIA_TYPE as PUBLICATION_MEDIA_TYPE, PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
5
+ import { reference as publicationReference } from '@astrale-os/kernel-protocol/publication/reference';
6
+ import * as routes from '@astrale-os/kernel-protocol/routes';
2
7
  import { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../errors/index.js';
3
8
  import { invocationContext } from '../invocation/index.js';
4
9
  import { handleDelivery } from '../publication/delivery/index.js';
5
10
  import { readPreparedPublicationSnapshot, } from '../publication/index.js';
6
- import { interruptedStreamError, readBoundedBody } from '../transport/index.js';
11
+ import { readBoundedBody } from '../transport/body.js';
12
+ import {} from '../transport/router.js';
13
+ import { interruptedStreamError } from '../transport/stream-error.js';
7
14
  export function mountRoutes(app, source, deliveryProvider, handler, active, nextInvocation, limits, cors, hostFor, reservations, externalIssuer) {
8
15
  const publicationPath = externalIssuer === undefined
9
- ? publication.PATH
10
- : new URL(publication.url(externalIssuer)).pathname;
16
+ ? PUBLICATION_PATH
17
+ : new URL(publicationUrl(externalIssuer)).pathname;
11
18
  const externalOrigin = externalIssuer === undefined ? undefined : new URL(externalIssuer).origin;
12
19
  if (cors.enabled) {
13
20
  app.options(publicationPath, (context) => cors.preflight(context.req.raw, {
@@ -37,7 +44,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
37
44
  status: 200,
38
45
  headers: {
39
46
  ...responseCors,
40
- 'content-type': 'application/json',
47
+ 'content-type': PUBLICATION_MEDIA_TYPE,
41
48
  etag: quoteEtag(snapshot.publication.etag),
42
49
  'cache-control': 'public, max-age=0, must-revalidate, no-transform',
43
50
  },
@@ -89,7 +96,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
89
96
  },
90
97
  });
91
98
  }
92
- routing = routes.decodeRouting(request, publication.reference(snapshot.publication));
99
+ routing = routes.decodeRouting(request, publicationReference(snapshot.publication));
93
100
  }
94
101
  catch (cause) {
95
102
  if (cause instanceof routes.RouteError) {
@@ -97,7 +104,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
97
104
  }
98
105
  throw cause;
99
106
  }
100
- const route = routes.match(snapshot.routes, request);
107
+ const route = routes.match(snapshot.table, request);
101
108
  if (route === undefined)
102
109
  return context.notFound();
103
110
  const assigned = nextInvocation();
@@ -110,12 +117,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
110
117
  let handedOff = false;
111
118
  try {
112
119
  const body = await readBoundedBody(request, limits.maximumRequestBytes, operation.signal);
113
- const invocation = routes.decode(route, {
114
- url: request.url,
115
- method: request.method,
116
- headers: request.headers,
117
- body,
118
- });
120
+ const invocation = routes.decode(route, { url: request.url, method: request.method, headers: request.headers, body }, snapshot.domain, snapshot.table.publication);
119
121
  const idempotencyHeader = request.headers.get(protocol.HTTP_IDEMPOTENCY_KEY);
120
122
  const idempotencyKey = idempotencyHeader === null
121
123
  ? undefined
@@ -136,8 +138,13 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
136
138
  });
137
139
  }
138
140
  if (result.kind === 'binary') {
141
+ if (isStreamingBinary(result.value)) {
142
+ handedOff = true;
143
+ return streamingBinaryResponse(result.value, operation, cors, request);
144
+ }
139
145
  const applicationHeaders = applicationBinaryHeaders(result.value.headers);
140
146
  return new Response(toBody(result.value.body), {
147
+ status: result.value.status ?? 200,
141
148
  headers: {
142
149
  ...cors.response(request, [
143
150
  protocol.http.BINARY_HEADERS_FIELD,
@@ -195,6 +202,66 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, next
195
202
  }
196
203
  });
197
204
  }
205
+ function streamingBinaryResponse(binary, operation, cors, request) {
206
+ const iterator = binary.body[Symbol.asyncIterator]();
207
+ const applicationHeaders = applicationBinaryHeaders(binary.headers);
208
+ let terminal = false;
209
+ let stopping;
210
+ const stop = () => {
211
+ stopping ??= Promise.resolve().then(async () => {
212
+ if (terminal)
213
+ return;
214
+ terminal = true;
215
+ try {
216
+ await iterator.return?.();
217
+ }
218
+ finally {
219
+ operation.settle();
220
+ }
221
+ });
222
+ return stopping;
223
+ };
224
+ const body = new ReadableStream({
225
+ async pull(controller) {
226
+ if (terminal)
227
+ return;
228
+ try {
229
+ const next = await iterator.next();
230
+ if (terminal)
231
+ return;
232
+ if (next.done) {
233
+ terminal = true;
234
+ controller.close();
235
+ operation.settle();
236
+ return;
237
+ }
238
+ controller.enqueue(next.value);
239
+ }
240
+ catch (cause) {
241
+ terminal = true;
242
+ controller.error(cause);
243
+ operation.settle();
244
+ }
245
+ },
246
+ async cancel(reason) {
247
+ const message = typeof reason === 'string' ? reason : 'native binary response cancelled';
248
+ operation.cancel(message);
249
+ await stop();
250
+ },
251
+ });
252
+ operation.onCancel(stop);
253
+ return new Response(body, {
254
+ status: binary.status ?? 200,
255
+ headers: {
256
+ ...cors.response(request, Object.keys(applicationHeaders)),
257
+ ...applicationHeaders,
258
+ 'content-type': binary.mediaType,
259
+ },
260
+ });
261
+ }
262
+ function isStreamingBinary(input) {
263
+ return !(input.body instanceof Uint8Array);
264
+ }
198
265
  function publicationPreflight(cors, request, snapshot, externalOrigin) {
199
266
  if (sameRoute(request.url, snapshot.publication.schema.bundle.href, externalOrigin)) {
200
267
  return cors.preflight(request, { methods: ['GET'], headers: ['if-none-match'] });
@@ -209,7 +276,7 @@ function publicationPreflight(cors, request, snapshot, externalOrigin) {
209
276
  const requestedMethod = request.headers.get('access-control-request-method');
210
277
  const route = requestedMethod === null
211
278
  ? undefined
212
- : routes.match(snapshot.routes, { url: request.url, method: requestedMethod });
279
+ : routes.match(snapshot.table, { url: request.url, method: requestedMethod });
213
280
  return cors.preflight(request, route === undefined ? undefined : nativeCorsRoute(route, requestedMethod));
214
281
  }
215
282
  function nativeCorsRoute(entry, requestedMethod) {
@@ -219,16 +286,16 @@ function nativeCorsRoute(entry, requestedMethod) {
219
286
  routes.HTTP_ROUTE_ISSUER,
220
287
  protocol.HTTP_IDEMPOTENCY_KEY,
221
288
  ]);
222
- if (entry.route.body !== undefined)
289
+ if (entry.body !== undefined)
223
290
  headers.add('content-type');
224
291
  let allowAnyHeaders = false;
225
- for (const pick of Object.values(entry.route.headers ?? {})) {
292
+ for (const pick of Object.values(entry.headers ?? {})) {
226
293
  if (pick === '*')
227
294
  allowAnyHeaders = true;
228
295
  else
229
296
  headers.add(pick.toLowerCase());
230
297
  }
231
- const credential = entry.route.credential;
298
+ const credential = entry.credential;
232
299
  const sources = credential === undefined
233
300
  ? [{ in: 'header' }]
234
301
  : Array.isArray(credential)
@@ -239,9 +306,7 @@ function nativeCorsRoute(entry, requestedMethod) {
239
306
  headers.add((source.name ?? 'authorization').toLowerCase());
240
307
  }
241
308
  return Object.freeze({
242
- methods: Object.freeze([
243
- entry.route.method === '*' ? requestedMethod.toUpperCase() : entry.route.method,
244
- ]),
309
+ methods: Object.freeze([entry.method === '*' ? requestedMethod.toUpperCase() : entry.method]),
245
310
  headers: Object.freeze([...headers]),
246
311
  ...(allowAnyHeaders ? { allowAnyHeaders: true } : {}),
247
312
  });
@@ -270,9 +335,9 @@ function servingSnapshot(source, limits, request, reservations, externalIssuer)
270
335
  function conflictsWithServingRoutes(snapshot, serving) {
271
336
  for (const reservation of serving.reservations) {
272
337
  const href = `${serving.origin}${reservation.path}`;
273
- for (const entry of snapshot.routes) {
274
- if (reservation.methods.some((method) => entry.route.method === '*' || entry.route.method === method) &&
275
- routes.templatesOverlap(entry.route.href, href)) {
338
+ for (const entry of snapshot.table.entries) {
339
+ if (reservation.methods.some((method) => entry.method === '*' || entry.method === method) &&
340
+ routes.templatesOverlap(entry.href, href)) {
276
341
  return true;
277
342
  }
278
343
  }
@@ -446,4 +511,3 @@ function encodedBody(input) {
446
511
  function toBody(input) {
447
512
  return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
448
513
  }
449
- //# sourceMappingURL=routes.js.map
package/dist/server.d.ts CHANGED
@@ -2,4 +2,3 @@ import type { Server, ServerConfig, ServerOptions, PreparedServer } from './type
2
2
  export declare function createServer(config: ServerConfig): Server;
3
3
  /** Validate all static transport configuration before a Host starts Runtime recovery/bootstrap. */
4
4
  export declare function prepareServer(options: ServerOptions): PreparedServer;
5
- //# sourceMappingURL=server.d.ts.map
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { publication as protocolPublication } from '@astrale-os/kernel-protocol';
1
+ import { PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
2
2
  import { captureMethod } from './capture.js';
3
3
  import { prepareCors } from './cors/index.js';
4
4
  import { captureInvocationSource } from './invocation/index.js';
@@ -6,7 +6,9 @@ import { capture as captureExchangeProvider } from './issuer/exchange.js';
6
6
  import { admitIssuer, issuerRouteReservations, mountIssuer } from './issuer/index.js';
7
7
  import { ActiveOperations } from './lifecycle/index.js';
8
8
  import { currentPublicationSnapshot, mountRoutes } from './routes/index.js';
9
- import { createHttpHandler, createServerRouter, handleWebSocketUpgrade } from './transport/index.js';
9
+ import { createHttpHandler } from './transport/http/handler.js';
10
+ import { createServerRouter } from './transport/router.js';
11
+ import { handleWebSocketUpgrade } from './transport/websocket/upgrade.js';
10
12
  export function createServer(config) {
11
13
  const { handler, ...options } = config;
12
14
  return prepareServer(options).create(handler);
@@ -66,7 +68,7 @@ function createPreparedServer(input, config) {
66
68
  app.all(config.path, (context) => handleHttp(context.req.raw, hosts.get(context.req.raw)));
67
69
  if (config.health !== undefined) {
68
70
  app.get(config.health.path, (context) => {
69
- const ready = active.isAccepting && config.health.probe.check();
71
+ const ready = active.isAvailable && config.health.probe.check();
70
72
  return context.json({ status: ready ? 'ready' : 'not-ready' }, ready ? 200 : 503, {
71
73
  ...config.cors.response(context.req.raw),
72
74
  'cache-control': 'no-store',
@@ -210,8 +212,8 @@ function routeReservations(invocationPath, healthPath, issuer, websocket, tokenE
210
212
  }
211
213
  function protocolPublicationPath(issuer) {
212
214
  if (issuer === undefined)
213
- return protocolPublication.PATH;
214
- return new URL(protocolPublication.url(issuer)).pathname;
215
+ return PUBLICATION_PATH;
216
+ return new URL(publicationUrl(issuer)).pathname;
215
217
  }
216
218
  function captureHealthProbe(probe) {
217
219
  const check = captureMethod(probe, 'check', 'Health probe is invalid.');
@@ -262,4 +264,3 @@ function admitRoute(path, label) {
262
264
  }
263
265
  return path;
264
266
  }
265
- //# sourceMappingURL=server.js.map
@@ -2,4 +2,3 @@ export { RequestBodyInvalidError, RequestBodyTooLargeError } from '../errors/ind
2
2
  /** Read a request body while enforcing the limit before retaining excess bytes. */
3
3
  export declare function readBoundedBody(request: Request, maximumBytes: number, signal: AbortSignal): Promise<Uint8Array>;
4
4
  export declare function byteLength(input: string | Uint8Array): number;
5
- //# sourceMappingURL=body.d.ts.map
@@ -48,4 +48,3 @@ export async function readBoundedBody(request, maximumBytes, signal) {
48
48
  export function byteLength(input) {
49
49
  return typeof input === 'string' ? new TextEncoder().encode(input).byteLength : input.byteLength;
50
50
  }
51
- //# sourceMappingURL=body.js.map
@@ -1,5 +1,4 @@
1
- import { codecs } from '@astrale-os/kernel-protocol';
1
+ import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
2
  export type LoadedCodec = codecs.Codec;
3
3
  /** Load the built-in Server representation set before pure Protocol binding selection. */
4
4
  export declare function loadCodecs(): Promise<readonly [codecs.Codec, codecs.Codec]>;
5
- //# sourceMappingURL=codecs.d.ts.map
@@ -1,6 +1,5 @@
1
- import { codecs } from '@astrale-os/kernel-protocol';
1
+ import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
2
  /** Load the built-in Server representation set before pure Protocol binding selection. */
3
3
  export async function loadCodecs() {
4
4
  return Object.freeze([codecs.json, await codecs.loadMsgpack()]);
5
5
  }
6
- //# sourceMappingURL=codecs.js.map
@@ -1,4 +1,4 @@
1
- import { errors } from '@astrale-os/kernel-protocol';
1
+ import * as errors from '@astrale-os/kernel-protocol/errors';
2
2
  export type CredentialResult = {
3
3
  readonly ok: true;
4
4
  readonly credential: string | undefined;
@@ -8,4 +8,3 @@ export type CredentialResult = {
8
8
  };
9
9
  /** Resolve exactly zero or one untrusted credential channel without authenticating it. */
10
10
  export declare function resolveCredential(envelope: string | undefined, authorization: string | null): CredentialResult;
11
- //# sourceMappingURL=credential.d.ts.map
@@ -1,4 +1,4 @@
1
- import { errors } from '@astrale-os/kernel-protocol';
1
+ import * as errors from '@astrale-os/kernel-protocol/errors';
2
2
  /** Resolve exactly zero or one untrusted credential channel without authenticating it. */
3
3
  export function resolveCredential(envelope, authorization) {
4
4
  if (envelope !== undefined && authorization !== null) {
@@ -26,4 +26,3 @@ function failure(message) {
26
26
  error: { code: errors.CODES.INVALID_REQUEST, message },
27
27
  };
28
28
  }
29
- //# sourceMappingURL=credential.js.map
@@ -15,4 +15,3 @@ export interface HttpHandlerConfig {
15
15
  readonly currentPublication?: (request: Request) => PublicationSnapshot | undefined;
16
16
  }
17
17
  export declare function createHttpHandler(config: HttpHandlerConfig): (request: Request, host?: ApplicationHost) => Promise<Response>;
18
- //# sourceMappingURL=handler.d.ts.map
@@ -1,4 +1,6 @@
1
- import { codecs, errors, invocation as protocol } from '@astrale-os/kernel-protocol';
1
+ import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
+ import * as errors from '@astrale-os/kernel-protocol/errors';
3
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
2
4
  import { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../../errors/index.js';
3
5
  import { invocationContext, publicationRouteDecision, } from '../../invocation/index.js';
4
6
  import { readBoundedBody } from '../body.js';
@@ -176,4 +178,3 @@ function noStore(response) {
176
178
  headers.set('cache-control', 'no-store');
177
179
  return new Response(response.body, { status: response.status, headers });
178
180
  }
179
- //# sourceMappingURL=handler.js.map
@@ -1,3 +1,2 @@
1
1
  export { createHttpHandler, type HttpHandlerConfig } from './handler.js';
2
2
  export { encodeEnvelope, renderBinary, renderHttpError, renderHttpResult, renderHttpStale, renderRedirect, type CorsHeaders, type ErrorRenderOptions, type RenderContext, } from './render.js';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,2 @@
1
1
  export { createHttpHandler } from './handler.js';
2
2
  export { encodeEnvelope, renderBinary, renderHttpError, renderHttpResult, renderHttpStale, renderRedirect, } from './render.js';
3
- //# sourceMappingURL=index.js.map
@@ -1,9 +1,12 @@
1
- import { errors, invocation as protocol, type codecs } from '@astrale-os/kernel-protocol';
1
+ import type { BufferedBinaryResult, StreamingBinaryResult } from '@astrale-os/kernel-core/callable';
2
+ import type { Codec } from '@astrale-os/kernel-protocol/codecs';
3
+ import * as errors from '@astrale-os/kernel-protocol/errors';
4
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
2
5
  import type { Handler } from '../../invocation/index.js';
3
6
  import type { ActiveOperation } from '../../lifecycle/index.js';
4
7
  export interface RenderContext {
5
8
  readonly requestId: protocol.RequestId;
6
- readonly codec: codecs.Codec;
9
+ readonly codec: Codec;
7
10
  readonly cors: CorsHeaders;
8
11
  readonly invocation: protocol.InvocationId;
9
12
  readonly active: ActiveOperation;
@@ -17,14 +20,14 @@ export interface ErrorRenderOptions {
17
20
  }
18
21
  type HandlerResult = Awaited<ReturnType<Handler['dispatch']>>;
19
22
  export declare function renderHttpResult(result: HandlerResult, context: RenderContext): Response;
20
- export declare function renderBinary(requestId: protocol.RequestId, invocation: protocol.InvocationId, binary: Extract<HandlerResult, {
21
- readonly kind: 'binary';
22
- }>['value']): protocol.BinaryResponse;
23
+ export declare function renderBinary(requestId: protocol.RequestId, invocation: protocol.InvocationId, binary: BufferedBinaryResult): protocol.BinaryResponse;
23
24
  export declare function renderRedirect(requestId: protocol.RequestId, invocation: protocol.InvocationId, redirect: Extract<HandlerResult, {
24
25
  readonly kind: 'redirect';
25
26
  }>['redirect']): protocol.RedirectResponse;
26
- export declare function renderHttpError(requestId: protocol.RequestId, invocation: protocol.InvocationId, error: errors.ErrorPayload, codec: codecs.Codec, cors: CorsHeaders, options?: ErrorRenderOptions): Response;
27
- export declare function renderHttpStale(requestId: protocol.RequestId, invocation: protocol.InvocationId, current: protocol.Stale['current'], codec: codecs.Codec, cors: CorsHeaders, maximumResponseBytes?: number): Response;
28
- export declare function encodeEnvelope(codec: codecs.Codec, input: unknown): string | Uint8Array;
27
+ export declare function renderHttpError(requestId: protocol.RequestId, invocation: protocol.InvocationId, error: errors.ErrorPayload, codec: Codec, cors: CorsHeaders, options?: ErrorRenderOptions): Response;
28
+ export declare function renderHttpStale(requestId: protocol.RequestId, invocation: protocol.InvocationId, current: protocol.Stale['current'], codec: Codec, cors: CorsHeaders, maximumResponseBytes?: number): Response;
29
+ export declare function encodeEnvelope(codec: Codec, input: unknown): string | Uint8Array;
30
+ export declare function isStreamingBinary(input: Extract<HandlerResult, {
31
+ readonly kind: 'binary';
32
+ }>['value']): input is StreamingBinaryResult;
29
33
  export {};
30
- //# sourceMappingURL=render.d.ts.map
@@ -1,4 +1,6 @@
1
- import { errors, invocation as protocol, routes } from '@astrale-os/kernel-protocol';
1
+ import * as errors from '@astrale-os/kernel-protocol/errors';
2
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
3
+ import { HTTP_ROUTE_MISS, HTTP_ROUTE_STALE } from '@astrale-os/kernel-protocol/routes/routing';
2
4
  import { classifyError } from '../../errors/index.js';
3
5
  import { ResponseBodyTooLargeError } from '../../errors/index.js';
4
6
  import { interruptedStreamError } from '../stream-error.js';
@@ -17,6 +19,8 @@ export function renderHttpResult(result, context) {
17
19
  case 'miss':
18
20
  return settled(context.active, envelopeResponse(context.codec, protocol.miss(context.requestId, context.invocation), context.cors(), 200, true, context.maximumResponseBytes));
19
21
  case 'binary': {
22
+ if (isStreamingBinary(result.value))
23
+ return streamingBinaryHttpResponse(result.value, context);
20
24
  const binary = renderBinary(context.requestId, context.invocation, result.value);
21
25
  return settled(context.active, binaryHttpResponse(binary, context.cors, context.maximumResponseBytes));
22
26
  }
@@ -28,8 +32,8 @@ export function renderBinary(requestId, invocation, binary) {
28
32
  const reserved = Object.keys(binary.headers ?? {}).find((name) => {
29
33
  const normalized = name.toLowerCase();
30
34
  return (normalized === protocol.http.INVOCATION_FIELD ||
31
- normalized === routes.HTTP_ROUTE_STALE ||
32
- normalized === routes.HTTP_ROUTE_MISS);
35
+ normalized === HTTP_ROUTE_STALE ||
36
+ normalized === HTTP_ROUTE_MISS);
33
37
  });
34
38
  if (reserved !== undefined)
35
39
  throw new TypeError(`${reserved} is reserved by Protocol.`);
@@ -39,6 +43,7 @@ export function renderBinary(requestId, invocation, binary) {
39
43
  invocation,
40
44
  body: binary.body,
41
45
  mediaType: binary.mediaType,
46
+ status: binary.status ?? 200,
42
47
  ...(binary.headers === undefined ? {} : { headers: binary.headers }),
43
48
  });
44
49
  }
@@ -73,11 +78,74 @@ function envelopeResponse(codec, input, cors, status = 200, negotiated = true, m
73
78
  }
74
79
  function binaryHttpResponse(binary, cors, maximumResponseBytes) {
75
80
  requireResponseBound(binary.body, maximumResponseBytes);
81
+ const headers = binaryHttpHeaders(binary, cors, false);
82
+ return new Response(toBody(binary.body), { status: binary.status, headers });
83
+ }
84
+ function streamingBinaryHttpResponse(binary, context) {
85
+ const iterator = binary.body[Symbol.asyncIterator]();
86
+ const headers = binaryHttpHeaders(Object.freeze({
87
+ requestId: context.requestId,
88
+ invocation: context.invocation,
89
+ mediaType: binary.mediaType,
90
+ headers: binary.headers,
91
+ }), context.cors, true);
92
+ let terminal = false;
93
+ let stopping;
94
+ const stop = () => {
95
+ stopping ??= Promise.resolve().then(async () => {
96
+ if (terminal)
97
+ return;
98
+ terminal = true;
99
+ try {
100
+ await iterator.return?.();
101
+ }
102
+ finally {
103
+ context.active.settle();
104
+ }
105
+ });
106
+ return stopping;
107
+ };
108
+ const body = new ReadableStream({
109
+ async pull(controller) {
110
+ if (terminal)
111
+ return;
112
+ try {
113
+ const next = await iterator.next();
114
+ if (terminal)
115
+ return;
116
+ if (next.done) {
117
+ terminal = true;
118
+ controller.close();
119
+ context.active.settle();
120
+ return;
121
+ }
122
+ controller.enqueue(next.value);
123
+ }
124
+ catch (cause) {
125
+ terminal = true;
126
+ controller.error(cause);
127
+ context.active.settle();
128
+ }
129
+ },
130
+ async cancel(reason) {
131
+ const message = typeof reason === 'string' ? reason : 'binary response consumer cancelled';
132
+ context.active.cancel(message);
133
+ await stop();
134
+ },
135
+ });
136
+ context.active.onCancel(stop);
137
+ return new Response(body, { status: binary.status ?? 200, headers });
138
+ }
139
+ export function isStreamingBinary(input) {
140
+ return !(input.body instanceof Uint8Array);
141
+ }
142
+ function binaryHttpHeaders(binary, cors, streaming) {
76
143
  const applicationHeaders = Object.keys(binary.headers ?? {}).filter((name) => protocol.http.isApplicationBinaryHeader(name));
77
144
  const headers = new Headers(cors([
78
145
  protocol.http.REQUEST_ID_FIELD,
79
146
  protocol.http.INVOCATION_FIELD,
80
147
  protocol.http.BINARY_HEADERS_FIELD,
148
+ ...(streaming ? [protocol.http.BINARY_DELIVERY_FIELD] : []),
81
149
  ...applicationHeaders,
82
150
  ]));
83
151
  for (const [name, value] of Object.entries(binary.headers ?? {})) {
@@ -88,9 +156,12 @@ function binaryHttpResponse(binary, cors, maximumResponseBytes) {
88
156
  headers.set(protocol.http.BINARY_HEADERS_FIELD, protocol.http.encodeBinaryHeaderNames(binary.headers));
89
157
  headers.set(protocol.http.REQUEST_ID_FIELD, String(binary.requestId));
90
158
  headers.set(protocol.http.INVOCATION_FIELD, protocol.http.encodeInvocationId(binary.invocation));
159
+ if (streaming) {
160
+ headers.set(protocol.http.BINARY_DELIVERY_FIELD, protocol.http.STREAMING_BINARY_DELIVERY);
161
+ }
91
162
  headers.set('cache-control', 'no-store');
92
163
  appendVary(headers, 'accept');
93
- return new Response(toBody(binary.body), { status: 200, headers });
164
+ return headers;
94
165
  }
95
166
  function streamHttpResponse(stream, context) {
96
167
  const iterator = stream[Symbol.asyncIterator]();
@@ -284,4 +355,3 @@ function toBody(input) {
284
355
  const copy = new Uint8Array(input);
285
356
  return copy.buffer;
286
357
  }
287
- //# sourceMappingURL=render.js.map