@loutrejs/loutre 0.9.2 → 0.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/aws-lambda.d.ts +1 -1
- package/dist/adapters/aws-lambda.js +8 -2
- package/dist/adapters/bun.d.ts +3 -2
- package/dist/adapters/bun.js +12 -6
- package/dist/adapters/cloudflare-workers.d.ts +1 -1
- package/dist/adapters/cloudflare-workers.js +2 -1
- package/dist/adapters/electron.d.ts +1 -1
- package/dist/adapters/electron.js +8 -51
- package/dist/adapters/server-transports.js +2 -2
- package/dist/adapters/websocket-native.js +4 -1
- package/dist/application/definition.d.ts +4 -7
- package/dist/application/definition.js +3 -1
- package/dist/application/kernel.d.ts +0 -2
- package/dist/application/kernel.js +1 -3
- package/dist/core/args.d.ts +1 -0
- package/dist/core/args.js +2 -1
- package/dist/core/env.d.ts +1 -0
- package/dist/core/env.js +2 -1
- package/dist/core/extension.d.ts +23 -13
- package/dist/core/extension.js +3 -1
- package/dist/core/generic-layer.d.ts +9 -2
- package/dist/core/generic-layer.js +2 -1
- package/dist/core/identity.d.ts +18 -0
- package/dist/core/identity.js +5 -0
- package/dist/core/index.d.ts +13 -5
- package/dist/core/index.js +7 -1
- package/dist/core/injection.d.ts +2 -1
- package/dist/core/instrumentation.d.ts +7 -5
- package/dist/core/lifecycle.d.ts +4 -0
- package/dist/core/logger-identity.d.ts +4 -0
- package/dist/core/logger-identity.js +9 -0
- package/dist/core/logger-internal.d.ts +5 -0
- package/dist/core/logger-internal.js +27 -0
- package/dist/core/logger-module.d.ts +5 -0
- package/dist/core/logger-module.js +8 -0
- package/dist/core/logger.d.ts +42 -0
- package/dist/core/logger.js +221 -0
- package/dist/core/model/context.d.ts +6 -3
- package/dist/core/model/context.js +17 -17
- package/dist/core/model/identity.d.ts +19 -0
- package/dist/core/model/identity.js +8 -0
- package/dist/core/model/lifecycle.d.ts +3 -2
- package/dist/core/model/lifecycle.js +7 -3
- package/dist/core/model/logger.d.ts +3 -0
- package/dist/core/model/logger.js +59 -0
- package/dist/core/model/modules.d.ts +4 -1
- package/dist/core/model/modules.js +6 -4
- package/dist/core/model/providers.d.ts +1 -1
- package/dist/core/model/providers.js +8 -8
- package/dist/core/model/types.d.ts +25 -18
- package/dist/core/model/validation.js +3 -2
- package/dist/core/model/visibility.js +7 -2
- package/dist/core/model.js +15 -7
- package/dist/core/module.d.ts +7 -7
- package/dist/core/module.js +0 -3
- package/dist/core/runtime-input.d.ts +2 -1
- package/dist/core/source-location.d.ts +14 -2
- package/dist/core/source-location.js +8 -3
- package/dist/core/token-internal.js +3 -1
- package/dist/core/token.d.ts +4 -2
- package/dist/core/token.js +3 -0
- package/dist/core/units.d.ts +22 -0
- package/dist/core/units.js +14 -0
- package/dist/devtools/capsule.d.ts +17 -13
- package/dist/devtools/capsule.js +14 -10
- package/dist/devtools/decoder.d.ts +15 -0
- package/dist/devtools/decoder.js +179 -0
- package/dist/devtools/identity-internal.d.ts +6 -0
- package/dist/devtools/identity-internal.js +5 -0
- package/dist/devtools/identity.d.ts +21 -0
- package/dist/devtools/identity.js +7 -0
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +2 -0
- package/dist/devtools/inspector.d.ts +18 -16
- package/dist/devtools/observer.d.ts +32 -28
- package/dist/devtools/runtime.js +34 -30
- package/dist/devtools/values.js +3 -5
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/graph/model.d.ts +25 -14
- package/dist/http/auth.d.ts +4 -2
- package/dist/http/client.d.ts +6 -3
- package/dist/http/client.js +8 -9
- package/dist/http/cors-internal.d.ts +7 -5
- package/dist/http/cors-internal.js +8 -6
- package/dist/http/extension.d.ts +60 -21
- package/dist/http/extension.js +91 -20
- package/dist/http/identity.d.ts +20 -0
- package/dist/http/identity.js +10 -0
- package/dist/http/index.d.ts +6 -2
- package/dist/http/index.js +3 -1
- package/dist/http/openapi-identity.d.ts +9 -0
- package/dist/http/openapi-identity.js +4 -0
- package/dist/http/openapi.d.ts +1 -1
- package/dist/http/openapi.js +10 -7
- package/dist/http/path.d.ts +8 -5
- package/dist/http/route-validation.js +2 -1
- package/dist/http/runtime/request.d.ts +3 -2
- package/dist/http/runtime/request.js +1 -4
- package/dist/http/websocket-upgrade.d.ts +2 -0
- package/dist/http/websocket-upgrade.js +4 -0
- package/dist/internal/brand.d.ts +8 -0
- package/dist/internal/brand.js +1 -0
- package/dist/message-port/extension.d.ts +4 -2
- package/dist/message-port/extension.js +15 -8
- package/dist/message-port/identity.d.ts +9 -0
- package/dist/message-port/identity.js +16 -0
- package/dist/message-port/index.d.ts +2 -0
- package/dist/message-port/index.js +1 -0
- package/dist/message-port/transport.d.ts +4 -2
- package/dist/message-port/transport.js +6 -1
- package/dist/runtime/async-iterator-lifecycle.d.ts +2 -1
- package/dist/runtime/async-iterator-lifecycle.js +2 -1
- package/dist/runtime/capability.d.ts +6 -2
- package/dist/runtime/capability.js +3 -1
- package/dist/runtime/di.d.ts +1 -3
- package/dist/runtime/di.js +62 -35
- package/dist/runtime/error.d.ts +3 -1
- package/dist/runtime/index.d.ts +6 -2
- package/dist/runtime/index.js +3 -1
- package/dist/runtime/kernel.d.ts +0 -2
- package/dist/runtime/kernel.js +68 -30
- package/dist/runtime/logger-source.d.ts +4 -0
- package/dist/runtime/logger-source.js +47 -0
- package/dist/runtime/logger.d.ts +2 -38
- package/dist/runtime/logger.js +1 -209
- package/dist/runtime/node-capabilities.d.ts +1 -1
- package/dist/runtime/node-capabilities.js +2 -1
- package/dist/runtime/server-port.d.ts +5 -1
- package/dist/runtime/server-port.js +2 -1
- package/dist/runtime/shutdown-hooks.d.ts +1 -1
- package/dist/runtime/shutdown-hooks.js +5 -4
- package/dist/tasks/extension.d.ts +18 -16
- package/dist/tasks/extension.js +28 -23
- package/dist/tasks/identity.d.ts +9 -0
- package/dist/tasks/identity.js +4 -0
- package/dist/tasks/index.d.ts +1 -0
- package/dist/tasks/runtime/cron.d.ts +6 -2
- package/dist/tasks/runtime/cron.js +9 -0
- package/dist/websocket/extension.d.ts +19 -9
- package/dist/websocket/extension.js +103 -30
- package/dist/websocket/identity.d.ts +9 -0
- package/dist/websocket/identity.js +4 -0
- package/dist/websocket/index.d.ts +1 -0
- package/package.json +1 -1
package/dist/http/extension.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { dependencyConsumerId } from '../core/identity.js';
|
|
2
|
+
import { epochNow, expiresAt, epochMilliseconds, } from '../core/units.js';
|
|
3
|
+
import { normalizeHttpMethod, requestPathname, httpProcedureName, } from './identity.js';
|
|
4
|
+
import { syntheticExecutionId } from '../core/identity.js';
|
|
1
5
|
import { collectInjectedDependencies, getSourceLocation, inheritSourceLocation, composeLayers, defineExecution, defineExecutionExtension, defineLayer, runInInjectionContext, runtimeCapability, SchemaValidationError, validateSchema, } from '../core/index.js';
|
|
2
6
|
import { createCorsActualResponseHeaders, createCorsPreflightResponseHeaders, } from './cors-internal.js';
|
|
3
7
|
import { assertValidHttpMethod, compareHttpPathSpecificity, createHttpDispatchKey, HttpPathDecodeError, matchHttpPath, parseHttpPath, } from './path.js';
|
|
@@ -6,8 +10,15 @@ import { IngressGate } from '../runtime/ingress-gate.js';
|
|
|
6
10
|
import { AsyncIteratorCleanupDeadlineError, AsyncIteratorLifecycle, } from '../runtime/async-iterator-lifecycle.js';
|
|
7
11
|
import { decodeBody, decodeQuery, HttpInputDecodeError, HttpInputValidationError, HttpUnsupportedMediaTypeError, validatedContentType, validateRequestHeaders, } from './runtime/request.js';
|
|
8
12
|
import { applyFrameworkHeadersToResponse, applyFrameworkResponseHeaders, applyResponseHeaders, isResponseHeadersWithDefaults, isStandardSchema, mergeResponseHeaders, responseHeadersDefaults, responseHeadersSchema, validateResponseHeaders, } from './runtime/response.js';
|
|
13
|
+
import { isWebSocketUpgrade } from './websocket-upgrade.js';
|
|
9
14
|
import { match as patternMatch } from 'ts-pattern';
|
|
10
15
|
export const HTTP_SERVER = runtimeCapability('http.server');
|
|
16
|
+
const httpContractRawRouteBrand = Symbol.for('loutre.http-contract-raw-route');
|
|
17
|
+
export function isHttpRawContractRoute(value) {
|
|
18
|
+
return (typeof value === 'object' &&
|
|
19
|
+
value !== null &&
|
|
20
|
+
value[httpContractRawRouteBrand] === true);
|
|
21
|
+
}
|
|
11
22
|
const httpContractBrand = Symbol.for('loutre.http-contract');
|
|
12
23
|
export function isHttpContract(value) {
|
|
13
24
|
if (typeof value !== 'object' || value === null)
|
|
@@ -29,18 +40,22 @@ export const httpExecutionExtension = defineExecutionExtension({
|
|
|
29
40
|
const raw = 'route' in definition;
|
|
30
41
|
const routes = raw
|
|
31
42
|
? [compileRawRoute(definition.route)]
|
|
32
|
-
: Object.entries(definition.contract.routes).
|
|
43
|
+
: Object.entries(definition.contract.routes).flatMap(([name, route]) => route.raw === true
|
|
44
|
+
? []
|
|
45
|
+
: [
|
|
46
|
+
compileHttpRoute(name, route, getSourceLocation(definition.contract)),
|
|
47
|
+
]);
|
|
33
48
|
const id = definition.name || `${context.moduleId}.http.${context.definitionIndex}`;
|
|
34
49
|
const dependencies = new Set(collectInjectedDependencies({
|
|
35
50
|
kind: 'execution',
|
|
36
|
-
id: `http:${id}
|
|
51
|
+
id: dependencyConsumerId(`http:${id}`),
|
|
37
52
|
name: id,
|
|
38
53
|
}, () => definition.factory()));
|
|
39
54
|
routes.forEach((route) => {
|
|
40
55
|
route.middlewares.forEach((middleware, index) => {
|
|
41
56
|
for (const dependency of collectInjectedDependencies({
|
|
42
57
|
kind: 'layer',
|
|
43
|
-
id: `http:${id}:${route.name}:${index}
|
|
58
|
+
id: dependencyConsumerId(`http:${id}:${route.name}:${index}`),
|
|
44
59
|
name: middleware.name,
|
|
45
60
|
}, () => middleware.factory())) {
|
|
46
61
|
dependencies.add(dependency);
|
|
@@ -123,7 +138,9 @@ export function defineHttpContract(routes) {
|
|
|
123
138
|
const resolvedRoutes = resolveHttpContractRoutes(routes);
|
|
124
139
|
const canonicalRoutes = Object.fromEntries(Object.entries(resolvedRoutes).map(([name, route]) => [
|
|
125
140
|
name,
|
|
126
|
-
|
|
141
|
+
route.raw === true
|
|
142
|
+
? snapshotHttpContractRawRoute(route)
|
|
143
|
+
: compileHttpRoute(name, route).definition,
|
|
127
144
|
]));
|
|
128
145
|
return Object.freeze({
|
|
129
146
|
kind: 'http-contract',
|
|
@@ -142,7 +159,28 @@ function resolveHttpContractRoutes(tree) {
|
|
|
142
159
|
if (resolved[name]) {
|
|
143
160
|
throw new TypeError(`Duplicate nested HTTP route name: ${name}`);
|
|
144
161
|
}
|
|
145
|
-
|
|
162
|
+
if (node.raw === true) {
|
|
163
|
+
if (node.responses !== undefined ||
|
|
164
|
+
node.request !== undefined ||
|
|
165
|
+
Object.keys(inheritedResponses).length > 0)
|
|
166
|
+
throw new TypeError('Raw routes cannot declare or inherit HTTP request schemas or responses.');
|
|
167
|
+
resolved[name] = inheritSourceLocation({
|
|
168
|
+
...node,
|
|
169
|
+
path: joinHttpPath(pathPrefix, node.path),
|
|
170
|
+
...(inheritedMiddlewares.length === 0 && !node.middlewares
|
|
171
|
+
? {}
|
|
172
|
+
: {
|
|
173
|
+
middlewares: [
|
|
174
|
+
...inheritedMiddlewares,
|
|
175
|
+
...(node.middlewares ?? []),
|
|
176
|
+
],
|
|
177
|
+
}),
|
|
178
|
+
}, node);
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
assertNoInheritedResponseCollision(name, inheritedResponses, node.responses);
|
|
183
|
+
}
|
|
146
184
|
resolved[name] = inheritSourceLocation({
|
|
147
185
|
...node,
|
|
148
186
|
path: joinHttpPath(pathPrefix, node.path),
|
|
@@ -208,6 +246,7 @@ export const validate = Object.freeze({
|
|
|
208
246
|
body: bodyValidation,
|
|
209
247
|
});
|
|
210
248
|
export const executionHttp = Object.freeze({
|
|
249
|
+
isWebSocketUpgrade,
|
|
211
250
|
raw: defineHttpRaw,
|
|
212
251
|
contract: defineHttpContract,
|
|
213
252
|
implementation: defineHttpImplementation,
|
|
@@ -226,40 +265,55 @@ export function withHttpFrameworkHeaders(result, headers) {
|
|
|
226
265
|
[httpFrameworkHeaders]: { ...current, ...headers },
|
|
227
266
|
};
|
|
228
267
|
}
|
|
268
|
+
function snapshotHttpContractRawRoute(route) {
|
|
269
|
+
if (route.method !== '*')
|
|
270
|
+
assertValidHttpMethod(route.method);
|
|
271
|
+
parseHttpPath(route.path);
|
|
272
|
+
return inheritSourceLocation(Object.freeze({
|
|
273
|
+
...route,
|
|
274
|
+
method: route.method.toUpperCase(),
|
|
275
|
+
[httpContractRawRouteBrand]: true,
|
|
276
|
+
...(route.middlewares === undefined
|
|
277
|
+
? {}
|
|
278
|
+
: { middlewares: Object.freeze([...route.middlewares]) }),
|
|
279
|
+
}), route);
|
|
280
|
+
}
|
|
229
281
|
function compileRawRoute(route) {
|
|
230
282
|
if (route.method !== '*')
|
|
231
283
|
assertValidHttpMethod(route.method);
|
|
232
284
|
const segments = parseHttpPath(route.path);
|
|
285
|
+
const source = getSourceLocation(route);
|
|
233
286
|
return Object.freeze({
|
|
234
|
-
name: 'raw',
|
|
235
|
-
method: route.method.
|
|
287
|
+
name: httpProcedureName('raw'),
|
|
288
|
+
method: route.method === '*' ? '*' : normalizeHttpMethod(route.method),
|
|
236
289
|
path: route.path,
|
|
237
290
|
segments,
|
|
238
|
-
dispatch: createHttpDispatchKey(route.method, segments),
|
|
291
|
+
dispatch: createHttpDispatchKey(normalizeHttpMethod(route.method), segments),
|
|
239
292
|
definition: Object.freeze({
|
|
240
293
|
method: route.method,
|
|
241
294
|
path: route.path,
|
|
242
295
|
responses: Object.freeze({}),
|
|
243
296
|
}),
|
|
244
297
|
middlewares: Object.freeze([...(route.middlewares ?? [])]),
|
|
298
|
+
...(source === undefined ? {} : { source }),
|
|
245
299
|
raw: true,
|
|
246
300
|
...(route.protocol === undefined ? {} : { protocol: route.protocol }),
|
|
247
301
|
});
|
|
248
302
|
}
|
|
249
303
|
function compileHttpRoute(name, route, routeSourceFallback) {
|
|
250
304
|
if (route.method === '*')
|
|
251
|
-
throw new TypeError('method
|
|
305
|
+
throw new TypeError('The wildcard method (*) is only supported by http.raw().');
|
|
252
306
|
assertValidHttpMethod(route.method);
|
|
253
307
|
const segments = parseHttpPath(route.path);
|
|
254
308
|
assertValidHttpRouteDefinition(route, segments);
|
|
255
309
|
const definition = snapshotHttpRouteDefinition(route);
|
|
256
310
|
const routeSource = getSourceLocation(route) ?? routeSourceFallback;
|
|
257
311
|
return Object.freeze({
|
|
258
|
-
name,
|
|
259
|
-
method: route.method.
|
|
312
|
+
name: httpProcedureName(name),
|
|
313
|
+
method: route.method === '*' ? '*' : normalizeHttpMethod(route.method),
|
|
260
314
|
path: route.path,
|
|
261
315
|
segments,
|
|
262
|
-
dispatch: createHttpDispatchKey(route.method, segments),
|
|
316
|
+
dispatch: createHttpDispatchKey(normalizeHttpMethod(route.method), segments),
|
|
263
317
|
definition,
|
|
264
318
|
middlewares: compileHttpMiddlewares(definition),
|
|
265
319
|
...(routeSource === undefined ? {} : { source: routeSource }),
|
|
@@ -278,6 +332,7 @@ function snapshotHttpRouteDefinition(route) {
|
|
|
278
332
|
name,
|
|
279
333
|
Object.freeze({
|
|
280
334
|
...response,
|
|
335
|
+
status: response.status,
|
|
281
336
|
...(response.headers === undefined
|
|
282
337
|
? {}
|
|
283
338
|
: { headers: snapshotHttpResponseHeaders(response.headers) }),
|
|
@@ -411,7 +466,7 @@ function createHttpExtensionRuntime(bindings, applicationRuntime) {
|
|
|
411
466
|
handlers.set(execution.id, runInInjectionContext({
|
|
412
467
|
consumer: {
|
|
413
468
|
kind: 'execution',
|
|
414
|
-
id: `http:${execution.id}
|
|
469
|
+
id: dependencyConsumerId(`http:${execution.id}`),
|
|
415
470
|
name: execution.id,
|
|
416
471
|
},
|
|
417
472
|
resolve: (token, consumer) => resolve(token, { consumer }),
|
|
@@ -427,7 +482,7 @@ function createHttpExtensionRuntime(bindings, applicationRuntime) {
|
|
|
427
482
|
return {
|
|
428
483
|
async drain({ timeoutMs }) {
|
|
429
484
|
ingress.stopAccepting();
|
|
430
|
-
const deadline =
|
|
485
|
+
const deadline = expiresAt(epochNow(), timeoutMs);
|
|
431
486
|
await ingress.waitForIdle();
|
|
432
487
|
const reason = new Error('LUTRE_HTTP_SERVER_STREAM_DRAIN: Application is shutting down.');
|
|
433
488
|
const results = await Promise.allSettled([...activeResponseStreams].map((stream) => stream.abort(reason, deadline)));
|
|
@@ -440,6 +495,13 @@ function createHttpExtensionRuntime(bindings, applicationRuntime) {
|
|
|
440
495
|
if (!ingress.isAccepting) {
|
|
441
496
|
return Response.json({ error: 'Service Unavailable' }, { status: 503 });
|
|
442
497
|
}
|
|
498
|
+
if (request.headers.has('upgrade') ||
|
|
499
|
+
request.headers
|
|
500
|
+
.get('connection')
|
|
501
|
+
?.split(',')
|
|
502
|
+
.some((value) => value.trim().toLowerCase() === 'upgrade')) {
|
|
503
|
+
return new Response('Upgrade requests must use the WebSocket entry point.', { status: 400 });
|
|
504
|
+
}
|
|
443
505
|
const url = new URL(request.url);
|
|
444
506
|
const method = request.method.toUpperCase();
|
|
445
507
|
if (isCorsPreflightRequest(request)) {
|
|
@@ -504,7 +566,7 @@ function createHttpExtensionRuntime(bindings, applicationRuntime) {
|
|
|
504
566
|
let lease;
|
|
505
567
|
try {
|
|
506
568
|
lease = applicationRuntime.beginExecution({
|
|
507
|
-
executionId: 'http.not-found',
|
|
569
|
+
executionId: syntheticExecutionId('http.not-found'),
|
|
508
570
|
executionKind: 'http.request',
|
|
509
571
|
name: `${method} ${url.pathname}`,
|
|
510
572
|
});
|
|
@@ -674,7 +736,7 @@ function createHttpExtensionRuntime(bindings, applicationRuntime) {
|
|
|
674
736
|
return complete(result);
|
|
675
737
|
}
|
|
676
738
|
if (match.route.raw)
|
|
677
|
-
throw new TypeError('Raw
|
|
739
|
+
throw new TypeError('Raw handlers and middleware must return a Response.');
|
|
678
740
|
const finalized = await finalizeHttpResult(match.route.definition, result, lease.signal, finishExecution);
|
|
679
741
|
if (finalized.streamControl)
|
|
680
742
|
adoptResponseStream(finalized.streamControl);
|
|
@@ -715,7 +777,7 @@ function findRuntimeHttpRoute(routes, method, pathname) {
|
|
|
715
777
|
for (const candidate of routes) {
|
|
716
778
|
if (candidate.route.method !== method && candidate.route.method !== '*')
|
|
717
779
|
continue;
|
|
718
|
-
const params = matchHttpPath(candidate.route.segments, pathname);
|
|
780
|
+
const params = matchHttpPath(candidate.route.segments, requestPathname(pathname));
|
|
719
781
|
if (!params)
|
|
720
782
|
continue;
|
|
721
783
|
return { ...candidate, params };
|
|
@@ -770,10 +832,14 @@ function createHttpResponseHelpers(responses) {
|
|
|
770
832
|
}),
|
|
771
833
|
]));
|
|
772
834
|
}
|
|
835
|
+
function isHttpInputRecord(value) {
|
|
836
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
837
|
+
}
|
|
773
838
|
async function replayHttpHandler(applicationRuntime, handler, match, input, state) {
|
|
774
|
-
if (
|
|
839
|
+
if (!isHttpInputRecord(input) || !isHttpInputRecord(input.params)) {
|
|
775
840
|
throw new TypeError('LUTRE_DEVTOOLS_HTTP_HANDLER_INPUT_REQUIRED');
|
|
776
841
|
}
|
|
842
|
+
const params = input.params;
|
|
777
843
|
const lease = applicationRuntime.beginExecution({
|
|
778
844
|
executionId: match.executionId,
|
|
779
845
|
executionKind: 'http.handler',
|
|
@@ -781,7 +847,12 @@ async function replayHttpHandler(applicationRuntime, handler, match, input, stat
|
|
|
781
847
|
name: `${match.executionId}.${match.route.name}`,
|
|
782
848
|
});
|
|
783
849
|
const invoke = () => handler({
|
|
784
|
-
input
|
|
850
|
+
input: {
|
|
851
|
+
params,
|
|
852
|
+
query: input.query,
|
|
853
|
+
headers: input.headers,
|
|
854
|
+
body: input.body,
|
|
855
|
+
},
|
|
785
856
|
response: createHttpResponseHelpers(match.route.definition.responses),
|
|
786
857
|
signal: lease.signal,
|
|
787
858
|
state,
|
|
@@ -1016,7 +1087,7 @@ function createServerSentEventStream(source, schema, signal, completeExecution)
|
|
|
1016
1087
|
const stop = (reason, errorStream, deadline) => {
|
|
1017
1088
|
if (cleanupAttempt) {
|
|
1018
1089
|
if (deadline !== undefined) {
|
|
1019
|
-
cleanupDeadline = Math.min(cleanupDeadline ?? deadline, deadline);
|
|
1090
|
+
cleanupDeadline = epochMilliseconds(Math.min(cleanupDeadline ?? deadline, deadline));
|
|
1020
1091
|
}
|
|
1021
1092
|
return cleanupAttempt;
|
|
1022
1093
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Brand } from '../internal/brand.js';
|
|
2
|
+
export type HttpPathTemplate = Brand<string, 'http.path-template'>;
|
|
3
|
+
export type HttpNormalizedPath = Brand<string, 'http.normalized-path'>;
|
|
4
|
+
export type RequestPathname = Brand<string, 'http.request-pathname'>;
|
|
5
|
+
export type HttpMethod = Brand<string, 'http.method'>;
|
|
6
|
+
export type HttpDispatchKey = Brand<string, 'http.dispatch-key'>;
|
|
7
|
+
export type HttpProcedureName = Brand<string, 'http.procedure'>;
|
|
8
|
+
export type HttpHeaderName = Brand<string, 'http.header-name'>;
|
|
9
|
+
export type CorsOriginValue = Brand<string, 'http.cors-origin'>;
|
|
10
|
+
export type ContentTypeValue = Brand<string, 'http.content-type'>;
|
|
11
|
+
export type NormalizedMediaType = Brand<string, 'http.media-type'>;
|
|
12
|
+
export type HttpResponseStatus = Brand<number, 'http.response-status'>;
|
|
13
|
+
export type HttpBaseUrl = Brand<string, 'http.base-url'>;
|
|
14
|
+
export declare const normalizeHttpMethod: (value: string) => HttpMethod;
|
|
15
|
+
export declare const requestPathname: (value: string) => RequestPathname;
|
|
16
|
+
export declare const httpProcedureName: (value: string) => HttpProcedureName;
|
|
17
|
+
export declare const httpHeaderName: (value: string) => HttpHeaderName;
|
|
18
|
+
export declare const corsOriginValue: (value: string) => CorsOriginValue;
|
|
19
|
+
export declare const httpBaseUrl: (value: string | URL) => HttpBaseUrl;
|
|
20
|
+
export declare function normalizeMediaType(value: string | null | undefined): NormalizedMediaType | undefined;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const normalizeHttpMethod = (value) => value.toUpperCase();
|
|
2
|
+
export const requestPathname = (value) => value;
|
|
3
|
+
export const httpProcedureName = (value) => value;
|
|
4
|
+
export const httpHeaderName = (value) => value;
|
|
5
|
+
export const corsOriginValue = (value) => value;
|
|
6
|
+
export const httpBaseUrl = (value) => String(value);
|
|
7
|
+
export function normalizeMediaType(value) {
|
|
8
|
+
const normalized = value?.split(';', 1)[0]?.trim().toLowerCase();
|
|
9
|
+
return normalized ? normalized : undefined;
|
|
10
|
+
}
|
package/dist/http/index.d.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
export { createHttpClient, fetchHttpTransport, HttpClientResponseError, } from './client.js';
|
|
2
2
|
export type { FetchHttpTransportOptions, HttpClient, HttpClientRequest, HttpClientResponse, HttpClientTransport, HttpClientTransportRequest, HttpClientTransportResponse, } from './client.js';
|
|
3
3
|
export { cors } from './cors.js';
|
|
4
|
+
export { isWebSocketUpgrade } from './websocket-upgrade.js';
|
|
4
5
|
export type { CorsLayerDescriptor, CorsOptions, CorsOrigin } from './cors.js';
|
|
5
6
|
export { basicAuth, bearerAuth } from './auth.js';
|
|
6
7
|
export type { BasicAuthContext, BasicAuthCredentials, BasicAuthDefinition, BasicAuthUnauthorized, BasicAuthRuntime, BearerAuthContext, BearerAuthDefinition, BearerAuthRuntime, BearerAuthUnauthorized, HttpAuthenticationFailure, } from './auth.js';
|
|
7
|
-
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, defineHttpRaw, httpError, executionHttp as http, httpExecutionExtension, isHttpContract, HTTP_SERVER, validate, } from './extension.js';
|
|
8
|
-
export type { HttpRawDefinition, HttpRawRouteDefinition, HttpRawExecutionContext, HttpRawExecutionDefinition, HttpContract, HttpContractBranchDefinition, HttpContractNodeDefinition, HttpContractRouteTree, HttpExecutionContext, HttpExecutionDefinition, HttpExecutionRequestDefinition, HttpExecutionResponseDefinition, HttpExecutionResult, HttpExecutionRouteDefinition, HttpErrorMapping, HttpErrorMatcher, HttpExtensionRuntime, HttpHandlers, HttpHeaderValue, HttpHeaders, HttpHostApi, HttpImplementationDefinition, HttpMiddleware, HttpMiddlewareContext, HttpResponseHeadersDefinition, HttpResponseResult, HttpResponseHeadersWithDefaults, HttpServerDriver, HttpValidationMiddleware, HttpValidationPart, ResolvedHttpContractRoutes, } from './extension.js';
|
|
8
|
+
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, defineHttpRaw, httpError, executionHttp as http, httpExecutionExtension, isHttpContract, isHttpRawContractRoute, HTTP_SERVER, validate, } from './extension.js';
|
|
9
|
+
export type { HttpRawDefinition, HttpRawRouteDefinition, HttpRawExecutionContext, HttpRawExecutionDefinition, HttpContract, HttpContractBranchDefinition, HttpContractNodeDefinition, HttpContractRouteTree, HttpContractRouteDefinition, HttpContractRawRouteDefinition, HttpContractRawRoute, HttpExecutionContext, HttpExecutionDefinition, HttpExecutionRequestDefinition, HttpExecutionResponseDefinition, HttpExecutionResult, HttpExecutionRouteDefinition, HttpErrorMapping, HttpErrorMatcher, HttpExtensionRuntime, HttpHandlers, HttpHeaderValue, HttpHeaders, HttpHostApi, HttpImplementationDefinition, HttpMiddleware, HttpMiddlewareContext, HttpResponseHeadersDefinition, HttpResponseResult, HttpResponseHeadersWithDefaults, HttpServerDriver, HttpValidationMiddleware, HttpValidationPart, ResolvedHttpContractRoutes, } from './extension.js';
|
|
9
10
|
export { compareHttpPathSpecificity, HttpPathDecodeError, matchHttpPath, normalizeHttpPath, parseHttpPath, } from './path.js';
|
|
10
11
|
export type { HttpPathSegment, PathParamNames } from './path.js';
|
|
11
12
|
export type { HttpRequestHeadDefinition } from './request-head.js';
|
|
13
|
+
export * from './identity.js';
|
|
14
|
+
export type { ParsedHttpPath } from './path.js';
|
|
15
|
+
export type { BearerCredential } from './auth.js';
|
package/dist/http/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { createHttpClient, fetchHttpTransport, HttpClientResponseError, } from './client.js';
|
|
2
2
|
export { cors } from './cors.js';
|
|
3
|
+
export { isWebSocketUpgrade } from './websocket-upgrade.js';
|
|
3
4
|
export { basicAuth, bearerAuth } from './auth.js';
|
|
4
|
-
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, defineHttpRaw, httpError, executionHttp as http, httpExecutionExtension, isHttpContract, HTTP_SERVER, validate, } from './extension.js';
|
|
5
|
+
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, defineHttpRaw, httpError, executionHttp as http, httpExecutionExtension, isHttpContract, isHttpRawContractRoute, HTTP_SERVER, validate, } from './extension.js';
|
|
5
6
|
export { compareHttpPathSpecificity, HttpPathDecodeError, matchHttpPath, normalizeHttpPath, parseHttpPath, } from './path.js';
|
|
7
|
+
export * from './identity.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Brand } from '../internal/brand.js';
|
|
2
|
+
export type OpenApiOperationId = Brand<string, 'openapi.operation-id'>;
|
|
3
|
+
export type OpenApiSchemaName = Brand<string, 'openapi.schema-name'>;
|
|
4
|
+
export type JsonSchemaReference = Brand<string, 'openapi.schema-reference'>;
|
|
5
|
+
export type SchemaComparisonKey = Brand<string, 'openapi.schema-comparison-key'>;
|
|
6
|
+
export declare const openApiOperationId: (value: string) => OpenApiOperationId;
|
|
7
|
+
export declare const openApiSchemaName: (value: string) => OpenApiSchemaName;
|
|
8
|
+
export declare const jsonSchemaReference: (value: string) => JsonSchemaReference;
|
|
9
|
+
export declare const schemaComparisonKey: (value: unknown) => SchemaComparisonKey;
|
package/dist/http/openapi.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type { OpenApiOperationId, OpenApiSchemaName, JsonSchemaReference, } from './openapi-identity.js';
|
|
1
2
|
import { type ApplicationModel } from '../core/index.js';
|
|
2
3
|
import { type HttpContract, type HttpExecutionResponseDefinition, type HttpExecutionRouteDefinition } from './extension.js';
|
|
3
4
|
export interface OpenApiInfo {
|
|
@@ -39,4 +40,3 @@ export interface HttpOperationTarget {
|
|
|
39
40
|
}
|
|
40
41
|
export type OpenApiSource = ApplicationModel | HttpContract;
|
|
41
42
|
export declare function generateOpenApi(source: OpenApiSource, options: GenerateOpenApiOptions): OpenApiDocument;
|
|
42
|
-
export {};
|
package/dist/http/openapi.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { openApiOperationId, openApiSchemaName, jsonSchemaReference, schemaComparisonKey, } from './openapi-identity.js';
|
|
1
2
|
import { supportsJsonSchema, assertValidApplicationModel, } from '../core/index.js';
|
|
2
3
|
import { httpExecutionExtension, isHttpContract, } from './extension.js';
|
|
3
4
|
import { findHttpRouteConflicts } from './route-validation.js';
|
|
@@ -34,7 +35,7 @@ function httpOperationTargets(source) {
|
|
|
34
35
|
if (!isHttpContract(source)) {
|
|
35
36
|
throw openApiError('LUTRE_OPENAPI_SOURCE_001', 'HttpContract source must be created by http.contract().');
|
|
36
37
|
}
|
|
37
|
-
const routes = Object.entries(source.routes).
|
|
38
|
+
const routes = Object.entries(source.routes).flatMap(([procedure, definition]) => definition.raw === true ? [] : [{ procedure, definition }]);
|
|
38
39
|
const [conflict] = findHttpRouteConflicts(routes.map(({ definition }) => definition));
|
|
39
40
|
if (conflict) {
|
|
40
41
|
throw openApiError('LUTRE_OPENAPI_OPERATION_001', `Duplicate OpenAPI operation: ${conflict.route.method.toUpperCase()} ${conflict.route.path} conflicts with ${conflict.existing.method.toUpperCase()} ${conflict.existing.path}`);
|
|
@@ -71,10 +72,10 @@ function createOperation(target, registry, operationIds, resolveOperationId) {
|
|
|
71
72
|
if (operationId.length === 0) {
|
|
72
73
|
throw openApiError('LUTRE_OPENAPI_OPERATION_ID_002', `operationId must not be empty for ${describeTarget(target)}`);
|
|
73
74
|
}
|
|
74
|
-
if (operationIds.has(operationId)) {
|
|
75
|
+
if (operationIds.has(openApiOperationId(operationId))) {
|
|
75
76
|
throw openApiError('LUTRE_OPENAPI_OPERATION_ID_001', `Duplicate operationId: ${operationId}`);
|
|
76
77
|
}
|
|
77
|
-
operationIds.add(operationId);
|
|
78
|
+
operationIds.add(openApiOperationId(operationId));
|
|
78
79
|
}
|
|
79
80
|
const parameters = [];
|
|
80
81
|
const request = definition.request;
|
|
@@ -387,7 +388,9 @@ class SchemaRegistry {
|
|
|
387
388
|
const materialized = {
|
|
388
389
|
name,
|
|
389
390
|
schema: rebased,
|
|
390
|
-
reference: {
|
|
391
|
+
reference: {
|
|
392
|
+
$ref: jsonSchemaReference(`#/components/schemas/${escapeJsonPointer(name)}`),
|
|
393
|
+
},
|
|
391
394
|
};
|
|
392
395
|
const directions = this.bySchema.get(schema) ?? {};
|
|
393
396
|
directions[direction] = materialized;
|
|
@@ -407,11 +410,11 @@ function sanitizeComponentName(value) {
|
|
|
407
410
|
}
|
|
408
411
|
function uniqueComponentName(preferred, schemas) {
|
|
409
412
|
if (!(preferred in schemas))
|
|
410
|
-
return preferred;
|
|
413
|
+
return openApiSchemaName(preferred);
|
|
411
414
|
let index = 2;
|
|
412
415
|
while (`${preferred}_${index}` in schemas)
|
|
413
416
|
index += 1;
|
|
414
|
-
return `${preferred}_${index}
|
|
417
|
+
return openApiSchemaName(`${preferred}_${index}`);
|
|
415
418
|
}
|
|
416
419
|
function escapeJsonPointer(value) {
|
|
417
420
|
return value.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
@@ -423,7 +426,7 @@ function dedupeJsonSchemas(schemas) {
|
|
|
423
426
|
const seen = new Set();
|
|
424
427
|
const result = [];
|
|
425
428
|
for (const schema of schemas) {
|
|
426
|
-
const serialized =
|
|
429
|
+
const serialized = schemaComparisonKey(schema);
|
|
427
430
|
if (seen.has(serialized))
|
|
428
431
|
continue;
|
|
429
432
|
seen.add(serialized);
|
package/dist/http/path.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Brand } from '../internal/brand.js';
|
|
2
|
+
import type { HttpNormalizedPath, HttpDispatchKey, HttpMethod, RequestPathname } from './identity.js';
|
|
1
3
|
export type HttpPathSegment = {
|
|
2
4
|
readonly kind: 'static';
|
|
3
5
|
readonly value: string;
|
|
@@ -6,12 +8,13 @@ export type HttpPathSegment = {
|
|
|
6
8
|
readonly name: string;
|
|
7
9
|
};
|
|
8
10
|
export type PathParamNames<TPath extends string> = TPath extends `${string}{${infer TName}}${infer TRest}` ? TName | PathParamNames<TRest> : never;
|
|
11
|
+
export type ParsedHttpPath = Brand<readonly HttpPathSegment[], 'http.parsed-path'>;
|
|
9
12
|
export declare function assertValidHttpMethod(method: string): void;
|
|
10
|
-
export declare function parseHttpPath(path: string):
|
|
11
|
-
export declare function normalizeHttpPath(segments:
|
|
12
|
-
export declare function createHttpDispatchKey(method:
|
|
13
|
-
export declare function matchHttpPath(segments:
|
|
14
|
-
export declare function compareHttpPathSpecificity(left:
|
|
13
|
+
export declare function parseHttpPath(path: string): ParsedHttpPath;
|
|
14
|
+
export declare function normalizeHttpPath(segments: ParsedHttpPath): HttpNormalizedPath;
|
|
15
|
+
export declare function createHttpDispatchKey(method: HttpMethod | '*', segments: ParsedHttpPath): HttpDispatchKey;
|
|
16
|
+
export declare function matchHttpPath(segments: ParsedHttpPath, pathname: RequestPathname): Record<string, string> | undefined;
|
|
17
|
+
export declare function compareHttpPathSpecificity(left: ParsedHttpPath, right: ParsedHttpPath): number;
|
|
15
18
|
export declare class HttpPathDecodeError extends Error {
|
|
16
19
|
readonly cause: unknown;
|
|
17
20
|
constructor(cause: unknown);
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { normalizeHttpMethod } from './identity.js';
|
|
1
2
|
import { createHttpDispatchKey, parseHttpPath } from './path.js';
|
|
2
3
|
export function findHttpRouteConflicts(routes) {
|
|
3
4
|
const dispatches = new Map();
|
|
4
5
|
const conflicts = [];
|
|
5
6
|
for (const route of routes) {
|
|
6
|
-
const dispatch = createHttpDispatchKey(route.method, parseHttpPath(route.path));
|
|
7
|
+
const dispatch = createHttpDispatchKey(normalizeHttpMethod(route.method), parseHttpPath(route.path));
|
|
7
8
|
const existing = dispatches.get(dispatch) ??
|
|
8
9
|
[...dispatches.values()].find((candidate) => (route.method === '*' || candidate.method === '*') &&
|
|
9
10
|
createHttpDispatchKey('*', parseHttpPath(candidate.path)) ===
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { type ContentTypeValue } from '../identity.js';
|
|
1
2
|
import { type StandardSchemaV1 } from '../../core/index.js';
|
|
2
3
|
export declare function decodeQuery(searchParams: URLSearchParams): Readonly<Record<string, string | string[]>>;
|
|
3
4
|
export declare function validateRequestHeaders(schema: StandardSchemaV1, bodyDeclared: boolean, headers: Headers): Promise<unknown>;
|
|
4
|
-
export declare function validatedContentType(headers: unknown):
|
|
5
|
-
export declare function decodeBody(request: Request, contentType:
|
|
5
|
+
export declare function validatedContentType(headers: unknown): ContentTypeValue;
|
|
6
|
+
export declare function decodeBody(request: Request, contentType: ContentTypeValue): Promise<unknown>;
|
|
6
7
|
export declare class HttpInputDecodeError extends Error {
|
|
7
8
|
readonly cause: unknown;
|
|
8
9
|
constructor(cause: unknown);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeMediaType } from '../identity.js';
|
|
1
2
|
import { SchemaValidationError, validateSchema, } from '../../core/index.js';
|
|
2
3
|
export function decodeQuery(searchParams) {
|
|
3
4
|
const query = {};
|
|
@@ -79,10 +80,6 @@ function hasContentTypeIssue(error) {
|
|
|
79
80
|
return key === 'content-type';
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
|
-
function normalizeMediaType(value) {
|
|
83
|
-
const normalized = value?.split(';', 1)[0]?.trim().toLowerCase();
|
|
84
|
-
return normalized ? normalized : undefined;
|
|
85
|
-
}
|
|
86
83
|
export class HttpInputDecodeError extends Error {
|
|
87
84
|
cause;
|
|
88
85
|
constructor(cause) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { type MessagePortProcedureName } from './identity.js';
|
|
2
|
+
import type { Descriptor } from '../internal/brand.js';
|
|
1
3
|
import { type ExecutionDefinition, type ExecutionExtension, type ExecutionExtensionDrainContext, type SchemaOutput, type StandardSchemaV1 } from '../core/index.js';
|
|
2
4
|
export interface MessagePortServerStreamResponseDefinition {
|
|
3
5
|
readonly body: StandardSchemaV1;
|
|
@@ -8,7 +10,7 @@ export interface MessagePortRouteDefinition {
|
|
|
8
10
|
readonly input?: StandardSchemaV1;
|
|
9
11
|
readonly responses: Readonly<Record<string, MessagePortResponseDefinition>>;
|
|
10
12
|
}
|
|
11
|
-
export interface MessagePortContract<TRoutes extends Readonly<Record<string, MessagePortRouteDefinition>> = Readonly<Record<string, MessagePortRouteDefinition>>> {
|
|
13
|
+
export interface MessagePortContract<TRoutes extends Readonly<Record<string, MessagePortRouteDefinition>> = Readonly<Record<string, MessagePortRouteDefinition>>> extends Descriptor<'message-port-contract'> {
|
|
12
14
|
readonly kind: 'message-port-contract';
|
|
13
15
|
readonly routes: TRoutes;
|
|
14
16
|
}
|
|
@@ -39,7 +41,7 @@ interface CompiledMessagePortExecution {
|
|
|
39
41
|
readonly factory: () => Readonly<Record<string, (context: MessagePortContext) => MessagePortResult | Promise<MessagePortResult>>>;
|
|
40
42
|
}
|
|
41
43
|
export interface MessagePortExtensionRuntime {
|
|
42
|
-
invoke(method:
|
|
44
|
+
invoke(method: MessagePortProcedureName, input?: unknown): Promise<MessagePortResult>;
|
|
43
45
|
drain(context: ExecutionExtensionDrainContext): Promise<void>;
|
|
44
46
|
}
|
|
45
47
|
export interface MessagePortHostApi {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { dependencyConsumerId } from '../core/identity.js';
|
|
2
|
+
import { epochNow, expiresAt, epochMilliseconds, } from '../core/units.js';
|
|
3
|
+
import { messagePortProcedureName, } from './identity.js';
|
|
1
4
|
import { collectInjectedDependencies, defineExecution, defineExecutionExtension, runInInjectionContext, validateSchema, } from '../core/index.js';
|
|
2
5
|
import { IngressGate } from '../runtime/ingress-gate.js';
|
|
3
6
|
import { AsyncIteratorCleanupDeadlineError, AsyncIteratorLifecycle, } from '../runtime/async-iterator-lifecycle.js';
|
|
@@ -13,7 +16,7 @@ export const messagePortExtension = defineExecutionExtension({
|
|
|
13
16
|
executionKind: 'message-port.invocation',
|
|
14
17
|
dependencies: collectInjectedDependencies({
|
|
15
18
|
kind: 'execution',
|
|
16
|
-
id: `message-port:${definition.name || `${context.moduleId}.message-port.${context.definitionIndex}`}
|
|
19
|
+
id: dependencyConsumerId(`message-port:${definition.name || `${context.moduleId}.message-port.${context.definitionIndex}`}`),
|
|
17
20
|
name: definition.name ||
|
|
18
21
|
`${context.moduleId}.message-port.${context.definitionIndex}`,
|
|
19
22
|
}, () => definition.factory()),
|
|
@@ -26,7 +29,8 @@ export const messagePortExtension = defineExecutionExtension({
|
|
|
26
29
|
},
|
|
27
30
|
validate({ executions }) {
|
|
28
31
|
const methods = new Set();
|
|
29
|
-
return executions.flatMap((execution) => Object.keys(execution.compiled.routes).flatMap((
|
|
32
|
+
return executions.flatMap((execution) => Object.keys(execution.compiled.routes).flatMap((name) => {
|
|
33
|
+
const method = messagePortProcedureName(name);
|
|
30
34
|
if (!methods.has(method)) {
|
|
31
35
|
methods.add(method);
|
|
32
36
|
return [];
|
|
@@ -49,12 +53,15 @@ export const messagePortExtension = defineExecutionExtension({
|
|
|
49
53
|
host: {
|
|
50
54
|
namespace: 'messagePort',
|
|
51
55
|
create: ({ runtime }) => ({
|
|
52
|
-
invoke: (method, input) => runtime.invoke(method, input),
|
|
56
|
+
invoke: (method, input) => runtime.invoke(messagePortProcedureName(method), input),
|
|
53
57
|
}),
|
|
54
58
|
},
|
|
55
59
|
});
|
|
56
60
|
export function defineMessagePortContract(routes) {
|
|
57
|
-
return Object.freeze({
|
|
61
|
+
return Object.freeze({
|
|
62
|
+
kind: 'message-port-contract',
|
|
63
|
+
routes,
|
|
64
|
+
});
|
|
58
65
|
}
|
|
59
66
|
export function defineMessagePortImplementation(definition) {
|
|
60
67
|
return defineExecution(messagePortExtension, {
|
|
@@ -90,7 +97,7 @@ function createMessagePortRuntime(bindings, applicationRuntime) {
|
|
|
90
97
|
const handlers = runInInjectionContext({
|
|
91
98
|
consumer: {
|
|
92
99
|
kind: 'execution',
|
|
93
|
-
id: `message-port:${execution.id}
|
|
100
|
+
id: dependencyConsumerId(`message-port:${execution.id}`),
|
|
94
101
|
name: execution.id,
|
|
95
102
|
},
|
|
96
103
|
resolve: (token, consumer) => resolve(token, { consumer }),
|
|
@@ -98,7 +105,7 @@ function createMessagePortRuntime(bindings, applicationRuntime) {
|
|
|
98
105
|
for (const [method, definition] of Object.entries(execution.compiled.routes)) {
|
|
99
106
|
const handler = handlers[method];
|
|
100
107
|
if (handler) {
|
|
101
|
-
routes.set(method, {
|
|
108
|
+
routes.set(messagePortProcedureName(method), {
|
|
102
109
|
executionId: execution.id,
|
|
103
110
|
route: definition,
|
|
104
111
|
handler,
|
|
@@ -178,7 +185,7 @@ function createMessagePortRuntime(bindings, applicationRuntime) {
|
|
|
178
185
|
},
|
|
179
186
|
async drain({ timeoutMs }) {
|
|
180
187
|
ingress.stopAccepting();
|
|
181
|
-
const deadline =
|
|
188
|
+
const deadline = expiresAt(epochNow(), timeoutMs);
|
|
182
189
|
await ingress.waitForIdle();
|
|
183
190
|
const reason = new Error('LUTRE_MESSAGE_PORT_DRAINING');
|
|
184
191
|
const results = await Promise.allSettled([...activeStreams].map((stream) => stream.abort(reason, deadline)));
|
|
@@ -230,7 +237,7 @@ function createLeasedMessagePortStream(schema, source, lease, activeStreams) {
|
|
|
230
237
|
const startIteratorCleanup = (reason, deadline) => {
|
|
231
238
|
if (iteratorCleanup) {
|
|
232
239
|
if (deadline !== undefined) {
|
|
233
|
-
iteratorCleanupDeadline = Math.min(iteratorCleanupDeadline ?? deadline, deadline);
|
|
240
|
+
iteratorCleanupDeadline = epochMilliseconds(Math.min(iteratorCleanupDeadline ?? deadline, deadline));
|
|
234
241
|
}
|
|
235
242
|
return iteratorCleanup;
|
|
236
243
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Brand } from '../internal/brand.js';
|
|
2
|
+
export type MessagePortRequestId = Brand<string, 'message-port.request-id'>;
|
|
3
|
+
export type MessagePortProcedureName = Brand<string, 'message-port.procedure'>;
|
|
4
|
+
export declare const messagePortProcedureName: (value: string) => MessagePortProcedureName;
|
|
5
|
+
export declare function decodeMessagePortRequest(value: unknown): {
|
|
6
|
+
readonly id: MessagePortRequestId;
|
|
7
|
+
readonly procedure: MessagePortProcedureName;
|
|
8
|
+
readonly input?: unknown;
|
|
9
|
+
} | undefined;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const messagePortProcedureName = (value) => value;
|
|
2
|
+
export function decodeMessagePortRequest(value) {
|
|
3
|
+
if (typeof value !== 'object' ||
|
|
4
|
+
value === null ||
|
|
5
|
+
Array.isArray(value) ||
|
|
6
|
+
!('id' in value) ||
|
|
7
|
+
!('procedure' in value) ||
|
|
8
|
+
typeof value.id !== 'string' ||
|
|
9
|
+
typeof value.procedure !== 'string')
|
|
10
|
+
return undefined;
|
|
11
|
+
return {
|
|
12
|
+
id: value.id,
|
|
13
|
+
procedure: messagePortProcedureName(value.procedure),
|
|
14
|
+
...('input' in value ? { input: value.input } : {}),
|
|
15
|
+
};
|
|
16
|
+
}
|