@astrale-os/kernel-server 0.5.0-beta.9 → 0.5.0

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 (237) 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 +11 -4
  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/admission.d.ts +7 -0
  10. package/dist/errors/admission.js +22 -0
  11. package/dist/errors/availability.d.ts +0 -1
  12. package/dist/errors/availability.js +0 -1
  13. package/dist/errors/classify.d.ts +10 -1
  14. package/dist/errors/classify.js +25 -1
  15. package/dist/errors/index.d.ts +2 -2
  16. package/dist/errors/index.js +2 -2
  17. package/dist/errors/request-body.d.ts +0 -1
  18. package/dist/errors/request-body.js +0 -1
  19. package/dist/errors/response-body.d.ts +0 -1
  20. package/dist/errors/response-body.js +0 -1
  21. package/dist/index.d.ts +3 -4
  22. package/dist/index.js +2 -3
  23. package/dist/invocation/context.d.ts +2 -2
  24. package/dist/invocation/context.js +2 -2
  25. package/dist/invocation/identity.d.ts +0 -1
  26. package/dist/invocation/identity.js +0 -1
  27. package/dist/invocation/index.d.ts +1 -2
  28. package/dist/invocation/index.js +1 -2
  29. package/dist/invocation/release-route.d.ts +22 -0
  30. package/dist/invocation/release-route.js +32 -0
  31. package/dist/invocation/types.d.ts +9 -6
  32. package/dist/invocation/types.js +0 -1
  33. package/dist/issuer/exchange.d.ts +0 -1
  34. package/dist/issuer/exchange.js +20 -10
  35. package/dist/issuer/index.d.ts +0 -1
  36. package/dist/issuer/index.js +0 -1
  37. package/dist/issuer/routes.d.ts +0 -1
  38. package/dist/issuer/routes.js +57 -48
  39. package/dist/issuer/types.d.ts +5 -1
  40. package/dist/issuer/types.js +0 -1
  41. package/dist/lifecycle/active.d.ts +6 -2
  42. package/dist/lifecycle/active.js +55 -14
  43. package/dist/lifecycle/index.d.ts +0 -1
  44. package/dist/lifecycle/index.js +0 -1
  45. package/dist/release/delivery/handler.d.ts +9 -0
  46. package/dist/{publication → release}/delivery/handler.js +18 -13
  47. package/dist/release/delivery/index.d.ts +1 -0
  48. package/dist/release/delivery/index.js +1 -0
  49. package/dist/{publication → release}/delivery/types.d.ts +4 -5
  50. package/dist/release/delivery/types.js +1 -0
  51. package/dist/release/index.d.ts +3 -0
  52. package/dist/release/index.js +2 -0
  53. package/dist/release/served.d.ts +21 -0
  54. package/dist/release/served.js +10 -0
  55. package/dist/release/snapshot.d.ts +22 -0
  56. package/dist/release/snapshot.js +49 -0
  57. package/dist/release/table.d.ts +4 -0
  58. package/dist/release/table.js +11 -0
  59. package/dist/release/types.d.ts +23 -0
  60. package/dist/release/types.js +1 -0
  61. package/dist/routes/address.d.ts +2 -0
  62. package/dist/routes/address.js +6 -0
  63. package/dist/routes/binary.d.ts +9 -0
  64. package/dist/routes/binary.js +110 -0
  65. package/dist/routes/bundle.d.ts +5 -0
  66. package/dist/routes/bundle.js +50 -0
  67. package/dist/routes/csrf.d.ts +16 -0
  68. package/dist/routes/csrf.js +35 -0
  69. package/dist/routes/document.d.ts +6 -0
  70. package/dist/routes/document.js +57 -0
  71. package/dist/routes/index.d.ts +3 -2
  72. package/dist/routes/index.js +2 -2
  73. package/dist/routes/model.d.ts +15 -0
  74. package/dist/routes/model.js +1 -0
  75. package/dist/routes/native.d.ts +17 -0
  76. package/dist/routes/native.js +136 -0
  77. package/dist/routes/preflight.d.ts +4 -0
  78. package/dist/routes/preflight.js +55 -0
  79. package/dist/routes/response.d.ts +14 -0
  80. package/dist/routes/response.js +33 -0
  81. package/dist/routes/routes.d.ts +7 -24
  82. package/dist/routes/routes.js +24 -439
  83. package/dist/routes/snapshot.d.ts +13 -0
  84. package/dist/routes/snapshot.js +39 -0
  85. package/dist/routes/stream.d.ts +6 -0
  86. package/dist/routes/stream.js +122 -0
  87. package/dist/server.d.ts +0 -1
  88. package/dist/server.js +121 -53
  89. package/dist/transport/body.d.ts +0 -1
  90. package/dist/transport/body.js +0 -1
  91. package/dist/transport/codecs.d.ts +2 -3
  92. package/dist/transport/codecs.js +4 -4
  93. package/dist/transport/credential.d.ts +0 -1
  94. package/dist/transport/credential.js +0 -1
  95. package/dist/transport/http/binary-request.d.ts +11 -0
  96. package/dist/transport/http/binary-request.js +102 -0
  97. package/dist/transport/http/binary.d.ts +6 -0
  98. package/dist/transport/http/binary.js +35 -0
  99. package/dist/transport/http/byte-stream.d.ts +4 -0
  100. package/dist/transport/http/byte-stream.js +123 -0
  101. package/dist/transport/http/control.d.ts +12 -0
  102. package/dist/transport/http/control.js +24 -0
  103. package/dist/transport/http/handler.d.ts +8 -3
  104. package/dist/transport/http/handler.js +98 -45
  105. package/dist/transport/http/index.d.ts +3 -2
  106. package/dist/transport/http/index.js +2 -2
  107. package/dist/transport/http/model.d.ts +20 -0
  108. package/dist/transport/http/model.js +1 -0
  109. package/dist/transport/http/render.d.ts +3 -31
  110. package/dist/transport/http/render.js +25 -269
  111. package/dist/transport/http/response.d.ts +14 -0
  112. package/dist/transport/http/response.js +77 -0
  113. package/dist/transport/http/stream.d.ts +5 -0
  114. package/dist/transport/http/stream.js +134 -0
  115. package/dist/transport/index.d.ts +2 -2
  116. package/dist/transport/index.js +2 -2
  117. package/dist/transport/result.d.ts +13 -0
  118. package/dist/transport/result.js +35 -0
  119. package/dist/transport/router.d.ts +0 -1
  120. package/dist/transport/router.js +0 -1
  121. package/dist/transport/stream-error.d.ts +0 -1
  122. package/dist/transport/stream-error.js +0 -1
  123. package/dist/transport/types.d.ts +0 -1
  124. package/dist/transport/types.js +0 -1
  125. package/dist/transport/websocket/index.d.ts +0 -1
  126. package/dist/transport/websocket/index.js +0 -1
  127. package/dist/transport/websocket/session.d.ts +3 -3
  128. package/dist/transport/websocket/session.js +38 -30
  129. package/dist/transport/websocket/upgrade.d.ts +2 -3
  130. package/dist/transport/websocket/upgrade.js +7 -7
  131. package/dist/types.d.ts +43 -10
  132. package/dist/types.js +0 -1
  133. package/package.json +11 -22
  134. package/dist/capture.d.ts.map +0 -1
  135. package/dist/capture.js.map +0 -1
  136. package/dist/cors/cors.d.ts.map +0 -1
  137. package/dist/cors/cors.js.map +0 -1
  138. package/dist/cors/index.d.ts.map +0 -1
  139. package/dist/cors/index.js.map +0 -1
  140. package/dist/cors/types.d.ts.map +0 -1
  141. package/dist/cors/types.js.map +0 -1
  142. package/dist/errors/availability.d.ts.map +0 -1
  143. package/dist/errors/availability.js.map +0 -1
  144. package/dist/errors/classify.d.ts.map +0 -1
  145. package/dist/errors/classify.js.map +0 -1
  146. package/dist/errors/index.d.ts.map +0 -1
  147. package/dist/errors/index.js.map +0 -1
  148. package/dist/errors/request-body.d.ts.map +0 -1
  149. package/dist/errors/request-body.js.map +0 -1
  150. package/dist/errors/response-body.d.ts.map +0 -1
  151. package/dist/errors/response-body.js.map +0 -1
  152. package/dist/index.d.ts.map +0 -1
  153. package/dist/index.js.map +0 -1
  154. package/dist/invocation/context.d.ts.map +0 -1
  155. package/dist/invocation/context.js.map +0 -1
  156. package/dist/invocation/identity.d.ts.map +0 -1
  157. package/dist/invocation/identity.js.map +0 -1
  158. package/dist/invocation/index.d.ts.map +0 -1
  159. package/dist/invocation/index.js.map +0 -1
  160. package/dist/invocation/publication-route.d.ts +0 -14
  161. package/dist/invocation/publication-route.d.ts.map +0 -1
  162. package/dist/invocation/publication-route.js +0 -45
  163. package/dist/invocation/publication-route.js.map +0 -1
  164. package/dist/invocation/types.d.ts.map +0 -1
  165. package/dist/invocation/types.js.map +0 -1
  166. package/dist/issuer/exchange.d.ts.map +0 -1
  167. package/dist/issuer/exchange.js.map +0 -1
  168. package/dist/issuer/index.d.ts.map +0 -1
  169. package/dist/issuer/index.js.map +0 -1
  170. package/dist/issuer/routes.d.ts.map +0 -1
  171. package/dist/issuer/routes.js.map +0 -1
  172. package/dist/issuer/types.d.ts.map +0 -1
  173. package/dist/issuer/types.js.map +0 -1
  174. package/dist/lifecycle/active.d.ts.map +0 -1
  175. package/dist/lifecycle/active.js.map +0 -1
  176. package/dist/lifecycle/index.d.ts.map +0 -1
  177. package/dist/lifecycle/index.js.map +0 -1
  178. package/dist/publication/delivery/handler.d.ts +0 -10
  179. package/dist/publication/delivery/handler.d.ts.map +0 -1
  180. package/dist/publication/delivery/handler.js.map +0 -1
  181. package/dist/publication/delivery/index.d.ts +0 -3
  182. package/dist/publication/delivery/index.d.ts.map +0 -1
  183. package/dist/publication/delivery/index.js +0 -2
  184. package/dist/publication/delivery/index.js.map +0 -1
  185. package/dist/publication/delivery/types.d.ts.map +0 -1
  186. package/dist/publication/delivery/types.js +0 -2
  187. package/dist/publication/delivery/types.js.map +0 -1
  188. package/dist/publication/index.d.ts +0 -4
  189. package/dist/publication/index.d.ts.map +0 -1
  190. package/dist/publication/index.js +0 -3
  191. package/dist/publication/index.js.map +0 -1
  192. package/dist/publication/snapshot.d.ts +0 -17
  193. package/dist/publication/snapshot.d.ts.map +0 -1
  194. package/dist/publication/snapshot.js +0 -38
  195. package/dist/publication/snapshot.js.map +0 -1
  196. package/dist/publication/table.d.ts +0 -5
  197. package/dist/publication/table.d.ts.map +0 -1
  198. package/dist/publication/table.js +0 -17
  199. package/dist/publication/table.js.map +0 -1
  200. package/dist/publication/types.d.ts +0 -23
  201. package/dist/publication/types.d.ts.map +0 -1
  202. package/dist/publication/types.js +0 -2
  203. package/dist/publication/types.js.map +0 -1
  204. package/dist/routes/index.d.ts.map +0 -1
  205. package/dist/routes/index.js.map +0 -1
  206. package/dist/routes/routes.d.ts.map +0 -1
  207. package/dist/routes/routes.js.map +0 -1
  208. package/dist/server.d.ts.map +0 -1
  209. package/dist/server.js.map +0 -1
  210. package/dist/transport/body.d.ts.map +0 -1
  211. package/dist/transport/body.js.map +0 -1
  212. package/dist/transport/codecs.d.ts.map +0 -1
  213. package/dist/transport/codecs.js.map +0 -1
  214. package/dist/transport/credential.d.ts.map +0 -1
  215. package/dist/transport/credential.js.map +0 -1
  216. package/dist/transport/http/handler.d.ts.map +0 -1
  217. package/dist/transport/http/handler.js.map +0 -1
  218. package/dist/transport/http/index.d.ts.map +0 -1
  219. package/dist/transport/http/index.js.map +0 -1
  220. package/dist/transport/http/render.d.ts.map +0 -1
  221. package/dist/transport/http/render.js.map +0 -1
  222. package/dist/transport/index.d.ts.map +0 -1
  223. package/dist/transport/index.js.map +0 -1
  224. package/dist/transport/router.d.ts.map +0 -1
  225. package/dist/transport/router.js.map +0 -1
  226. package/dist/transport/stream-error.d.ts.map +0 -1
  227. package/dist/transport/stream-error.js.map +0 -1
  228. package/dist/transport/types.d.ts.map +0 -1
  229. package/dist/transport/types.js.map +0 -1
  230. package/dist/transport/websocket/index.d.ts.map +0 -1
  231. package/dist/transport/websocket/index.js.map +0 -1
  232. package/dist/transport/websocket/session.d.ts.map +0 -1
  233. package/dist/transport/websocket/session.js.map +0 -1
  234. package/dist/transport/websocket/upgrade.d.ts.map +0 -1
  235. package/dist/transport/websocket/upgrade.js.map +0 -1
  236. package/dist/types.d.ts.map +0 -1
  237. package/dist/types.js.map +0 -1
