@loutrejs/loutre 0.9.2 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type WebSocketSessionId, type WebSocketProcedureName, type NegotiatedSubprotocol } from './identity.js';
|
|
2
|
+
import { type HttpPathTemplate, type HttpNormalizedPath } from '../http/identity.js';
|
|
3
|
+
import type { ParsedHttpPath } from '../http/path.js';
|
|
4
|
+
import type { Descriptor } from '../internal/brand.js';
|
|
5
|
+
import { type ExecutionDefinition, type ExecutionContextView, type ExecutionExtensionDrainContext, type LayerState, type RuntimeCapabilityBinding, type SchemaOutput, type StandardSchemaV1 } from '../core/index.js';
|
|
2
6
|
import type { HttpRequestHeadDefinition } from '../http/request-head.js';
|
|
3
|
-
import { type
|
|
7
|
+
import { type HttpRawRouteDefinition } from '../http/index.js';
|
|
4
8
|
export type WebSocketDataMessage = {
|
|
5
9
|
readonly type: 'text';
|
|
6
10
|
readonly data: string;
|
|
@@ -29,8 +33,8 @@ export interface WebSocketUpgradeOptions {
|
|
|
29
33
|
readonly protocol?: string;
|
|
30
34
|
}
|
|
31
35
|
export interface WebSocketSession {
|
|
32
|
-
readonly id:
|
|
33
|
-
readonly protocol:
|
|
36
|
+
readonly id: WebSocketSessionId;
|
|
37
|
+
readonly protocol: NegotiatedSubprotocol;
|
|
34
38
|
}
|
|
35
39
|
export interface WebSocketServerDriver {
|
|
36
40
|
readonly runtime: string;
|
|
@@ -57,6 +61,7 @@ export interface WebSocketBranchDefinition {
|
|
|
57
61
|
}
|
|
58
62
|
export interface WebSocketHandshakeDefinition extends HttpRequestHeadDefinition {
|
|
59
63
|
readonly protocols?: readonly string[];
|
|
64
|
+
readonly middlewares?: HttpRawRouteDefinition['middlewares'];
|
|
60
65
|
}
|
|
61
66
|
export interface WebSocketRouteDefinition {
|
|
62
67
|
readonly path: string;
|
|
@@ -64,7 +69,7 @@ export interface WebSocketRouteDefinition {
|
|
|
64
69
|
readonly messages?: WebSocketMessageCodec;
|
|
65
70
|
}
|
|
66
71
|
export type WebSocketRouteTree = Readonly<Record<string, WebSocketRouteDefinition | WebSocketBranchDefinition>>;
|
|
67
|
-
export interface WebSocketContract<TRoutes extends WebSocketRouteTree = WebSocketRouteTree> {
|
|
72
|
+
export interface WebSocketContract<TRoutes extends WebSocketRouteTree = WebSocketRouteTree> extends Descriptor<'websocket-contract'> {
|
|
68
73
|
readonly kind: 'websocket-contract';
|
|
69
74
|
readonly routes: TRoutes;
|
|
70
75
|
}
|
|
@@ -95,6 +100,11 @@ type OpeningRequestInput<TRoute extends WebSocketRouteDefinition> = {
|
|
|
95
100
|
};
|
|
96
101
|
export type WebSocketHandlerContext<TRoute extends WebSocketRouteDefinition = WebSocketRouteDefinition> = {
|
|
97
102
|
readonly request: Request;
|
|
103
|
+
readonly state: Readonly<TRoute extends {
|
|
104
|
+
readonly handshake: {
|
|
105
|
+
readonly middlewares: infer TLayers extends NonNullable<WebSocketHandshakeDefinition['middlewares']>;
|
|
106
|
+
};
|
|
107
|
+
} ? LayerState<TLayers> : {}>;
|
|
98
108
|
readonly session: WebSocketSession;
|
|
99
109
|
readonly protocol: string;
|
|
100
110
|
readonly execution: ExecutionContextView;
|
|
@@ -116,10 +126,10 @@ export interface WebSocketImplementationDefinition<TContract extends WebSocketCo
|
|
|
116
126
|
readonly factory: () => WebSocketHandlers<TContract>;
|
|
117
127
|
}
|
|
118
128
|
interface CompiledWebSocketRoute {
|
|
119
|
-
readonly name:
|
|
120
|
-
readonly path:
|
|
121
|
-
readonly normalizedPath:
|
|
122
|
-
readonly segments:
|
|
129
|
+
readonly name: WebSocketProcedureName;
|
|
130
|
+
readonly path: HttpPathTemplate;
|
|
131
|
+
readonly normalizedPath: HttpNormalizedPath;
|
|
132
|
+
readonly segments: ParsedHttpPath;
|
|
123
133
|
readonly handshake?: WebSocketHandshakeDefinition;
|
|
124
134
|
readonly messages?: WebSocketMessageCodec;
|
|
125
135
|
}
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dependencyConsumerId } from '../core/identity.js';
|
|
2
|
+
import { milliseconds } from '../core/units.js';
|
|
3
|
+
import { createWebSocketSessionId, webSocketProcedureName, negotiatedSubprotocol, observedCloseCode, } from './identity.js';
|
|
4
|
+
import { requestPathname, } from '../http/identity.js';
|
|
5
|
+
import { collectInjectedDependencies, getSourceLocation, composeLayers, defineExecution, defineExecutionExtension, runtimeCapability, runInInjectionContext, validateSchema, } from '../core/index.js';
|
|
2
6
|
import { IngressGate } from '../runtime/ingress-gate.js';
|
|
3
7
|
import { WebSocketConnectionNotOpenError, WebSocketMessageDecodeError, WebSocketMessageEncodeError, } from './errors.js';
|
|
4
8
|
import { compareHttpPathSpecificity, HttpPathDecodeError, matchHttpPath, normalizeHttpPath, parseHttpPath, } from '../http/index.js';
|
|
@@ -8,20 +12,37 @@ export const websocketExtension = defineExecutionExtension({
|
|
|
8
12
|
abiVersion: '2',
|
|
9
13
|
name: 'loutre:websocket',
|
|
10
14
|
compile(definition, context) {
|
|
15
|
+
const id = definition.name ||
|
|
16
|
+
`${context.moduleId}.websocket.${context.definitionIndex}`;
|
|
17
|
+
const routes = compileRouteTree(definition.contract.routes);
|
|
18
|
+
const dependencies = new Set(collectInjectedDependencies({
|
|
19
|
+
kind: 'execution',
|
|
20
|
+
id: dependencyConsumerId(`websocket:${id}`),
|
|
21
|
+
name: id,
|
|
22
|
+
}, () => definition.factory()));
|
|
23
|
+
for (const route of routes) {
|
|
24
|
+
route.handshake?.middlewares?.forEach((middleware, index) => {
|
|
25
|
+
for (const dependency of collectInjectedDependencies({
|
|
26
|
+
kind: 'layer',
|
|
27
|
+
id: dependencyConsumerId(`websocket:${id}:${route.name}:${index}`),
|
|
28
|
+
name: middleware.name,
|
|
29
|
+
}, () => middleware.factory()))
|
|
30
|
+
dependencies.add(dependency);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
11
33
|
return {
|
|
12
34
|
kind: 'execution',
|
|
13
|
-
id
|
|
14
|
-
`${context.moduleId}.websocket.${context.definitionIndex}`,
|
|
35
|
+
id,
|
|
15
36
|
executionKind: 'websocket.session',
|
|
16
|
-
dependencies:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
dependencies: [...dependencies],
|
|
38
|
+
capabilities: [
|
|
39
|
+
...new Set([
|
|
40
|
+
WEBSOCKET_SERVER,
|
|
41
|
+
...routes.flatMap((route) => route.handshake?.middlewares?.flatMap((middleware) => middleware.capabilities) ?? []),
|
|
42
|
+
]),
|
|
43
|
+
],
|
|
23
44
|
compiled: Object.freeze({
|
|
24
|
-
routes: Object.freeze(
|
|
45
|
+
routes: Object.freeze(routes),
|
|
25
46
|
factory: definition.factory,
|
|
26
47
|
}),
|
|
27
48
|
};
|
|
@@ -50,6 +71,17 @@ export const websocketExtension = defineExecutionExtension({
|
|
|
50
71
|
routes: execution.compiled.routes.map((route) => ({
|
|
51
72
|
name: route.name,
|
|
52
73
|
path: route.path,
|
|
74
|
+
...(route.handshake?.middlewares?.length
|
|
75
|
+
? {
|
|
76
|
+
middlewares: route.handshake.middlewares.map((middleware) => ({
|
|
77
|
+
name: middleware.name,
|
|
78
|
+
capabilities: middleware.capabilities.map((capability) => capability.id),
|
|
79
|
+
...(getSourceLocation(middleware) === undefined
|
|
80
|
+
? {}
|
|
81
|
+
: { source: getSourceLocation(middleware) }),
|
|
82
|
+
})),
|
|
83
|
+
}
|
|
84
|
+
: {}),
|
|
53
85
|
...(route.messages === undefined
|
|
54
86
|
? {}
|
|
55
87
|
: {
|
|
@@ -69,7 +101,10 @@ export const websocketExtension = defineExecutionExtension({
|
|
|
69
101
|
},
|
|
70
102
|
});
|
|
71
103
|
export function defineWebSocketContract(routes) {
|
|
72
|
-
return Object.freeze({
|
|
104
|
+
return Object.freeze({
|
|
105
|
+
kind: 'websocket-contract',
|
|
106
|
+
routes,
|
|
107
|
+
});
|
|
73
108
|
}
|
|
74
109
|
export function defineWebSocketImplementation(definition) {
|
|
75
110
|
return defineExecution(websocketExtension, {
|
|
@@ -104,7 +139,7 @@ export const websocket = Object.freeze({
|
|
|
104
139
|
function compileRouteTree(tree, parentPath = '', prefix = '', routeNames = new Set()) {
|
|
105
140
|
return Object.entries(tree).flatMap(([name, node]) => {
|
|
106
141
|
if ('request' in node || 'responses' in node)
|
|
107
|
-
throw new TypeError('WebSocket v2
|
|
142
|
+
throw new TypeError('Use handshake for WebSocket v2 routes.');
|
|
108
143
|
if (isBranch(node)) {
|
|
109
144
|
const branchPath = node.path ?? '';
|
|
110
145
|
if (branchPath !== '')
|
|
@@ -115,14 +150,14 @@ function compileRouteTree(tree, parentPath = '', prefix = '', routeNames = new S
|
|
|
115
150
|
const path = joinPath(parentPath, node.path);
|
|
116
151
|
const segments = parseHttpPath(path);
|
|
117
152
|
assertExactPathParams(path, segments, node.handshake?.params);
|
|
118
|
-
const routeName = prefix ? `${prefix}.${name}` : name;
|
|
153
|
+
const routeName = webSocketProcedureName(prefix ? `${prefix}.${name}` : name);
|
|
119
154
|
if (routeNames.has(routeName))
|
|
120
155
|
throw new TypeError(`Duplicate nested WebSocket route name: ${routeName}`);
|
|
121
156
|
routeNames.add(routeName);
|
|
122
157
|
return [
|
|
123
158
|
Object.freeze({
|
|
124
159
|
name: routeName,
|
|
125
|
-
path,
|
|
160
|
+
path: path,
|
|
126
161
|
normalizedPath: normalizeHttpPath(segments),
|
|
127
162
|
segments,
|
|
128
163
|
...(node.handshake === undefined
|
|
@@ -150,6 +185,9 @@ function assertExactPathParams(path, segments, params) {
|
|
|
150
185
|
function snapshotHandshake(handshake) {
|
|
151
186
|
return Object.freeze({
|
|
152
187
|
...handshake,
|
|
188
|
+
...(handshake.middlewares === undefined
|
|
189
|
+
? {}
|
|
190
|
+
: { middlewares: Object.freeze([...handshake.middlewares]) }),
|
|
153
191
|
...(handshake.params === undefined
|
|
154
192
|
? {}
|
|
155
193
|
: { params: Object.freeze({ ...handshake.params }) }),
|
|
@@ -179,15 +217,16 @@ function createWebSocketRuntime(bindings, driver, applicationRuntime) {
|
|
|
179
217
|
handlers.set(execution.id, runInInjectionContext({
|
|
180
218
|
consumer: {
|
|
181
219
|
kind: 'execution',
|
|
182
|
-
id: `websocket:${execution.id}
|
|
220
|
+
id: dependencyConsumerId(`websocket:${execution.id}`),
|
|
183
221
|
name: execution.id,
|
|
184
222
|
},
|
|
185
223
|
resolve: (token, consumer) => resolve(token, { consumer }),
|
|
186
224
|
}, () => execution.compiled.factory()));
|
|
187
225
|
}
|
|
188
226
|
const routes = bindings
|
|
189
|
-
.flatMap(({ execution }) => execution.compiled.routes.map((route) => ({
|
|
227
|
+
.flatMap(({ execution, resolve }) => execution.compiled.routes.map((route) => ({
|
|
190
228
|
executionId: execution.id,
|
|
229
|
+
resolve,
|
|
191
230
|
route,
|
|
192
231
|
})))
|
|
193
232
|
.toSorted((left, right) => compareHttpPathSpecificity(left.route.segments, right.route.segments));
|
|
@@ -196,6 +235,12 @@ function createWebSocketRuntime(bindings, driver, applicationRuntime) {
|
|
|
196
235
|
if (state !== 'running') {
|
|
197
236
|
return Response.json({ error: 'Service Unavailable' }, { status: 503 });
|
|
198
237
|
}
|
|
238
|
+
if (request.method !== 'GET') {
|
|
239
|
+
return new Response('WebSocket handshake requires GET.', {
|
|
240
|
+
status: 405,
|
|
241
|
+
headers: { allow: 'GET' },
|
|
242
|
+
});
|
|
243
|
+
}
|
|
199
244
|
let match;
|
|
200
245
|
try {
|
|
201
246
|
match = findRoute(routes, request);
|
|
@@ -225,7 +270,7 @@ function createWebSocketRuntime(bindings, driver, applicationRuntime) {
|
|
|
225
270
|
.filter(Boolean);
|
|
226
271
|
const protocol = match.route.handshake?.protocols?.find((value) => offered.includes(value));
|
|
227
272
|
if (match.route.handshake?.protocols?.length && protocol === undefined) {
|
|
228
|
-
return new Response('WebSocket subprotocol
|
|
273
|
+
return new Response('WebSocket subprotocol mismatch.', {
|
|
229
274
|
status: 400,
|
|
230
275
|
});
|
|
231
276
|
}
|
|
@@ -247,16 +292,42 @@ function createWebSocketRuntime(bindings, driver, applicationRuntime) {
|
|
|
247
292
|
throw error;
|
|
248
293
|
}
|
|
249
294
|
let upgraded;
|
|
295
|
+
let handshakeState = {};
|
|
296
|
+
const signal = AbortSignal.any([request.signal, lease.signal]);
|
|
250
297
|
try {
|
|
251
|
-
|
|
298
|
+
const dispatch = () => composeLayers({
|
|
299
|
+
context: { request, input: { ...input, body: undefined }, signal },
|
|
300
|
+
layers: match.route.handshake?.middlewares ?? [],
|
|
301
|
+
resolve: match.resolve,
|
|
302
|
+
terminal: async ({ state: middlewareState }) => {
|
|
303
|
+
signal.throwIfAborted();
|
|
304
|
+
handshakeState = middlewareState;
|
|
305
|
+
upgraded = await driver.upgrade(request, protocol === undefined ? {} : { protocol });
|
|
306
|
+
return upgraded.response;
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
const response = await (lease.run ? lease.run(dispatch) : dispatch());
|
|
310
|
+
if (!(response instanceof Response))
|
|
311
|
+
throw new TypeError('WebSocket handshake middleware must return a Response.');
|
|
312
|
+
signal.throwIfAborted();
|
|
313
|
+
if (!upgraded) {
|
|
314
|
+
lease.complete();
|
|
315
|
+
completePendingIngress();
|
|
316
|
+
return response;
|
|
317
|
+
}
|
|
252
318
|
}
|
|
253
319
|
catch (error) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
320
|
+
try {
|
|
321
|
+
await upgraded?.connection.terminate();
|
|
322
|
+
}
|
|
323
|
+
finally {
|
|
324
|
+
lease.fail?.(error);
|
|
325
|
+
lease.complete();
|
|
326
|
+
completePendingIngress();
|
|
327
|
+
}
|
|
257
328
|
throw error;
|
|
258
329
|
}
|
|
259
|
-
const session = createSession(upgraded.connection, request, applicationRuntime, match.route, input, lease, handlers.get(match.executionId)?.[match.route.name], lease.run);
|
|
330
|
+
const session = createSession(upgraded.connection, request, applicationRuntime, match.route, input, handshakeState, lease, handlers.get(match.executionId)?.[match.route.name], lease.run);
|
|
260
331
|
sessions.add(session.active);
|
|
261
332
|
void session.completion.then(() => sessions.delete(session.active), () => sessions.delete(session.active));
|
|
262
333
|
completePendingIngress();
|
|
@@ -306,10 +377,11 @@ function createWebSocketRuntime(bindings, driver, applicationRuntime) {
|
|
|
306
377
|
function findRoute(routes, request) {
|
|
307
378
|
const url = new URL(request.url);
|
|
308
379
|
for (const candidate of routes) {
|
|
309
|
-
const params = matchHttpPath(candidate.route.segments, url.pathname);
|
|
380
|
+
const params = matchHttpPath(candidate.route.segments, requestPathname(url.pathname));
|
|
310
381
|
if (params) {
|
|
311
382
|
return {
|
|
312
383
|
executionId: candidate.executionId,
|
|
384
|
+
resolve: candidate.resolve,
|
|
313
385
|
route: candidate.route,
|
|
314
386
|
params,
|
|
315
387
|
};
|
|
@@ -347,10 +419,10 @@ function decodeQuery(searchParams) {
|
|
|
347
419
|
}
|
|
348
420
|
return query;
|
|
349
421
|
}
|
|
350
|
-
function createSession(connection, request, applicationRuntime, route, input, lease, handler, runInExecution) {
|
|
422
|
+
function createSession(connection, request, applicationRuntime, route, input, handshakeState, lease, handler, runInExecution) {
|
|
351
423
|
const session = Object.freeze({
|
|
352
|
-
id:
|
|
353
|
-
protocol: connection.protocol,
|
|
424
|
+
id: createWebSocketSessionId(),
|
|
425
|
+
protocol: negotiatedSubprotocol(connection.protocol),
|
|
354
426
|
});
|
|
355
427
|
lease.annotate?.({
|
|
356
428
|
'websocket.session.id': session.id,
|
|
@@ -408,6 +480,7 @@ function createSession(connection, request, applicationRuntime, route, input, le
|
|
|
408
480
|
const messages = decodeMessages(connection.messages, route.messages);
|
|
409
481
|
const context = {
|
|
410
482
|
request,
|
|
483
|
+
state: handshakeState,
|
|
411
484
|
session,
|
|
412
485
|
protocol: session.protocol,
|
|
413
486
|
execution: {
|
|
@@ -537,13 +610,13 @@ async function encodeMessage(codec, value) {
|
|
|
537
610
|
}
|
|
538
611
|
function normalizeCloseInfo(info) {
|
|
539
612
|
return {
|
|
540
|
-
code: Number.isInteger(info.code) ? info.code : 1006,
|
|
613
|
+
code: observedCloseCode(Number.isInteger(info.code) ? info.code : 1006),
|
|
541
614
|
reason: info.reason ?? '',
|
|
542
615
|
wasClean: info.wasClean === true,
|
|
543
616
|
};
|
|
544
617
|
}
|
|
545
618
|
function webSocketGracefulShutdownBudget(timeoutMs) {
|
|
546
619
|
if (timeoutMs <= 0)
|
|
547
|
-
return 0;
|
|
548
|
-
return Math.min(5_000, Math.max(0, Math.floor(timeoutMs * 0.8)));
|
|
620
|
+
return milliseconds(0);
|
|
621
|
+
return milliseconds(Math.min(5_000, Math.max(0, Math.floor(timeoutMs * 0.8))));
|
|
549
622
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Brand } from '../internal/brand.js';
|
|
2
|
+
export type WebSocketSessionId = Brand<string, 'websocket.session-id'>;
|
|
3
|
+
export type WebSocketProcedureName = Brand<string, 'websocket.procedure'>;
|
|
4
|
+
export type NegotiatedSubprotocol = Brand<string, 'websocket.negotiated-subprotocol'>;
|
|
5
|
+
export type ObservedCloseCode = Brand<number, 'websocket.observed-close-code'>;
|
|
6
|
+
export declare const createWebSocketSessionId: () => WebSocketSessionId;
|
|
7
|
+
export declare const webSocketProcedureName: (value: string) => WebSocketProcedureName;
|
|
8
|
+
export declare const negotiatedSubprotocol: (value: string) => NegotiatedSubprotocol;
|
|
9
|
+
export declare const observedCloseCode: (value: number) => ObservedCloseCode;
|
|
@@ -3,3 +3,4 @@ export { WebSocketConnectionNotOpenError, WebSocketMessageDecodeError, WebSocket
|
|
|
3
3
|
export type { WebSocketBranchDefinition, WebSocketHandshakeDefinition, WebSocketSession, WebSocketUpgradeOptions, WebSocketCloseInfo, WebSocketCodecKind, WebSocketConnectionDriver, WebSocketContract, WebSocketDataMessage, WebSocketExecutionDefinition, WebSocketExtensionRuntime, WebSocketHandlerContext, WebSocketHandlers, WebSocketHostApi, WebSocketImplementationDefinition, WebSocketIncomingMessage, WebSocketMessageCodec, WebSocketRouteDefinition, WebSocketRouteTree, WebSocketServerDriver, WebSocketUpgradeResult, } from './extension.js';
|
|
4
4
|
export { createEventWebSocketConnection, createWebSocketDriverChannel, } from './driver.js';
|
|
5
5
|
export type { EventWebSocket, WebSocketDriverChannel } from './driver.js';
|
|
6
|
+
export type { WebSocketSessionId, WebSocketProcedureName, NegotiatedSubprotocol, ObservedCloseCode, } from './identity.js';
|