@astrale-os/kernel-server 0.4.17 → 0.5.0-beta.10

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 (235) hide show
  1. package/dist/capture.d.ts +2 -0
  2. package/dist/capture.js +9 -0
  3. package/dist/cors/cors.d.ts +21 -0
  4. package/dist/cors/cors.js +135 -0
  5. package/dist/cors/index.d.ts +2 -0
  6. package/dist/cors/index.js +1 -0
  7. package/dist/cors/types.d.ts +11 -0
  8. package/dist/cors/types.js +1 -0
  9. package/dist/errors/availability.d.ts +10 -0
  10. package/dist/errors/availability.js +14 -0
  11. package/dist/errors/classify.d.ts +3 -0
  12. package/dist/errors/classify.js +41 -0
  13. package/dist/errors/index.d.ts +4 -0
  14. package/dist/errors/index.js +4 -0
  15. package/dist/errors/request-body.d.ts +7 -0
  16. package/dist/errors/request-body.js +14 -0
  17. package/dist/errors/response-body.d.ts +4 -0
  18. package/dist/errors/response-body.js +8 -0
  19. package/dist/index.d.ts +5 -12
  20. package/dist/index.js +4 -19
  21. package/dist/invocation/context.d.ts +6 -0
  22. package/dist/invocation/context.js +60 -0
  23. package/dist/invocation/identity.d.ts +9 -0
  24. package/dist/invocation/identity.js +11 -0
  25. package/dist/invocation/index.d.ts +4 -3
  26. package/dist/invocation/index.js +3 -3
  27. package/dist/invocation/publication-route.d.ts +13 -0
  28. package/dist/invocation/publication-route.js +44 -0
  29. package/dist/invocation/types.d.ts +62 -0
  30. package/dist/invocation/types.js +1 -0
  31. package/dist/issuer/exchange.d.ts +12 -0
  32. package/dist/issuer/exchange.js +136 -0
  33. package/dist/issuer/index.d.ts +3 -0
  34. package/dist/issuer/index.js +1 -0
  35. package/dist/issuer/routes.d.ts +13 -0
  36. package/dist/issuer/routes.js +149 -0
  37. package/dist/issuer/types.d.ts +11 -0
  38. package/dist/issuer/types.js +1 -0
  39. package/dist/lifecycle/active.d.ts +39 -0
  40. package/dist/lifecycle/active.js +199 -0
  41. package/dist/lifecycle/index.d.ts +1 -0
  42. package/dist/lifecycle/index.js +1 -0
  43. package/dist/publication/delivery/handler.d.ts +9 -0
  44. package/dist/publication/delivery/handler.js +127 -0
  45. package/dist/publication/delivery/index.d.ts +2 -0
  46. package/dist/publication/delivery/index.js +1 -0
  47. package/dist/publication/delivery/types.d.ts +11 -0
  48. package/dist/publication/delivery/types.js +1 -0
  49. package/dist/publication/index.d.ts +3 -0
  50. package/dist/publication/index.js +2 -0
  51. package/dist/publication/snapshot.d.ts +16 -0
  52. package/dist/publication/snapshot.js +37 -0
  53. package/dist/publication/table.d.ts +4 -0
  54. package/dist/publication/table.js +16 -0
  55. package/dist/publication/types.d.ts +22 -0
  56. package/dist/publication/types.js +1 -0
  57. package/dist/routes/index.d.ts +1 -0
  58. package/dist/routes/index.js +1 -0
  59. package/dist/routes/routes.d.ts +25 -0
  60. package/dist/routes/routes.js +448 -0
  61. package/dist/server.d.ts +4 -0
  62. package/dist/server.js +266 -0
  63. package/dist/transport/body.d.ts +4 -0
  64. package/dist/transport/body.js +50 -0
  65. package/dist/transport/codecs.d.ts +4 -0
  66. package/dist/transport/codecs.js +5 -0
  67. package/dist/transport/credential.d.ts +10 -0
  68. package/dist/transport/credential.js +28 -0
  69. package/dist/transport/http/handler.d.ts +17 -0
  70. package/dist/transport/http/handler.js +180 -0
  71. package/dist/transport/http/index.d.ts +2 -0
  72. package/dist/transport/http/index.js +2 -0
  73. package/dist/transport/http/render.d.ts +31 -0
  74. package/dist/transport/http/render.js +288 -0
  75. package/dist/transport/index.d.ts +9 -0
  76. package/dist/transport/index.js +8 -0
  77. package/dist/transport/router.d.ts +4 -0
  78. package/dist/transport/router.js +4 -0
  79. package/dist/transport/stream-error.d.ts +3 -0
  80. package/dist/transport/stream-error.js +13 -0
  81. package/dist/transport/types.d.ts +28 -0
  82. package/dist/transport/types.js +1 -0
  83. package/dist/transport/websocket/index.d.ts +2 -0
  84. package/dist/transport/websocket/index.js +2 -0
  85. package/dist/transport/websocket/session.d.ts +20 -0
  86. package/dist/transport/websocket/session.js +335 -0
  87. package/dist/transport/websocket/upgrade.d.ts +20 -0
  88. package/dist/transport/websocket/upgrade.js +46 -0
  89. package/dist/types.d.ts +65 -0
  90. package/dist/types.js +1 -0
  91. package/package.json +24 -14
  92. package/dist/app/create.d.ts +0 -48
  93. package/dist/app/create.d.ts.map +0 -1
  94. package/dist/app/create.js +0 -108
  95. package/dist/app/create.js.map +0 -1
  96. package/dist/app/index.d.ts +0 -4
  97. package/dist/app/index.d.ts.map +0 -1
  98. package/dist/app/index.js +0 -4
  99. package/dist/app/index.js.map +0 -1
  100. package/dist/app/multiplexed.d.ts +0 -53
  101. package/dist/app/multiplexed.d.ts.map +0 -1
  102. package/dist/app/multiplexed.js +0 -64
  103. package/dist/app/multiplexed.js.map +0 -1
  104. package/dist/app/ws-upgrade.d.ts +0 -34
  105. package/dist/app/ws-upgrade.d.ts.map +0 -1
  106. package/dist/app/ws-upgrade.js +0 -94
  107. package/dist/app/ws-upgrade.js.map +0 -1
  108. package/dist/capabilities/contract.d.ts +0 -8
  109. package/dist/capabilities/contract.d.ts.map +0 -1
  110. package/dist/capabilities/contract.js +0 -2
  111. package/dist/capabilities/contract.js.map +0 -1
  112. package/dist/capabilities/defer.d.ts +0 -4
  113. package/dist/capabilities/defer.d.ts.map +0 -1
  114. package/dist/capabilities/defer.js +0 -2
  115. package/dist/capabilities/defer.js.map +0 -1
  116. package/dist/capabilities/hono.d.ts +0 -8
  117. package/dist/capabilities/hono.d.ts.map +0 -1
  118. package/dist/capabilities/hono.js +0 -13
  119. package/dist/capabilities/hono.js.map +0 -1
  120. package/dist/capabilities/index.d.ts +0 -6
  121. package/dist/capabilities/index.d.ts.map +0 -1
  122. package/dist/capabilities/index.js +0 -3
  123. package/dist/capabilities/index.js.map +0 -1
  124. package/dist/capabilities/lifecycle.d.ts +0 -17
  125. package/dist/capabilities/lifecycle.d.ts.map +0 -1
  126. package/dist/capabilities/lifecycle.js +0 -37
  127. package/dist/capabilities/lifecycle.js.map +0 -1
  128. package/dist/capabilities/sleep.d.ts +0 -4
  129. package/dist/capabilities/sleep.d.ts.map +0 -1
  130. package/dist/capabilities/sleep.js +0 -8
  131. package/dist/capabilities/sleep.js.map +0 -1
  132. package/dist/cors.d.ts +0 -21
  133. package/dist/cors.d.ts.map +0 -1
  134. package/dist/cors.js +0 -17
  135. package/dist/cors.js.map +0 -1
  136. package/dist/envelope/batch.d.ts +0 -13
  137. package/dist/envelope/batch.d.ts.map +0 -1
  138. package/dist/envelope/batch.js +0 -96
  139. package/dist/envelope/batch.js.map +0 -1
  140. package/dist/envelope/codec.d.ts +0 -26
  141. package/dist/envelope/codec.d.ts.map +0 -1
  142. package/dist/envelope/codec.js +0 -52
  143. package/dist/envelope/codec.js.map +0 -1
  144. package/dist/envelope/credential.d.ts +0 -20
  145. package/dist/envelope/credential.d.ts.map +0 -1
  146. package/dist/envelope/credential.js +0 -32
  147. package/dist/envelope/credential.js.map +0 -1
  148. package/dist/envelope/http.d.ts +0 -31
  149. package/dist/envelope/http.d.ts.map +0 -1
  150. package/dist/envelope/http.js +0 -132
  151. package/dist/envelope/http.js.map +0 -1
  152. package/dist/envelope/sse.d.ts +0 -11
  153. package/dist/envelope/sse.d.ts.map +0 -1
  154. package/dist/envelope/sse.js +0 -73
  155. package/dist/envelope/sse.js.map +0 -1
  156. package/dist/envelope/ws.d.ts +0 -35
  157. package/dist/envelope/ws.d.ts.map +0 -1
  158. package/dist/envelope/ws.js +0 -142
  159. package/dist/envelope/ws.js.map +0 -1
  160. package/dist/index.d.ts.map +0 -1
  161. package/dist/index.js.map +0 -1
  162. package/dist/invocation/dispatch.d.ts +0 -32
  163. package/dist/invocation/dispatch.d.ts.map +0 -1
  164. package/dist/invocation/dispatch.js +0 -68
  165. package/dist/invocation/dispatch.js.map +0 -1
  166. package/dist/invocation/dispatcher.d.ts +0 -19
  167. package/dist/invocation/dispatcher.d.ts.map +0 -1
  168. package/dist/invocation/dispatcher.js +0 -9
  169. package/dist/invocation/dispatcher.js.map +0 -1
  170. package/dist/invocation/index.d.ts.map +0 -1
  171. package/dist/invocation/index.js.map +0 -1
  172. package/dist/jwks/discovery.d.ts +0 -40
  173. package/dist/jwks/discovery.d.ts.map +0 -1
  174. package/dist/jwks/discovery.js +0 -113
  175. package/dist/jwks/discovery.js.map +0 -1
  176. package/dist/jwks/index.d.ts +0 -3
  177. package/dist/jwks/index.d.ts.map +0 -1
  178. package/dist/jwks/index.js +0 -3
  179. package/dist/jwks/index.js.map +0 -1
  180. package/dist/jwks/issuers.d.ts +0 -47
  181. package/dist/jwks/issuers.d.ts.map +0 -1
  182. package/dist/jwks/issuers.js +0 -76
  183. package/dist/jwks/issuers.js.map +0 -1
  184. package/dist/jwks/keys.d.ts +0 -44
  185. package/dist/jwks/keys.d.ts.map +0 -1
  186. package/dist/jwks/keys.js +0 -56
  187. package/dist/jwks/keys.js.map +0 -1
  188. package/dist/plan/host-params.d.ts +0 -13
  189. package/dist/plan/host-params.d.ts.map +0 -1
  190. package/dist/plan/host-params.js +0 -23
  191. package/dist/plan/host-params.js.map +0 -1
  192. package/dist/plan/host.d.ts +0 -24
  193. package/dist/plan/host.d.ts.map +0 -1
  194. package/dist/plan/host.js +0 -24
  195. package/dist/plan/host.js.map +0 -1
  196. package/dist/plan/index.d.ts +0 -4
  197. package/dist/plan/index.d.ts.map +0 -1
  198. package/dist/plan/index.js +0 -4
  199. package/dist/plan/index.js.map +0 -1
  200. package/dist/plan/walk.d.ts +0 -51
  201. package/dist/plan/walk.d.ts.map +0 -1
  202. package/dist/plan/walk.js +0 -110
  203. package/dist/plan/walk.js.map +0 -1
  204. package/dist/render/binary.d.ts +0 -19
  205. package/dist/render/binary.d.ts.map +0 -1
  206. package/dist/render/binary.js +0 -33
  207. package/dist/render/binary.js.map +0 -1
  208. package/dist/render/body.d.ts +0 -10
  209. package/dist/render/body.d.ts.map +0 -1
  210. package/dist/render/body.js +0 -17
  211. package/dist/render/body.js.map +0 -1
  212. package/dist/render/index.d.ts +0 -3
  213. package/dist/render/index.d.ts.map +0 -1
  214. package/dist/render/index.js +0 -3
  215. package/dist/render/index.js.map +0 -1
  216. package/dist/routed/credential.d.ts +0 -10
  217. package/dist/routed/credential.d.ts.map +0 -1
  218. package/dist/routed/credential.js +0 -55
  219. package/dist/routed/credential.js.map +0 -1
  220. package/dist/routed/handler.d.ts +0 -38
  221. package/dist/routed/handler.d.ts.map +0 -1
  222. package/dist/routed/handler.js +0 -123
  223. package/dist/routed/handler.js.map +0 -1
  224. package/dist/routed/index.d.ts +0 -5
  225. package/dist/routed/index.d.ts.map +0 -1
  226. package/dist/routed/index.js +0 -5
  227. package/dist/routed/index.js.map +0 -1
  228. package/dist/routed/params.d.ts +0 -40
  229. package/dist/routed/params.d.ts.map +0 -1
  230. package/dist/routed/params.js +0 -146
  231. package/dist/routed/params.js.map +0 -1
  232. package/dist/routed/stream.d.ts +0 -21
  233. package/dist/routed/stream.d.ts.map +0 -1
  234. package/dist/routed/stream.js +0 -69
  235. package/dist/routed/stream.js.map +0 -1
