@loutrejs/loutre 0.7.0 → 0.8.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.
Files changed (48) hide show
  1. package/dist/adapters/bun.d.ts +2 -4
  2. package/dist/adapters/bun.js +36 -28
  3. package/dist/adapters/cloudflare-workers.d.ts +2 -4
  4. package/dist/adapters/cloudflare-workers.js +6 -11
  5. package/dist/adapters/server-transports.d.ts +4 -0
  6. package/dist/adapters/server-transports.js +18 -0
  7. package/dist/adapters/websocket-native.d.ts +24 -0
  8. package/dist/adapters/websocket-native.js +150 -0
  9. package/dist/core/extension.d.ts +13 -1
  10. package/dist/core/extension.js +19 -0
  11. package/dist/core/index.d.ts +1 -1
  12. package/dist/core/model/modules.js +13 -17
  13. package/dist/core/model.js +14 -4
  14. package/dist/core/module.d.ts +3 -3
  15. package/dist/devtools/capsule.js +13 -11
  16. package/dist/devtools/values.js +10 -13
  17. package/dist/generated/version.d.ts +1 -1
  18. package/dist/generated/version.js +1 -1
  19. package/dist/graph/model.js +37 -40
  20. package/dist/http/auth.d.ts +2 -2
  21. package/dist/http/auth.js +9 -0
  22. package/dist/http/client.js +5 -5
  23. package/dist/http/extension.d.ts +37 -10
  24. package/dist/http/extension.js +117 -39
  25. package/dist/http/index.d.ts +3 -2
  26. package/dist/http/index.js +1 -1
  27. package/dist/http/openapi.d.ts +3 -4
  28. package/dist/http/openapi.js +45 -26
  29. package/dist/http/request-head.d.ts +6 -0
  30. package/dist/http/request-head.js +1 -0
  31. package/dist/http/route-validation.d.ts +9 -0
  32. package/dist/http/route-validation.js +18 -0
  33. package/dist/http/runtime/response.js +10 -10
  34. package/dist/runtime/di.js +40 -39
  35. package/dist/runtime/engine.d.ts +1 -1
  36. package/dist/runtime/engine.js +1 -2
  37. package/dist/runtime/node-capabilities.d.ts +1 -1
  38. package/dist/runtime/node-capabilities.js +2 -1
  39. package/dist/tasks/extension.js +103 -114
  40. package/dist/websocket/driver.d.ts +30 -0
  41. package/dist/websocket/driver.js +96 -0
  42. package/dist/websocket/extension.d.ts +33 -12
  43. package/dist/websocket/extension.js +60 -44
  44. package/dist/websocket/index.d.ts +3 -1
  45. package/dist/websocket/index.js +1 -0
  46. package/package.json +3 -6
  47. package/dist/adapters/deno.d.ts +0 -35
  48. package/dist/adapters/deno.js +0 -231
