@astrale-os/kernel-server 0.5.0-beta.8 → 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 (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 +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 +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 +10 -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 +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 +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 +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 +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 +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 -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
@@ -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
+ }
@@ -1,18 +1,23 @@
1
1
  import type { CorsPolicy } from '../../cors/index.js';
2
2
  import type { NextInvocationId } from '../../invocation/index.js';
3
3
  import type { ActiveOperations } from '../../lifecycle/index.js';
4
- import type { PublicationSnapshot } from '../../publication/index.js';
4
+ import type { RequestServing } from '../../release/served.js';
5
5
  import { type ApplicationHost, type Handler } from '../../invocation/index.js';
6
6
  export interface HttpHandlerConfig {
7
7
  readonly handler: Handler;
8
8
  readonly active: ActiveOperations;
9
9
  readonly invocation: NextInvocationId;
10
10
  readonly maximumRequestBytes: number;
11
+ readonly maximumBinaryRequestBytes: number;
11
12
  readonly maximumResponseBytes: number;
12
13
  readonly invocationTimeoutMs: number;
14
+ readonly binaryRequestTimeoutMs: number;
15
+ readonly binaryRequestIdleTimeoutMs?: number;
16
+ readonly binaryDownloadTarget?: string;
17
+ readonly binaryDownloadTimeoutMs?: number;
13
18
  readonly cors: CorsPolicy;
14
19
  readonly websocket: boolean;
15
- readonly currentPublication?: (request: Request) => PublicationSnapshot | undefined;
20
+ /** Present exactly when the Server serves a Release. */
21
+ readonly serving?: (request: Request) => RequestServing;
16
22
  }
17
23
  export declare function createHttpHandler(config: HttpHandlerConfig): (request: Request, host?: ApplicationHost) => Promise<Response>;
18
- //# sourceMappingURL=handler.d.ts.map
@@ -1,12 +1,16 @@
1
1
  import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
2
  import * as errors from '@astrale-os/kernel-protocol/errors';
3
3
  import * as protocol from '@astrale-os/kernel-protocol/invocation';