package/dist/server.js CHANGED
@@ -1,11 +1,13 @@
1
- import { PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
1
+ import { accept as acceptIssuer } from '@astrale-os/kernel-core/auth/issuer';
2
+ import { Path } from '@astrale-os/kernel-core/path';
3
+ import { PATH as RELEASE_PATH, url as releaseUrl } from '@astrale-os/kernel-protocol/release';
2
4
  import { captureMethod } from './capture.js';
3
5
  import { prepareCors } from './cors/index.js';
4
6
  import { captureInvocationSource } from './invocation/index.js';
5
7
  import { capture as captureExchangeProvider } from './issuer/exchange.js';
6
8
  import { admitIssuer, issuerRouteReservations, mountIssuer } from './issuer/index.js';
7
9
  import { ActiveOperations } from './lifecycle/index.js';
8
- import { currentPublicationSnapshot, mountRoutes } from './routes/index.js';
10
+ import { currentServed, mountRoutes } from './routes/index.js';
9
11
  import { createHttpHandler } from './transport/http/handler.js';
10
12
  import { createServerRouter } from './transport/router.js';
11
13
  import { handleWebSocketUpgrade } from './transport/websocket/upgrade.js';
@@ -26,7 +28,23 @@ function createPreparedServer(input, config) {
26
28
  const app = createServerRouter();
27
29
  const hosts = new WeakMap();
28
30
  const externalIssuer = config.issuer?.issuer;
29
- const externalOrigin = externalIssuer === undefined ? undefined : new URL(externalIssuer).origin;
31
+ const externalOrigin = config.publicOrigin ??
32
+ (externalIssuer === undefined ? undefined : new URL(externalIssuer).origin);
33
+ const source = config.serving;
34
+ const serving = source === undefined
35
+ ? undefined
36
+ : (request) => {
37
+ const origin = externalOrigin ?? new URL(request.url).origin;
38
+ return Object.freeze({
39
+ kind: source.kind,
40
+ current: () => currentServed(source, config.limits, {
41
+ origin,
42
+ ...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
43
+ reservations: config.routeReservations,
44
+ }),
45
+ deployment: () => acceptIssuer(origin),
46
+ });
47
+ };
30
48
  const handleHttp = createHttpHandler({
31
49
  handler,
32
50
  active,
@@ -34,15 +52,7 @@ function createPreparedServer(input, config) {
34
52
  ...config.limits,
35
53
  cors: config.cors,
36
54
  websocket: config.websocket !== undefined,
37
- ...(config.publication === undefined
38
- ? {}
39
- : {
40
- currentPublication: (request) => currentPublicationSnapshot(config.publication, config.limits, {
41
- origin: externalOrigin ?? new URL(request.url).origin,
42
- ...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
43
- reservations: config.routeReservations,
44
- })?.snapshot,
45
- }),
55
+ ...(serving === undefined ? {} : { serving }),
46
56
  });
47
57
  if (config.websocket !== undefined) {
48
58
  app.get(config.path, (context) => handleWebSocketUpgrade({
@@ -53,23 +63,21 @@ function createPreparedServer(input, config) {
53
63
  invocation: config.invocation,
54
64
  ...config.limits,
55
65
  cors: config.cors,
56
- ...(config.publication === undefined
57
- ? {}
58
- : {
59
- currentPublication: () => currentPublicationSnapshot(config.publication, config.limits, {
60
- origin: externalOrigin ?? new URL(context.req.raw.url).origin,
61
- ...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
62
- reservations: config.routeReservations,
63
- })?.snapshot,
64
- }),
66
+ ...(serving === undefined ? {} : { serving: serving(context.req.raw) }),
65
67
  host: hosts.get(context.req.raw),
66
68
  }));
67
69
  }
68
70
  app.all(config.path, (context) => handleHttp(context.req.raw, hosts.get(context.req.raw)));
69
71
  if (config.health !== undefined) {
70
72
  app.get(config.health.path, (context) => {
71
- const ready = active.isAccepting && config.health.probe.check();
72
- return context.json({ status: ready ? 'ready' : 'not-ready' }, ready ? 200 : 503, {
73
+ const ready = active.isAvailable && config.health.probe.check();
74
+ const maintenance = config.health.probe.maintenance?.();
75
+ const uncertified = config.health.probe.uncertified?.() === true;
76
+ return context.json({
77
+ status: ready ? 'ready' : 'not-ready',
78
+ ...(maintenance === undefined ? {} : { maintenance }),
79
+ ...(uncertified ? { certification: 'uncertified' } : {}),
80
+ }, ready ? 200 : 503, {
73
81
  ...config.cors.response(context.req.raw),
74
82
  'cache-control': 'no-store',
75
83
  });
@@ -81,8 +89,8 @@ function createPreparedServer(input, config) {
81
89
  if (config.issuer !== undefined) {
82
90
  mountIssuer(app, config.issuer, config.cors, active, config.limits.invocationTimeoutMs, config.tokenExchange);
83
91
  }
84
- if (config.publication !== undefined) {
85
- mountRoutes(app, config.publication, config.delivery, handler, active, config.invocation, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalIssuer);
92
+ if (config.serving !== undefined) {
93
+ mountRoutes(app, config.serving, config.delivery, handler, active, config.invocation, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalIssuer, externalOrigin);
86
94
  }
87
95
  app.onError((_error, context) => new Response('{"error":"Internal server error."}', {
88
96
  status: 500,
@@ -131,6 +139,7 @@ function captureHandler(input) {
131
139
  return Object.freeze({ dispatch });
132
140
  }
133
141
  function admitOptions(config) {
142
+ const publicOrigin = config.publicOrigin === undefined ? undefined : admitPublicOrigin(config.publicOrigin);
134
143
  const invocation = captureInvocationSource(config.invocation);
135
144
  const limits = admitLimits(config.limits);
136
145
  const path = admitRoute(config.path ?? '/', 'invocation path');
@@ -146,23 +155,23 @@ function admitOptions(config) {
146
155
  probe: captureHealthProbe(config.health.probe),
147
156
  });
148
157
  const websocket = config.websocket === undefined ? undefined : captureWebSocket(config.websocket);
149
- const publication = config.publication === undefined ? undefined : capturePublicationSource(config.publication);
158
+ const serving = config.release === undefined
159
+ ? undefined
160
+ : Object.freeze({ kind: 'release', source: captureReleaseSource(config.release) });
150
161
  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.');
162
+ if (delivery !== undefined && serving === undefined) {
163
+ throw new TypeError('Delivery requires a release source.');
153
164
  }
154
- const publicationPath = publication === undefined
155
- ? undefined
156
- : issuer === undefined
157
- ? protocolPublicationPath()
158
- : protocolPublicationPath(issuer.issuer);
165
+ const documentPaths = serving === undefined ? [] : servedDocumentPaths(serving.kind, issuer?.issuer);
159
166
  if (health?.path === path)
160
167
  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.');
168
+ for (const { name, path: documentPath } of documentPaths) {
169
+ if (documentPath === path) {
170
+ throw new TypeError(`${name} and invocation paths must be distinct.`);
171
+ }
172
+ if (health !== undefined && documentPath === health.path) {
173
+ throw new TypeError(`Health and ${name} paths must be distinct.`);
174
+ }
166
175
  }
167
176
  if (issuer !== undefined) {
168
177
  const issuerReservations = issuerRouteReservations(issuer, tokenExchange !== undefined);
@@ -180,19 +189,20 @@ function admitOptions(config) {
180
189
  }
181
190
  return Object.freeze({
182
191
  invocation,
192
+ ...(publicOrigin === undefined ? {} : { publicOrigin }),
183
193
  limits,
184
194
  path,
185
195
  cors: prepareCors(config.cors),
186
196
  ...(health === undefined ? {} : { health }),
187
197
  ...(issuer === undefined ? {} : { issuer }),
188
198
  ...(tokenExchange === undefined ? {} : { tokenExchange }),
189
- ...(publication === undefined ? {} : { publication }),
199
+ ...(serving === undefined ? {} : { serving }),
190
200
  ...(delivery === undefined ? {} : { delivery }),
191
- routeReservations: routeReservations(path, health?.path, issuer, websocket !== undefined, tokenExchange !== undefined, publicationPath),
201
+ routeReservations: routeReservations(path, health?.path, issuer, websocket !== undefined, tokenExchange !== undefined, documentPaths.map(({ path: documentPath }) => documentPath)),
192
202
  ...(websocket === undefined ? {} : { websocket }),
193
203
  });
194
204
  }
195
- function routeReservations(invocationPath, healthPath, issuer, websocket, tokenExchange, publicationPath) {
205
+ function routeReservations(invocationPath, healthPath, issuer, websocket, tokenExchange, documentPaths) {
196
206
  const reservations = [
197
207
  Object.freeze({
198
208
  path: invocationPath,
@@ -205,30 +215,51 @@ function routeReservations(invocationPath, healthPath, issuer, websocket, tokenE
205
215
  if (issuer !== undefined) {
206
216
  reservations.push(...issuerRouteReservations(issuer, tokenExchange));
207
217
  }
208
- if (publicationPath !== undefined) {
209
- reservations.push(Object.freeze({ path: publicationPath, methods: Object.freeze(['GET']) }));
218
+ for (const documentPath of documentPaths) {
219
+ reservations.push(Object.freeze({ path: documentPath, methods: Object.freeze(['GET']) }));
210
220
  }
211
221
  return Object.freeze(reservations);
212
222
  }
213
- function protocolPublicationPath(issuer) {
214
- if (issuer === undefined)
215
- return PUBLICATION_PATH;
216
- return new URL(publicationUrl(issuer)).pathname;
223
+ function servedDocumentPaths(_kind, issuer) {
224
+ return [
225
+ {
226
+ name: 'Release',
227
+ path: issuer === undefined ? RELEASE_PATH : new URL(releaseUrl(issuer)).pathname,
228
+ },
229
+ ];
217
230
  }
218
231
  function captureHealthProbe(probe) {
219
232
  const check = captureMethod(probe, 'check', 'Health probe is invalid.');
220
- return Object.freeze({ check });
233
+ const maintenance = probe.maintenance === undefined
234
+ ? undefined
235
+ : captureMethod(probe, 'maintenance', 'Health probe is invalid.');
236
+ const uncertified = probe.uncertified === undefined
237
+ ? undefined
238
+ : captureMethod(probe, 'uncertified', 'Health probe is invalid.');
239
+ return Object.freeze({
240
+ check,
241
+ ...(maintenance === undefined
242
+ ? {}
243
+ : {
244
+ // A mode that is not a short token is not reported rather than echoed.
245
+ maintenance: () => {
246
+ const mode = maintenance();
247
+ return typeof mode === 'string' && /^[a-z]{1,32}$/u.test(mode) ? mode : undefined;
248
+ },
249
+ }),
250
+ ...(uncertified === undefined ? {} : { uncertified: () => uncertified() === true }),
251
+ });
221
252
  }
222
253
  function captureWebSocket(adapter) {
223
254
  const upgrade = captureMethod(adapter, 'upgrade', 'WebSocket adapter is invalid.');
224
255
  return Object.freeze({ upgrade });
225
256
  }
226
- function capturePublicationSource(input) {
227
- const current = captureMethod(input, 'current', 'Publication source is invalid.');
257
+ function captureReleaseSource(input) {
258
+ const current = captureMethod(input, 'current', 'Release source is invalid.');
228
259
  return Object.freeze({ current });
229
260
  }
230
261
  function captureDeliveryProvider(input) {
231
- const create = captureMethod(input, 'create', 'Publication Delivery provider is invalid.');
262
+ const create = captureMethod(input, 'create', 'Delivery provider is invalid.');
232
263
  return Object.freeze({ create });
233
264
  }
234
265
  function admitLimits(limits) {
@@ -241,16 +272,42 @@ function admitLimits(limits) {
241
272
  if (limits.invocationTimeoutMs > 2_147_483_647) {
242
273
  throw new TypeError('invocationTimeoutMs exceeds the platform timer range.');
243
274
  }
275
+ if ((limits.binaryDownloadTarget === undefined) !==
276
+ (limits.binaryDownloadTimeoutMs === undefined)) {
277
+ throw new TypeError('binaryDownloadTarget and binaryDownloadTimeoutMs must be configured together.');
278
+ }
279
+ const binaryDownloadTarget = limits.binaryDownloadTarget === undefined
280
+ ? undefined
281
+ : Path.parse(limits.binaryDownloadTarget).raw;
282
+ const binaryDownloadTimeoutMs = limits.binaryDownloadTimeoutMs === undefined
283
+ ? undefined
284
+ : admitTimerLimit(limits.binaryDownloadTimeoutMs, 'binaryDownloadTimeoutMs');
244
285
  return Object.freeze({
245
286
  maximumRequestBytes: limits.maximumRequestBytes,
287
+ maximumBinaryRequestBytes: admitOptionalLimit(limits.maximumBinaryRequestBytes, limits.maximumRequestBytes, 'maximumBinaryRequestBytes'),
246
288
  maximumResponseBytes: admitOptionalLimit(limits.maximumResponseBytes, 16 * 1024 * 1024, 'maximumResponseBytes'),
247
289
  maximumConcurrentOperations: admitOptionalLimit(limits.maximumConcurrentOperations, 1_024, 'maximumConcurrentOperations'),
248
- maximumPublicationBytes: admitOptionalLimit(limits.maximumPublicationBytes, limits.maximumRequestBytes, 'maximumPublicationBytes'),
290
+ maximumReleaseBytes: admitOptionalLimit(limits.maximumReleaseBytes, limits.maximumRequestBytes, 'maximumReleaseBytes'),
249
291
  maximumBundleBytes: admitOptionalLimit(limits.maximumBundleBytes, 16 * 1024 * 1024, 'maximumBundleBytes'),
250
292
  maximumDeliveryBytes: admitOptionalLimit(limits.maximumDeliveryBytes, limits.maximumRequestBytes, 'maximumDeliveryBytes'),
251
293
  invocationTimeoutMs: limits.invocationTimeoutMs,
294
+ binaryRequestTimeoutMs: admitTimerLimit(limits.binaryRequestTimeoutMs ?? limits.invocationTimeoutMs, 'binaryRequestTimeoutMs'),
295
+ ...(limits.binaryRequestIdleTimeoutMs === undefined
296
+ ? {}
297
+ : {
298
+ binaryRequestIdleTimeoutMs: admitTimerLimit(limits.binaryRequestIdleTimeoutMs, 'binaryRequestIdleTimeoutMs'),
299
+ }),
300
+ ...(binaryDownloadTarget === undefined
301
+ ? {}
302
+ : { binaryDownloadTarget, binaryDownloadTimeoutMs }),
252
303
  });
253
304
  }
305
+ function admitTimerLimit(input, label) {
306
+ if (!Number.isSafeInteger(input) || input <= 0 || input > 2_147_483_647) {
307
+ throw new TypeError(`${label} must be a positive platform timer duration.`);
308
+ }
309
+ return input;
310
+ }
254
311
  function admitOptionalLimit(input, fallback, label) {
255
312
  const value = input ?? fallback;
256
313
  if (!Number.isSafeInteger(value) || value <= 0) {
@@ -264,4 +321,15 @@ function admitRoute(path, label) {
264
321
  }
265
322
  return path;
266
323
  }
267
- //# sourceMappingURL=server.js.map
324
+ function admitPublicOrigin(input) {
325
+ const url = new URL(input);
326
+ if (!['http:', 'https:'].includes(url.protocol) ||
327
+ url.username !== '' ||
328
+ url.password !== '' ||
329
+ url.pathname !== '/' ||
330
+ url.search !== '' ||
331
+ url.hash !== '') {
332
+ throw new TypeError('Server publicOrigin must be an HTTP(S) origin.');
333
+ }
334
+ return url.origin;
335
+ }
@@ -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
1
  import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
2
  export type LoadedCodec = codecs.Codec;
3
- /** Load the built-in Server representation set before pure Protocol binding selection. */
4
- export declare function loadCodecs(): Promise<readonly [codecs.Codec, codecs.Codec]>;
5
- //# sourceMappingURL=codecs.d.ts.map
3
+ /** The built-in Server representation set before pure Protocol binding selection. */
4
+ export declare function loadCodecs(): readonly [codecs.Codec];
@@ -1,6 +1,6 @@
1
1
  import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
- /** Load the built-in Server representation set before pure Protocol binding selection. */
3
- export async function loadCodecs() {
4
- return Object.freeze([codecs.json, await codecs.loadMsgpack()]);
2
+ const SUPPORTED = Object.freeze([codecs.json]);
3
+ /** The built-in Server representation set before pure Protocol binding selection. */
4
+ export function loadCodecs() {
5
+ return SUPPORTED;
5
6
  }
6
- //# sourceMappingURL=codecs.js.map
@@ -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
@@ -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
@@ -0,0 +1,11 @@
1
+ import { type BinaryRequestBody } from '@astrale-os/kernel-core/callable';
2
+ interface IdleBudget {
3
+ readonly timeoutMs: number;
4
+ readonly cancel: () => void;
5
+ }
6
+ /** Admit a declared length before dispatch and expose one counted, non-buffering body. */
7
+ export declare function binaryRequestBody(request: Request, maximumBytes: number, signal: AbortSignal, idle?: IdleBudget): BinaryRequestBody & {
8
+ readonly completed: () => boolean;
9
+ readonly cancel: (reason: unknown) => void;
10
+ };
11
+ export {};
@@ -0,0 +1,102 @@
1
+ import { BinaryBodyLengthError } from '@astrale-os/kernel-core/callable';
2
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
3
+ import { RequestBodyInvalidError, RequestBodyTooLargeError } from '../../errors/index.js';
4
+ /** Admit a declared length before dispatch and expose one counted, non-buffering body. */
5
+ export function binaryRequestBody(request, maximumBytes, signal, idle) {
6
+ const declared = request.headers.get(protocol.http.BINARY_LENGTH_FIELD);
7
+ if (declared === null) {
8
+ throw new RequestBodyInvalidError('Binary request length is required and must be exact.');
9
+ }
10
+ const length = protocol.http.decodeBinaryLength(declared);
11
+ if (length === undefined) {
12
+ throw new RequestBodyInvalidError('Binary request length is invalid.');
13
+ }
14
+ const contentLength = request.headers.get('content-length');
15
+ if (contentLength !== null && contentLength !== declared) {
16
+ throw new RequestBodyInvalidError('Binary request length differs from Content-Length.');
17
+ }
18
+ if (length > maximumBytes)
19
+ throw new RequestBodyTooLargeError(maximumBytes);
20
+ if (request.body === null) {
21
+ if (length !== 0)
22
+ throw new RequestBodyInvalidError('Binary request body is missing.');
23
+ return Object.freeze({
24
+ body: new ReadableStream({ start: (controller) => controller.close() }),
25
+ length,
26
+ completed: () => true,
27
+ cancel: () => undefined,
28
+ });
29
+ }
30
+ const reader = request.body.getReader();
31
+ let size = 0;
32
+ let finished = false;
33
+ let completed = false;
34
+ let idleTimer;
35
+ const clearIdle = () => {
36
+ if (idleTimer !== undefined)
37
+ clearTimeout(idleTimer);
38
+ idleTimer = undefined;
39
+ };
40
+ const release = () => {
41
+ if (finished)
42
+ return;
43
+ finished = true;
44
+ clearIdle();
45
+ signal.removeEventListener('abort', abort);
46
+ reader.releaseLock();
47
+ };
48
+ const cancelSource = (reason) => {
49
+ if (finished)
50
+ return;
51
+ clearIdle();
52
+ void reader.cancel(reason).then(release, release);
53
+ };
54
+ const abort = () => cancelSource(signal.reason);
55
+ signal.addEventListener('abort', abort, { once: true });
56
+ if (signal.aborted)
57
+ abort();
58
+ const startIdle = () => {
59
+ if (idle !== undefined && idleTimer === undefined) {
60
+ idleTimer = setTimeout(idle.cancel, idle.timeoutMs);
61
+ }
62
+ };
63
+ const body = new ReadableStream({
64
+ async pull(controller) {
65
+ try {
66
+ if (signal.aborted)
67
+ throw signal.reason;
68
+ startIdle();
69
+ const next = await reader.read();
70
+ if (signal.aborted)
71
+ throw signal.reason;
72
+ if (next.done) {
73
+ if (size !== length)
74
+ throw new BinaryBodyLengthError();
75
+ completed = true;
76
+ release();
77
+ controller.close();
78
+ return;
79
+ }
80
+ if (!(next.value instanceof Uint8Array)) {
81
+ throw new RequestBodyInvalidError('Binary request yielded a non-byte chunk.');
82
+ }
83
+ size += next.value.byteLength;
84
+ if (size > length)
85
+ throw new BinaryBodyLengthError();
86
+ if (next.value.byteLength > 0)
87
+ clearIdle();
88
+ controller.enqueue(next.value);
89
+ }
90
+ catch (cause) {
91
+ cancelSource(cause);
92
+ release();
93
+ controller.error(cause);
94
+ }
95
+ },
96
+ cancel(reason) {
97
+ cancelSource(reason);
98
+ release();
99
+ },
100
+ }, { highWaterMark: 0 });
101
+ return Object.freeze({ body, length, completed: () => completed, cancel: cancelSource });
102
+ }
@@ -0,0 +1,6 @@
1
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
2
+ import type { CorsHeaders } from './model.js';
3
+ /** Present admitted buffered bytes under the response budget and binary manifest. */
4
+ export declare function binaryHttpResponse(binary: protocol.BinaryResponse, cors: CorsHeaders, maximumResponseBytes: number): Response;
5
+ /** Bind binary correlation and application headers to the HTTP response. */
6
+ export declare function binaryHttpHeaders(binary: Pick<protocol.BinaryResponse, 'headers' | 'invocation' | 'mediaType' | 'requestId'>, cors: CorsHeaders, streaming: boolean): Headers;
@@ -0,0 +1,35 @@
1
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
2
+ import { appendVary, requireResponseBound, toBody } from './response.js';
3
+ /** Present admitted buffered bytes under the response budget and binary manifest. */
4
+ export function binaryHttpResponse(binary, cors, maximumResponseBytes) {
5
+ requireResponseBound(binary.body, maximumResponseBytes);
6
+ const headers = binaryHttpHeaders(binary, cors, false);
7
+ return new Response(toBody(binary.body), { status: binary.status, headers });
8
+ }
9
+ /** Bind binary correlation and application headers to the HTTP response. */
10
+ export function binaryHttpHeaders(binary, cors, streaming) {
11
+ const applicationHeaders = Object.keys(binary.headers ?? {}).filter((name) => protocol.http.isApplicationBinaryHeader(name));
12
+ const headers = new Headers(cors([
13
+ protocol.http.REQUEST_ID_FIELD,
14
+ protocol.http.INVOCATION_FIELD,
15
+ protocol.http.BINARY_HEADERS_FIELD,
16
+ ...(streaming
17
+ ? [protocol.http.BINARY_DELIVERY_FIELD, protocol.http.BINARY_LENGTH_FIELD]
18
+ : []),
19
+ ...applicationHeaders,
20
+ ]));
21
+ for (const [name, value] of Object.entries(binary.headers ?? {})) {
22
+ if (protocol.http.isApplicationBinaryHeader(name))
23
+ headers.set(name, value);
24
+ }
25
+ headers.set('content-type', binary.mediaType);
26
+ headers.set(protocol.http.BINARY_HEADERS_FIELD, protocol.http.encodeBinaryHeaderNames(binary.headers));
27
+ headers.set(protocol.http.REQUEST_ID_FIELD, String(binary.requestId));
28
+ headers.set(protocol.http.INVOCATION_FIELD, protocol.http.encodeInvocationId(binary.invocation));
29
+ if (streaming) {
30
+ headers.set(protocol.http.BINARY_DELIVERY_FIELD, protocol.http.STREAMING_BINARY_DELIVERY);
31
+ }
32
+ headers.set('cache-control', 'no-store');
33
+ appendVary(headers, 'accept');
34
+ return headers;
35
+ }
@@ -0,0 +1,4 @@
1
+ import type { StreamingBinaryResult } from '@astrale-os/kernel-core/callable';
2
+ import type { RenderContext } from './model.js';
3
+ /** Deliver raw binary chunks with exact length, idle budget and producer cleanup. */
4
+ export declare function streamingBinaryHttpResponse(binary: StreamingBinaryResult, context: RenderContext): Response;
@@ -0,0 +1,123 @@
1
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
2
+ import { binaryHttpHeaders } from './binary.js';
3
+ /** Deliver raw binary chunks with exact length, idle budget and producer cleanup. */
4
+ export function streamingBinaryHttpResponse(binary, context) {
5
+ if (binary.length !== undefined && (!Number.isSafeInteger(binary.length) || binary.length < 0)) {
6
+ throw new TypeError('Streaming binary length must be a non-negative safe integer.');
7
+ }
8
+ const delivery = binary.delivery;
9
+ if (delivery !== undefined) {
10
+ if (!Number.isSafeInteger(delivery.maximumDurationMs) ||
11
+ delivery.maximumDurationMs <= 0 ||
12
+ delivery.maximumDurationMs > 2_147_483_647 ||
13
+ !Number.isSafeInteger(delivery.idleTimeoutMs) ||
14
+ delivery.idleTimeoutMs <= 0 ||
15
+ delivery.idleTimeoutMs > 2_147_483_647) {
16
+ throw new TypeError('Streaming binary delivery budget is invalid.');
17
+ }
18
+ context.active.extend(delivery.maximumDurationMs);
19
+ }
20
+ const iterator = binary.body[Symbol.asyncIterator]();
21
+ const headers = binaryHttpHeaders(Object.freeze({
22
+ requestId: context.requestId,
23
+ invocation: context.invocation,
24
+ mediaType: binary.mediaType,
25
+ headers: binary.headers,
26
+ }), context.cors, true);
27
+ if (binary.length !== undefined) {
28
+ headers.set('content-length', String(binary.length));
29
+ // Content-Length belongs to HTTP and an intermediary may rewrite it; the Client counts the
30
+ // delivered bytes against this protocol field instead.
31
+ headers.set(protocol.http.BINARY_LENGTH_FIELD, String(binary.length));
32
+ }
33
+ if (delivery !== undefined)
34
+ headers.set('cache-control', 'private, no-store');
35
+ let terminal = false;
36
+ let delivered = 0;
37
+ let controller;
38
+ let stopping;
39
+ let idleTimer;
40
+ const clearIdle = () => {
41
+ if (idleTimer !== undefined)
42
+ clearTimeout(idleTimer);
43
+ };
44
+ const resetIdle = () => {
45
+ if (delivery === undefined || terminal)
46
+ return;
47
+ clearIdle();
48
+ idleTimer = setTimeout(() => context.active.cancel('binary response idle timeout exceeded'), delivery.idleTimeoutMs);
49
+ };
50
+ const stop = (reason = 'Binary response was interrupted.') => {
51
+ if (terminal)
52
+ return stopping ?? Promise.resolve();
53
+ terminal = true;
54
+ clearIdle();
55
+ controller?.error(new Error(reason));
56
+ stopping = Promise.resolve().then(async () => {
57
+ try {
58
+ await iterator.return?.();
59
+ }
60
+ finally {
61
+ context.active.settle();
62
+ }
63
+ });
64
+ return stopping;
65
+ };
66
+ const fail = (cause) => {
67
+ if (terminal)
68
+ return;
69
+ terminal = true;
70
+ clearIdle();
71
+ controller?.error(cause);
72
+ stopping = Promise.resolve()
73
+ .then(() => iterator.return?.())
74
+ .catch(() => undefined)
75
+ .then(() => context.active.settle());
76
+ };
77
+ const body = new ReadableStream({
78
+ start(output) {
79
+ controller = output;
80
+ resetIdle();
81
+ },
82
+ async pull(controller) {
83
+ if (terminal)
84
+ return;
85
+ try {
86
+ const next = await iterator.next();
87
+ if (terminal)
88
+ return;
89
+ if (next.done) {
90
+ if (binary.length !== undefined && delivered !== binary.length) {
91
+ fail(new Error('OUTCOME_UNKNOWN: streaming binary ended before its declared length.'));
92
+ return;
93
+ }
94
+ terminal = true;
95
+ clearIdle();
96
+ controller.close();
97
+ context.active.settle();
98
+ return;
99
+ }
100
+ if (!(next.value instanceof Uint8Array)) {
101
+ throw new TypeError('Streaming binary yielded a non-byte chunk.');
102
+ }
103
+ delivered += next.value.byteLength;
104
+ if (binary.length !== undefined && delivered > binary.length) {
105
+ fail(new Error('OUTCOME_UNKNOWN: streaming binary exceeded its declared length.'));
106
+ return;
107
+ }
108
+ controller.enqueue(next.value);
109
+ resetIdle();
110
+ }
111
+ catch (cause) {
112
+ fail(cause);
113
+ }
114
+ },
115
+ async cancel(reason) {
116
+ const message = typeof reason === 'string' ? reason : 'binary response consumer cancelled';
117
+ context.active.cancel(message);
118
+ await stop();
119
+ },
120
+ });
121
+ context.active.onCancel(stop);
122
+ return new Response(body, { status: binary.status ?? 200, headers });
123
+ }
@@ -0,0 +1,12 @@
1
+ import type { IssuerId } from '@astrale-os/kernel-core/auth/issuer';
2
+ import type { Codec } from '@astrale-os/kernel-protocol/codecs';
3
+ import type { ReleaseDigest } from '@astrale-os/kernel-protocol/release';
4
+ import * as errors from '@astrale-os/kernel-protocol/errors';
5
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
6
+ import type { CorsHeaders, ErrorRenderOptions } from './model.js';
7
+ /** Render a correlated error with its status, authentication challenge and retry delay. */
8
+ export declare function renderHttpError(requestId: protocol.RequestId, invocation: protocol.InvocationId, error: errors.ErrorPayload, codec: Codec, cors: CorsHeaders, options?: ErrorRenderOptions): Response;
9
+ /** Present stale routing as a bounded status-200 Invocation envelope. */
10
+ export declare function renderHttpStale(requestId: protocol.RequestId, invocation: protocol.InvocationId, current: protocol.Stale['current'], codec: Codec, cors: CorsHeaders, maximumResponseBytes?: number): Response;
11
+ /** Retirement controls always use JSON, regardless of the negotiated request codec. */
12
+ export declare function renderDeploymentRetired(deployment: IssuerId, release: ReleaseDigest | undefined, cors: CorsHeaders): Response;