@astrale-os/kernel-server 0.5.0-beta.2 → 0.5.0-beta.20
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.
- package/dist/capture.d.ts +0 -1
- package/dist/capture.js +0 -1
- package/dist/cors/cors.d.ts +0 -1
- package/dist/cors/cors.js +0 -1
- package/dist/cors/index.d.ts +0 -1
- package/dist/cors/index.js +0 -1
- package/dist/cors/types.d.ts +0 -1
- package/dist/cors/types.js +0 -1
- package/dist/errors/availability.d.ts +0 -1
- package/dist/errors/availability.js +0 -1
- package/dist/errors/classify.d.ts +2 -3
- package/dist/errors/classify.js +4 -279
- package/dist/errors/index.d.ts +0 -1
- package/dist/errors/index.js +0 -1
- package/dist/errors/request-body.d.ts +0 -1
- package/dist/errors/request-body.js +0 -1
- package/dist/errors/response-body.d.ts +0 -1
- package/dist/errors/response-body.js +0 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +0 -1
- package/dist/invocation/context.d.ts +2 -3
- package/dist/invocation/context.js +2 -3
- package/dist/invocation/identity.d.ts +9 -0
- package/dist/invocation/identity.js +11 -0
- package/dist/invocation/index.d.ts +2 -2
- package/dist/invocation/index.js +1 -1
- package/dist/invocation/publication-route.d.ts +5 -4
- package/dist/invocation/publication-route.js +8 -7
- package/dist/invocation/types.d.ts +33 -9
- package/dist/invocation/types.js +0 -1
- package/dist/issuer/exchange.d.ts +2 -3
- package/dist/issuer/exchange.js +13 -12
- package/dist/issuer/index.d.ts +0 -1
- package/dist/issuer/index.js +0 -1
- package/dist/issuer/routes.d.ts +0 -1
- package/dist/issuer/routes.js +14 -12
- package/dist/issuer/types.d.ts +2 -2
- package/dist/issuer/types.js +0 -1
- package/dist/lifecycle/active.d.ts +2 -1
- package/dist/lifecycle/active.js +5 -3
- package/dist/lifecycle/index.d.ts +0 -1
- package/dist/lifecycle/index.js +0 -1
- package/dist/publication/delivery/handler.d.ts +0 -1
- package/dist/publication/delivery/handler.js +2 -2
- package/dist/publication/delivery/index.d.ts +0 -1
- package/dist/publication/delivery/index.js +0 -1
- package/dist/publication/delivery/types.d.ts +1 -2
- package/dist/publication/delivery/types.js +0 -1
- package/dist/publication/index.d.ts +0 -1
- package/dist/publication/index.js +0 -1
- package/dist/publication/snapshot.d.ts +5 -3
- package/dist/publication/snapshot.js +14 -7
- package/dist/publication/table.d.ts +4 -0
- package/dist/publication/table.js +16 -0
- package/dist/publication/types.d.ts +5 -2
- package/dist/publication/types.js +0 -1
- package/dist/routes/index.d.ts +0 -1
- package/dist/routes/index.js +0 -1
- package/dist/routes/routes.d.ts +3 -3
- package/dist/routes/routes.js +91 -26
- package/dist/server.d.ts +0 -1
- package/dist/server.js +13 -7
- package/dist/transport/body.d.ts +0 -1
- package/dist/transport/body.js +0 -1
- package/dist/transport/codecs.d.ts +1 -2
- package/dist/transport/codecs.js +1 -2
- package/dist/transport/credential.d.ts +1 -2
- package/dist/transport/credential.js +1 -2
- package/dist/transport/http/handler.d.ts +2 -1
- package/dist/transport/http/handler.js +36 -33
- package/dist/transport/http/index.d.ts +0 -1
- package/dist/transport/http/index.js +0 -1
- package/dist/transport/http/render.d.ts +17 -13
- package/dist/transport/http/render.js +120 -41
- package/dist/transport/index.d.ts +0 -1
- package/dist/transport/index.js +0 -1
- package/dist/transport/router.d.ts +0 -1
- package/dist/transport/router.js +0 -1
- package/dist/transport/stream-error.d.ts +2 -3
- package/dist/transport/stream-error.js +3 -4
- package/dist/transport/types.d.ts +1 -2
- package/dist/transport/types.js +0 -1
- package/dist/transport/websocket/index.d.ts +0 -1
- package/dist/transport/websocket/index.js +0 -1
- package/dist/transport/websocket/session.d.ts +4 -3
- package/dist/transport/websocket/session.js +56 -39
- package/dist/transport/websocket/upgrade.d.ts +2 -1
- package/dist/transport/websocket/upgrade.js +2 -2
- package/dist/types.d.ts +6 -4
- package/dist/types.js +0 -1
- package/package.json +23 -13
- package/dist/capture.d.ts.map +0 -1
- package/dist/capture.js.map +0 -1
- package/dist/cors/cors.d.ts.map +0 -1
- package/dist/cors/cors.js.map +0 -1
- package/dist/cors/index.d.ts.map +0 -1
- package/dist/cors/index.js.map +0 -1
- package/dist/cors/types.d.ts.map +0 -1
- package/dist/cors/types.js.map +0 -1
- package/dist/errors/availability.d.ts.map +0 -1
- package/dist/errors/availability.js.map +0 -1
- package/dist/errors/classify.d.ts.map +0 -1
- package/dist/errors/classify.js.map +0 -1
- package/dist/errors/index.d.ts.map +0 -1
- package/dist/errors/index.js.map +0 -1
- package/dist/errors/request-body.d.ts.map +0 -1
- package/dist/errors/request-body.js.map +0 -1
- package/dist/errors/response-body.d.ts.map +0 -1
- package/dist/errors/response-body.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/invocation/context.d.ts.map +0 -1
- package/dist/invocation/context.js.map +0 -1
- package/dist/invocation/index.d.ts.map +0 -1
- package/dist/invocation/index.js.map +0 -1
- package/dist/invocation/publication-route.d.ts.map +0 -1
- package/dist/invocation/publication-route.js.map +0 -1
- package/dist/invocation/types.d.ts.map +0 -1
- package/dist/invocation/types.js.map +0 -1
- package/dist/issuer/exchange.d.ts.map +0 -1
- package/dist/issuer/exchange.js.map +0 -1
- package/dist/issuer/index.d.ts.map +0 -1
- package/dist/issuer/index.js.map +0 -1
- package/dist/issuer/routes.d.ts.map +0 -1
- package/dist/issuer/routes.js.map +0 -1
- package/dist/issuer/types.d.ts.map +0 -1
- package/dist/issuer/types.js.map +0 -1
- package/dist/lifecycle/active.d.ts.map +0 -1
- package/dist/lifecycle/active.js.map +0 -1
- package/dist/lifecycle/index.d.ts.map +0 -1
- package/dist/lifecycle/index.js.map +0 -1
- package/dist/publication/delivery/handler.d.ts.map +0 -1
- package/dist/publication/delivery/handler.js.map +0 -1
- package/dist/publication/delivery/index.d.ts.map +0 -1
- package/dist/publication/delivery/index.js.map +0 -1
- package/dist/publication/delivery/types.d.ts.map +0 -1
- package/dist/publication/delivery/types.js.map +0 -1
- package/dist/publication/index.d.ts.map +0 -1
- package/dist/publication/index.js.map +0 -1
- package/dist/publication/snapshot.d.ts.map +0 -1
- package/dist/publication/snapshot.js.map +0 -1
- package/dist/publication/types.d.ts.map +0 -1
- package/dist/publication/types.js.map +0 -1
- package/dist/routes/index.d.ts.map +0 -1
- package/dist/routes/index.js.map +0 -1
- package/dist/routes/routes.d.ts.map +0 -1
- package/dist/routes/routes.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/transport/body.d.ts.map +0 -1
- package/dist/transport/body.js.map +0 -1
- package/dist/transport/codecs.d.ts.map +0 -1
- package/dist/transport/codecs.js.map +0 -1
- package/dist/transport/credential.d.ts.map +0 -1
- package/dist/transport/credential.js.map +0 -1
- package/dist/transport/http/handler.d.ts.map +0 -1
- package/dist/transport/http/handler.js.map +0 -1
- package/dist/transport/http/index.d.ts.map +0 -1
- package/dist/transport/http/index.js.map +0 -1
- package/dist/transport/http/render.d.ts.map +0 -1
- package/dist/transport/http/render.js.map +0 -1
- package/dist/transport/index.d.ts.map +0 -1
- package/dist/transport/index.js.map +0 -1
- package/dist/transport/router.d.ts.map +0 -1
- package/dist/transport/router.js.map +0 -1
- package/dist/transport/stream-error.d.ts.map +0 -1
- package/dist/transport/stream-error.js.map +0 -1
- package/dist/transport/types.d.ts.map +0 -1
- package/dist/transport/types.js.map +0 -1
- package/dist/transport/websocket/index.d.ts.map +0 -1
- package/dist/transport/websocket/index.js.map +0 -1
- package/dist/transport/websocket/session.d.ts.map +0 -1
- package/dist/transport/websocket/session.js.map +0 -1
- package/dist/transport/websocket/upgrade.d.ts.map +0 -1
- package/dist/transport/websocket/upgrade.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
package/dist/routes/routes.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import
|
|
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 { MEDIA_TYPE as PUBLICATION_MEDIA_TYPE, PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
|
|
5
|
+
import { reference as publicationReference } from '@astrale-os/kernel-protocol/publication/reference';
|
|
6
|
+
import * as routes from '@astrale-os/kernel-protocol/routes';
|
|
2
7
|
import { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../errors/index.js';
|
|
3
8
|
import { invocationContext } from '../invocation/index.js';
|
|
4
9
|
import { handleDelivery } from '../publication/delivery/index.js';
|
|
5
10
|
import { readPreparedPublicationSnapshot, } from '../publication/index.js';
|
|
6
|
-
import {
|
|
7
|
-
|
|
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) {
|
|
8
15
|
const publicationPath = externalIssuer === undefined
|
|
9
|
-
?
|
|
10
|
-
: new URL(
|
|
16
|
+
? PUBLICATION_PATH
|
|
17
|
+
: new URL(publicationUrl(externalIssuer)).pathname;
|
|
11
18
|
const externalOrigin = externalIssuer === undefined ? undefined : new URL(externalIssuer).origin;
|
|
12
19
|
if (cors.enabled) {
|
|
13
20
|
app.options(publicationPath, (context) => cors.preflight(context.req.raw, {
|
|
@@ -37,7 +44,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, limi
|
|
|
37
44
|
status: 200,
|
|
38
45
|
headers: {
|
|
39
46
|
...responseCors,
|
|
40
|
-
'content-type':
|
|
47
|
+
'content-type': PUBLICATION_MEDIA_TYPE,
|
|
41
48
|
etag: quoteEtag(snapshot.publication.etag),
|
|
42
49
|
'cache-control': 'public, max-age=0, must-revalidate, no-transform',
|
|
43
50
|
},
|
|
@@ -89,7 +96,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, limi
|
|
|
89
96
|
},
|
|
90
97
|
});
|
|
91
98
|
}
|
|
92
|
-
routing = routes.decodeRouting(request,
|
|
99
|
+
routing = routes.decodeRouting(request, publicationReference(snapshot.publication));
|
|
93
100
|
}
|
|
94
101
|
catch (cause) {
|
|
95
102
|
if (cause instanceof routes.RouteError) {
|
|
@@ -97,9 +104,10 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, limi
|
|
|
97
104
|
}
|
|
98
105
|
throw cause;
|
|
99
106
|
}
|
|
100
|
-
const route = routes.match(snapshot.
|
|
107
|
+
const route = routes.match(snapshot.table, request);
|
|
101
108
|
if (route === undefined)
|
|
102
109
|
return context.notFound();
|
|
110
|
+
const assigned = nextInvocation();
|
|
103
111
|
const operation = active.begin(limits.invocationTimeoutMs, request.signal);
|
|
104
112
|
if (operation === undefined)
|
|
105
113
|
return nativeError({
|
|
@@ -109,12 +117,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, limi
|
|
|
109
117
|
let handedOff = false;
|
|
110
118
|
try {
|
|
111
119
|
const body = await readBoundedBody(request, limits.maximumRequestBytes, operation.signal);
|
|
112
|
-
const invocation = routes.decode(route, {
|
|
113
|
-
url: request.url,
|
|
114
|
-
method: request.method,
|
|
115
|
-
headers: request.headers,
|
|
116
|
-
body,
|
|
117
|
-
});
|
|
120
|
+
const invocation = routes.decode(route, { url: request.url, method: request.method, headers: request.headers, body }, snapshot.domain, snapshot.table.publication);
|
|
118
121
|
const idempotencyHeader = request.headers.get(protocol.HTTP_IDEMPOTENCY_KEY);
|
|
119
122
|
const idempotencyKey = idempotencyHeader === null
|
|
120
123
|
? undefined
|
|
@@ -124,7 +127,7 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, limi
|
|
|
124
127
|
...(invocation.credential === undefined ? {} : { credential: invocation.credential }),
|
|
125
128
|
...(routing === undefined ? {} : { via: routing }),
|
|
126
129
|
publication: snapshot.snapshot,
|
|
127
|
-
context: invocationContext(operation, active, hostFor(request), body, idempotencyKey),
|
|
130
|
+
context: invocationContext(assigned, operation, active, hostFor(request), body, idempotencyKey),
|
|
128
131
|
}));
|
|
129
132
|
if (result.kind === 'value') {
|
|
130
133
|
return new Response(encodedBody(codecs.json.encode(result.value)), {
|
|
@@ -135,8 +138,13 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, limi
|
|
|
135
138
|
});
|
|
136
139
|
}
|
|
137
140
|
if (result.kind === 'binary') {
|
|
141
|
+
if (isStreamingBinary(result.value)) {
|
|
142
|
+
handedOff = true;
|
|
143
|
+
return streamingBinaryResponse(result.value, operation, cors, request);
|
|
144
|
+
}
|
|
138
145
|
const applicationHeaders = applicationBinaryHeaders(result.value.headers);
|
|
139
146
|
return new Response(toBody(result.value.body), {
|
|
147
|
+
status: result.value.status ?? 200,
|
|
140
148
|
headers: {
|
|
141
149
|
...cors.response(request, [
|
|
142
150
|
protocol.http.BINARY_HEADERS_FIELD,
|
|
@@ -194,6 +202,66 @@ export function mountRoutes(app, source, deliveryProvider, handler, active, limi
|
|
|
194
202
|
}
|
|
195
203
|
});
|
|
196
204
|
}
|
|
205
|
+
function streamingBinaryResponse(binary, operation, cors, request) {
|
|
206
|
+
const iterator = binary.body[Symbol.asyncIterator]();
|
|
207
|
+
const applicationHeaders = applicationBinaryHeaders(binary.headers);
|
|
208
|
+
let terminal = false;
|
|
209
|
+
let stopping;
|
|
210
|
+
const stop = () => {
|
|
211
|
+
stopping ??= Promise.resolve().then(async () => {
|
|
212
|
+
if (terminal)
|
|
213
|
+
return;
|
|
214
|
+
terminal = true;
|
|
215
|
+
try {
|
|
216
|
+
await iterator.return?.();
|
|
217
|
+
}
|
|
218
|
+
finally {
|
|
219
|
+
operation.settle();
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
return stopping;
|
|
223
|
+
};
|
|
224
|
+
const body = new ReadableStream({
|
|
225
|
+
async pull(controller) {
|
|
226
|
+
if (terminal)
|
|
227
|
+
return;
|
|
228
|
+
try {
|
|
229
|
+
const next = await iterator.next();
|
|
230
|
+
if (terminal)
|
|
231
|
+
return;
|
|
232
|
+
if (next.done) {
|
|
233
|
+
terminal = true;
|
|
234
|
+
controller.close();
|
|
235
|
+
operation.settle();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
controller.enqueue(next.value);
|
|
239
|
+
}
|
|
240
|
+
catch (cause) {
|
|
241
|
+
terminal = true;
|
|
242
|
+
controller.error(cause);
|
|
243
|
+
operation.settle();
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
async cancel(reason) {
|
|
247
|
+
const message = typeof reason === 'string' ? reason : 'native binary response cancelled';
|
|
248
|
+
operation.cancel(message);
|
|
249
|
+
await stop();
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
operation.onCancel(stop);
|
|
253
|
+
return new Response(body, {
|
|
254
|
+
status: binary.status ?? 200,
|
|
255
|
+
headers: {
|
|
256
|
+
...cors.response(request, Object.keys(applicationHeaders)),
|
|
257
|
+
...applicationHeaders,
|
|
258
|
+
'content-type': binary.mediaType,
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function isStreamingBinary(input) {
|
|
263
|
+
return !(input.body instanceof Uint8Array);
|
|
264
|
+
}
|
|
197
265
|
function publicationPreflight(cors, request, snapshot, externalOrigin) {
|
|
198
266
|
if (sameRoute(request.url, snapshot.publication.schema.bundle.href, externalOrigin)) {
|
|
199
267
|
return cors.preflight(request, { methods: ['GET'], headers: ['if-none-match'] });
|
|
@@ -208,7 +276,7 @@ function publicationPreflight(cors, request, snapshot, externalOrigin) {
|
|
|
208
276
|
const requestedMethod = request.headers.get('access-control-request-method');
|
|
209
277
|
const route = requestedMethod === null
|
|
210
278
|
? undefined
|
|
211
|
-
: routes.match(snapshot.
|
|
279
|
+
: routes.match(snapshot.table, { url: request.url, method: requestedMethod });
|
|
212
280
|
return cors.preflight(request, route === undefined ? undefined : nativeCorsRoute(route, requestedMethod));
|
|
213
281
|
}
|
|
214
282
|
function nativeCorsRoute(entry, requestedMethod) {
|
|
@@ -218,16 +286,16 @@ function nativeCorsRoute(entry, requestedMethod) {
|
|
|
218
286
|
routes.HTTP_ROUTE_ISSUER,
|
|
219
287
|
protocol.HTTP_IDEMPOTENCY_KEY,
|
|
220
288
|
]);
|
|
221
|
-
if (entry.
|
|
289
|
+
if (entry.body !== undefined)
|
|
222
290
|
headers.add('content-type');
|
|
223
291
|
let allowAnyHeaders = false;
|
|
224
|
-
for (const pick of Object.values(entry.
|
|
292
|
+
for (const pick of Object.values(entry.headers ?? {})) {
|
|
225
293
|
if (pick === '*')
|
|
226
294
|
allowAnyHeaders = true;
|
|
227
295
|
else
|
|
228
296
|
headers.add(pick.toLowerCase());
|
|
229
297
|
}
|
|
230
|
-
const credential = entry.
|
|
298
|
+
const credential = entry.credential;
|
|
231
299
|
const sources = credential === undefined
|
|
232
300
|
? [{ in: 'header' }]
|
|
233
301
|
: Array.isArray(credential)
|
|
@@ -238,9 +306,7 @@ function nativeCorsRoute(entry, requestedMethod) {
|
|
|
238
306
|
headers.add((source.name ?? 'authorization').toLowerCase());
|
|
239
307
|
}
|
|
240
308
|
return Object.freeze({
|
|
241
|
-
methods: Object.freeze([
|
|
242
|
-
entry.route.method === '*' ? requestedMethod.toUpperCase() : entry.route.method,
|
|
243
|
-
]),
|
|
309
|
+
methods: Object.freeze([entry.method === '*' ? requestedMethod.toUpperCase() : entry.method]),
|
|
244
310
|
headers: Object.freeze([...headers]),
|
|
245
311
|
...(allowAnyHeaders ? { allowAnyHeaders: true } : {}),
|
|
246
312
|
});
|
|
@@ -269,9 +335,9 @@ function servingSnapshot(source, limits, request, reservations, externalIssuer)
|
|
|
269
335
|
function conflictsWithServingRoutes(snapshot, serving) {
|
|
270
336
|
for (const reservation of serving.reservations) {
|
|
271
337
|
const href = `${serving.origin}${reservation.path}`;
|
|
272
|
-
for (const entry of snapshot.
|
|
273
|
-
if (reservation.methods.some((method) => entry.
|
|
274
|
-
routes.templatesOverlap(entry.
|
|
338
|
+
for (const entry of snapshot.table.entries) {
|
|
339
|
+
if (reservation.methods.some((method) => entry.method === '*' || entry.method === method) &&
|
|
340
|
+
routes.templatesOverlap(entry.href, href)) {
|
|
275
341
|
return true;
|
|
276
342
|
}
|
|
277
343
|
}
|
|
@@ -445,4 +511,3 @@ function encodedBody(input) {
|
|
|
445
511
|
function toBody(input) {
|
|
446
512
|
return input.buffer.slice(input.byteOffset, input.byteOffset + input.byteLength);
|
|
447
513
|
}
|
|
448
|
-
//# sourceMappingURL=routes.js.map
|
package/dist/server.d.ts
CHANGED
|
@@ -2,4 +2,3 @@ import type { Server, ServerConfig, ServerOptions, PreparedServer } from './type
|
|
|
2
2
|
export declare function createServer(config: ServerConfig): Server;
|
|
3
3
|
/** Validate all static transport configuration before a Host starts Runtime recovery/bootstrap. */
|
|
4
4
|
export declare function prepareServer(options: ServerOptions): PreparedServer;
|
|
5
|
-
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PATH as PUBLICATION_PATH, url as publicationUrl, } from '@astrale-os/kernel-protocol/publication/publication';
|
|
2
2
|
import { captureMethod } from './capture.js';
|
|
3
3
|
import { prepareCors } from './cors/index.js';
|
|
4
|
+
import { captureInvocationSource } from './invocation/index.js';
|
|
4
5
|
import { capture as captureExchangeProvider } from './issuer/exchange.js';
|
|
5
6
|
import { admitIssuer, issuerRouteReservations, mountIssuer } from './issuer/index.js';
|
|
6
7
|
import { ActiveOperations } from './lifecycle/index.js';
|
|
7
8
|
import { currentPublicationSnapshot, mountRoutes } from './routes/index.js';
|
|
8
|
-
import { createHttpHandler
|
|
9
|
+
import { createHttpHandler } from './transport/http/handler.js';
|
|
10
|
+
import { createServerRouter } from './transport/router.js';
|
|
11
|
+
import { handleWebSocketUpgrade } from './transport/websocket/upgrade.js';
|
|
9
12
|
export function createServer(config) {
|
|
10
13
|
const { handler, ...options } = config;
|
|
11
14
|
return prepareServer(options).create(handler);
|
|
@@ -27,6 +30,7 @@ function createPreparedServer(input, config) {
|
|
|
27
30
|
const handleHttp = createHttpHandler({
|
|
28
31
|
handler,
|
|
29
32
|
active,
|
|
33
|
+
invocation: config.invocation,
|
|
30
34
|
...config.limits,
|
|
31
35
|
cors: config.cors,
|
|
32
36
|
websocket: config.websocket !== undefined,
|
|
@@ -46,6 +50,7 @@ function createPreparedServer(input, config) {
|
|
|
46
50
|
adapter: config.websocket,
|
|
47
51
|
handler,
|
|
48
52
|
active,
|
|
53
|
+
invocation: config.invocation,
|
|
49
54
|
...config.limits,
|
|
50
55
|
cors: config.cors,
|
|
51
56
|
...(config.publication === undefined
|
|
@@ -63,7 +68,7 @@ function createPreparedServer(input, config) {
|
|
|
63
68
|
app.all(config.path, (context) => handleHttp(context.req.raw, hosts.get(context.req.raw)));
|
|
64
69
|
if (config.health !== undefined) {
|
|
65
70
|
app.get(config.health.path, (context) => {
|
|
66
|
-
const ready = active.
|
|
71
|
+
const ready = active.isAvailable && config.health.probe.check();
|
|
67
72
|
return context.json({ status: ready ? 'ready' : 'not-ready' }, ready ? 200 : 503, {
|
|
68
73
|
...config.cors.response(context.req.raw),
|
|
69
74
|
'cache-control': 'no-store',
|
|
@@ -77,7 +82,7 @@ function createPreparedServer(input, config) {
|
|
|
77
82
|
mountIssuer(app, config.issuer, config.cors, active, config.limits.invocationTimeoutMs, config.tokenExchange);
|
|
78
83
|
}
|
|
79
84
|
if (config.publication !== undefined) {
|
|
80
|
-
mountRoutes(app, config.publication, config.delivery, handler, active, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalIssuer);
|
|
85
|
+
mountRoutes(app, config.publication, config.delivery, handler, active, config.invocation, config.limits, config.cors, (request) => hosts.get(request), config.routeReservations, externalIssuer);
|
|
81
86
|
}
|
|
82
87
|
app.onError((_error, context) => new Response('{"error":"Internal server error."}', {
|
|
83
88
|
status: 500,
|
|
@@ -126,6 +131,7 @@ function captureHandler(input) {
|
|
|
126
131
|
return Object.freeze({ dispatch });
|
|
127
132
|
}
|
|
128
133
|
function admitOptions(config) {
|
|
134
|
+
const invocation = captureInvocationSource(config.invocation);
|
|
129
135
|
const limits = admitLimits(config.limits);
|
|
130
136
|
const path = admitRoute(config.path ?? '/', 'invocation path');
|
|
131
137
|
const issuer = config.issuer === undefined ? undefined : admitIssuer(config.issuer);
|
|
@@ -173,6 +179,7 @@ function admitOptions(config) {
|
|
|
173
179
|
}
|
|
174
180
|
}
|
|
175
181
|
return Object.freeze({
|
|
182
|
+
invocation,
|
|
176
183
|
limits,
|
|
177
184
|
path,
|
|
178
185
|
cors: prepareCors(config.cors),
|
|
@@ -205,8 +212,8 @@ function routeReservations(invocationPath, healthPath, issuer, websocket, tokenE
|
|
|
205
212
|
}
|
|
206
213
|
function protocolPublicationPath(issuer) {
|
|
207
214
|
if (issuer === undefined)
|
|
208
|
-
return
|
|
209
|
-
return new URL(
|
|
215
|
+
return PUBLICATION_PATH;
|
|
216
|
+
return new URL(publicationUrl(issuer)).pathname;
|
|
210
217
|
}
|
|
211
218
|
function captureHealthProbe(probe) {
|
|
212
219
|
const check = captureMethod(probe, 'check', 'Health probe is invalid.');
|
|
@@ -257,4 +264,3 @@ function admitRoute(path, label) {
|
|
|
257
264
|
}
|
|
258
265
|
return path;
|
|
259
266
|
}
|
|
260
|
-
//# sourceMappingURL=server.js.map
|
package/dist/transport/body.d.ts
CHANGED
|
@@ -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
|
package/dist/transport/body.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
2
|
export type LoadedCodec = codecs.Codec;
|
|
3
3
|
/** Load the built-in Server representation set before pure Protocol binding selection. */
|
|
4
4
|
export declare function loadCodecs(): Promise<readonly [codecs.Codec, codecs.Codec]>;
|
|
5
|
-
//# sourceMappingURL=codecs.d.ts.map
|
package/dist/transport/codecs.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as codecs from '@astrale-os/kernel-protocol/codecs';
|
|
2
2
|
/** Load the built-in Server representation set before pure Protocol binding selection. */
|
|
3
3
|
export async function loadCodecs() {
|
|
4
4
|
return Object.freeze([codecs.json, await codecs.loadMsgpack()]);
|
|
5
5
|
}
|
|
6
|
-
//# sourceMappingURL=codecs.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
2
2
|
export type CredentialResult = {
|
|
3
3
|
readonly ok: true;
|
|
4
4
|
readonly credential: string | undefined;
|
|
@@ -8,4 +8,3 @@ export type CredentialResult = {
|
|
|
8
8
|
};
|
|
9
9
|
/** Resolve exactly zero or one untrusted credential channel without authenticating it. */
|
|
10
10
|
export declare function resolveCredential(envelope: string | undefined, authorization: string | null): CredentialResult;
|
|
11
|
-
//# sourceMappingURL=credential.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
2
2
|
/** Resolve exactly zero or one untrusted credential channel without authenticating it. */
|
|
3
3
|
export function resolveCredential(envelope, authorization) {
|
|
4
4
|
if (envelope !== undefined && authorization !== null) {
|
|
@@ -26,4 +26,3 @@ function failure(message) {
|
|
|
26
26
|
error: { code: errors.CODES.INVALID_REQUEST, message },
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
|
-
//# sourceMappingURL=credential.js.map
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { CorsPolicy } from '../../cors/index.js';
|
|
2
|
+
import type { NextInvocationId } from '../../invocation/index.js';
|
|
2
3
|
import type { ActiveOperations } from '../../lifecycle/index.js';
|
|
3
4
|
import type { PublicationSnapshot } from '../../publication/index.js';
|
|
4
5
|
import { type ApplicationHost, type Handler } from '../../invocation/index.js';
|
|
5
6
|
export interface HttpHandlerConfig {
|
|
6
7
|
readonly handler: Handler;
|
|
7
8
|
readonly active: ActiveOperations;
|
|
9
|
+
readonly invocation: NextInvocationId;
|
|
8
10
|
readonly maximumRequestBytes: number;
|
|
9
11
|
readonly maximumResponseBytes: number;
|
|
10
12
|
readonly invocationTimeoutMs: number;
|
|
@@ -13,4 +15,3 @@ export interface HttpHandlerConfig {
|
|
|
13
15
|
readonly currentPublication?: (request: Request) => PublicationSnapshot | undefined;
|
|
14
16
|
}
|
|
15
17
|
export declare function createHttpHandler(config: HttpHandlerConfig): (request: Request, host?: ApplicationHost) => Promise<Response>;
|
|
16
|
-
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
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';
|
|
2
4
|
import { classifyError, RequestBodyInvalidError, RequestBodyTooLargeError, } from '../../errors/index.js';
|
|
3
5
|
import { invocationContext, publicationRouteDecision, } from '../../invocation/index.js';
|
|
4
6
|
import { readBoundedBody } from '../body.js';
|
|
@@ -8,10 +10,6 @@ import { renderHttpError, renderHttpResult } from './render.js';
|
|
|
8
10
|
export function createHttpHandler(config) {
|
|
9
11
|
return async (request, host) => {
|
|
10
12
|
const cors = (exposedHeaders) => config.cors.response(request, exposedHeaders);
|
|
11
|
-
const renderError = (id, error, codec, options = {}) => renderHttpError(id, error, codec, cors, {
|
|
12
|
-
...options,
|
|
13
|
-
maximumResponseBytes: config.maximumResponseBytes,
|
|
14
|
-
});
|
|
15
13
|
const allow = [
|
|
16
14
|
...(config.websocket ? ['GET'] : []),
|
|
17
15
|
'POST',
|
|
@@ -34,8 +32,13 @@ export function createHttpHandler(config) {
|
|
|
34
32
|
headers: { ...cors(), allow: allow.join(', '), 'cache-control': 'no-store' },
|
|
35
33
|
});
|
|
36
34
|
}
|
|
37
|
-
const
|
|
38
|
-
|
|
35
|
+
const assigned = config.invocation();
|
|
36
|
+
const renderError = (requestId, error, codec, options = {}) => renderHttpError(requestId, assigned, error, codec, cors, {
|
|
37
|
+
...options,
|
|
38
|
+
maximumResponseBytes: config.maximumResponseBytes,
|
|
39
|
+
});
|
|
40
|
+
const active = config.active.begin(config.invocationTimeoutMs, request.signal);
|
|
41
|
+
if (active === undefined) {
|
|
39
42
|
return renderError(null, {
|
|
40
43
|
code: errors.CODES.BACKEND_UNAVAILABLE,
|
|
41
44
|
message: 'server is unavailable.',
|
|
@@ -63,19 +66,19 @@ export function createHttpHandler(config) {
|
|
|
63
66
|
}
|
|
64
67
|
let body;
|
|
65
68
|
try {
|
|
66
|
-
body = await readBoundedBody(request, config.maximumRequestBytes,
|
|
69
|
+
body = await readBoundedBody(request, config.maximumRequestBytes, active.signal);
|
|
67
70
|
}
|
|
68
71
|
catch (input) {
|
|
69
72
|
const payload = input instanceof RequestBodyTooLargeError
|
|
70
73
|
? classifyError(input)
|
|
71
74
|
: input instanceof RequestBodyInvalidError
|
|
72
75
|
? { code: errors.CODES.PARSE_ERROR, message: 'Request body is invalid.' }
|
|
73
|
-
:
|
|
76
|
+
: active.timedOut
|
|
74
77
|
? {
|
|
75
78
|
code: errors.CODES.DEADLINE_EXCEEDED,
|
|
76
79
|
message: 'Invocation deadline exceeded.',
|
|
77
80
|
}
|
|
78
|
-
:
|
|
81
|
+
: active.signal.aborted
|
|
79
82
|
? { code: errors.CODES.CANCELLED, message: 'Invocation was cancelled.' }
|
|
80
83
|
: undefined;
|
|
81
84
|
if (payload === undefined)
|
|
@@ -93,69 +96,70 @@ export function createHttpHandler(config) {
|
|
|
93
96
|
throw input;
|
|
94
97
|
return renderError(null, { code: errors.CODES.PARSE_ERROR, message: 'Request body is invalid.' }, outgoing);
|
|
95
98
|
}
|
|
96
|
-
let
|
|
99
|
+
let envelope;
|
|
97
100
|
try {
|
|
98
|
-
|
|
101
|
+
envelope = protocol.decodeRequest(decoded);
|
|
99
102
|
}
|
|
100
103
|
catch (input) {
|
|
101
104
|
return renderError(null, classifyError(input), outgoing);
|
|
102
105
|
}
|
|
103
106
|
const publication = config.currentPublication?.(request);
|
|
104
107
|
if (config.currentPublication !== undefined && publication === undefined) {
|
|
105
|
-
return renderError(
|
|
108
|
+
return renderError(envelope.requestId, {
|
|
106
109
|
code: errors.CODES.BACKEND_UNAVAILABLE,
|
|
107
110
|
message: 'Publication is unavailable.',
|
|
108
111
|
}, outgoing, { status: 503 });
|
|
109
112
|
}
|
|
110
|
-
if (
|
|
113
|
+
if (envelope.routing?.kind === 'via') {
|
|
111
114
|
if (publication === undefined) {
|
|
112
|
-
return renderError(
|
|
115
|
+
return renderError(envelope.requestId, {
|
|
113
116
|
code: errors.CODES.INVALID_REQUEST,
|
|
114
117
|
message: 'Publication routing requires a Publication-backed destination.',
|
|
115
118
|
}, outgoing);
|
|
116
119
|
}
|
|
117
|
-
const decision = publicationRouteDecision(publication.publication,
|
|
120
|
+
const decision = publicationRouteDecision(publication.publication, envelope.routing.publication);
|
|
118
121
|
if (decision.kind === 'rejected') {
|
|
119
|
-
return renderError(
|
|
122
|
+
return renderError(envelope.requestId, decision.error, outgoing);
|
|
120
123
|
}
|
|
121
124
|
}
|
|
122
|
-
const credential = resolveCredential(
|
|
125
|
+
const credential = resolveCredential(envelope.credential, request.headers.get('authorization'));
|
|
123
126
|
if (!credential.ok) {
|
|
124
|
-
return renderError(
|
|
127
|
+
return renderError(envelope.requestId, credential.error, outgoing);
|
|
125
128
|
}
|
|
126
129
|
const trace = protocol.http.decodeTraceHeaders(request.headers.get(protocol.http.TRACEPARENT_FIELD), request.headers.get(protocol.http.TRACESTATE_FIELD));
|
|
127
130
|
try {
|
|
128
131
|
const result = await config.handler.dispatch(Object.freeze({
|
|
129
|
-
call:
|
|
132
|
+
call: envelope.call,
|
|
130
133
|
...(trace === undefined ? {} : { trace }),
|
|
131
134
|
...(credential.credential === undefined ? {} : { credential: credential.credential }),
|
|
132
|
-
...(
|
|
133
|
-
...(
|
|
134
|
-
...(
|
|
135
|
+
...(envelope.delegate === undefined ? {} : { delegate: envelope.delegate }),
|
|
136
|
+
...(envelope.routing?.kind === 'via' ? { via: envelope.routing } : {}),
|
|
137
|
+
...(envelope.schema === undefined ? {} : { schema: envelope.schema }),
|
|
135
138
|
...(publication === undefined ? {} : { publication }),
|
|
136
|
-
context: invocationContext(
|
|
139
|
+
context: invocationContext(assigned, active, config.active, host, undefined, envelope.idempotencyKey),
|
|
137
140
|
}));
|
|
138
141
|
const response = renderHttpResult(result, {
|
|
139
|
-
|
|
142
|
+
requestId: envelope.requestId,
|
|
140
143
|
codec: outgoing,
|
|
141
144
|
cors,
|
|
142
|
-
|
|
145
|
+
invocation: assigned,
|
|
146
|
+
active,
|
|
143
147
|
maximumResponseBytes: config.maximumResponseBytes,
|
|
144
148
|
});
|
|
145
149
|
handedOff = result.kind === 'stream';
|
|
146
150
|
return response;
|
|
147
151
|
}
|
|
148
152
|
catch (input) {
|
|
149
|
-
if (
|
|
150
|
-
return renderError(
|
|
153
|
+
if (active.timedOut) {
|
|
154
|
+
return renderError(envelope.requestId, {
|
|
151
155
|
code: errors.CODES.DEADLINE_EXCEEDED,
|
|
152
156
|
message: 'Invocation deadline exceeded.',
|
|
153
157
|
}, outgoing, { status: 504 });
|
|
154
158
|
}
|
|
155
|
-
if (
|
|
156
|
-
return renderError(
|
|
159
|
+
if (active.signal.aborted) {
|
|
160
|
+
return renderError(envelope.requestId, { code: errors.CODES.CANCELLED, message: 'Invocation was cancelled.' }, outgoing, { status: 408 });
|
|
157
161
|
}
|
|
158
|
-
return renderError(
|
|
162
|
+
return renderError(envelope.requestId, classifyError(input), outgoing);
|
|
159
163
|
}
|
|
160
164
|
}
|
|
161
165
|
catch (input) {
|
|
@@ -165,7 +169,7 @@ export function createHttpHandler(config) {
|
|
|
165
169
|
}
|
|
166
170
|
finally {
|
|
167
171
|
if (!handedOff)
|
|
168
|
-
|
|
172
|
+
active.settle();
|
|
169
173
|
}
|
|
170
174
|
};
|
|
171
175
|
}
|
|
@@ -174,4 +178,3 @@ function noStore(response) {
|
|
|
174
178
|
headers.set('cache-control', 'no-store');
|
|
175
179
|
return new Response(response.body, { status: response.status, headers });
|
|
176
180
|
}
|
|
177
|
-
//# sourceMappingURL=handler.js.map
|
|
@@ -1,3 +1,2 @@
|
|
|
1
1
|
export { createHttpHandler, type HttpHandlerConfig } from './handler.js';
|
|
2
2
|
export { encodeEnvelope, renderBinary, renderHttpError, renderHttpResult, renderHttpStale, renderRedirect, type CorsHeaders, type ErrorRenderOptions, type RenderContext, } from './render.js';
|
|
3
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { BufferedBinaryResult, StreamingBinaryResult } from '@astrale-os/kernel-core/callable';
|
|
2
|
+
import type { Codec } from '@astrale-os/kernel-protocol/codecs';
|
|
3
|
+
import * as errors from '@astrale-os/kernel-protocol/errors';
|
|
4
|
+
import * as protocol from '@astrale-os/kernel-protocol/invocation';
|
|
2
5
|
import type { Handler } from '../../invocation/index.js';
|
|
3
6
|
import type { ActiveOperation } from '../../lifecycle/index.js';
|
|
4
7
|
export interface RenderContext {
|
|
5
|
-
readonly
|
|
6
|
-
readonly codec:
|
|
8
|
+
readonly requestId: protocol.RequestId;
|
|
9
|
+
readonly codec: Codec;
|
|
7
10
|
readonly cors: CorsHeaders;
|
|
8
|
-
readonly
|
|
11
|
+
readonly invocation: protocol.InvocationId;
|
|
12
|
+
readonly active: ActiveOperation;
|
|
9
13
|
readonly maximumResponseBytes: number;
|
|
10
14
|
}
|
|
11
15
|
export type CorsHeaders = (exposedHeaders?: readonly string[]) => Readonly<Record<string, string>>;
|
|
@@ -16,14 +20,14 @@ export interface ErrorRenderOptions {
|
|
|
16
20
|
}
|
|
17
21
|
type HandlerResult = Awaited<ReturnType<Handler['dispatch']>>;
|
|
18
22
|
export declare function renderHttpResult(result: HandlerResult, context: RenderContext): Response;
|
|
19
|
-
export declare function renderBinary(
|
|
20
|
-
|
|
21
|
-
}>['value']): protocol.BinaryResponse;
|
|
22
|
-
export declare function renderRedirect(id: protocol.RequestId, redirect: Extract<HandlerResult, {
|
|
23
|
+
export declare function renderBinary(requestId: protocol.RequestId, invocation: protocol.InvocationId, binary: BufferedBinaryResult): protocol.BinaryResponse;
|
|
24
|
+
export declare function renderRedirect(requestId: protocol.RequestId, invocation: protocol.InvocationId, redirect: Extract<HandlerResult, {
|
|
23
25
|
readonly kind: 'redirect';
|
|
24
|
-
}>['redirect']): protocol.
|
|
25
|
-
export declare function renderHttpError(
|
|
26
|
-
export declare function renderHttpStale(
|
|
27
|
-
export declare function encodeEnvelope(codec:
|
|
26
|
+
}>['redirect']): protocol.RedirectResponse;
|
|
27
|
+
export declare function renderHttpError(requestId: protocol.RequestId, invocation: protocol.InvocationId, error: errors.ErrorPayload, codec: Codec, cors: CorsHeaders, options?: ErrorRenderOptions): Response;
|
|
28
|
+
export declare function renderHttpStale(requestId: protocol.RequestId, invocation: protocol.InvocationId, current: protocol.Stale['current'], codec: Codec, cors: CorsHeaders, maximumResponseBytes?: number): Response;
|
|
29
|
+
export declare function encodeEnvelope(codec: Codec, input: unknown): string | Uint8Array;
|
|
30
|
+
export declare function isStreamingBinary(input: Extract<HandlerResult, {
|
|
31
|
+
readonly kind: 'binary';
|
|
32
|
+
}>['value']): input is StreamingBinaryResult;
|
|
28
33
|
export {};
|
|
29
|
-
//# sourceMappingURL=render.d.ts.map
|