@@ -0,0 +1,96 @@
1
+ export function createWebSocketDriverChannel(options) {
2
+ const queue = [];
3
+ let done = false;
4
+ let wake;
5
+ let resolveClosed;
6
+ const closed = new Promise((resolve) => {
7
+ resolveClosed = resolve;
8
+ });
9
+ return {
10
+ connection: {
11
+ ...options,
12
+ closed,
13
+ messages: {
14
+ async *[Symbol.asyncIterator]() {
15
+ while (true) {
16
+ if (queue.length > 0) {
17
+ yield queue.shift();
18
+ continue;
19
+ }
20
+ if (done)
21
+ return;
22
+ await new Promise((resolve) => {
23
+ wake = resolve;
24
+ });
25
+ }
26
+ },
27
+ },
28
+ },
29
+ receive(message) {
30
+ if (done)
31
+ return;
32
+ queue.push(message);
33
+ wake?.();
34
+ wake = undefined;
35
+ },
36
+ finish(info) {
37
+ if (done)
38
+ return;
39
+ done = true;
40
+ queue.length = 0;
41
+ resolveClosed(info);
42
+ wake?.();
43
+ wake = undefined;
44
+ },
45
+ };
46
+ }
47
+ export function createEventWebSocketConnection(socket, protocol, options = {}) {
48
+ const run = (operation) => options.run ? options.run(operation) : Promise.resolve().then(operation);
49
+ const opened = socket.readyState === 1
50
+ ? Promise.resolve()
51
+ : new Promise((resolve) => socket.addEventListener('open', resolve, { once: true }));
52
+ socket.binaryType = 'arraybuffer';
53
+ const channel = createWebSocketDriverChannel({
54
+ protocol,
55
+ async send(message) {
56
+ await Promise.race([
57
+ opened,
58
+ channel.connection.closed.then(() => {
59
+ throw new Error('WebSocketは終了しました。');
60
+ }),
61
+ ]);
62
+ await run(() => {
63
+ if (socket.readyState !== 1)
64
+ throw new Error('WebSocketはopenではありません。');
65
+ socket.send(message.data);
66
+ });
67
+ },
68
+ async close(code = 1000, reason = '') {
69
+ await run(() => socket.close(code, reason));
70
+ },
71
+ async terminate() {
72
+ try {
73
+ await run(() => options.terminate
74
+ ? options.terminate()
75
+ : socket.close(1001, 'Going Away'));
76
+ }
77
+ finally {
78
+ channel.finish({ code: 1006, reason: '', wasClean: false });
79
+ }
80
+ },
81
+ });
82
+ socket.addEventListener('message', ({ data }) => {
83
+ if (typeof data === 'string')
84
+ channel.receive({ type: 'text', data });
85
+ else if (data instanceof ArrayBuffer)
86
+ channel.receive({ type: 'binary', data: new Uint8Array(data) });
87
+ else if (ArrayBuffer.isView(data))
88
+ channel.receive({
89
+ type: 'binary',
90
+ data: new Uint8Array(data.buffer, data.byteOffset, data.byteLength),
91
+ });
92
+ });
93
+ socket.addEventListener('close', (info) => channel.finish(info));
94
+ socket.addEventListener('error', () => channel.finish({ code: 1006, reason: '', wasClean: false }));
95
+ return channel.connection;
96
+ }
@@ -1,5 +1,5 @@
1
- import { type ExecutionDefinition, type ExecutionExtensionDrainContext, type RuntimeCapabilityBinding, type SchemaOutput, type StandardSchemaV1 } from '../core/index.js';
2
- import type { HttpExecutionRequestDefinition, HttpExecutionResponseDefinition } from '../http/index.js';
1
+ import { type ExecutionDefinition, type ExecutionContextView, type ExecutionExtensionDrainContext, type RuntimeCapabilityBinding, type SchemaOutput, type StandardSchemaV1 } from '../core/index.js';
2
+ import type { HttpRequestHeadDefinition } from '../http/request-head.js';
3
3
  import { type HttpPathSegment } from '../http/index.js';