@@ -0,0 +1,448 @@
1
+ import * as codecs from '@astrale-os/kernel-protocol/codecs';
2
+ import * as errors from '@astrale-os/kernel-protocol/errors';
3
+ import * as protocol from '@astrale-os/kernel-protocol/invocation';
4
+ import { PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
5
+ import { reference as publicationReference } from '@astrale-os/kernel-protocol/publication/reference';
6
+ import * as routes from '@astrale-os/kernel-protocol/routes';
7
+ import { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../errors/index.js';
8
+ import { invocationContext } from '../invocation/index.js';
9
+ import { handleDelivery } from '../publication/delivery/index.js';
10
+ import { readPreparedPublicationSnapshot, } from '../publication/index.js';
11
+ import { readBoundedBody } from '../transport/body.js';
12
+ import {} from '../transport/router.js';
13
+ import { interruptedStreamError } from '../transport/stream-error.js';
14
+ export function mountRoutes(app, source, deliveryProvider, handler, active, nextInvocation, limits, cors, hostFor, reservations, externalIssuer) {
15
+ const publicationPath = externalIssuer === undefined
16
+ ? PUBLICATION_PATH
17
+ : new URL(publicationUrl(externalIssuer)).pathname;
18
+ const externalOrigin = externalIssuer === undefined ? undefined : new URL(externalIssuer).origin;
19
+ if (cors.enabled) {
20
+ app.options(publicationPath, (context) => cors.preflight(context.req.raw, {
21
+ methods: ['GET'],
22
+ headers: ['if-none-match'],
23
+ }));
24
+ }
25
+ app.get(publicationPath, (context) => {
26
+ const responseCors = cors.response(context.req.raw, ['etag']);
27
+ if (!active.isAccepting)
28
+ return response({ error: 'Publication is unavailable while draining.' }, 503, responseCors);
29
+ const snapshot = servingSnapshot(source, limits, context.req.raw, reservations, externalIssuer);
30
+ if (snapshot === undefined)
31
+ return response({ error: 'Publication is not ready.' }, 503, responseCors);
32
+ const body = snapshot.publicationBytes;
33
+ if (context.req.raw.headers.get('if-none-match') === quoteEtag(snapshot.publication.etag)) {
34
+ return new Response(null, {
35
+ status: 304,
36
+ headers: {
37
+ ...responseCors,
38
+ etag: quoteEtag(snapshot.publication.etag),
39
+ 'cache-control': 'public, max-age=0, must-revalidate, no-transform',
40
+ },
41
+ });
42
+ }
43
+ return new Response(toBody(body), {
44
+ status: 200,
45
+ headers: {
46
+ ...responseCors,
47
+ 'content-type': 'application/json',
48
+ etag: quoteEtag(snapshot.publication.etag),
49
+ 'cache-control': 'public, max-age=0, must-revalidate, no-transform',
50
+ },
51
+ });
52
+ });
53
+ app.all('*', async (context) => {
54
+ const snapshot = servingSnapshot(source, limits, context.req.raw, reservations, externalIssuer);
55
+ if (snapshot === undefined)
56
+ return context.notFound();
57
+ const request = context.req.raw;
58
+ if (request.method === 'OPTIONS' && cors.enabled) {
59
+ return publicationPreflight(cors, request, snapshot, externalOrigin);
60
+ }
61
+ const responseCors = cors.response(request);
62
+ if (sameRoute(request.url, snapshot.publication.schema.bundle.href, externalOrigin)) {
63
+ if (!active.isAccepting)
64
+ return response({ error: 'Publication artifact is unavailable while draining.' }, 503, responseCors);
65
+ if (request.method !== 'GET')
66
+ return response({ error: 'Bundle retrieval requires GET.' }, 405, responseCors);
67
+ const body = snapshot.bundle;
68
+ if (body.byteLength !== snapshot.publication.schema.bundle.ref.size) {
69
+ return response({ error: 'Bundle differs from its Publication.' }, 500, responseCors);
70
+ }
71
+ return new Response(toBody(body), {
72
+ status: 200,
73
+ headers: {
74
+ ...responseCors,
75
+ 'content-type': snapshot.publication.schema.bundle.ref.mediaType,
76
+ etag: quoteEtag(snapshot.publication.schema.bundle.ref.digest),
77
+ 'cache-control': 'public, max-age=31536000, immutable, no-transform',
78
+ },
79
+ });
80
+ }
81
+ const deliveryUrl = snapshot.publication.delivery?.href;
82
+ if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
83
+ return handleDelivery(request, snapshot, deliveryProvider, active, limits, cors.response(request));
84
+ }
85
+ let routing;
86
+ try {
87
+ const ifMatch = request.headers.get(routes.HTTP_ROUTE_PUBLICATION);
88
+ if (ifMatch !== null &&
89
+ /^"sha256:[0-9a-f]{64}"$/u.test(ifMatch) &&
90
+ ifMatch !== quoteEtag(snapshot.publication.etag)) {
91
+ return new Response(null, {
92
+ status: routes.HTTP_ROUTE_STALE_STATUS,
93
+ headers: {
94
+ ...cors.response(request, [routes.HTTP_ROUTE_STALE]),
95
+ ...routes.staleHeaders(snapshot.publication.etag),
96
+ },
97
+ });
98
+ }
99
+ routing = routes.decodeRouting(request, publicationReference(snapshot.publication));
100
+ }
101
+ catch (cause) {
102
+ if (cause instanceof routes.RouteError) {
103
+ return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route routing is invalid.' }, 400, responseCors);
104
+ }
105
+ throw cause;
106
+ }
107
+ const route = routes.match(snapshot.table, request);
108
+ if (route === undefined)
109
+ return context.notFound();
110
+ const assigned = nextInvocation();
111
+ const operation = active.begin(limits.invocationTimeoutMs, request.signal);
112
+ if (operation === undefined)
113
+ return nativeError({
114
+ code: errors.CODES.BACKEND_UNAVAILABLE,
115
+ message: 'Server is draining.',
116
+ }, 503, responseCors);
117
+ let handedOff = false;
118
+ try {
119
+ const body = await readBoundedBody(request, limits.maximumRequestBytes, operation.signal);
120
+ const invocation = routes.decode(route, { url: request.url, method: request.method, headers: request.headers, body }, snapshot.domain, snapshot.table.publication);
121
+ const idempotencyHeader = request.headers.get(protocol.HTTP_IDEMPOTENCY_KEY);
122
+ const idempotencyKey = idempotencyHeader === null
123
+ ? undefined
124
+ : protocol.acceptIdempotencyKey(idempotencyHeader, protocol.HTTP_IDEMPOTENCY_KEY);
125
+ const result = await handler.dispatch(Object.freeze({
126
+ call: invocation.call,
127
+ ...(invocation.credential === undefined ? {} : { credential: invocation.credential }),
128
+ ...(routing === undefined ? {} : { via: routing }),
129
+ publication: snapshot.snapshot,
130
+ context: invocationContext(assigned, operation, active, hostFor(request), body, idempotencyKey),
131
+ }));
132
+ if (result.kind === 'value') {
133
+ return new Response(encodedBody(codecs.json.encode(result.value)), {
134
+ headers: {
135
+ ...responseCors,
136
+ 'content-type': 'application/json',
137
+ },
138
+ });
139
+ }
140
+ if (result.kind === 'binary') {
141
+ const applicationHeaders = applicationBinaryHeaders(result.value.headers);
142
+ return new Response(toBody(result.value.body), {
143
+ headers: {
144
+ ...cors.response(request, [
145
+ protocol.http.BINARY_HEADERS_FIELD,
146
+ ...Object.keys(applicationHeaders),
147
+ ]),
148
+ ...applicationHeaders,
149
+ 'content-type': result.value.mediaType,
150
+ [protocol.http.BINARY_HEADERS_FIELD]: protocol.http.encodeBinaryHeaderNames(applicationHeaders),
151
+ },
152
+ });
153
+ }
154
+ if (result.kind === 'stream') {
155
+ handedOff = true;
156
+ return await streamResponse(result.stream, operation, responseCors);
157
+ }
158
+ if (result.kind === 'miss') {
159
+ return new Response(null, {
160
+ status: routes.HTTP_ROUTE_MISS_STATUS,
161
+ headers: {
162
+ ...cors.response(request, [routes.HTTP_ROUTE_MISS]),
163
+ ...routes.missHeaders(),
164
+ },
165
+ });
166
+ }
167
+ return nativeError({
168
+ code: errors.CODES.UNSUPPORTED,
169
+ message: 'Native routes do not forward Runtime redirects.',
170
+ }, 502, responseCors);
171
+ }
172
+ catch (cause) {
173
+ if (cause instanceof RequestBodyTooLargeError) {
174
+ return nativeError({ code: errors.CODES.LIMIT_EXCEEDED, message: 'Request body is too large.' }, 413, responseCors);
175
+ }
176
+ if (cause instanceof RequestBodyInvalidError) {
177
+ return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Request body is invalid.' }, 400, responseCors);
178
+ }
179
+ if (cause instanceof routes.RouteError) {
180
+ return nativeError({ code: errors.CODES.INVALID_REQUEST, message: 'Native route request is invalid.' }, 400, responseCors);
181
+ }
182
+ if (operation.timedOut) {
183
+ return nativeError({
184
+ code: errors.CODES.DEADLINE_EXCEEDED,
185
+ message: 'Invocation deadline exceeded.',
186
+ }, 504, responseCors);
187
+ }
188
+ if (operation.signal.aborted) {
189
+ return nativeError({ code: errors.CODES.CANCELLED, message: 'Invocation was cancelled.' }, 408, responseCors);
190
+ }
191
+ const payload = classifyError(cause);
192
+ return nativeError(payload, errors.httpStatusOf(payload), responseCors);
193
+ }
194
+ finally {
195
+ if (!handedOff)
196
+ operation.settle();
197
+ }
198
+ });
199
+ }
200
+ function publicationPreflight(cors, request, snapshot, externalOrigin) {
201
+ if (sameRoute(request.url, snapshot.publication.schema.bundle.href, externalOrigin)) {
202
+ return cors.preflight(request, { methods: ['GET'], headers: ['if-none-match'] });
203
+ }
204
+ const deliveryUrl = snapshot.publication.delivery?.href;
205
+ if (deliveryUrl !== undefined && sameRoute(request.url, deliveryUrl, externalOrigin)) {
206
+ return cors.preflight(request, {
207
+ methods: ['POST'],
208
+ headers: ['authorization', 'content-type'],
209
+ });
210
+ }
211
+ const requestedMethod = request.headers.get('access-control-request-method');
212
+ const route = requestedMethod === null
213
+ ? undefined
214
+ : routes.match(snapshot.table, { url: request.url, method: requestedMethod });
215
+ return cors.preflight(request, route === undefined ? undefined : nativeCorsRoute(route, requestedMethod));
216
+ }
217
+ function nativeCorsRoute(entry, requestedMethod) {
218
+ const headers = new Set([
219
+ 'accept',
220
+ routes.HTTP_ROUTE_PUBLICATION,
221
+ routes.HTTP_ROUTE_ISSUER,
222
+ protocol.HTTP_IDEMPOTENCY_KEY,
223
+ ]);
224
+ if (entry.body !== undefined)
225
+ headers.add('content-type');
226
+ let allowAnyHeaders = false;
227
+ for (const pick of Object.values(entry.headers ?? {})) {
228
+ if (pick === '*')
229
+ allowAnyHeaders = true;
230
+ else
231
+ headers.add(pick.toLowerCase());
232
+ }
233
+ const credential = entry.credential;
234
+ const sources = credential === undefined
235
+ ? [{ in: 'header' }]
236
+ : Array.isArray(credential)
237
+ ? credential
238
+ : [credential];
239
+ for (const source of sources) {
240
+ if (source.in === 'header')
241
+ headers.add((source.name ?? 'authorization').toLowerCase());
242
+ }
243
+ return Object.freeze({
244
+ methods: Object.freeze([entry.method === '*' ? requestedMethod.toUpperCase() : entry.method]),
245
+ headers: Object.freeze([...headers]),
246
+ ...(allowAnyHeaders ? { allowAnyHeaders: true } : {}),
247
+ });
248
+ }
249
+ export function currentPublicationSnapshot(source, limits, serving) {
250
+ const snapshot = source.current();
251
+ if (snapshot === undefined)
252
+ return undefined;
253
+ const prepared = readPreparedPublicationSnapshot(snapshot);
254
+ if (prepared === undefined ||
255
+ prepared.publicationBytes.byteLength > limits.maximumPublicationBytes ||
256
+ prepared.bundle.byteLength > limits.maximumBundleBytes ||
257
+ (serving?.issuer !== undefined && prepared.publication.identity.issuer !== serving.issuer) ||
258
+ (serving !== undefined && conflictsWithServingRoutes(prepared, serving))) {
259
+ return undefined;
260
+ }
261
+ return prepared;
262
+ }
263
+ function servingSnapshot(source, limits, request, reservations, externalIssuer) {
264
+ return currentPublicationSnapshot(source, limits, {
265
+ origin: externalIssuer === undefined ? new URL(request.url).origin : new URL(externalIssuer).origin,
266
+ ...(externalIssuer === undefined ? {} : { issuer: externalIssuer }),
267
+ reservations,
268
+ });
269
+ }
270
+ function conflictsWithServingRoutes(snapshot, serving) {
271
+ for (const reservation of serving.reservations) {
272
+ const href = `${serving.origin}${reservation.path}`;
273
+ for (const entry of snapshot.table.entries) {
274
+ if (reservation.methods.some((method) => entry.method === '*' || entry.method === method) &&
275
+ routes.templatesOverlap(entry.href, href)) {
276
+ return true;
277
+ }
278
+ }
279
+ }
280
+ return false;
281
+ }
282
+ async function streamResponse(stream, operation, cors) {
283
+ const iterator = stream[Symbol.asyncIterator]();
284
+ let rejectSetup;
285
+ const setupInterrupted = new Promise((_resolve, reject) => {
286
+ rejectSetup = reject;
287
+ });
288
+ let setupCleanup;
289
+ const cancelSetup = (reason) => {
290
+ rejectSetup(operation.signal.reason ?? new Error(reason));
291
+ setupCleanup ??= Promise.allSettled([
292
+ Promise.resolve().then(() => stream.cancel(reason)),
293
+ Promise.resolve().then(() => iterator.return?.()),
294
+ ]).then(() => operation.settle());
295
+ return setupCleanup;
296
+ };
297
+ const unregisterSetupCancellation = operation.onCancel(cancelSetup);
298
+ let first;
299
+ try {
300
+ first = await Promise.race([iterator.next(), setupInterrupted]);
301
+ }
302
+ catch (cause) {
303
+ unregisterSetupCancellation();
304
+ const cleanup = cancelSetup('native stream setup failed');
305
+ if (!operation.signal.aborted)
306
+ await cleanup;
307
+ const payload = preStreamError(cause, operation);
308
+ return nativeError(payload, errors.httpStatusOf(payload), cors);
309
+ }
310
+ unregisterSetupCancellation();
311
+ if (operation.signal.aborted) {
312
+ void cancelSetup('native stream setup cancelled');
313
+ const payload = preStreamError(operation.signal.reason, operation);
314
+ return response({ error: payload }, errors.httpStatusOf(payload), cors);
315
+ }
316
+ const encoder = new TextEncoder();
317
+ let pending = first;
318
+ let terminal = false;
319
+ let controller;
320
+ let stopping;
321
+ const encoded = (frame) => encoder.encode(routes.encodeStreamFrame(frame));
322
+ const stop = (reason, error) => {
323
+ stopping ??= (async () => {
324
+ if (terminal)
325
+ return;
326
+ terminal = true;
327
+ if (controller !== undefined) {
328
+ if (error !== undefined) {
329
+ try {
330
+ controller.enqueue(encoded({ kind: 'error', error }));
331
+ }
332
+ catch {
333
+ // The transport consumer may already have disconnected.
334
+ }
335
+ }
336
+ try {
337
+ controller.close();
338
+ }
339
+ catch {
340
+ // A cancelled response body is already terminal.
341
+ }
342
+ }
343
+ try {
344
+ await Promise.allSettled([
345
+ Promise.resolve().then(() => stream.cancel(reason)),
346
+ Promise.resolve().then(() => iterator.return?.()),
347
+ ]);
348
+ }
349
+ finally {
350
+ operation.settle();
351
+ }
352
+ })();
353
+ return stopping;
354
+ };
355
+ const body = new ReadableStream({
356
+ start(output) {
357
+ controller = output;
358
+ },
359
+ async pull(output) {
360
+ if (terminal)
361
+ return;
362
+ try {
363
+ const next = pending ?? (await iterator.next());
364
+ pending = undefined;
365
+ if (terminal)
366
+ return;
367
+ if (next.done) {
368
+ terminal = true;
369
+ output.enqueue(encoded({ kind: 'end' }));
370
+ output.close();
371
+ operation.settle();
372
+ }
373
+ else {
374
+ output.enqueue(encoded({ kind: 'item', value: next.value }));
375
+ }
376
+ }
377
+ catch (cause) {
378
+ await stop('native stream failed', operation.signal.aborted
379
+ ? interruptedStreamError(operation.timedOut)
380
+ : classifyError(cause));
381
+ }
382
+ },
383
+ async cancel(reason) {
384
+ const message = typeof reason === 'string' ? reason : 'native response cancelled';
385
+ operation.cancel(message);
386
+ await stop(message);
387
+ },
388
+ });
389
+ operation.onCancel((reason) => stop(reason, interruptedStreamError(operation.timedOut)));
390
+ return new Response(body, {
391
+ headers: {
392
+ ...cors,
393
+ 'content-type': routes.STREAM_MEDIA_TYPE,
394
+ 'cache-control': 'no-cache',
395
+ },
396
+ });
397
+ }
398
+ function preStreamError(cause, operation) {
399
+ if (operation.timedOut) {
400
+ return {
401
+ code: errors.CODES.DEADLINE_EXCEEDED,
402
+ message: 'Invocation deadline exceeded before the native stream began.',
403
+ };
404
+ }
405
+ if (operation.signal.aborted) {
406
+ return {
407
+ code: errors.CODES.CANCELLED,
408
+ message: 'Invocation was cancelled before the native stream began.',
409
+ };
410
+ }
411
+ return classifyError(cause);
412
+ }
413
+ function sameRoute(left, right, externalOrigin) {
414
+ const a = new URL(left);
415
+ const b = new URL(right);
416
+ return ((externalOrigin ?? a.origin) === b.origin && a.pathname === b.pathname && a.search === b.search);
417
+ }
418
+ function applicationBinaryHeaders(input) {
419
+ const output = {};
420
+ for (const [name, value] of Object.entries(input ?? {})) {
421
+ if (name.toLowerCase() === routes.HTTP_ROUTE_STALE ||
422
+ name.toLowerCase() === routes.HTTP_ROUTE_MISS) {
423
+ throw new TypeError(`${name.toLowerCase()} is reserved for native routing control.`);
424
+ }
425
+ if (protocol.http.isApplicationBinaryHeader(name))
426
+ output[name] = value;
427
+ }
428
+ return output;
429
+ }
430
+ function quoteEtag(digest) {
431
+ return `"${digest}"`;
432
+ }
433
+ function response(body, status, cors) {
434
+ return new Response(encodedBody(codecs.json.encode(body)), {
435
+ status,
436
+ headers: { ...cors, 'content-type': 'application/json' },
437
+ });
438
+ }
439
+ function nativeError(error, status, cors) {
440
+ const admitted = routes.decodeErrorResponse({ error });
441
+ return response(admitted, status, cors);
442
+ }
443
+ function encodedBody(input) {
444
+ return typeof input === 'string' ? input : toBody(input);
445
+ }
446
+ function toBody(input) {
447
+ return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
448
+ }
@@ -0,0 +1,4 @@
1
+ import type { Server, ServerConfig, ServerOptions, PreparedServer } from './types.js';
2
+ export declare function createServer(config: ServerConfig): Server;
3
+ /** Validate all static transport configuration before a Host starts Runtime recovery/bootstrap. */
4
+ export declare function prepareServer(options: ServerOptions): PreparedServer;