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

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 (241) 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 +8 -1
  14. package/dist/errors/classify.js +15 -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 +3 -3
  24. package/dist/invocation/context.js +2 -2
  25. package/dist/invocation/identity.d.ts +1 -2
  26. package/dist/invocation/identity.js +1 -2
  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 +8 -7
  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 +4 -1
  42. package/dist/lifecycle/active.js +48 -11
  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 +19 -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 +126 -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 +95 -51
  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 +97 -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 +18 -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 +1 -2
  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 +30 -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 +33 -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 -16
  193. package/dist/publication/snapshot.d.ts.map +0 -1
  194. package/dist/publication/snapshot.js +0 -39
  195. package/dist/publication/snapshot.js.map +0 -1
  196. package/dist/publication/table.d.ts +0 -4
  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/publication/validator.d.ts +0 -4
  205. package/dist/publication/validator.d.ts.map +0 -1
  206. package/dist/publication/validator.js +0 -17
  207. package/dist/publication/validator.js.map +0 -1
  208. package/dist/routes/index.d.ts.map +0 -1
  209. package/dist/routes/index.js.map +0 -1
  210. package/dist/routes/routes.d.ts.map +0 -1
  211. package/dist/routes/routes.js.map +0 -1
  212. package/dist/server.d.ts.map +0 -1
  213. package/dist/server.js.map +0 -1
  214. package/dist/transport/body.d.ts.map +0 -1
  215. package/dist/transport/body.js.map +0 -1
  216. package/dist/transport/codecs.d.ts.map +0 -1
  217. package/dist/transport/codecs.js.map +0 -1
  218. package/dist/transport/credential.d.ts.map +0 -1
  219. package/dist/transport/credential.js.map +0 -1
  220. package/dist/transport/http/handler.d.ts.map +0 -1
  221. package/dist/transport/http/handler.js.map +0 -1
  222. package/dist/transport/http/index.d.ts.map +0 -1
  223. package/dist/transport/http/index.js.map +0 -1
  224. package/dist/transport/http/render.d.ts.map +0 -1
  225. package/dist/transport/http/render.js.map +0 -1
  226. package/dist/transport/index.d.ts.map +0 -1
  227. package/dist/transport/index.js.map +0 -1
  228. package/dist/transport/router.d.ts.map +0 -1
  229. package/dist/transport/router.js.map +0 -1
  230. package/dist/transport/stream-error.d.ts.map +0 -1
  231. package/dist/transport/stream-error.js.map +0 -1
  232. package/dist/transport/types.d.ts.map +0 -1
  233. package/dist/transport/types.js.map +0 -1
  234. package/dist/transport/websocket/index.d.ts.map +0 -1
  235. package/dist/transport/websocket/index.js.map +0 -1
  236. package/dist/transport/websocket/session.d.ts.map +0 -1
  237. package/dist/transport/websocket/session.js.map +0 -1
  238. package/dist/transport/websocket/upgrade.d.ts.map +0 -1
  239. package/dist/transport/websocket/upgrade.js.map +0 -1
  240. package/dist/types.d.ts.map +0 -1
  241. 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,22 +63,14 @@ 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();