4
- import { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../../errors/index.js';
5
- import { invocationContext, publicationRouteDecision, } from '../../invocation/index.js';
4
+ import { admissionRefusal, classifyError, classifyRequestError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../../errors/index.js';
5
+ import { admitVia, invocationContext, } from '../../invocation/index.js';
6
+ import { pinned } from '../../release/served.js';
6
7
  import { readBoundedBody } from '../body.js';
7
8
  import { loadCodecs } from '../codecs.js';
8
9
  import { resolveCredential } from '../credential.js';
9
- import { renderHttpError, renderHttpResult } from './render.js';
10
+ import { isStreamingBinary } from '../result.js';
11
+ import { binaryRequestBody } from './binary-request.js';
12
+ import { renderDeploymentRetired, renderHttpError } from './control.js';
13
+ import { renderHttpResult } from './render.js';
10
14
  export function createHttpHandler(config) {
11
15
  return async (request, host) => {
12
16
  const cors = (exposedHeaders) => config.cors.response(request, exposedHeaders);
@@ -21,6 +25,8 @@ export function createHttpHandler(config) {
21
25
  headers: [
22
26
  'authorization',
23
27
  'content-type',
28
+ protocol.http.REQUEST_ENVELOPE_FIELD,
29
+ protocol.http.BINARY_LENGTH_FIELD,
24
30
  protocol.http.TRACEPARENT_FIELD,
25
31
  protocol.http.TRACESTATE_FIELD,
26
32
  ],
@@ -37,24 +43,34 @@ export function createHttpHandler(config) {
37
43
  ...options,
38
44
  maximumResponseBytes: config.maximumResponseBytes,
39
45
  });
40
- const active = config.active.begin(config.invocationTimeoutMs, request.signal);
46
+ const binaryRequest = request.headers.get('content-type')?.trim().toLowerCase() ===
47
+ protocol.http.BINARY_REQUEST_CONTENT_TYPE;
48
+ const active = config.active.begin(binaryRequest ? config.binaryRequestTimeoutMs : config.invocationTimeoutMs, request.signal);
41
49
  if (active === undefined) {
42
- return renderError(null, {
43
- code: errors.CODES.BACKEND_UNAVAILABLE,
44
- message: 'server is unavailable.',
45
- }, codecs.json, { status: 503, negotiated: false });
50
+ if (binaryRequest && request.body !== null && !request.body.locked) {
51
+ void request.body.cancel('binary request rejected').catch(() => undefined);
52
+ }
53
+ return renderError(null, admissionRefusal(config.active.isAccepting), codecs.json, {
54
+ status: 503,
55
+ negotiated: false,
56
+ });
46
57
  }
47
58
  let handedOff = false;
59
+ let binary;
48
60
  try {
49
- const supported = await loadCodecs();
61
+ const supported = loadCodecs();
50
62
  const contentType = request.headers.get('content-type');
51
- const incoming = contentType === null || contentType.trim() === ''
63
+ // No default representation: a body without a declared Astrale type is never decoded, so a
64
+ // browser cannot send an admissible invocation from another origin without a CORS preflight.
65
+ const incoming = binaryRequest
52
66
  ? codecs.json
53
67
  : protocol.http.codecForContentType(contentType, supported);
54
68
  if (incoming === undefined) {
55
69
  return renderError(null, {
56
70
  code: errors.CODES.UNSUPPORTED,
57
- message: 'Unsupported request Content-Type.',
71
+ message: contentType === null || contentType.trim() === ''
72
+ ? 'Missing request Content-Type.'
73
+ : 'Unsupported request Content-Type.',
58
74
  }, codecs.json, { status: 415, negotiated: false });
59
75
  }
60
76
  const outgoing = protocol.http.negotiateResponseCodec(request.headers.get('accept'), incoming, supported);
@@ -66,7 +82,15 @@ export function createHttpHandler(config) {
66
82
  }
67
83
  let body;
68
84
  try {
69
- body = await readBoundedBody(request, config.maximumRequestBytes, active.signal);
85
+ if (binaryRequest)
86
+ binary = binaryRequestBody(request, config.maximumBinaryRequestBytes, active.signal, config.binaryRequestIdleTimeoutMs === undefined
87
+ ? undefined
88
+ : {
89
+ timeoutMs: config.binaryRequestIdleTimeoutMs,
90
+ cancel: () => active.cancel('binary request idle timeout exceeded'),
91
+ });
92
+ else
93
+ body = await readBoundedBody(request, config.maximumRequestBytes, active.signal);
70
94
  }
71
95
  catch (input) {
72
96
  const payload = input instanceof RequestBodyTooLargeError
@@ -87,37 +111,54 @@ export function createHttpHandler(config) {
87
111
  status: input instanceof RequestBodyTooLargeError ? 413 : errors.httpStatusOf(payload),
88
112
  });
89
113
  }
90
- let decoded;
91
- try {
92
- decoded = incoming.decode(body);
93
- }
94
- catch (input) {
95
- if (!(input instanceof codecs.DecodeError))
96
- throw input;
97
- return renderError(null, { code: errors.CODES.PARSE_ERROR, message: 'Request body is invalid.' }, outgoing);
98
- }
99
114
  let envelope;
100
115
  try {
101
- envelope = protocol.decodeRequest(decoded);
116
+ if (binaryRequest) {
117
+ envelope = protocol.http.decodeBinaryRequestEnvelope(request.headers.get(protocol.http.REQUEST_ENVELOPE_FIELD));
118
+ if (envelope.idempotencyKey !== undefined) {
119
+ return renderError(envelope.requestId, {
120
+ code: errors.CODES.INVALID_REQUEST,
121
+ message: 'Binary requests forbid idempotencyKey.',
122
+ }, outgoing);
123
+ }
124
+ }
125
+ else {
126
+ let decoded;
127
+ try {
128
+ decoded = incoming.decode(body);
129
+ }
130
+ catch (input) {
131
+ if (!(input instanceof codecs.DecodeError))
132
+ throw input;
133
+ return renderError(null, { code: errors.CODES.PARSE_ERROR, message: 'Request body is invalid.' }, outgoing);
134
+ }
135
+ envelope = protocol.decodeRequest(decoded);
136
+ }
102
137
  }
103
138
  catch (input) {
104
139
  return renderError(null, classifyError(input), outgoing);
105
140
  }
106
- const publication = config.currentPublication?.(request);
107
- if (config.currentPublication !== undefined && publication === undefined) {
141
+ if (!binaryRequest &&
142
+ config.binaryDownloadTarget !== undefined &&
143
+ protocol.targetPath(envelope.call.target) === config.binaryDownloadTarget) {
144
+ active.extend(config.binaryDownloadTimeoutMs);
145
+ }
146
+ if (active.signal.aborted) {
147
+ return renderError(envelope.requestId, classifyRequestError(active.signal.reason, active), outgoing);
148
+ }
149
+ const serving = config.serving?.(request);
150
+ const served = serving?.current();
151
+ if (serving !== undefined && served === undefined) {
108
152
  return renderError(envelope.requestId, {
109
153
  code: errors.CODES.BACKEND_UNAVAILABLE,
110
- message: 'Publication is unavailable.',
154
+ message: 'Release is unavailable.',
111
155
  }, outgoing, { status: 503 });
112
156
  }
113
157
  if (envelope.routing?.kind === 'via') {
114
- if (publication === undefined) {
115
- return renderError(envelope.requestId, {
116
- code: errors.CODES.INVALID_REQUEST,
117
- message: 'Publication routing requires a Publication-backed destination.',
118
- }, outgoing);
158
+ const decision = admitVia(served, envelope.routing);
159
+ if (decision.kind === 'retired') {
160
+ return renderDeploymentRetired(serving.deployment(), decision.release, cors);
119
161
  }
120
- const decision = publicationRouteDecision(publication.publication, envelope.routing.publication);
121
162
  if (decision.kind === 'rejected') {
122
163
  return renderError(envelope.requestId, decision.error, outgoing);
123
164
  }
@@ -135,9 +176,21 @@ export function createHttpHandler(config) {
135
176
  ...(envelope.delegate === undefined ? {} : { delegate: envelope.delegate }),
136
177
  ...(envelope.routing?.kind === 'via' ? { via: envelope.routing } : {}),
137
178
  ...(envelope.schema === undefined ? {} : { schema: envelope.schema }),
138
- ...(publication === undefined ? {} : { publication }),
139
- context: invocationContext(assigned, active, config.active, host, undefined, envelope.idempotencyKey),
179
+ ...(envelope.maintenance === undefined ? {} : { maintenance: envelope.maintenance }),
180
+ ...(served === undefined ? {} : pinned(served)),
181
+ context: invocationContext(assigned, active, config.active, host, undefined, envelope.idempotencyKey, binary),
140
182
  }));
183
+ if (binary !== undefined && result.kind !== 'redirect' && result.kind !== 'miss') {
184
+ if (!binary.completed()) {
185
+ if (result.kind === 'stream') {
186
+ await result.stream.cancel('Binary request body was not fully consumed.');
187
+ }
188
+ else if (result.kind === 'binary' && isStreamingBinary(result.value)) {
189
+ await result.value.body[Symbol.asyncIterator]().return?.();
190
+ }
191
+ throw new Error('Binary Function returned before consuming its request body.');
192
+ }
193
+ }
141
194
  const response = renderHttpResult(result, {
142
195
  requestId: envelope.requestId,
143
196
  codec: outgoing,
@@ -146,20 +199,12 @@ export function createHttpHandler(config) {
146
199
  active,
147
200
  maximumResponseBytes: config.maximumResponseBytes,
148
201
  });
149
- handedOff = result.kind === 'stream';
202
+ handedOff =
203
+ result.kind === 'stream' || (result.kind === 'binary' && isStreamingBinary(result.value));
150
204
  return response;
151
205
  }
152
206
  catch (input) {
153
- if (active.timedOut) {
154
- return renderError(envelope.requestId, {
155
- code: errors.CODES.DEADLINE_EXCEEDED,
156
- message: 'Invocation deadline exceeded.',
157
- }, outgoing, { status: 504 });
158
- }
159
- if (active.signal.aborted) {
160
- return renderError(envelope.requestId, { code: errors.CODES.CANCELLED, message: 'Invocation was cancelled.' }, outgoing, { status: 408 });
161
- }
162
- return renderError(envelope.requestId, classifyError(input), outgoing);
207
+ return renderError(envelope.requestId, classifyRequestError(input, active), outgoing);
163
208
  }
164
209
  }
165
210
  catch (input) {
@@ -168,6 +213,15 @@ export function createHttpHandler(config) {
168
213
  });
169
214
  }
170
215
  finally {
216
+ if (binary !== undefined) {
217
+ binary.cancel('binary invocation completed');
218
+ }
219
+ else if (binaryRequest &&
220
+ binary === undefined &&
221
+ request.body !== null &&
222
+ !request.body.locked) {
223
+ void request.body.cancel('binary request rejected').catch(() => undefined);
224
+ }
171
225
  if (!handedOff)
172
226
  active.settle();
173
227
  }
@@ -178,4 +232,3 @@ function noStore(response) {
178
232
  headers.set('cache-control', 'no-store');
179
233
  return new Response(response.body, { status: response.status, headers });
180
234
  }
181
- //# sourceMappingURL=handler.js.map
@@ -1,3 +1,4 @@
1
1
  export { createHttpHandler, type HttpHandlerConfig } from './handler.js';
2
- export { encodeEnvelope, renderBinary, renderHttpError, renderHttpResult, renderHttpStale, renderRedirect, type CorsHeaders, type ErrorRenderOptions, type RenderContext, } from './render.js';
3
- //# sourceMappingURL=index.d.ts.map
2
+ export { renderHttpResult } from './render.js';
3
+ export { renderHttpError, renderHttpStale } from './control.js';
4
+ export type { CorsHeaders, ErrorRenderOptions, RenderContext } from './model.js';
@@ -1,3 +1,3 @@
1
1
  export { createHttpHandler } from './handler.js';
2
- export { encodeEnvelope, renderBinary, renderHttpError, renderHttpResult, renderHttpStale, renderRedirect, } from './render.js';
3
- //# sourceMappingURL=index.js.map
2
+ export { renderHttpResult } from './render.js';
3
+ export { renderHttpError, renderHttpStale } from './control.js';
@@ -0,0 +1,20 @@
1
+ import type { Codec } from '@astrale-os/kernel-protocol/codecs';
2
+ import type * as protocol from '@astrale-os/kernel-protocol/invocation';
3
+ import type { ActiveOperation } from '../../lifecycle/index.js';
4
+ /** Correlation, binding and active lifetime retained while presenting one result. */
5
+ export interface RenderContext {
6
+ readonly requestId: protocol.RequestId;
7
+ readonly codec: Codec;
8
+ readonly cors: CorsHeaders;
9
+ readonly invocation: protocol.InvocationId;
10
+ readonly active: ActiveOperation;
11
+ readonly maximumResponseBytes: number;
12
+ }
13
+ /** Derive response headers with the transport fields exposed to the browser. */
14
+ export type CorsHeaders = (exposedHeaders?: readonly string[]) => Readonly<Record<string, string>>;
15
+ /** Bound and negotiate an error using the admitted request presentation. */
16
+ export interface ErrorRenderOptions {
17
+ readonly status?: number;
18
+ readonly negotiated?: boolean;
19
+ readonly maximumResponseBytes?: number;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,32 +1,4 @@
1
- import type { Codec } 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 type { Handler } from '../../invocation/index.js';
5
- import type { ActiveOperation } from '../../lifecycle/index.js';
6
- export interface RenderContext {
7
- readonly requestId: protocol.RequestId;
8
- readonly codec: Codec;
9
- readonly cors: CorsHeaders;
10
- readonly invocation: protocol.InvocationId;
11
- readonly active: ActiveOperation;
12
- readonly maximumResponseBytes: number;
13
- }
14
- export type CorsHeaders = (exposedHeaders?: readonly string[]) => Readonly<Record<string, string>>;
15
- export interface ErrorRenderOptions {
16
- readonly status?: number;
17
- readonly negotiated?: boolean;
18
- readonly maximumResponseBytes?: number;
19
- }
20
- type HandlerResult = Awaited<ReturnType<Handler['dispatch']>>;
1
+ import type { HandlerResult } from '../../invocation/index.js';
2
+ import type { RenderContext } from './model.js';
3
+ /** Render one admitted Handler result and retain only live producers in the active operation. */
21
4
  export declare function renderHttpResult(result: HandlerResult, context: RenderContext): Response;
22
- export declare function renderBinary(requestId: protocol.RequestId, invocation: protocol.InvocationId, binary: Extract<HandlerResult, {
23
- readonly kind: 'binary';
24
- }>['value']): protocol.BinaryResponse;
25
- export declare function renderRedirect(requestId: protocol.RequestId, invocation: protocol.InvocationId, redirect: Extract<HandlerResult, {
26
- readonly kind: 'redirect';
27
- }>['redirect']): protocol.RedirectResponse;
28
- export declare function renderHttpError(requestId: protocol.RequestId, invocation: protocol.InvocationId, error: errors.ErrorPayload, codec: Codec, cors: CorsHeaders, options?: ErrorRenderOptions): Response;
29
- export declare function renderHttpStale(requestId: protocol.RequestId, invocation: protocol.InvocationId, current: protocol.Stale['current'], codec: Codec, cors: CorsHeaders, maximumResponseBytes?: number): Response;
30
- export declare function encodeEnvelope(codec: Codec, input: unknown): string | Uint8Array;
31
- export {};
32
- //# sourceMappingURL=render.d.ts.map