@loutrejs/loutre 0.6.0 → 0.8.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.
Files changed (71) 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/application/kernel.d.ts +2 -1
  10. package/dist/core/extension.d.ts +26 -3
  11. package/dist/core/extension.js +19 -0
  12. package/dist/core/index.d.ts +2 -1
  13. package/dist/core/instrumentation.d.ts +36 -0
  14. package/dist/core/instrumentation.js +1 -0
  15. package/dist/core/model/modules.js +13 -17
  16. package/dist/core/model.js +14 -4
  17. package/dist/core/module.d.ts +3 -3
  18. package/dist/devtools/capsule.d.ts +51 -0
  19. package/dist/devtools/capsule.js +247 -0
  20. package/dist/devtools/index.d.ts +11 -0
  21. package/dist/devtools/index.js +6 -0
  22. package/dist/devtools/inspector.d.ts +57 -0
  23. package/dist/devtools/inspector.js +1 -0
  24. package/dist/devtools/module.d.ts +18 -0
  25. package/dist/devtools/module.js +26 -0
  26. package/dist/devtools/observer.d.ts +77 -0
  27. package/dist/devtools/observer.js +10 -0
  28. package/dist/devtools/protocol.d.ts +1 -0
  29. package/dist/devtools/protocol.js +1 -0
  30. package/dist/devtools/provider-instrumentation.d.ts +14 -0
  31. package/dist/devtools/provider-instrumentation.js +117 -0
  32. package/dist/devtools/runtime.d.ts +20 -0
  33. package/dist/devtools/runtime.js +519 -0
  34. package/dist/devtools/values.d.ts +35 -0
  35. package/dist/devtools/values.js +172 -0
  36. package/dist/generated/version.d.ts +1 -1
  37. package/dist/generated/version.js +1 -1
  38. package/dist/graph/model.js +37 -40
  39. package/dist/http/auth.d.ts +2 -2
  40. package/dist/http/auth.js +9 -0
  41. package/dist/http/client.js +5 -5
  42. package/dist/http/extension.d.ts +37 -10
  43. package/dist/http/extension.js +249 -57
  44. package/dist/http/index.d.ts +3 -2
  45. package/dist/http/index.js +1 -1
  46. package/dist/http/openapi.d.ts +3 -4
  47. package/dist/http/openapi.js +45 -26
  48. package/dist/http/request-head.d.ts +6 -0
  49. package/dist/http/request-head.js +1 -0
  50. package/dist/http/route-validation.d.ts +9 -0
  51. package/dist/http/route-validation.js +18 -0
  52. package/dist/http/runtime/response.js +10 -10
  53. package/dist/message-port/extension.js +20 -4
  54. package/dist/runtime/di.d.ts +4 -0
  55. package/dist/runtime/di.js +42 -31
  56. package/dist/runtime/engine.d.ts +1 -1
  57. package/dist/runtime/engine.js +1 -2
  58. package/dist/runtime/kernel.d.ts +4 -2
  59. package/dist/runtime/kernel.js +96 -3
  60. package/dist/runtime/node-capabilities.d.ts +1 -1
  61. package/dist/runtime/node-capabilities.js +2 -1
  62. package/dist/tasks/extension.js +133 -118
  63. package/dist/websocket/driver.d.ts +30 -0
  64. package/dist/websocket/driver.js +96 -0
  65. package/dist/websocket/extension.d.ts +33 -12
  66. package/dist/websocket/extension.js +72 -47
  67. package/dist/websocket/index.d.ts +3 -1
  68. package/dist/websocket/index.js +1 -0
  69. package/package.json +7 -6
  70. package/dist/adapters/deno.d.ts +0 -35
  71. package/dist/adapters/deno.js +0 -231
@@ -1,5 +1,6 @@
1
1
  import { collectInjectedDependencies, defineExecution, defineExecutionExtension, provide, runInInjectionContext, token, validateSchema, } from '../core/index.js';
2
2
  import { isValidCronExpression, matchesCronTrigger } from './runtime/cron.js';
3
+ import { match } from 'ts-pattern';
3
4
  const taskExecutionIdentityKey = Symbol.for('loutre.tasks.task-execution-identity');
4
5
  const queueDriverToken = Symbol.for('loutre.tasks.queue-driver');
