@astrale-os/kernel-server 0.5.0-beta.1 → 0.5.0-beta.11

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 (178) 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 +0 -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 +3 -4
  10. package/dist/errors/availability.js +4 -5
  11. package/dist/errors/classify.d.ts +2 -3
  12. package/dist/errors/classify.js +4 -217
  13. package/dist/errors/index.d.ts +1 -2
  14. package/dist/errors/index.js +1 -2
  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 +3 -3
  20. package/dist/index.js +2 -2
  21. package/dist/invocation/context.d.ts +2 -3
  22. package/dist/invocation/context.js +2 -4
  23. package/dist/invocation/identity.d.ts +9 -0
  24. package/dist/invocation/identity.js +11 -0
  25. package/dist/invocation/index.d.ts +3 -2
  26. package/dist/invocation/index.js +2 -1
  27. package/dist/invocation/publication-route.d.ts +13 -0
  28. package/dist/invocation/publication-route.js +44 -0
  29. package/dist/invocation/types.d.ts +38 -10
  30. package/dist/invocation/types.js +0 -1
  31. package/dist/issuer/exchange.d.ts +12 -0
  32. package/dist/issuer/exchange.js +136 -0
  33. package/dist/issuer/index.d.ts +1 -1
  34. package/dist/issuer/index.js +0 -1
  35. package/dist/issuer/routes.d.ts +3 -3
  36. package/dist/issuer/routes.js +25 -14
  37. package/dist/issuer/types.d.ts +2 -2
  38. package/dist/issuer/types.js +0 -1
  39. package/dist/lifecycle/active.d.ts +0 -1
  40. package/dist/lifecycle/active.js +0 -1
  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 +9 -0
  44. package/dist/{installation → publication/delivery}/handler.js +29 -28
  45. package/dist/publication/delivery/index.d.ts +2 -0
  46. package/dist/publication/delivery/index.js +1 -0
  47. package/dist/publication/delivery/types.d.ts +11 -0
  48. package/dist/publication/delivery/types.js +1 -0
  49. package/dist/publication/index.d.ts +1 -1
  50. package/dist/publication/index.js +1 -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 +6 -5
  60. package/dist/routes/routes.js +51 -41
  61. package/dist/server.d.ts +0 -1
  62. package/dist/server.js +56 -16
  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 +2 -1
  70. package/dist/transport/http/handler.js +47 -37
  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 +13 -11
  74. package/dist/transport/http/render.js +50 -39
  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 +4 -3
  86. package/dist/transport/websocket/session.js +46 -39
  87. package/dist/transport/websocket/upgrade.d.ts +2 -1
  88. package/dist/transport/websocket/upgrade.js +2 -2
  89. package/dist/types.d.ts +13 -10
  90. package/dist/types.js +0 -1
  91. package/package.json +23 -11
  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/installation/handler.d.ts +0 -10
  113. package/dist/installation/handler.d.ts.map +0 -1
  114. package/dist/installation/handler.js.map +0 -1
  115. package/dist/installation/index.d.ts +0 -3
  116. package/dist/installation/index.d.ts.map +0 -1
  117. package/dist/installation/index.js +0 -2
  118. package/dist/installation/index.js.map +0 -1
  119. package/dist/installation/types.d.ts +0 -12
  120. package/dist/installation/types.d.ts.map +0 -1
  121. package/dist/installation/types.js +0 -2
  122. package/dist/installation/types.js.map +0 -1
  123. package/dist/invocation/context.d.ts.map +0 -1
  124. package/dist/invocation/context.js.map +0 -1
  125. package/dist/invocation/index.d.ts.map +0 -1
  126. package/dist/invocation/index.js.map +0 -1
  127. package/dist/invocation/types.d.ts.map +0 -1
  128. package/dist/invocation/types.js.map +0 -1
  129. package/dist/issuer/index.d.ts.map +0 -1
  130. package/dist/issuer/index.js.map +0 -1
  131. package/dist/issuer/routes.d.ts.map +0 -1
  132. package/dist/issuer/routes.js.map +0 -1
  133. package/dist/issuer/types.d.ts.map +0 -1
  134. package/dist/issuer/types.js.map +0 -1
  135. package/dist/lifecycle/active.d.ts.map +0 -1
  136. package/dist/lifecycle/active.js.map +0 -1
  137. package/dist/lifecycle/index.d.ts.map +0 -1
  138. package/dist/lifecycle/index.js.map +0 -1
  139. package/dist/publication/index.d.ts.map +0 -1
  140. package/dist/publication/index.js.map +0 -1
  141. package/dist/publication/snapshot.d.ts.map +0 -1
  142. package/dist/publication/snapshot.js.map +0 -1
  143. package/dist/publication/types.d.ts.map +0 -1
  144. package/dist/publication/types.js.map +0 -1
  145. package/dist/routes/index.d.ts.map +0 -1
  146. package/dist/routes/index.js.map +0 -1
  147. package/dist/routes/routes.d.ts.map +0 -1
  148. package/dist/routes/routes.js.map +0 -1
  149. package/dist/server.d.ts.map +0 -1
  150. package/dist/server.js.map +0 -1
  151. package/dist/transport/body.d.ts.map +0 -1
  152. package/dist/transport/body.js.map +0 -1
  153. package/dist/transport/codecs.d.ts.map +0 -1
  154. package/dist/transport/codecs.js.map +0 -1
  155. package/dist/transport/credential.d.ts.map +0 -1
  156. package/dist/transport/credential.js.map +0 -1
  157. package/dist/transport/http/handler.d.ts.map +0 -1
  158. package/dist/transport/http/handler.js.map +0 -1
  159. package/dist/transport/http/index.d.ts.map +0 -1
  160. package/dist/transport/http/index.js.map +0 -1
  161. package/dist/transport/http/render.d.ts.map +0 -1
  162. package/dist/transport/http/render.js.map +0 -1
  163. package/dist/transport/index.d.ts.map +0 -1
  164. package/dist/transport/index.js.map +0 -1
  165. package/dist/transport/router.d.ts.map +0 -1
  166. package/dist/transport/router.js.map +0 -1
  167. package/dist/transport/stream-error.d.ts.map +0 -1
  168. package/dist/transport/stream-error.js.map +0 -1
  169. package/dist/transport/types.d.ts.map +0 -1
  170. package/dist/transport/types.js.map +0 -1
  171. package/dist/transport/websocket/index.d.ts.map +0 -1
  172. package/dist/transport/websocket/index.js.map +0 -1
  173. package/dist/transport/websocket/session.d.ts.map +0 -1
  174. package/dist/transport/websocket/session.js.map +0 -1
  175. package/dist/transport/websocket/upgrade.d.ts.map +0 -1
  176. package/dist/transport/websocket/upgrade.js.map +0 -1
  177. package/dist/types.d.ts.map +0 -1
  178. package/dist/types.js.map +0 -1