4
4
  export type WebSocketDataMessage = {
5
5
  readonly type: 'text';
@@ -14,6 +14,7 @@ export interface WebSocketCloseInfo {
14
14
  readonly wasClean: boolean;
15
15
  }
16
16
  export interface WebSocketConnectionDriver {
17
+ readonly protocol: string;
17
18
  readonly messages: AsyncIterable<WebSocketDataMessage>;
18
19
  readonly closed: Promise<WebSocketCloseInfo>;
19
20
  send(message: WebSocketDataMessage): Promise<void>;
@@ -24,9 +25,16 @@ export interface WebSocketUpgradeResult {
24
25
  readonly response: Response;
25
26
  readonly connection: WebSocketConnectionDriver;
26
27
  }
28
+ export interface WebSocketUpgradeOptions {
29
+ readonly protocol?: string;
30
+ }
31
+ export interface WebSocketSession {
32
+ readonly id: string;
33
+ readonly protocol: string;
34
+ }
27
35
  export interface WebSocketServerDriver {
28
36
  readonly runtime: string;
29
- upgrade(request: Request): Promise<WebSocketUpgradeResult>;
37
+ upgrade(request: Request, options: WebSocketUpgradeOptions): Promise<WebSocketUpgradeResult>;
30
38
  }
31
39
  export declare const WEBSOCKET_SERVER: import("../core/extension.js").RuntimeCapability<WebSocketServerDriver>;
32
40
  export type WebSocketCodecKind = 'json' | 'text' | 'binary';
@@ -45,13 +53,14 @@ export type WebSocketIncomingMessage<TValue> = {
45
53
  };
46
54
  export interface WebSocketBranchDefinition {
47
55
  readonly path?: string;
48
- readonly responses?: Readonly<Record<string, HttpExecutionResponseDefinition>>;
49
56
  readonly routes: WebSocketRouteTree;
50
57
  }
58
+ export interface WebSocketHandshakeDefinition extends HttpRequestHeadDefinition {
59
+ readonly protocols?: readonly string[];
60
+ }
51
61
  export interface WebSocketRouteDefinition {
52
62
  readonly path: string;
53
- readonly request?: Omit<HttpExecutionRequestDefinition, 'body'>;
54
- readonly responses?: Readonly<Record<string, HttpExecutionResponseDefinition>>;
63
+ readonly handshake?: WebSocketHandshakeDefinition;
55
64
  readonly messages?: WebSocketMessageCodec;
56
65
  }
57
66
  export type WebSocketRouteTree = Readonly<Record<string, WebSocketRouteDefinition | WebSocketBranchDefinition>>;
@@ -67,24 +76,28 @@ type SendApi<TRoute extends WebSocketRouteDefinition> = TRoute['messages'] exten
67
76
  } : {} : {};
68
77
  type OpeningRequestInput<TRoute extends WebSocketRouteDefinition> = {
69
78
  readonly params: TRoute extends {
70
- readonly request: {
79
+ readonly handshake: {
71
80
  readonly params: infer TParams extends Readonly<Record<string, StandardSchemaV1>>;
72
81
  };
73
82
  } ? {
74
83
  readonly [TName in keyof TParams]: SchemaOutput<TParams[TName]>;
75
84
  } : Readonly<Record<string, string>>;
76
85
  readonly query: TRoute extends {
77
- readonly request: {
86
+ readonly handshake: {
78
87
  readonly query: infer TQuery extends StandardSchemaV1;
79
88
  };
80
89
  } ? SchemaOutput<TQuery> : Readonly<Record<string, string | string[]>>;
81
90
  readonly headers: TRoute extends {
82
- readonly request: {
91
+ readonly handshake: {
83
92
  readonly headers: infer THeaders extends StandardSchemaV1;
84
93
  };
85
94
  } ? SchemaOutput<THeaders> : Headers;
86
95
  };
87
96
  export type WebSocketHandlerContext<TRoute extends WebSocketRouteDefinition = WebSocketRouteDefinition> = {
97
+ readonly request: Request;
98
+ readonly session: WebSocketSession;
99
+ readonly protocol: string;
100
+ readonly execution: ExecutionContextView;
88
101
  readonly input: OpeningRequestInput<TRoute> & IncomingApi<TRoute>;
89
102
  readonly signal: AbortSignal;
90
103
  readonly closed: Promise<WebSocketCloseInfo>;
@@ -107,8 +120,7 @@ interface CompiledWebSocketRoute {
107
120
  readonly path: string;
108
121
  readonly normalizedPath: string;
109
122
  readonly segments: readonly HttpPathSegment[];
110
- readonly request?: Omit<HttpExecutionRequestDefinition, 'body'>;
111
- readonly responses: Readonly<Record<string, HttpExecutionResponseDefinition>>;
123
+ readonly handshake?: WebSocketHandshakeDefinition;
112
124
  readonly messages?: WebSocketMessageCodec;
113
125
  }
114
126
  interface CompiledWebSocketExecution {
@@ -125,7 +137,16 @@ export interface WebSocketHostApi {
125
137
  }
126
138
  export declare const websocketExtension: import("../core/extension.js").ExecutionExtension<WebSocketImplementationDefinition<WebSocketContract<Readonly<Record<string, WebSocketBranchDefinition | WebSocketRouteDefinition>>>> & ExecutionDefinition<import("../core/extension.js").AnyExecutionExtension>, CompiledWebSocketExecution, "websocket", WebSocketHostApi, WebSocketExtensionRuntime>;
127
139
  export type WebSocketExecutionDefinition<TContract extends WebSocketContract = WebSocketContract> = WebSocketImplementationDefinition<TContract> & ExecutionDefinition<typeof websocketExtension>;
128
- export declare function defineWebSocketContract<const TRoutes extends WebSocketRouteTree>(routes: TRoutes): WebSocketContract<TRoutes>;
140
+ type WebSocketV2Tree<TTree extends WebSocketRouteTree> = {
141
+ readonly [K in keyof TTree]: TTree[K] extends WebSocketBranchDefinition ? TTree[K] & {
142
+ readonly responses?: never;
143
+ readonly routes: WebSocketV2Tree<TTree[K]['routes']>;
144
+ } : TTree[K] & {
145
+ readonly request?: never;
146
+ readonly responses?: never;
147
+ };
148
+ };
149
+ export declare function defineWebSocketContract<const TRoutes extends WebSocketRouteTree>(routes: TRoutes & WebSocketV2Tree<TRoutes>): WebSocketContract<TRoutes>;
129
150
  export declare function defineWebSocketImplementation<const TContract extends WebSocketContract>(definition: {
130
151
  readonly name?: string;
131
152
  readonly contract: TContract;
@@ -5,7 +5,7 @@ import { compareHttpPathSpecificity, HttpPathDecodeError, matchHttpPath, normali
5
5
  export const WEBSOCKET_SERVER = runtimeCapability('websocket.server');
6
6
  export const websocketExtension = defineExecutionExtension({
7
7
  kind: 'execution-extension',
8
- abiVersion: '1',
8
+ abiVersion: '2',
9
9
  name: 'loutre:websocket',
10
10
  compile(definition, context) {
11
11
  return {
@@ -101,24 +101,23 @@ export const websocket = Object.freeze({
101
101
  serverCapability: WEBSOCKET_SERVER,
102
102
  bindServer: bindWebSocketServer,
103
103
  });
104
- function compileRouteTree(tree, parentPath = '', parentResponses = {}, prefix = '', routeNames = new Set()) {
104
+ function compileRouteTree(tree, parentPath = '', prefix = '', routeNames = new Set()) {
105
105
  return Object.entries(tree).flatMap(([name, node]) => {
106
+ if ('request' in node || 'responses' in node)
107
+ throw new TypeError('WebSocket v2ではhandshakeを使用してください。');
106
108
  if (isBranch(node)) {
107
109
  const branchPath = node.path ?? '';
108
110
  if (branchPath !== '')
109
111
  parseHttpPath(branchPath);
110
- assertNoInheritedResponseCollision(name, parentResponses, node.responses ?? {});
111
- return compileRouteTree(node.routes, joinPath(parentPath, branchPath), { ...parentResponses, ...node.responses }, prefix ? `${prefix}.${name}` : name, routeNames);
112
+ return compileRouteTree(node.routes, joinPath(parentPath, branchPath), prefix ? `${prefix}.${name}` : name, routeNames);
112
113
  }
113
114
  parseHttpPath(node.path);
114
115
  const path = joinPath(parentPath, node.path);
115
116
  const segments = parseHttpPath(path);
116
- assertExactPathParams(path, segments, node.request?.params);
117
- assertNoInheritedResponseCollision(name, parentResponses, node.responses ?? {});
117
+ assertExactPathParams(path, segments, node.handshake?.params);
118
118
  const routeName = prefix ? `${prefix}.${name}` : name;
119
- if (routeNames.has(routeName)) {
119
+ if (routeNames.has(routeName))
120
120
  throw new TypeError(`Duplicate nested WebSocket route name: ${routeName}`);
121
- }
122
121
  routeNames.add(routeName);
123
122
  return [
124
123
  Object.freeze({
@@ -126,13 +125,9 @@ function compileRouteTree(tree, parentPath = '', parentResponses = {}, prefix =
126
125
  path,
127
126
  normalizedPath: normalizeHttpPath(segments),
128
127
  segments,
129
- ...(node.request === undefined
128
+ ...(node.handshake === undefined
130
129
  ? {}
131
- : { request: snapshotWebSocketRequest(node.request) }),
132
- responses: snapshotWebSocketResponses({
133
- ...parentResponses,
134
- ...node.responses,
135
- }),
130
+ : { handshake: snapshotHandshake(node.handshake) }),
136
131
  ...(node.messages === undefined
137
132
  ? {}
138
133
  : { messages: Object.freeze({ ...node.messages }) }),
@@ -152,27 +147,17 @@ function assertExactPathParams(path, segments, params) {
152
147
  throw new TypeError(`WebSocket ${path} request params must exactly match path parameters.`);
153
148
  }
154
149
  }
155
- function assertNoInheritedResponseCollision(nodeName, inherited, declared) {
156
- for (const name of Object.keys(declared)) {
157
- if (name in inherited) {
158
- throw new TypeError(`Duplicate inherited WebSocket response ${name} at ${nodeName}.`);
159
- }
160
- }
161
- }
162
- function snapshotWebSocketRequest(request) {
150
+ function snapshotHandshake(handshake) {
163
151
  return Object.freeze({
164
- ...request,
165
- ...(request.params === undefined
152
+ ...handshake,
153
+ ...(handshake.params === undefined
154
+ ? {}
155
+ : { params: Object.freeze({ ...handshake.params }) }),
156
+ ...(handshake.protocols === undefined
166
157
  ? {}
167
- : { params: Object.freeze({ ...request.params }) }),
158
+ : { protocols: Object.freeze([...handshake.protocols]) }),
168
159
  });
169
160
  }
170
- function snapshotWebSocketResponses(responses) {
171
- return Object.freeze(Object.fromEntries(Object.entries(responses).map(([name, response]) => [
172
- name,
173
- Object.freeze({ ...response }),
174
- ])));
175
- }
176
161
  function isBranch(node) {
177
162
  return 'routes' in node;
178
163
  }
@@ -234,6 +219,16 @@ function createWebSocketRuntime(executions, driver, applicationRuntime) {
234
219
  if (state !== 'running') {
235
220
  return Response.json({ error: 'Service Unavailable' }, { status: 503 });
236
221
  }
222
+ const offered = (request.headers.get('sec-websocket-protocol') ?? '')
223
+ .split(',')
224
+ .map((value) => value.trim())
225
+ .filter(Boolean);
226
+ const protocol = match.route.handshake?.protocols?.find((value) => offered.includes(value));
227
+ if (match.route.handshake?.protocols?.length && protocol === undefined) {
228
+ return new Response('WebSocket subprotocolが一致しません。', {
229
+ status: 400,
230
+ });
231
+ }
237
232
  const completePendingIngress = ingress.enter();
238
233
  if (!completePendingIngress) {
239
234
  return Response.json({ error: 'Service Unavailable' }, { status: 503 });
@@ -253,7 +248,7 @@ function createWebSocketRuntime(executions, driver, applicationRuntime) {
253
248
  }
254
249
  let upgraded;
255
250
  try {
256
- upgraded = await driver.upgrade(request);
251
+ upgraded = await driver.upgrade(request, protocol === undefined ? {} : { protocol });
257
252
  }
258
253
  catch (error) {
259
254
  lease.fail?.(error);
@@ -261,7 +256,7 @@ function createWebSocketRuntime(executions, driver, applicationRuntime) {
261
256
  completePendingIngress();
262
257
  throw error;
263
258
  }
264
- const session = createSession(upgraded.connection, match.route, input, lease, handlers.get(match.executionId)?.[match.route.name], lease.run);
259
+ const session = createSession(upgraded.connection, request, applicationRuntime, match.route, input, lease, handlers.get(match.executionId)?.[match.route.name], lease.run);
265
260
  sessions.add(session.active);
266
261
  void session.completion.then(() => sessions.delete(session.active), () => sessions.delete(session.active));
267
262
  completePendingIngress();
@@ -281,14 +276,19 @@ function createWebSocketRuntime(executions, driver, applicationRuntime) {
281
276
  const results = await Promise.allSettled([...sessions].map(async (session) => {
282
277
  const graceful = session
283
278
  .close(1001, 'Going Away')
279
+ .then(() => session.completion)
284
280
  .then(() => true)
285
281
  .catch(() => false);
282
+ let timer;
286
283
  const completed = gracefulTimeoutMs === 0
287
284
  ? false
288
285
  : await Promise.race([
289
286
  graceful,
290
- delay(gracefulTimeoutMs).then(() => false),
287
+ new Promise((resolve) => {
288
+ timer = setTimeout(() => resolve(false), gracefulTimeoutMs);
289
+ }),
291
290
  ]);
291
+ clearTimeout(timer);
292
292
  if (!completed)
293
293
  await session.terminate();
294
294
  await session.completion.catch(() => undefined);
@@ -318,19 +318,19 @@ function findRoute(routes, request) {
318
318
  return undefined;
319
319
  }
320
320
  async function validateOpeningRequest(request, rawParams, route) {
321
- const params = route.request?.params
322
- ? Object.fromEntries(await Promise.all(Object.entries(route.request.params).map(async ([name, schema]) => [
321
+ const params = route.handshake?.params
322
+ ? Object.fromEntries(await Promise.all(Object.entries(route.handshake.params).map(async ([name, schema]) => [
323
323
  name,
324
324
  await validateSchema(schema, rawParams[name]),
325
325
  ])))
326
326
  : rawParams;
327
327
  const url = new URL(request.url);
328
328
  const rawQuery = decodeQuery(url.searchParams);
329
- const query = route.request?.query
330
- ? await validateSchema(route.request.query, rawQuery)
329
+ const query = route.handshake?.query
330
+ ? await validateSchema(route.handshake.query, rawQuery)
331
331
  : rawQuery;
332
- const headers = route.request?.headers
333
- ? await validateSchema(route.request.headers, Object.fromEntries(request.headers))
332
+ const headers = route.handshake?.headers
333
+ ? await validateSchema(route.handshake.headers, Object.fromEntries(request.headers))
334
334
  : request.headers;
335
335
  return { params, query, headers };
336
336
  }
@@ -347,7 +347,15 @@ function decodeQuery(searchParams) {
347
347
  }
348
348
  return query;
349
349
  }
350
- function createSession(connection, route, input, lease, handler, runInExecution) {
350
+ function createSession(connection, request, applicationRuntime, route, input, lease, handler, runInExecution) {
351
+ const session = Object.freeze({
352
+ id: `ws_${crypto.randomUUID()}`,
353
+ protocol: connection.protocol,
354
+ });
355
+ lease.annotate?.({
356
+ 'websocket.session.id': session.id,
357
+ 'websocket.protocol': session.protocol,
358
+ });
351
359
  let state = 'open';
352
360
  let transportFailed = false;
353
361
  let sendTail = Promise.resolve();
@@ -399,6 +407,17 @@ function createSession(connection, route, input, lease, handler, runInExecution)
399
407
  };
400
408
  const messages = decodeMessages(connection.messages, route.messages);
401
409
  const context = {
410
+ request,
411
+ session,
412
+ protocol: session.protocol,
413
+ execution: {
414
+ signal: lease.signal,
415
+ annotate: (attributes) => lease.annotate?.(attributes),
416
+ beginOperation: (metadata) => {
417
+ const begin = () => applicationRuntime.beginOperation?.(metadata) ?? { complete() { } };
418
+ return lease.run ? lease.run(begin) : begin();
419
+ },
420
+ },
402
421
  input: {
403
422
  ...input,
404
423
  ...(route.messages?.input === undefined ? {} : { messages }),
@@ -528,6 +547,3 @@ function webSocketGracefulShutdownBudget(timeoutMs) {
528
547
  return 0;
529
548
  return Math.min(5_000, Math.max(0, Math.floor(timeoutMs * 0.8)));
530
549
  }
531
- function delay(milliseconds) {
532
- return new Promise((resolve) => setTimeout(resolve, milliseconds));
533
- }
@@ -1,3 +1,5 @@
1
1
  export { bindWebSocketServer, defineWebSocketContract, defineWebSocketImplementation, WEBSOCKET_SERVER, websocket, websocketExtension, } from './extension.js';
2
2
  export { WebSocketConnectionNotOpenError, WebSocketMessageDecodeError, WebSocketMessageEncodeError, } from './errors.js';
3
- export type { WebSocketBranchDefinition, WebSocketCloseInfo, WebSocketCodecKind, WebSocketConnectionDriver, WebSocketContract, WebSocketDataMessage, WebSocketExecutionDefinition, WebSocketExtensionRuntime, WebSocketHandlerContext, WebSocketHandlers, WebSocketHostApi, WebSocketImplementationDefinition, WebSocketIncomingMessage, WebSocketMessageCodec, WebSocketRouteDefinition, WebSocketRouteTree, WebSocketServerDriver, WebSocketUpgradeResult, } from './extension.js';
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
+ export { createEventWebSocketConnection, createWebSocketDriverChannel, } from './driver.js';
5
+ export type { EventWebSocket, WebSocketDriverChannel } from './driver.js';
@@ -1,2 +1,3 @@
1
1
  export { bindWebSocketServer, defineWebSocketContract, defineWebSocketImplementation, WEBSOCKET_SERVER, websocket, websocketExtension, } from './extension.js';
2
2
  export { WebSocketConnectionNotOpenError, WebSocketMessageDecodeError, WebSocketMessageEncodeError, } from './errors.js';
3
+ export { createEventWebSocketConnection, createWebSocketDriverChannel, } from './driver.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loutrejs/loutre",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "files": [
@@ -48,10 +48,6 @@
48
48
  "types": "./dist/adapters/bun.d.ts",
49
49
  "import": "./dist/adapters/bun.js"
50
50
  },
51
- "./runtime/deno": {
52
- "types": "./dist/adapters/deno.d.ts",
53
- "import": "./dist/adapters/deno.js"
54
- },
55
51
  "./runtime/cloudflare-workers": {
56
52
  "types": "./dist/adapters/cloudflare-workers.d.ts",
57
53
  "import": "./dist/adapters/cloudflare-workers.js"
@@ -75,7 +71,8 @@
75
71
  "provenance": true
76
72
  },
77
73
  "dependencies": {
78
- "@standard-schema/spec": "^1.1.0"
74
+ "@standard-schema/spec": "^1.1.0",
75
+ "ts-pattern": "^5.9.0"
79
76
  },
80
77
  "repository": {
81
78
  "type": "git",
@@ -1,35 +0,0 @@
1
- import { type ApplicationDefinition, type BootstrapArguments, type KernelHostedApplication, type RequireApplicationExtension } from '../application/index.js';
2
- import type { RuntimeCapabilityBinding } from '../core/index.js';
3
- import { httpExecutionExtension } from '../http/index.js';
4
- type HttpApplication<TDefinition extends ApplicationDefinition> = RequireApplicationExtension<TDefinition, typeof httpExecutionExtension>;
5
- export type DenoRuntimeOptions<TDefinition extends ApplicationDefinition> = {
6
- readonly application: HttpApplication<TDefinition>;
7
- readonly environment?: unknown;
8
- readonly capabilities?: readonly RuntimeCapabilityBinding[];
9
- readonly forceShutdownTimeoutMs?: number;
10
- } & BootstrapArguments<TDefinition>;
11
- export type DenoCreateOptions<TDefinition extends ApplicationDefinition> = DenoRuntimeOptions<TDefinition>;
12
- export interface DenoServeOptions {
13
- readonly port?: number;
14
- readonly hostname?: string;
15
- readonly shutdownHooks?: boolean;
16
- }
17
- export interface DenoBinding {
18
- fetch(request: Request): Promise<Response>;
19
- close(signal?: string): Promise<void>;
20
- }
21
- export interface DenoListenerHandle {
22
- readonly port: number;
23
- }
24
- export type DenoRuntimeApplication<TDefinition extends ApplicationDefinition = ApplicationDefinition> = KernelHostedApplication<TDefinition> & {
25
- serve(options?: DenoServeOptions): Promise<DenoListenerHandle>;
26
- };
27
- export declare const denoRuntime: {
28
- readonly runtime: 'deno';
29
- readonly capabilities: Set<string>;
30
- readonly bind: typeof bind;
31
- readonly create: typeof create;
32
- };
33
- declare function bind<const TDefinition extends ApplicationDefinition>(options: DenoRuntimeOptions<TDefinition>): DenoBinding;
34
- declare function create<const TDefinition extends ApplicationDefinition>(options: DenoCreateOptions<TDefinition>): Promise<DenoRuntimeApplication<TDefinition>>;
35
- export {};