5
6
  export const tasksExtension = defineExecutionExtension({
@@ -7,86 +8,78 @@ export const tasksExtension = defineExecutionExtension({
7
8
  abiVersion: '1',
8
9
  name: 'loutre:tasks',
9
10
  compile(definition) {
10
- switch (definition.type) {
11
- case 'task':
12
- return {
13
- kind: 'execution',
14
- id: `task.${definition.name}`,
15
- executionKind: 'task.invocation',
16
- dependencies: collectInjectedDependencies({
17
- kind: 'execution',
18
- id: `task:${definition.name}`,
19
- name: definition.name,
20
- }, () => definition.factory()),
21
- capabilities: [],
22
- compiled: Object.freeze({
23
- type: 'task',
24
- taskExecutionId: definition[taskExecutionIdentityKey],
25
- name: definition.name,
26
- factory: definition.factory,
27
- }),
28
- };
29
- case 'cron':
30
- return {
31
- kind: 'execution',
32
- id: `trigger.${definition.name}`,
33
- executionKind: 'trigger.cron',
34
- dependencies: [],
35
- capabilities: [],
36
- compiled: Object.freeze({
37
- type: 'cron',
38
- name: definition.name,
39
- expression: definition.expression,
40
- timezone: definition.timezone,
41
- overlap: definition.overlap,
42
- taskExecutionId: definition.task[taskExecutionIdentityKey],
43
- taskName: definition.task.name,
44
- }),
45
- };
46
- case 'fixed-delay':
47
- return {
48
- kind: 'execution',
49
- id: `trigger.${definition.name}`,
50
- executionKind: 'trigger.fixed-delay',
51
- dependencies: [],
52
- capabilities: [],
53
- compiled: Object.freeze({
54
- type: 'fixed-delay',
55
- name: definition.name,
56
- delay: definition.delay,
57
- immediate: definition.immediate,
58
- taskExecutionId: definition.task[taskExecutionIdentityKey],
59
- taskName: definition.task.name,
60
- }),
61
- };
62
- case 'queue-consumer':
63
- return {
64
- kind: 'execution',
65
- id: `trigger.${definition.name}`,
66
- executionKind: 'trigger.queue-consumer',
67
- dependencies: [queueRuntimeToken(definition.queue)],
68
- capabilities: [],
69
- compiled: Object.freeze({
70
- type: 'queue-consumer',
71
- name: definition.name,
72
- queueName: definition.queue.name,
73
- queuePayload: definition.queue.payload,
74
- queueDriver: queueRuntimeToken(definition.queue),
75
- taskExecutionId: definition.task[taskExecutionIdentityKey],
76
- taskName: definition.task.name,
77
- }),
78
- };
79
- }
11
+ return match(definition)
12
+ .with({ type: 'task' }, (candidate) => ({
13
+ kind: 'execution',
14
+ id: `task.${candidate.name}`,
15
+ executionKind: 'task.invocation',
16
+ dependencies: collectInjectedDependencies({
17
+ kind: 'execution',
18
+ id: `task:${candidate.name}`,
19
+ name: candidate.name,
20
+ }, () => candidate.factory()),
21
+ capabilities: [],
22
+ compiled: Object.freeze({
23
+ type: 'task',
24
+ taskExecutionId: candidate[taskExecutionIdentityKey],
25
+ name: candidate.name,
26
+ factory: candidate.factory,
27
+ }),
28
+ }))
29
+ .with({ type: 'cron' }, (candidate) => ({
30
+ kind: 'execution',
31
+ id: `trigger.${candidate.name}`,
32
+ executionKind: 'trigger.cron',
33
+ dependencies: [],
34
+ capabilities: [],
35
+ compiled: Object.freeze({
36
+ type: 'cron',
37
+ name: candidate.name,
38
+ expression: candidate.expression,
39
+ timezone: candidate.timezone,
40
+ overlap: candidate.overlap,
41
+ taskExecutionId: candidate.task[taskExecutionIdentityKey],
42
+ taskName: candidate.task.name,
43
+ }),
44
+ }))
45
+ .with({ type: 'fixed-delay' }, (candidate) => ({
46
+ kind: 'execution',
47
+ id: `trigger.${candidate.name}`,
48
+ executionKind: 'trigger.fixed-delay',
49
+ dependencies: [],
50
+ capabilities: [],
51
+ compiled: Object.freeze({
52
+ type: 'fixed-delay',
53
+ name: candidate.name,
54
+ delay: candidate.delay,
55
+ immediate: candidate.immediate,
56
+ taskExecutionId: candidate.task[taskExecutionIdentityKey],
57
+ taskName: candidate.task.name,
58
+ }),
59
+ }))
60
+ .with({ type: 'queue-consumer' }, (candidate) => ({
61
+ kind: 'execution',
62
+ id: `trigger.${candidate.name}`,
63
+ executionKind: 'trigger.queue-consumer',
64
+ dependencies: [queueRuntimeToken(candidate.queue)],
65
+ capabilities: [],
66
+ compiled: Object.freeze({
67
+ type: 'queue-consumer',
68
+ name: candidate.name,
69
+ queueName: candidate.queue.name,
70
+ queuePayload: candidate.queue.payload,
71
+ queueDriver: queueRuntimeToken(candidate.queue),
72
+ taskExecutionId: candidate.task[taskExecutionIdentityKey],
73
+ taskName: candidate.task.name,
74
+ }),
75
+ }))
76
+ .exhaustive();
80
77
  },
81
78
  references(definition) {
82
- switch (definition.type) {
83
- case 'task':
84
- return [];
85
- case 'cron':
86
- case 'fixed-delay':
87
- case 'queue-consumer':
88
- return [definition.task];
89
- }
79
+ return match(definition)
80
+ .with({ type: 'task' }, () => [])
81
+ .with({ type: 'cron' }, { type: 'fixed-delay' }, { type: 'queue-consumer' }, (candidate) => [candidate.task])
82
+ .exhaustive();
90
83
  },
91
84
  validate({ executions }) {
92
85
  const taskExecutionIds = new Set(executions.flatMap((execution) => execution.compiled.type === 'task'
@@ -133,34 +126,33 @@ export const tasksExtension = defineExecutionExtension({
133
126
  },
134
127
  projectGraph: ({ execution }) => {
135
128
  const compiled = execution.compiled;
136
- switch (compiled.type) {
137
- case 'task':
138
- return { type: 'task', name: compiled.name };
139
- case 'cron':
140
- return {
141
- type: 'cron',
142
- name: compiled.name,
143
- expression: compiled.expression,
144
- timezone: compiled.timezone,
145
- overlap: compiled.overlap,
146
- task: compiled.taskName,
147
- };
148
- case 'fixed-delay':
149
- return {
150
- type: 'fixed-delay',
151
- name: compiled.name,
152
- delay: compiled.delay,
153
- immediate: compiled.immediate,
154
- task: compiled.taskName,
155
- };
156
- case 'queue-consumer':
157
- return {
158
- type: 'queue-consumer',
159
- name: compiled.name,
160
- queue: compiled.queueName,
161
- task: compiled.taskName,
162
- };
163
- }
129
+ return match(compiled)
130
+ .with({ type: 'task' }, (candidate) => ({
131
+ type: 'task',
132
+ name: candidate.name,
133
+ }))
134
+ .with({ type: 'cron' }, (candidate) => ({
135
+ type: 'cron',
136
+ name: candidate.name,
137
+ expression: candidate.expression,
138
+ timezone: candidate.timezone,
139
+ overlap: candidate.overlap,
140
+ task: candidate.taskName,
141
+ }))
142
+ .with({ type: 'fixed-delay' }, (candidate) => ({
143
+ type: 'fixed-delay',
144
+ name: candidate.name,
145
+ delay: candidate.delay,
146
+ immediate: candidate.immediate,
147
+ task: candidate.taskName,
148
+ }))
149
+ .with({ type: 'queue-consumer' }, (candidate) => ({
150
+ type: 'queue-consumer',
151
+ name: candidate.name,
152
+ queue: candidate.queueName,
153
+ task: candidate.taskName,
154
+ }))
155
+ .exhaustive();
164
156
  },
165
157
  host: {
166
158
  namespace: 'tasks',
@@ -238,6 +230,7 @@ export function consume(declaration) {
238
230
  }
239
231
  function createTasksRuntime(executions, applicationRuntime) {
240
232
  const runtimes = new Map();
233
+ const runtimeExecutionIds = new Map();
241
234
  const triggers = [];
242
235
  for (const execution of executions) {
243
236
  const compiled = execution.compiled;
@@ -254,6 +247,7 @@ function createTasksRuntime(executions, applicationRuntime) {
254
247
  resolve: (dependency) => applicationRuntime.resolve(dependency, execution.id),
255
248
  }, () => compiled.factory());
256
249
  runtimes.set(compiled.taskExecutionId, runtime);
250
+ runtimeExecutionIds.set(compiled.taskExecutionId, execution.id);
257
251
  }
258
252
  let triggerHandles = [];
259
253
  let triggerStartup;
@@ -261,7 +255,7 @@ function createTasksRuntime(executions, applicationRuntime) {
261
255
  let triggersStarted = false;
262
256
  let state = 'running';
263
257
  const activeInvocations = new Set();
264
- const run = (taskExecutionId, taskName, ...arguments_) => {
258
+ const invokeTask = (taskExecutionId, taskName, arguments_) => {
265
259
  if (state !== 'running') {
266
260
  return Promise.reject(new Error(`LUTRE_TASKS_${state.toUpperCase()}`));
267
261
  }
@@ -270,18 +264,42 @@ function createTasksRuntime(executions, applicationRuntime) {
270
264
  return Promise.reject(new Error(`LUTRE_TASK_NOT_REGISTERED: ${taskName}`));
271
265
  }
272
266
  const invocation = (async () => {
273
- const lease = applicationRuntime.beginExecution();
267
+ const executionId = runtimeExecutionIds.get(taskExecutionId) ?? taskName;
268
+ const hasInput = arguments_.length > 0;
269
+ const input = hasInput ? arguments_[0] : undefined;
270
+ const lease = applicationRuntime.beginExecution({
271
+ executionId,
272
+ executionKind: 'task.invocation',
273
+ graphNodeId: executionId,
274
+ name: taskName,
275
+ }, {
276
+ input,
277
+ invoke: (nextInput) => invokeTask(taskExecutionId, taskName, hasInput ? [nextInput] : []),
278
+ });
279
+ let result;
280
+ let hasResult = false;
274
281
  try {
275
- return await Reflect.apply(runtime, undefined, arguments_);
282
+ const invoke = () => Reflect.apply(runtime, undefined, arguments_);
283
+ result = await (lease.run ? lease.run(invoke) : invoke());
284
+ hasResult = true;
285
+ return result;
286
+ }
287
+ catch (error) {
288
+ lease.fail?.(error);
289
+ throw error;
276
290
  }
277
291
  finally {
278
- lease.complete();
292
+ if (hasResult)
293
+ lease.complete(result);
294
+ else
295
+ lease.complete();
279
296
  }
280
297
  })();
281
298
  activeInvocations.add(invocation);
282
299
  void invocation.then(() => activeInvocations.delete(invocation), () => activeInvocations.delete(invocation));
283
300
  return invocation;
284
301
  };
302
+ const run = (taskExecutionId, taskName, ...arguments_) => invokeTask(taskExecutionId, taskName, arguments_);
285
303
  const stopTriggers = () => {
286
304
  if (triggerStop)
287
305
  return triggerStop;
@@ -389,14 +407,11 @@ function createTasksRuntime(executions, applicationRuntime) {
389
407
  };
390
408
  }
391
409
  async function startTrigger(trigger, run, applicationRuntime) {
392
- switch (trigger.type) {
393
- case 'cron':
394
- return startCronTrigger(trigger, run);
395
- case 'fixed-delay':
396
- return startFixedDelayTrigger(trigger, run);
397
- case 'queue-consumer':
398
- return startQueueTrigger(trigger, run, applicationRuntime);
399
- }
410
+ return match(trigger)
411
+ .with({ type: 'cron' }, (candidate) => startCronTrigger(candidate, run))
412
+ .with({ type: 'fixed-delay' }, (candidate) => startFixedDelayTrigger(candidate, run))
413
+ .with({ type: 'queue-consumer' }, (candidate) => startQueueTrigger(candidate, run, applicationRuntime))
414
+ .exhaustive();
400
415
  }
401
416
  function startCronTrigger(trigger, run) {
402
417
  let stopped = false;
@@ -0,0 +1,30 @@
1
+ import type { WebSocketCloseInfo, WebSocketConnectionDriver, WebSocketDataMessage } from './extension.js';
2
+ export interface WebSocketDriverChannel {
3
+ readonly connection: WebSocketConnectionDriver;
4
+ receive(message: WebSocketDataMessage): void;
5
+ finish(info: WebSocketCloseInfo): void;
6
+ }
7
+ export declare function createWebSocketDriverChannel(options: {
8
+ readonly protocol: string;
9
+ readonly send: WebSocketConnectionDriver['send'];
10
+ readonly close: WebSocketConnectionDriver['close'];
11
+ readonly terminate: WebSocketConnectionDriver['terminate'];
12
+ }): WebSocketDriverChannel;
13
+ export interface EventWebSocket {
14
+ readonly readyState: number;
15
+ binaryType: string;
16
+ send(data: string | Uint8Array): void;
17
+ close(code?: number, reason?: string): void;
18
+ addEventListener(type: 'open', listener: () => void, options?: {
19
+ once?: boolean;
20
+ }): void;
21
+ addEventListener(type: 'message', listener: (event: {
22
+ data: unknown;
23
+ }) => void): void;
24
+ addEventListener(type: 'close', listener: (event: WebSocketCloseInfo) => void): void;
25
+ addEventListener(type: 'error', listener: () => void): void;
26
+ }
27
+ export declare function createEventWebSocketConnection(socket: EventWebSocket, protocol: string, options?: {
28
+ readonly terminate?: () => void;
29
+ readonly run?: <T>(operation: () => T) => Promise<T>;
30
+ }): WebSocketConnectionDriver;
@@ -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;