@@ -1,28 +1,43 @@
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 { 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
- import { handleInstallation } from '../installation/index.js';
4
8
  import { invocationContext } from '../invocation/index.js';
9
+ import { handleDelivery } from '../publication/delivery/index.js';
5
10
  import { readPreparedPublicationSnapshot, } from '../publication/index.js';
6
- import { interruptedStreamError, readBoundedBody } from '../transport/index.js';
7
- export function mountRoutes(app, source, installationProvider, handler, active, limits, cors, hostFor, reservations, externalOrigin) {
11
+ import { readBoundedBody } from '../transport/body.js';
12
+ import {} from '../transport/router.js';
13
+ import { interruptedStreamError } from '../transport/stream-error.js';
14
+ export function mountRoutes(app, source, deliveryProvider, handler, active, nextInvocation, limits, cors, hostFor, reservations, externalIssuer) {
15
+ const publicationPath = externalIssuer === undefined
16
+ ? PUBLICATION_PATH
17
+ : new URL(publicationUrl(externalIssuer)).pathname;
18
+ const externalOrigin = externalIssuer === undefined ? undefined : new URL(externalIssuer).origin;
8
19
  if (cors.enabled) {
9
- app.options(publication.PATH, (context) => cors.preflight(context.req.raw, {
20
+ app.options(publicationPath, (context) => cors.preflight(context.req.raw, {
10
21
  methods: ['GET'],
11
22
  headers: ['if-none-match'],
12
23
  }));
13
24
  }
14
- app.get(publication.PATH, (context) => {
25
+ app.get(publicationPath, (context) => {
15
26
  const responseCors = cors.response(context.req.raw, ['etag']);
16
27
  if (!active.isAccepting)
17
28
  return response({ error: 'Publication is unavailable while draining.' }, 503, responseCors);
18
- const snapshot = servingSnapshot(source, limits, context.req.raw, reservations, externalOrigin);
29
+ const snapshot = servingSnapshot(source, limits, context.req.raw, reservations, externalIssuer);
19
30
  if (snapshot === undefined)
20
31
  return response({ error: 'Publication is not ready.' }, 503, responseCors);
21
32
  const body = snapshot.publicationBytes;
22
33
  if (context.req.raw.headers.get('if-none-match') === quoteEtag(snapshot.publication.etag)) {
23
34
  return new Response(null, {
24
35
  status: 304,
25
- headers: { ...responseCors, etag: quoteEtag(snapshot.publication.etag) },
36
+ headers: {
37
+ ...responseCors,
38
+ etag: quoteEtag(snapshot.publication.etag),
39
+ 'cache-control': 'public, max-age=0, must-revalidate, no-transform',
40
+ },
26
41
  });
27
42
  }
28
43
  return new Response(toBody(body), {
@@ -31,12 +46,12 @@ export function mountRoutes(app, source, installationProvider, handler, active,
31
46
  ...responseCors,
32
47
  'content-type': 'application/json',
33
48
  etag: quoteEtag(snapshot.publication.etag),
34
- 'cache-control': 'public, max-age=0, must-revalidate',
49
+ 'cache-control': 'public, max-age=0, must-revalidate, no-transform',
35
50
  },
36
51
  });
37
52
  });
38
53
  app.all('*', async (context) => {
39
- const snapshot = servingSnapshot(source, limits, context.req.raw, reservations, externalOrigin);
54
+ const snapshot = servingSnapshot(source, limits, context.req.raw, reservations, externalIssuer);
40
55
  if (snapshot === undefined)
41
56
  return context.notFound();
42
57
  const request = context.req.raw;
@@ -50,22 +65,22 @@ export function mountRoutes(app, source, installationProvider, handler, active,
50
65
  if (request.method !== 'GET')
51
66
  return response({ error: 'Bundle retrieval requires GET.' }, 405, responseCors);
52
67
  const body = snapshot.bundle;
53
- if (body.byteLength !== snapshot.publication.schema.bundle.size) {
68
+ if (body.byteLength !== snapshot.publication.schema.bundle.ref.size) {
54
69
  return response({ error: 'Bundle differs from its Publication.' }, 500, responseCors);
55
70
  }
56
71
  return new Response(toBody(body), {
57
72
  status: 200,
58
73
  headers: {
59
74
  ...responseCors,
60
- 'content-type': snapshot.publication.schema.bundle.mediaType,
61
- etag: quoteEtag(snapshot.publication.schema.bundle.digest),
62
- 'cache-control': 'public, max-age=31536000, immutable',
75
+ 'content-type': snapshot.publication.schema.bundle.ref.mediaType,
76
+ etag: quoteEtag(snapshot.publication.schema.bundle.ref.digest),
77
+ 'cache-control': 'public, max-age=31536000, immutable, no-transform',
63
78
  },
64
79
  });
65
80
  }
66
- const installationUrl = snapshot.publication.installation?.href;
67
- if (installationUrl !== undefined && sameRoute(request.url, installationUrl, externalOrigin)) {
68
- return handleInstallation(request, snapshot, installationProvider, active, limits, cors.response(request));
81
+ const deliveryUrl = snapshot.publication.delivery?.href;
82
+ if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
83
+ return handleDelivery(request, snapshot, deliveryProvider, active, limits, cors.response(request));
69
84
  }
70
85
  let routing;
71
86
  try {
@@ -81,7 +96,7 @@ export function mountRoutes(app, source, installationProvider, handler, active,
81
96
  },
82
97
  });
83
98
  }
84
- routing = routes.decodeRouting(request, publication.reference(snapshot.publication));
99
+ routing = routes.decodeRouting(request, publicationReference(snapshot.publication));
85
100
  }
86
101
  catch (cause) {
87
102
  if (cause instanceof routes.RouteError) {
@@ -89,9 +104,10 @@ export function mountRoutes(app, source, installationProvider, handler, active,
89
104
  }
90
105
  throw cause;
91
106
  }
92
- const route = routes.match(snapshot.routes, request);
107
+ const route = routes.match(snapshot.table, request);
93
108
  if (route === undefined)
94
109
  return context.notFound();
110
+ const assigned = nextInvocation();
95
111
  const operation = active.begin(limits.invocationTimeoutMs, request.signal);
96
112
  if (operation === undefined)
97
113
  return nativeError({
@@ -101,12 +117,7 @@ export function mountRoutes(app, source, installationProvider, handler, active,
101
117
  let handedOff = false;
102
118
  try {
103
119
  const body = await readBoundedBody(request, limits.maximumRequestBytes, operation.signal);
104
- const invocation = routes.decode(route, {
105
- url: request.url,
106
- method: request.method,
107
- headers: request.headers,
108
- body,
109
- });
120
+ const invocation = routes.decode(route, { url: request.url, method: request.method, headers: request.headers, body }, snapshot.domain, snapshot.table.publication);
110
121
  const idempotencyHeader = request.headers.get(protocol.HTTP_IDEMPOTENCY_KEY);
111
122
  const idempotencyKey = idempotencyHeader === null
112
123
  ? undefined
@@ -116,7 +127,7 @@ export function mountRoutes(app, source, installationProvider, handler, active,
116
127
  ...(invocation.credential === undefined ? {} : { credential: invocation.credential }),
117
128
  ...(routing === undefined ? {} : { via: routing }),
118
129
  publication: snapshot.snapshot,
119
- context: invocationContext(operation, active, hostFor(request), body, idempotencyKey),
130
+ context: invocationContext(assigned, operation, active, hostFor(request), body, idempotencyKey),
120
131
  }));
121
132
  if (result.kind === 'value') {
122
133
  return new Response(encodedBody(codecs.json.encode(result.value)), {
@@ -190,8 +201,8 @@ function publicationPreflight(cors, request, snapshot, externalOrigin) {
190
201
  if (sameRoute(request.url, snapshot.publication.schema.bundle.href, externalOrigin)) {
191
202
  return cors.preflight(request, { methods: ['GET'], headers: ['if-none-match'] });
192
203
  }
193
- const installationUrl = snapshot.publication.installation?.href;
194
- if (installationUrl !== undefined && sameRoute(request.url, installationUrl, externalOrigin)) {
204
+ const deliveryUrl = snapshot.publication.delivery?.href;
205
+ if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
195
206
  return cors.preflight(request, {
196
207
  methods: ['POST'],
197
208
  headers: ['authorization', 'content-type'],
@@ -200,7 +211,7 @@ function publicationPreflight(cors, request, snapshot, externalOrigin) {
200
211
  const requestedMethod = request.headers.get('access-control-request-method');
201
212
  const route = requestedMethod === null
202
213
  ? undefined
203
- : routes.match(snapshot.routes, { url: request.url, method: requestedMethod });
214
+ : routes.match(snapshot.table, { url: request.url, method: requestedMethod });
204
215
  return cors.preflight(request, route === undefined ? undefined : nativeCorsRoute(route, requestedMethod));
205
216
  }
206
217
  function nativeCorsRoute(entry, requestedMethod) {
@@ -210,16 +221,16 @@ function nativeCorsRoute(entry, requestedMethod) {
210
221
  routes.HTTP_ROUTE_ISSUER,
211
222
  protocol.HTTP_IDEMPOTENCY_KEY,
212
223
  ]);
213
- if (entry.route.body !== undefined)
224
+ if (entry.body !== undefined)
214
225
  headers.add('content-type');
215
226
  let allowAnyHeaders = false;
216
- for (const pick of Object.values(entry.route.headers ?? {})) {
227
+ for (const pick of Object.values(entry.headers ?? {})) {
217
228
  if (pick === '*')
218
229
  allowAnyHeaders = true;
219
230
  else
220
231
  headers.add(pick.toLowerCase());
221
232
  }
222
- const credential = entry.route.credential;
233
+ const credential = entry.credential;
223
234
  const sources = credential === undefined
224
235
  ? [{ in: 'header' }]
225
236
  : Array.isArray(credential)
@@ -230,9 +241,7 @@ function nativeCorsRoute(entry, requestedMethod) {
230
241
  headers.add((source.name ?? 'authorization').toLowerCase());
231
242
  }
232
243
  return Object.freeze({
233
- methods: Object.freeze([
234
- entry.route.method === '*' ? requestedMethod.toUpperCase() : entry.route.method,
235
- ]),
244
+ methods: Object.freeze([entry.method === '*' ? requestedMethod.toUpperCase() : entry.method]),
236
245
  headers: Object.freeze([...headers]),
237
246
  ...(allowAnyHeaders ? { allowAnyHeaders: true } : {}),
238
247
  });
@@ -245,23 +254,25 @@ export function currentPublicationSnapshot(source, limits, serving) {
245
254
  if (prepared === undefined ||
246
255
  prepared.publicationBytes.byteLength > limits.maximumPublicationBytes ||
247
256
  prepared.bundle.byteLength > limits.maximumBundleBytes ||
257
+ (serving?.issuer !== undefined && prepared.publication.identity.issuer !== serving.issuer) ||
248
258
  (serving !== undefined && conflictsWithServingRoutes(prepared, serving))) {
249
259
  return undefined;
250
260
  }
251
261
  return prepared;
252
262
  }
253
- function servingSnapshot(source, limits, request, reservations, externalOrigin) {
263
+ function servingSnapshot(source, limits, request, reservations, externalIssuer) {
254
264
  return currentPublicationSnapshot(source, limits, {
255
- origin: externalOrigin ?? new URL(request.url).origin,
265
+ origin: externalIssuer === undefined ? new URL(request.url).origin : new URL(externalIssuer).origin,
266
+ ...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
256
267
  reservations,
257
268
  });
258
269
  }
259
270
  function conflictsWithServingRoutes(snapshot, serving) {
260
271
  for (const reservation of serving.reservations) {
261
272
  const href = `${serving.origin}${reservation.path}`;
262
- for (const entry of snapshot.routes) {
263
- if (reservation.methods.some((method) => entry.route.method === '*' || entry.route.method === method) &&
264
- routes.templatesOverlap(entry.route.href, href)) {
273
+ for (const entry of snapshot.table.entries) {
274
+ if (reservation.methods.some((method) => entry.method === '*' || entry.method === method) &&
275
+ routes.templatesOverlap(entry.href, href)) {
265
276
  return true;
266
277
  }
267
278
  }
@@ -435,4 +446,3 @@ function encodedBody(input) {
435
446
  function toBody(input) {
436
447
  return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
437
448
  }
438
- //# 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,9 +1,14 @@
1
+ import { PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
1
2
  import { captureMethod } from './capture.js';
2
3
  import { prepareCors } from './cors/index.js';
4
+ import { captureInvocationSource } from './invocation/index.js';
5
+ import { capture as captureExchangeProvider } from './issuer/exchange.js';
3
6
  import { admitIssuer, issuerRouteReservations, mountIssuer } from './issuer/index.js';
4
7
  import { ActiveOperations } from './lifecycle/index.js';
5
8
  import { currentPublicationSnapshot, mountRoutes } from './routes/index.js';
6
- 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';
7
12
  export function createServer(config) {
8
13
  const { handler, ...options } = config;
9
14
  return prepareServer(options).create(handler);
@@ -20,10 +25,12 @@ function createPreparedServer(input, config) {
20
25
  const active = new ActiveOperations(config.limits.maximumConcurrentOperations);
21
26
  const app = createServerRouter();
22
27
  const hosts = new WeakMap();
23
- const externalOrigin = config.issuer === undefined ? undefined : new URL(config.issuer.issuer).origin;
28
+ const externalIssuer = config.issuer?.issuer;
29
+ const externalOrigin = externalIssuer === undefined ? undefined : new URL(externalIssuer).origin;
24
30
  const handleHttp = createHttpHandler({
25
31
  handler,
26
32
  active,
33
+ invocation: config.invocation,
27
34
  ...config.limits,
28
35
  cors: config.cors,
29
36
  websocket: config.websocket !== undefined,
@@ -32,6 +39,7 @@ function createPreparedServer(input, config) {
32
39
  : {
33
40
  currentPublication: (request) => currentPublicationSnapshot(config.publication, config.limits, {
34
41
  origin: externalOrigin ?? new URL(request.url).origin,
42
+ ...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
35
43
  reservations: config.routeReservations,
36
44
  })?.snapshot,
37
45
  }),
@@ -42,6 +50,7 @@ function createPreparedServer(input, config) {
42
50
  adapter: config.websocket,
43
51
  handler,
44
52
  active,
53
+ invocation: config.invocation,
45
54
  ...config.limits,
46
55
  cors: config.cors,
47
56
  ...(config.publication === undefined
@@ -49,6 +58,7 @@ function createPreparedServer(input, config) {
49
58
  : {
50
59
  currentPublication: () => currentPublicationSnapshot(config.publication, config.limits, {
51
60
  origin: externalOrigin ?? new URL(context.req.raw.url).origin,
61
+ ...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
52
62
  reservations: config.routeReservations,
53
63
  })?.snapshot,
54
64
  }),
@@ -69,10 +79,10 @@ function createPreparedServer(input, config) {
69
79
  }
70
80
  }
71
81
  if (config.issuer !== undefined) {
72
- mountIssuer(app, config.issuer, config.cors, active, config.limits.invocationTimeoutMs);
82
+ mountIssuer(app, config.issuer, config.cors, active, config.limits.invocationTimeoutMs, config.tokenExchange);
73
83
  }
74
84
  if (config.publication !== undefined) {
75
- mountRoutes(app, config.publication, config.installation, handler, active, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalOrigin);
85
+ mountRoutes(app, config.publication, config.delivery, handler, active, config.invocation, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalIssuer);
76
86
  }
77
87
  app.onError((_error, context) => new Response('{"error":"Internal server error."}', {
78
88
  status: 500,
@@ -121,9 +131,14 @@ function captureHandler(input) {
121
131
  return Object.freeze({ dispatch });
122
132
  }
123
133
  function admitOptions(config) {
134
+ const invocation = captureInvocationSource(config.invocation);
124
135
  const limits = admitLimits(config.limits);
125
136
  const path = admitRoute(config.path ?? '/', 'invocation path');
126
137
  const issuer = config.issuer === undefined ? undefined : admitIssuer(config.issuer);
138
+ const tokenExchange = config.tokenExchange === undefined ? undefined : captureExchangeProvider(config.tokenExchange);
139
+ if (tokenExchange !== undefined && issuer === undefined) {
140
+ throw new TypeError('Token exchange requires issuer configuration.');
141
+ }
127
142
  const health = config.health === undefined || config.health === false
128
143
  ? undefined
129
144
  : Object.freeze({
@@ -132,34 +147,52 @@ function admitOptions(config) {
132
147
  });
133
148
  const websocket = config.websocket === undefined ? undefined : captureWebSocket(config.websocket);
134
149
  const publication = config.publication === undefined ? undefined : capturePublicationSource(config.publication);
135
- const installation = config.installation === undefined ? undefined : captureInstallationProvider(config.installation);
136
- if (installation !== undefined && publication === undefined) {
137
- throw new TypeError('Installation requires a Publication source.');
150
+ const delivery = config.delivery === undefined ? undefined : captureDeliveryProvider(config.delivery);
151
+ if (delivery !== undefined && publication === undefined) {
152
+ throw new TypeError('Publication Delivery requires a Publication source.');
138
153
  }
154
+ const publicationPath = publication === undefined
155
+ ? undefined
156
+ : issuer === undefined
157
+ ? protocolPublicationPath()
158
+ : protocolPublicationPath(issuer.issuer);
139
159
  if (health?.path === path)
140
160
  throw new TypeError('Health and invocation paths must be distinct.');
161
+ if (publicationPath === path) {
162
+ throw new TypeError('Publication and invocation paths must be distinct.');
163
+ }
164
+ if (health !== undefined && publicationPath === health.path) {
165
+ throw new TypeError('Health and Publication paths must be distinct.');
166
+ }
141
167
  if (issuer !== undefined) {
142
- const issuerPaths = issuerRouteReservations(issuer).map(({ path }) => path);
168
+ const issuerReservations = issuerRouteReservations(issuer, tokenExchange !== undefined);
169
+ const issuerPaths = issuerReservations.map(({ path }) => path);
143
170
  if (health !== undefined && issuerPaths.includes(health.path)) {
144
171
  throw new TypeError('Health and issuer paths must be distinct.');
145
172
  }
146
173
  if (websocket !== undefined && issuerPaths.includes(path)) {
147
174
  throw new TypeError('WebSocket invocation and issuer paths must be distinct.');
148
175
  }
176
+ if (websocket === undefined &&
177
+ issuerReservations.some((reservation) => reservation.path === path && reservation.methods.includes('POST'))) {
178
+ throw new TypeError('Invocation and token-exchange paths must be distinct.');
179
+ }
149
180
  }
150
181
  return Object.freeze({
182
+ invocation,
151
183
  limits,
152
184
  path,
153
185
  cors: prepareCors(config.cors),
154
186
  ...(health === undefined ? {} : { health }),
155
187
  ...(issuer === undefined ? {} : { issuer }),
188
+ ...(tokenExchange === undefined ? {} : { tokenExchange }),
156
189
  ...(publication === undefined ? {} : { publication }),
157
- ...(installation === undefined ? {} : { installation }),
158
- routeReservations: routeReservations(path, health?.path, issuer, websocket !== undefined),
190
+ ...(delivery === undefined ? {} : { delivery }),
191
+ routeReservations: routeReservations(path, health?.path, issuer, websocket !== undefined, tokenExchange !== undefined, publicationPath),
159
192
  ...(websocket === undefined ? {} : { websocket }),
160
193
  });
161
194
  }
162
- function routeReservations(invocationPath, healthPath, issuer, websocket) {
195
+ function routeReservations(invocationPath, healthPath, issuer, websocket, tokenExchange, publicationPath) {
163
196
  const reservations = [
164
197
  Object.freeze({
165
198
  path: invocationPath,
@@ -170,10 +203,18 @@ function routeReservations(invocationPath, healthPath, issuer, websocket) {
170
203
  reservations.push(Object.freeze({ path: healthPath, methods: Object.freeze(['GET']) }));
171
204
  }
172
205
  if (issuer !== undefined) {
173
- reservations.push(...issuerRouteReservations(issuer));
206
+ reservations.push(...issuerRouteReservations(issuer, tokenExchange));
207
+ }
208
+ if (publicationPath !== undefined) {
209
+ reservations.push(Object.freeze({ path: publicationPath, methods: Object.freeze(['GET']) }));
174
210
  }
175
211
  return Object.freeze(reservations);
176
212
  }
213
+ function protocolPublicationPath(issuer) {
214
+ if (issuer === undefined)
215
+ return PUBLICATION_PATH;
216
+ return new URL(publicationUrl(issuer)).pathname;
217
+ }
177
218
  function captureHealthProbe(probe) {
178
219
  const check = captureMethod(probe, 'check', 'Health probe is invalid.');
179
220
  return Object.freeze({ check });
@@ -186,8 +227,8 @@ function capturePublicationSource(input) {
186
227
  const current = captureMethod(input, 'current', 'Publication source is invalid.');
187
228
  return Object.freeze({ current });
188
229
  }
189
- function captureInstallationProvider(input) {
190
- const create = captureMethod(input, 'create', 'Installation provider is invalid.');
230
+ function captureDeliveryProvider(input) {
231
+ const create = captureMethod(input, 'create', 'Publication Delivery provider is invalid.');
191
232
  return Object.freeze({ create });
192
233
  }
193
234
  function admitLimits(limits) {
@@ -206,7 +247,7 @@ function admitLimits(limits) {
206
247
  maximumConcurrentOperations: admitOptionalLimit(limits.maximumConcurrentOperations, 1_024, 'maximumConcurrentOperations'),
207
248
  maximumPublicationBytes: admitOptionalLimit(limits.maximumPublicationBytes, limits.maximumRequestBytes, 'maximumPublicationBytes'),
208
249
  maximumBundleBytes: admitOptionalLimit(limits.maximumBundleBytes, 16 * 1024 * 1024, 'maximumBundleBytes'),
209
- maximumInstallationBytes: admitOptionalLimit(limits.maximumInstallationBytes, limits.maximumRequestBytes, 'maximumInstallationBytes'),
250
+ maximumDeliveryBytes: admitOptionalLimit(limits.maximumDeliveryBytes, limits.maximumRequestBytes, 'maximumDeliveryBytes'),
210
251
  invocationTimeoutMs: limits.invocationTimeoutMs,
211
252
  });
212
253
  }
@@ -223,4 +264,3 @@ function admitRoute(path, label) {
223
264
  }
224
265
  return path;
225
266
  }
226
- //# 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
@@ -1,10 +1,12 @@
1
1
  import type { CorsPolicy } from '../../cors/index.js';
2
+ import type { NextInvocationId } from '../../invocation/index.js';
2
3
  import type { ActiveOperations } from '../../lifecycle/index.js';
3
4
  import type { PublicationSnapshot } from '../../publication/index.js';
4
5
  import { type ApplicationHost, type Handler } from '../../invocation/index.js';
5
6
  export interface HttpHandlerConfig {
6
7
  readonly handler: Handler;
7
8
  readonly active: ActiveOperations;
9
+ readonly invocation: NextInvocationId;
8
10
  readonly maximumRequestBytes: number;
9
11
  readonly maximumResponseBytes: number;
10
12
  readonly invocationTimeoutMs: number;
@@ -13,4 +15,3 @@ export interface HttpHandlerConfig {
13
15
  readonly currentPublication?: (request: Request) => PublicationSnapshot | undefined;
14
16
  }
15
17
  export declare function createHttpHandler(config: HttpHandlerConfig): (request: Request, host?: ApplicationHost) => Promise<Response>;
16
- //# sourceMappingURL=handler.d.ts.map