73
+ const ready = active.isAvailable && config.health.probe.check();
72
74
  return context.json({ status: ready ? 'ready' : 'not-ready' }, ready ? 200 : 503, {
73
75
  ...config.cors.response(context.req.raw),
74
76
  'cache-control': 'no-store',
@@ -81,8 +83,8 @@ function createPreparedServer(input, config) {
81
83
  if (config.issuer !== undefined) {
82
84
  mountIssuer(app, config.issuer, config.cors, active, config.limits.invocationTimeoutMs, config.tokenExchange);
83
85
  }
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);
86
+ if (config.serving !== undefined) {
87
+ mountRoutes(app, config.serving, config.delivery, handler, active, config.invocation, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalIssuer, externalOrigin);
86
88
  }
87
89
  app.onError((_error, context) => new Response('{"error":"Internal server error."}', {
88
90
  status: 500,
@@ -131,6 +133,7 @@ function captureHandler(input) {
131
133
  return Object.freeze({ dispatch });
132
134
  }
133
135
  function admitOptions(config) {
136
+ const publicOrigin = config.publicOrigin === undefined ? undefined : admitPublicOrigin(config.publicOrigin);
134
137
  const invocation = captureInvocationSource(config.invocation);
135
138
  const limits = admitLimits(config.limits);
136
139
  const path = admitRoute(config.path ?? '/', 'invocation path');
@@ -146,23 +149,23 @@ function admitOptions(config) {
146
149
  probe: captureHealthProbe(config.health.probe),
147
150
  });
148
151
  const websocket = config.websocket === undefined ? undefined : captureWebSocket(config.websocket);
149
- const publication = config.publication === undefined ? undefined : capturePublicationSource(config.publication);
152
+ const serving = config.release === undefined
153
+ ? undefined
154
+ : Object.freeze({ kind: 'release', source: captureReleaseSource(config.release) });
150
155
  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.');
156
+ if (delivery !== undefined && serving === undefined) {
157
+ throw new TypeError('Delivery requires a release source.');
153
158
  }
154
- const publicationPath = publication === undefined
155
- ? undefined
156
- : issuer === undefined
157
- ? protocolPublicationPath()
158
- : protocolPublicationPath(issuer.issuer);
159
+ const documentPaths = serving === undefined ? [] : servedDocumentPaths(serving.kind, issuer?.issuer);
159
160
  if (health?.path === path)
160
161
  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.');
162
+ for (const { name, path: documentPath } of documentPaths) {
163
+ if (documentPath === path) {
164
+ throw new TypeError(`${name} and invocation paths must be distinct.`);
165
+ }
166
+ if (health !== undefined && documentPath === health.path) {
167
+ throw new TypeError(`Health and ${name} paths must be distinct.`);
168
+ }
166
169
  }
167
170
  if (issuer !== undefined) {
168
171
  const issuerReservations = issuerRouteReservations(issuer, tokenExchange !== undefined);
@@ -180,19 +183,20 @@ function admitOptions(config) {
180
183
  }
181
184
  return Object.freeze({
182
185
  invocation,
186
+ ...(publicOrigin === undefined ? {} : { publicOrigin }),
183
187
  limits,
184
188
  path,
185
189
  cors: prepareCors(config.cors),
186
190
  ...(health === undefined ? {} : { health }),
187
191
  ...(issuer === undefined ? {} : { issuer }),
188
192
  ...(tokenExchange === undefined ? {} : { tokenExchange }),
189
- ...(publication === undefined ? {} : { publication }),
193
+ ...(serving === undefined ? {} : { serving }),
190
194
  ...(delivery === undefined ? {} : { delivery }),
191
- routeReservations: routeReservations(path, health?.path, issuer, websocket !== undefined, tokenExchange !== undefined, publicationPath),
195
+ routeReservations: routeReservations(path, health?.path, issuer, websocket !== undefined, tokenExchange !== undefined, documentPaths.map(({ path: documentPath }) => documentPath)),
192
196
  ...(websocket === undefined ? {} : { websocket }),
193
197
  });
194
198
  }
195
- function routeReservations(invocationPath, healthPath, issuer, websocket, tokenExchange, publicationPath) {
199
+ function routeReservations(invocationPath, healthPath, issuer, websocket, tokenExchange, documentPaths) {
196
200
  const reservations = [
197
201
  Object.freeze({
198
202
  path: invocationPath,
@@ -205,15 +209,18 @@ function routeReservations(invocationPath, healthPath, issuer, websocket, tokenE
205
209
  if (issuer !== undefined) {
206
210
  reservations.push(...issuerRouteReservations(issuer, tokenExchange));
207
211
  }
208
- if (publicationPath !== undefined) {
209
- reservations.push(Object.freeze({ path: publicationPath, methods: Object.freeze(['GET']) }));
212
+ for (const documentPath of documentPaths) {
213
+ reservations.push(Object.freeze({ path: documentPath, methods: Object.freeze(['GET']) }));
210
214
  }
211
215
  return Object.freeze(reservations);
212
216
  }
213
- function protocolPublicationPath(issuer) {
214
- if (issuer === undefined)
215
- return PUBLICATION_PATH;
216
- return new URL(publicationUrl(issuer)).pathname;
217
+ function servedDocumentPaths(_kind, issuer) {
218
+ return [
219
+ {
220
+ name: 'Release',
221
+ path: issuer === undefined ? RELEASE_PATH : new URL(releaseUrl(issuer)).pathname,
222
+ },
223
+ ];
217
224
  }
218
225
  function captureHealthProbe(probe) {
219
226
  const check = captureMethod(probe, 'check', 'Health probe is invalid.');
@@ -223,12 +230,12 @@ function captureWebSocket(adapter) {
223
230
  const upgrade = captureMethod(adapter, 'upgrade', 'WebSocket adapter is invalid.');
224
231
  return Object.freeze({ upgrade });
225
232
  }
226
- function capturePublicationSource(input) {
227
- const current = captureMethod(input, 'current', 'Publication source is invalid.');
233
+ function captureReleaseSource(input) {
234
+ const current = captureMethod(input, 'current', 'Release source is invalid.');
228
235
  return Object.freeze({ current });
229
236
  }
230
237
  function captureDeliveryProvider(input) {
231
- const create = captureMethod(input, 'create', 'Publication Delivery provider is invalid.');
238
+ const create = captureMethod(input, 'create', 'Delivery provider is invalid.');
232
239
  return Object.freeze({ create });
233
240
  }
234
241
  function admitLimits(limits) {
@@ -241,16 +248,42 @@ function admitLimits(limits) {
241
248
  if (limits.invocationTimeoutMs > 2_147_483_647) {
242
249
  throw new TypeError('invocationTimeoutMs exceeds the platform timer range.');
243
250
  }
251
+ if ((limits.binaryDownloadTarget === undefined) !==
252
+ (limits.binaryDownloadTimeoutMs === undefined)) {
253
+ throw new TypeError('binaryDownloadTarget and binaryDownloadTimeoutMs must be configured together.');
254
+ }
255
+ const binaryDownloadTarget = limits.binaryDownloadTarget === undefined
256
+ ? undefined
257
+ : Path.parse(limits.binaryDownloadTarget).raw;
258
+ const binaryDownloadTimeoutMs = limits.binaryDownloadTimeoutMs === undefined
259
+ ? undefined
260
+ : admitTimerLimit(limits.binaryDownloadTimeoutMs, 'binaryDownloadTimeoutMs');
244
261
  return Object.freeze({
245
262
  maximumRequestBytes: limits.maximumRequestBytes,
263
+ maximumBinaryRequestBytes: admitOptionalLimit(limits.maximumBinaryRequestBytes, limits.maximumRequestBytes, 'maximumBinaryRequestBytes'),
246
264
  maximumResponseBytes: admitOptionalLimit(limits.maximumResponseBytes, 16 * 1024 * 1024, 'maximumResponseBytes'),
247
265
  maximumConcurrentOperations: admitOptionalLimit(limits.maximumConcurrentOperations, 1_024, 'maximumConcurrentOperations'),
248
- maximumPublicationBytes: admitOptionalLimit(limits.maximumPublicationBytes, limits.maximumRequestBytes, 'maximumPublicationBytes'),
266
+ maximumReleaseBytes: admitOptionalLimit(limits.maximumReleaseBytes, limits.maximumRequestBytes, 'maximumReleaseBytes'),
249
267
  maximumBundleBytes: admitOptionalLimit(limits.maximumBundleBytes, 16 * 1024 * 1024, 'maximumBundleBytes'),
250
268
  maximumDeliveryBytes: admitOptionalLimit(limits.maximumDeliveryBytes, limits.maximumRequestBytes, 'maximumDeliveryBytes'),
251
269
  invocationTimeoutMs: limits.invocationTimeoutMs,
270
+ binaryRequestTimeoutMs: admitTimerLimit(limits.binaryRequestTimeoutMs ?? limits.invocationTimeoutMs, 'binaryRequestTimeoutMs'),
271
+ ...(limits.binaryRequestIdleTimeoutMs === undefined
272
+ ? {}
273
+ : {
274
+ binaryRequestIdleTimeoutMs: admitTimerLimit(limits.binaryRequestIdleTimeoutMs, 'binaryRequestIdleTimeoutMs'),
275
+ }),
276
+ ...(binaryDownloadTarget === undefined
277
+ ? {}
278
+ : { binaryDownloadTarget, binaryDownloadTimeoutMs }),
252
279
  });
253
280
  }
281
+ function admitTimerLimit(input, label) {
282
+ if (!Number.isSafeInteger(input) || input <= 0 || input > 2_147_483_647) {
283
+ throw new TypeError(`${label} must be a positive platform timer duration.`);
284
+ }
285
+ return input;
286
+ }
254
287
  function admitOptionalLimit(input, fallback, label) {
255
288
  const value = input ?? fallback;
256
289
  if (!Number.isSafeInteger(value) || value <= 0) {
@@ -264,4 +297,15 @@ function admitRoute(path, label) {
264
297
  }
265
298
  return path;
266
299
  }
267
- //# sourceMappingURL=server.js.map
300
+ function admitPublicOrigin(input) {
301
+ const url = new URL(input);
302
+ if (!['http:', 'https:'].includes(url.protocol) ||
303
+ url.username !== '' ||
304
+ url.password !== '' ||
305
+ url.pathname !== '/' ||
306
+ url.search !== '' ||
307
+ url.hash !== '') {
308
+ throw new TypeError('Server publicOrigin must be an HTTP(S) origin.');
309
+ }
310
+ return url.origin;
311
+ }
@@ -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;
@@ -0,0 +1,24 @@
1
+ import { json } 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 { deploymentRetiredBody, deploymentRetiredHeaders, HTTP_DEPLOYMENT_RETIRED, HTTP_DEPLOYMENT_RETIRED_STATUS, } from '@astrale-os/kernel-protocol/routes';
5
+ import { retryAfterHeaders } from '../../errors/index.js';
6
+ import { envelopeResponse, errorEnvelopeResponse, toBody } from './response.js';
7
+ /** Render a correlated error with its status, authentication challenge and retry delay. */
8
+ export function renderHttpError(requestId, invocation, error, codec, cors, options = {}) {
9
+ const status = options.status ?? errors.httpStatusOf(error);
10
+ const retry = retryAfterHeaders(error);
11
+ const extra = status === 401 ? { 'www-authenticate': 'Bearer realm="astrale-invocation"' } : retry;
12
+ return errorEnvelopeResponse(codec, { error, requestId, invocation }, { ...cors(Object.keys(retry)), ...extra }, status, options.negotiated ?? true, options.maximumResponseBytes);
13
+ }
14
+ /** Present stale routing as a bounded status-200 Invocation envelope. */
15
+ export function renderHttpStale(requestId, invocation, current, codec, cors, maximumResponseBytes) {
16
+ return envelopeResponse(codec, protocol.stale(requestId, invocation, current), cors(), 200, true, maximumResponseBytes);
17
+ }
18
+ /** Retirement controls always use JSON, regardless of the negotiated request codec. */
19
+ export function renderDeploymentRetired(deployment, release, cors) {
20
+ return new Response(toBody(json.encode(deploymentRetiredBody(deployment, release))), {
21
+ status: HTTP_DEPLOYMENT_RETIRED_STATUS,
22
+ headers: { ...cors([HTTP_DEPLOYMENT_RETIRED]), ...deploymentRetiredHeaders() },
23
+ });
24
+ }