@firtoz/websocket-do 10.0.0 → 13.0.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 (65) hide show
  1. package/README.md +68 -61
  2. package/dist/BaseSession.d.ts +41 -0
  3. package/dist/BaseSession.js +5 -0
  4. package/dist/BaseSession.js.map +1 -0
  5. package/dist/BaseWebSocketDO.d.ts +42 -0
  6. package/dist/BaseWebSocketDO.js +4 -0
  7. package/dist/BaseWebSocketDO.js.map +1 -0
  8. package/dist/StandardSchemaSession.d.ts +41 -0
  9. package/dist/StandardSchemaSession.js +8 -0
  10. package/dist/StandardSchemaSession.js.map +1 -0
  11. package/dist/StandardSchemaWebSocketClient.d.ts +45 -0
  12. package/dist/StandardSchemaWebSocketClient.js +5 -0
  13. package/dist/StandardSchemaWebSocketClient.js.map +1 -0
  14. package/dist/StandardSchemaWebSocketDO.d.ts +28 -0
  15. package/dist/StandardSchemaWebSocketDO.js +5 -0
  16. package/dist/StandardSchemaWebSocketDO.js.map +1 -0
  17. package/dist/WebsocketWrapper.d.ts +9 -0
  18. package/dist/WebsocketWrapper.js +4 -0
  19. package/dist/WebsocketWrapper.js.map +1 -0
  20. package/dist/chunk-3C77OSOD.js +54 -0
  21. package/dist/chunk-3C77OSOD.js.map +1 -0
  22. package/dist/chunk-3LWVEY3R.js +130 -0
  23. package/dist/chunk-3LWVEY3R.js.map +1 -0
  24. package/dist/chunk-53MFRNQS.js +153 -0
  25. package/dist/chunk-53MFRNQS.js.map +1 -0
  26. package/dist/chunk-CAX4POIL.js +13 -0
  27. package/dist/chunk-CAX4POIL.js.map +1 -0
  28. package/dist/chunk-KCPOB32E.js +20 -0
  29. package/dist/chunk-KCPOB32E.js.map +1 -0
  30. package/dist/chunk-NOUFNU2O.js +10 -0
  31. package/dist/chunk-NOUFNU2O.js.map +1 -0
  32. package/dist/chunk-QMGIRIHJ.js +18 -0
  33. package/dist/chunk-QMGIRIHJ.js.map +1 -0
  34. package/dist/chunk-ULGH6X42.js +23 -0
  35. package/dist/chunk-ULGH6X42.js.map +1 -0
  36. package/dist/chunk-WJIQBI6I.js +35 -0
  37. package/dist/chunk-WJIQBI6I.js.map +1 -0
  38. package/dist/chunk-XFB6C3NZ.js +134 -0
  39. package/dist/chunk-XFB6C3NZ.js.map +1 -0
  40. package/dist/index.d.ts +14 -0
  41. package/dist/index.js +11 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/parseStandardSchema.d.ts +9 -0
  44. package/dist/parseStandardSchema.js +4 -0
  45. package/dist/parseStandardSchema.js.map +1 -0
  46. package/dist/standardSchemaMsgpack.d.ts +8 -0
  47. package/dist/standardSchemaMsgpack.js +5 -0
  48. package/dist/standardSchemaMsgpack.js.map +1 -0
  49. package/dist/standardSchemaRpc.d.ts +30 -0
  50. package/dist/standardSchemaRpc.js +6 -0
  51. package/dist/standardSchemaRpc.js.map +1 -0
  52. package/dist/standardSchemaRpcReact.d.ts +27 -0
  53. package/dist/standardSchemaRpcReact.js +54 -0
  54. package/dist/standardSchemaRpcReact.js.map +1 -0
  55. package/package.json +36 -18
  56. package/src/BaseSession.ts +15 -5
  57. package/src/{ZodSession.ts → StandardSchemaSession.ts} +71 -70
  58. package/src/{ZodWebSocketClient.ts → StandardSchemaWebSocketClient.ts} +30 -50
  59. package/src/{ZodWebSocketDO.ts → StandardSchemaWebSocketDO.ts} +29 -22
  60. package/src/index.ts +15 -12
  61. package/src/parseStandardSchema.ts +17 -0
  62. package/src/standardSchemaMsgpack.ts +17 -0
  63. package/src/standardSchemaRpc.ts +83 -0
  64. package/src/standardSchemaRpcReact.ts +107 -0
  65. package/src/zodMsgpack.ts +0 -13
@@ -1,11 +1,12 @@
1
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
1
2
  import type { Context } from "hono";
2
- import type { ZodType } from "zod";
3
3
  import { BaseSession } from "./BaseSession";
4
- import { zodMsgpack } from "./zodMsgpack";
4
+ import { parseStandardSchema } from "./parseStandardSchema";
5
+ import { standardSchemaMsgpack } from "./standardSchemaMsgpack";
5
6
 
6
- export type ZodSessionOptions<TClientMessage, TServerMessage> = {
7
- clientSchema: ZodType<TClientMessage>;
8
- serverSchema: ZodType<TServerMessage>;
7
+ export type StandardSchemaSessionOptions<TClientMessage, TServerMessage> = {
8
+ clientSchema: StandardSchemaV1<unknown, TClientMessage>;
9
+ serverSchema: StandardSchemaV1<unknown, TServerMessage>;
9
10
  serializeJson?: (value: unknown) => string;
10
11
  deserializeJson?: (raw: string) => unknown;
11
12
  enableBufferMessages?: boolean;
@@ -15,9 +16,9 @@ export type ZodSessionOptions<TClientMessage, TServerMessage> = {
15
16
  ) => Promise<void>;
16
17
  };
17
18
 
18
- export type ZodSessionHandlers<
19
+ export type StandardSchemaSessionHandlers<
19
20
  TData,
20
- _TServerMessage,
21
+ TServerMessage,
21
22
  TClientMessage,
22
23
  TEnv extends object,
23
24
  > = {
@@ -27,27 +28,36 @@ export type ZodSessionHandlers<
27
28
  error: unknown,
28
29
  originalMessage: unknown,
29
30
  ) => Promise<void>;
30
- handleClose: () => Promise<void>;
31
+ handleClose: (
32
+ session: StandardSchemaSession<TData, TServerMessage, TClientMessage, TEnv>,
33
+ ) => Promise<void>;
31
34
  };
32
35
 
33
- export class ZodSession<
36
+ export class StandardSchemaSession<
34
37
  TData,
35
38
  TServerMessage,
36
39
  TClientMessage,
37
40
  TEnv extends object = Cloudflare.Env,
38
41
  > extends BaseSession<TData, TServerMessage, TClientMessage, TEnv> {
39
- private readonly clientCodec: ReturnType<typeof zodMsgpack<TClientMessage>>;
40
- private readonly serverCodec: ReturnType<typeof zodMsgpack<TServerMessage>>;
42
+ private readonly clientCodec: ReturnType<
43
+ typeof standardSchemaMsgpack<TClientMessage>
44
+ >;
45
+ private readonly serverCodec: ReturnType<
46
+ typeof standardSchemaMsgpack<TServerMessage>
47
+ >;
41
48
  protected readonly enableBufferMessages: boolean;
42
49
 
43
50
  constructor(
44
51
  websocket: WebSocket,
45
52
  sessions: Map<
46
53
  WebSocket,
47
- ZodSession<TData, TServerMessage, TClientMessage, TEnv>
54
+ StandardSchemaSession<TData, TServerMessage, TClientMessage, TEnv>
55
+ >,
56
+ private readonly options: StandardSchemaSessionOptions<
57
+ TClientMessage,
58
+ TServerMessage
48
59
  >,
49
- private readonly options: ZodSessionOptions<TClientMessage, TServerMessage>,
50
- private readonly zodHandlers: ZodSessionHandlers<
60
+ private readonly schemaHandlers: StandardSchemaSessionHandlers<
51
61
  TData,
52
62
  TServerMessage,
53
63
  TClientMessage,
@@ -55,25 +65,33 @@ export class ZodSession<
55
65
  >,
56
66
  ) {
57
67
  super(websocket, sessions, {
58
- createData: zodHandlers.createData,
68
+ createData: schemaHandlers.createData,
59
69
  handleMessage: async (message) => {
60
70
  return this._internalHandleMessage(message);
61
71
  },
62
72
  handleBufferMessage: async (message) => {
63
73
  return this._internalHandleBufferMessage(message);
64
74
  },
65
- handleClose: async () => {
66
- return zodHandlers.handleClose();
75
+ handleClose: async (
76
+ session: BaseSession<TData, TServerMessage, TClientMessage, TEnv>,
77
+ ) => {
78
+ return schemaHandlers.handleClose(
79
+ session as StandardSchemaSession<
80
+ TData,
81
+ TServerMessage,
82
+ TClientMessage,
83
+ TEnv
84
+ >,
85
+ );
67
86
  },
68
87
  });
69
88
 
70
- this.clientCodec = zodMsgpack(options.clientSchema);
71
- this.serverCodec = zodMsgpack(options.serverSchema);
89
+ this.clientCodec = standardSchemaMsgpack(options.clientSchema);
90
+ this.serverCodec = standardSchemaMsgpack(options.serverSchema);
72
91
  this.enableBufferMessages = options.enableBufferMessages ?? false;
73
92
  }
74
93
 
75
94
  public async handleRawMessage(rawMessage: string): Promise<void> {
76
- // If buffer messages are enabled, reject string messages
77
95
  if (this.enableBufferMessages) {
78
96
  console.error(
79
97
  "String messages not allowed when buffer messages are enabled",
@@ -86,17 +104,18 @@ export class ZodSession<
86
104
 
87
105
  try {
88
106
  const parsed = this.deserializeJson(rawMessage);
89
- const validatedMessage = this.options.clientSchema.parse(parsed);
90
- await this.zodHandlers.handleValidatedMessage(validatedMessage);
107
+ const validatedMessage = await parseStandardSchema(
108
+ this.options.clientSchema,
109
+ parsed,
110
+ );
111
+ await this.schemaHandlers.handleValidatedMessage(validatedMessage);
91
112
  } catch (error) {
92
113
  console.error("Invalid client message received:", error);
93
114
  await this._internalHandleValidationError(error, rawMessage);
94
115
  }
95
116
  }
96
117
 
97
- // Internal method used by the base class handlers
98
118
  private async _internalHandleMessage(message: TClientMessage): Promise<void> {
99
- // If buffer messages are enabled, reject JSON messages
100
119
  if (this.enableBufferMessages) {
101
120
  console.error(
102
121
  "String messages not allowed when buffer messages are enabled",
@@ -108,48 +127,44 @@ export class ZodSession<
108
127
  }
109
128
 
110
129
  try {
111
- // Validate the message using the client schema
112
- const validatedMessage = this.options.clientSchema.parse(message);
113
- await this.zodHandlers.handleValidatedMessage(validatedMessage);
130
+ const validatedMessage = await parseStandardSchema(
131
+ this.options.clientSchema,
132
+ message,
133
+ );
134
+ await this.schemaHandlers.handleValidatedMessage(validatedMessage);
114
135
  } catch (error) {
115
136
  console.error("Invalid client message received:", error);
116
137
  await this._internalHandleValidationError(error, message);
117
138
  }
118
139
  }
119
140
 
120
- // Internal method used by the base class handlers
121
141
  private async _internalHandleBufferMessage(
122
142
  buffer: ArrayBuffer,
123
143
  ): Promise<void> {
124
- // If buffer messages are disabled, reject buffer messages
125
144
  if (!this.enableBufferMessages) {
126
145
  console.error(
127
146
  "Buffer messages not allowed when buffer messages are disabled",
128
147
  );
129
- // We can't use sendProtocolError here because it would send JSON
130
- // Just close the connection or ignore
131
148
  return;
132
149
  }
133
150
 
134
151
  try {
135
152
  const bytes = new Uint8Array(buffer);
136
- const decodedMessage = this.clientCodec.decode(bytes);
137
- await this.zodHandlers.handleValidatedMessage(decodedMessage);
153
+ const decodedMessage = await this.clientCodec.decode(bytes);
154
+ await this.schemaHandlers.handleValidatedMessage(decodedMessage);
138
155
  } catch (error) {
139
156
  console.error("Failed to decode buffer message:", error);
140
157
  await this._internalHandleValidationError(error, buffer);
141
158
  }
142
159
  }
143
160
 
144
- // Internal validation error handler
145
161
  private async _internalHandleValidationError(
146
162
  error: unknown,
147
163
  originalMessage: unknown,
148
164
  ): Promise<void> {
149
- if (this.zodHandlers.handleValidationError) {
150
- await this.zodHandlers.handleValidationError(error, originalMessage);
165
+ if (this.schemaHandlers.handleValidationError) {
166
+ await this.schemaHandlers.handleValidationError(error, originalMessage);
151
167
  } else {
152
- // Default implementation logs and continues
153
168
  console.error(
154
169
  "Validation error:",
155
170
  error,
@@ -159,50 +174,38 @@ export class ZodSession<
159
174
  }
160
175
  }
161
176
 
162
- // Type-safe send method that automatically uses the correct format
163
177
  public send(message: TServerMessage): void {
164
178
  if (this.enableBufferMessages) {
165
- this.sendBuffer(message);
179
+ void this.sendBufferAsync(message).catch((error: unknown) => {
180
+ console.error("Failed to encode buffer message:", error);
181
+ });
166
182
  } else {
167
- this.sendJson(message);
183
+ void this.sendJsonAsync(message).catch((error: unknown) => {
184
+ console.error("Invalid server message to send:", error);
185
+ });
168
186
  }
169
187
  }
170
188
 
171
- // Explicitly send as JSON
172
- private sendJson(message: TServerMessage): void {
173
- try {
174
- // Validate the message using the server schema
175
- const validatedMessage = this.options.serverSchema.parse(message);
176
-
177
- if (this.websocket.readyState !== WebSocket.OPEN) return;
178
-
179
- this.websocket.send(this.serializeJson(validatedMessage));
180
- } catch (error) {
181
- console.error("Invalid server message to send:", error);
182
- }
189
+ private async sendJsonAsync(message: TServerMessage): Promise<void> {
190
+ const validatedMessage = await parseStandardSchema(
191
+ this.options.serverSchema,
192
+ message,
193
+ );
194
+ if (this.websocket.readyState !== WebSocket.OPEN) return;
195
+ this.websocket.send(this.serializeJson(validatedMessage));
183
196
  }
184
197
 
185
- // Explicitly send as buffer using msgpack
186
- private sendBuffer(message: TServerMessage): void {
187
- try {
188
- const encodedMessage = this.serverCodec.encode(message);
189
-
190
- if (this.websocket.readyState !== WebSocket.OPEN) return;
191
-
192
- this.websocket.send(encodedMessage);
193
- } catch (error) {
194
- console.error("Failed to encode buffer message:", error);
195
- }
198
+ private async sendBufferAsync(message: TServerMessage): Promise<void> {
199
+ const encodedMessage = await this.serverCodec.encode(message);
200
+ if (this.websocket.readyState !== WebSocket.OPEN) return;
201
+ this.websocket.send(encodedMessage);
196
202
  }
197
203
 
198
- // Send a protocol error message (always as JSON for compatibility by default)
199
204
  private async sendProtocolError(errorMessage: string): Promise<void> {
200
205
  try {
201
- // Use custom handler if provided, otherwise use default
202
206
  if (this.options.sendProtocolError) {
203
207
  await this.options.sendProtocolError(this.websocket, errorMessage);
204
208
  } else {
205
- // Default implementation: send a simple error object - no schema validation needed
206
209
  if (this.websocket.readyState !== WebSocket.OPEN) return;
207
210
  this.websocket.send(this.serializeJson({ error: errorMessage }));
208
211
  }
@@ -223,13 +226,11 @@ export class ZodSession<
223
226
  : JSON.parse(raw);
224
227
  }
225
228
 
226
- // Type-safe broadcast that validates server messages
227
- // Automatically uses the correct format based on session configuration
228
229
  public broadcast(message: TServerMessage, excludeSelf = false): void {
229
230
  for (const session of this.sessions.values()) {
230
231
  if (excludeSelf && session === this) continue;
231
- if (session instanceof ZodSession) {
232
- session.send(message); // send() automatically uses correct format
232
+ if (session instanceof StandardSchemaSession) {
233
+ session.send(message);
233
234
  }
234
235
  }
235
236
  }
@@ -1,7 +1,11 @@
1
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
1
2
  import { pack, unpack } from "msgpackr";
2
- import type { ZodType } from "zod";
3
+ import { parseStandardSchema } from "./parseStandardSchema";
3
4
 
4
- export interface ZodWebSocketClientOptions<TClientMessage, TServerMessage> {
5
+ export interface StandardSchemaWebSocketClientOptions<
6
+ TClientMessage,
7
+ TServerMessage,
8
+ > {
5
9
  /**
6
10
  * URL to connect to (required if webSocket not provided)
7
11
  */
@@ -11,8 +15,8 @@ export interface ZodWebSocketClientOptions<TClientMessage, TServerMessage> {
11
15
  * Useful when getting a WebSocket from honoDoFetcher
12
16
  */
13
17
  webSocket?: WebSocket;
14
- clientSchema: ZodType<TClientMessage>;
15
- serverSchema: ZodType<TServerMessage>;
18
+ clientSchema: StandardSchemaV1<unknown, TClientMessage>;
19
+ serverSchema: StandardSchemaV1<unknown, TServerMessage>;
16
20
  serializeJson?: (value: unknown) => string;
17
21
  deserializeJson?: (raw: string) => unknown;
18
22
  enableBufferMessages?: boolean;
@@ -23,10 +27,10 @@ export interface ZodWebSocketClientOptions<TClientMessage, TServerMessage> {
23
27
  onValidationError?: (error: Error, rawMessage: unknown) => void;
24
28
  }
25
29
 
26
- export class ZodWebSocketClient<TClientMessage, TServerMessage> {
30
+ export class StandardSchemaWebSocketClient<TClientMessage, TServerMessage> {
27
31
  private ws: WebSocket;
28
- private readonly clientSchema: ZodType<TClientMessage>;
29
- private readonly serverSchema: ZodType<TServerMessage>;
32
+ private readonly clientSchema: StandardSchemaV1<unknown, TClientMessage>;
33
+ private readonly serverSchema: StandardSchemaV1<unknown, TServerMessage>;
30
34
  private readonly serializeJson: (value: unknown) => string;
31
35
  private readonly deserializeJson: (raw: string) => unknown;
32
36
  private readonly enableBufferMessages: boolean;
@@ -37,7 +41,10 @@ export class ZodWebSocketClient<TClientMessage, TServerMessage> {
37
41
  ) => void;
38
42
 
39
43
  constructor(
40
- options: ZodWebSocketClientOptions<TClientMessage, TServerMessage>,
44
+ options: StandardSchemaWebSocketClientOptions<
45
+ TClientMessage,
46
+ TServerMessage
47
+ >,
41
48
  ) {
42
49
  this.clientSchema = options.clientSchema;
43
50
  this.serverSchema = options.serverSchema;
@@ -47,29 +54,24 @@ export class ZodWebSocketClient<TClientMessage, TServerMessage> {
47
54
  this.onMessageCallback = options.onMessage;
48
55
  this.onValidationError = options.onValidationError;
49
56
 
50
- // Use provided WebSocket or create new one from URL
51
57
  if (options.webSocket) {
52
- // Use existing WebSocket (e.g., from honoDoFetcher)
53
58
  this.ws = options.webSocket;
54
59
  } else if (options.url) {
55
- // Create new WebSocket from URL
56
60
  this.ws = new WebSocket(options.url);
57
61
  } else {
58
62
  throw new Error("Either 'url' or 'webSocket' must be provided");
59
63
  }
60
64
 
61
- // Set binary type for buffer messages
62
65
  if (this.enableBufferMessages) {
63
66
  this.ws.binaryType = "arraybuffer";
64
67
  }
65
68
 
66
- // Setup event handlers
67
69
  this.ws.addEventListener("open", (event) => {
68
70
  options.onOpen?.(event);
69
71
  });
70
72
 
71
73
  this.ws.addEventListener("message", (event) => {
72
- this.handleMessage(event);
74
+ void this.handleMessageEvent(event);
73
75
  });
74
76
 
75
77
  this.ws.addEventListener("close", (event) => {
@@ -81,12 +83,11 @@ export class ZodWebSocketClient<TClientMessage, TServerMessage> {
81
83
  });
82
84
  }
83
85
 
84
- private handleMessage(event: MessageEvent): void {
86
+ private async handleMessageEvent(event: MessageEvent): Promise<void> {
85
87
  try {
86
88
  let parsedMessage: TServerMessage;
87
89
 
88
90
  if (this.enableBufferMessages) {
89
- // Buffer mode: expect ArrayBuffer
90
91
  if (!(event.data instanceof ArrayBuffer)) {
91
92
  console.error(
92
93
  "Expected ArrayBuffer but received:",
@@ -99,11 +100,9 @@ export class ZodWebSocketClient<TClientMessage, TServerMessage> {
99
100
  return;
100
101
  }
101
102
 
102
- // Unpack and validate
103
103
  const unpacked = unpack(new Uint8Array(event.data));
104
- parsedMessage = this.serverSchema.parse(unpacked);
104
+ parsedMessage = await parseStandardSchema(this.serverSchema, unpacked);
105
105
  } else {
106
- // JSON mode: expect string
107
106
  if (typeof event.data !== "string") {
108
107
  console.error("Expected string but received:", typeof event.data);
109
108
  this.onValidationError?.(
@@ -113,12 +112,10 @@ export class ZodWebSocketClient<TClientMessage, TServerMessage> {
113
112
  return;
114
113
  }
115
114
 
116
- // Parse and validate
117
115
  const parsed = this.deserializeJson(event.data);
118
- parsedMessage = this.serverSchema.parse(parsed);
116
+ parsedMessage = await parseStandardSchema(this.serverSchema, parsed);
119
117
  }
120
118
 
121
- // Call message handler
122
119
  this.onMessageCallback?.(parsedMessage);
123
120
  } catch (error) {
124
121
  console.error("Failed to process message:", error);
@@ -130,51 +127,34 @@ export class ZodWebSocketClient<TClientMessage, TServerMessage> {
130
127
  }
131
128
 
132
129
  /**
133
- * Send a message (automatically encodes based on mode)
130
+ * Send a message (automatically encodes based on mode).
134
131
  */
135
- send(message: TClientMessage): void {
136
- try {
137
- // Validate message
138
- const validatedMessage = this.clientSchema.parse(message);
132
+ async send(message: TClientMessage): Promise<void> {
133
+ const validatedMessage = await parseStandardSchema(
134
+ this.clientSchema,
135
+ message,
136
+ );
139
137
 
140
- if (this.enableBufferMessages) {
141
- // Encode as msgpack (ensure ArrayBufferView for WebSocket.send)
142
- const packed = pack(validatedMessage);
143
- this.ws.send(new Uint8Array(packed));
144
- } else {
145
- // Encode as JSON
146
- this.ws.send(this.serializeJson(validatedMessage));
147
- }
148
- } catch (error) {
149
- console.error("Failed to send message:", error);
150
- throw error;
138
+ if (this.enableBufferMessages) {
139
+ const packed = pack(validatedMessage);
140
+ this.ws.send(new Uint8Array(packed));
141
+ } else {
142
+ this.ws.send(this.serializeJson(validatedMessage));
151
143
  }
152
144
  }
153
145
 
154
- /**
155
- * Close the WebSocket connection
156
- */
157
146
  close(code?: number, reason?: string): void {
158
147
  this.ws.close(code, reason);
159
148
  }
160
149
 
161
- /**
162
- * Get the current WebSocket ready state
163
- */
164
150
  get readyState(): number {
165
151
  return this.ws.readyState;
166
152
  }
167
153
 
168
- /**
169
- * Get the underlying WebSocket instance (use with caution)
170
- */
171
154
  get socket(): WebSocket {
172
155
  return this.ws;
173
156
  }
174
157
 
175
- /**
176
- * Wait for the connection to open
177
- */
178
158
  async waitForOpen(): Promise<void> {
179
159
  if (this.ws.readyState === WebSocket.OPEN) {
180
160
  return;
@@ -5,62 +5,65 @@ import type {
5
5
  SessionServerMessage,
6
6
  } from "./BaseSession";
7
7
  import { BaseWebSocketDO } from "./BaseWebSocketDO";
8
- import type { ZodSession, ZodSessionOptions } from "./ZodSession";
8
+ import type {
9
+ StandardSchemaSession,
10
+ StandardSchemaSessionOptions,
11
+ } from "./StandardSchemaSession";
9
12
 
10
- export type ZodSessionOptionsOrFactory<
13
+ export type StandardSchemaSessionOptionsOrFactory<
11
14
  TClientMessage,
12
15
  TServerMessage,
13
16
  TEnv extends Cloudflare.Env = Cloudflare.Env,
14
17
  > =
15
- | ZodSessionOptions<TClientMessage, TServerMessage>
18
+ | StandardSchemaSessionOptions<TClientMessage, TServerMessage>
16
19
  | ((
17
20
  ctx: Context<{ Bindings: TEnv }> | undefined,
18
21
  websocket: WebSocket,
19
- ) => ZodSessionOptions<TClientMessage, TServerMessage>);
22
+ ) => StandardSchemaSessionOptions<TClientMessage, TServerMessage>);
20
23
 
21
- export type ZodWebSocketDOOptions<
24
+ export type StandardSchemaWebSocketDOOptions<
22
25
  // biome-ignore lint/suspicious/noExplicitAny: We are using any on purpose to allow any type of session.
23
- TSession extends ZodSession<any, any, any, any>,
26
+ TSession extends StandardSchemaSession<any, any, any, any>,
24
27
  TClientMessage,
25
28
  TServerMessage,
26
29
  TEnv extends SessionEnv<TSession>,
27
30
  > = {
28
- zodSessionOptions: ZodSessionOptionsOrFactory<
31
+ standardSchemaSessionOptions: StandardSchemaSessionOptionsOrFactory<
29
32
  TClientMessage,
30
33
  TServerMessage,
31
34
  TEnv
32
35
  >;
33
- createZodSession: (
36
+ createStandardSchemaSession: (
34
37
  ctx: Context<{ Bindings: TEnv }> | undefined,
35
38
  websocket: WebSocket,
36
- options: ZodSessionOptions<TClientMessage, TServerMessage>,
39
+ options: StandardSchemaSessionOptions<TClientMessage, TServerMessage>,
37
40
  ) => TSession | Promise<TSession>;
38
41
  };
39
42
 
40
- export abstract class ZodWebSocketDO<
43
+ export abstract class StandardSchemaWebSocketDO<
41
44
  // biome-ignore lint/suspicious/noExplicitAny: We are using any on purpose to allow any type of session.
42
- TSession extends ZodSession<any, any, any, any>,
45
+ TSession extends StandardSchemaSession<any, any, any, any>,
43
46
  TClientMessage extends
44
47
  SessionClientMessage<TSession> = SessionClientMessage<TSession>,
45
48
  TServerMessage extends
46
49
  SessionServerMessage<TSession> = SessionServerMessage<TSession>,
47
50
  TEnv extends SessionEnv<TSession> = SessionEnv<TSession>,
48
51
  > extends BaseWebSocketDO<TSession, TEnv> {
49
- protected readonly zodSessionOptions: ZodSessionOptionsOrFactory<
52
+ protected readonly standardSchemaSessionOptions: StandardSchemaSessionOptionsOrFactory<
50
53
  TClientMessage,
51
54
  TServerMessage,
52
55
  TEnv
53
56
  >;
54
- protected readonly createZodSessionFn: (
57
+ protected readonly createStandardSchemaSessionFn: (
55
58
  ctx: Context<{ Bindings: TEnv }> | undefined,
56
59
  websocket: WebSocket,
57
- options: ZodSessionOptions<TClientMessage, TServerMessage>,
60
+ options: StandardSchemaSessionOptions<TClientMessage, TServerMessage>,
58
61
  ) => TSession | Promise<TSession>;
59
62
 
60
63
  constructor(
61
64
  ctx: DurableObjectState,
62
65
  env: TEnv,
63
- options: ZodWebSocketDOOptions<
66
+ options: StandardSchemaWebSocketDOOptions<
64
67
  TSession,
65
68
  TClientMessage,
66
69
  TServerMessage,
@@ -69,15 +72,19 @@ export abstract class ZodWebSocketDO<
69
72
  ) {
70
73
  super(ctx, env, {
71
74
  createSession: (ctx, websocket) => {
72
- const zodOptions =
73
- typeof options.zodSessionOptions === "function"
74
- ? options.zodSessionOptions(ctx, websocket)
75
- : options.zodSessionOptions;
75
+ const schemaOptions =
76
+ typeof options.standardSchemaSessionOptions === "function"
77
+ ? options.standardSchemaSessionOptions(ctx, websocket)
78
+ : options.standardSchemaSessionOptions;
76
79
 
77
- return options.createZodSession(ctx, websocket, zodOptions);
80
+ return options.createStandardSchemaSession(
81
+ ctx,
82
+ websocket,
83
+ schemaOptions,
84
+ );
78
85
  },
79
86
  });
80
- this.zodSessionOptions = options.zodSessionOptions;
81
- this.createZodSessionFn = options.createZodSession;
87
+ this.standardSchemaSessionOptions = options.standardSchemaSessionOptions;
88
+ this.createStandardSchemaSessionFn = options.createStandardSchemaSession;
82
89
  }
83
90
  }
package/src/index.ts CHANGED
@@ -2,6 +2,8 @@ export {
2
2
  BaseSession,
3
3
  type BaseSessionHandlers,
4
4
  type SessionClientMessage,
5
+ type SessionEnv,
6
+ type SessionServerMessage,
5
7
  } from "./BaseSession";
6
8
  export {
7
9
  BaseWebSocketDO,
@@ -9,17 +11,18 @@ export {
9
11
  } from "./BaseWebSocketDO";
10
12
  export { WebsocketWrapper } from "./WebsocketWrapper";
11
13
  export {
12
- ZodSession,
13
- type ZodSessionHandlers,
14
- type ZodSessionOptions,
15
- } from "./ZodSession";
14
+ StandardSchemaSession,
15
+ type StandardSchemaSessionHandlers,
16
+ type StandardSchemaSessionOptions,
17
+ } from "./StandardSchemaSession";
16
18
  export {
17
- ZodWebSocketClient,
18
- type ZodWebSocketClientOptions,
19
- } from "./ZodWebSocketClient";
19
+ StandardSchemaWebSocketClient,
20
+ type StandardSchemaWebSocketClientOptions,
21
+ } from "./StandardSchemaWebSocketClient";
20
22
  export {
21
- type ZodSessionOptionsOrFactory,
22
- ZodWebSocketDO,
23
- type ZodWebSocketDOOptions,
24
- } from "./ZodWebSocketDO";
25
- export { zodMsgpack } from "./zodMsgpack";
23
+ type StandardSchemaSessionOptionsOrFactory,
24
+ StandardSchemaWebSocketDO,
25
+ type StandardSchemaWebSocketDOOptions,
26
+ } from "./StandardSchemaWebSocketDO";
27
+ export { parseStandardSchema } from "./parseStandardSchema";
28
+ export { standardSchemaMsgpack } from "./standardSchemaMsgpack";
@@ -0,0 +1,17 @@
1
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
2
+
3
+ /**
4
+ * Validates {@link value} with a Standard Schema v1 schema and returns the output,
5
+ * or throws an {@link Error} whose message aggregates issue messages.
6
+ */
7
+ export async function parseStandardSchema<T>(
8
+ schema: StandardSchemaV1<unknown, T>,
9
+ value: unknown,
10
+ ): Promise<T> {
11
+ const result = await schema["~standard"].validate(value);
12
+ if (result.issues) {
13
+ const messages = result.issues.map((issue) => issue.message).join("; ");
14
+ throw new Error(messages || "Validation failed");
15
+ }
16
+ return result.value;
17
+ }
@@ -0,0 +1,17 @@
1
+ import type { StandardSchemaV1 } from "@standard-schema/spec";
2
+ import { pack, unpack } from "msgpackr";
3
+ import { parseStandardSchema } from "./parseStandardSchema";
4
+
5
+ export const standardSchemaMsgpack = <T>(
6
+ schema: StandardSchemaV1<unknown, T>,
7
+ ) => ({
8
+ async encode(value: T): Promise<Uint8Array> {
9
+ const validated = await parseStandardSchema(schema, value);
10
+ const packed = pack(validated);
11
+ return new Uint8Array(packed);
12
+ },
13
+ async decode(bytes: Uint8Array): Promise<T> {
14
+ const unpacked = unpack(bytes);
15
+ return parseStandardSchema(schema, unpacked);
16
+ },
17
+ });