@firtoz/socka 3.0.2 → 4.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 (46) hide show
  1. package/README.md +15 -1
  2. package/dist/{SockaWebSocketSession-B1w7RAid.d.ts → SockaWebSocketSession-BaGvSerM.d.ts} +28 -10
  3. package/dist/bun/index.d.ts +6 -6
  4. package/dist/bun/index.js +2 -2
  5. package/dist/bun/index.js.map +1 -1
  6. package/dist/{chunk-5WQTYLIC.js → chunk-JR2GENNT.js} +2 -2
  7. package/dist/chunk-JR2GENNT.js.map +1 -0
  8. package/dist/{chunk-P3JEEOJL.js → chunk-THFUHQJ3.js} +2 -2
  9. package/dist/chunk-THFUHQJ3.js.map +1 -0
  10. package/dist/{chunk-LVVCHLNW.js → chunk-TTXY7O5P.js} +20 -4
  11. package/dist/chunk-TTXY7O5P.js.map +1 -0
  12. package/dist/client/index.d.ts +10 -10
  13. package/dist/client/index.js +1 -1
  14. package/dist/core/index.d.ts +1 -1
  15. package/dist/core/index.js.map +1 -1
  16. package/dist/do/index.d.ts +83 -18
  17. package/dist/do/index.js +62 -6
  18. package/dist/do/index.js.map +1 -1
  19. package/dist/hono/cloudflare-workers.d.ts +4 -4
  20. package/dist/hono/cloudflare-workers.js +2 -2
  21. package/dist/hono/cloudflare-workers.js.map +1 -1
  22. package/dist/hono/index.d.ts +4 -4
  23. package/dist/hono/index.js +2 -2
  24. package/dist/hono/index.js.map +1 -1
  25. package/dist/react/index.d.ts +13 -13
  26. package/dist/react/index.js +1 -1
  27. package/dist/react/index.js.map +1 -1
  28. package/dist/server/index.d.ts +8 -8
  29. package/dist/server/index.js +4 -4
  30. package/dist/server/index.js.map +1 -1
  31. package/dist/{socka-report-error-CXwpAUgl.d.ts → socka-report-error-nTXJIzNb.d.ts} +32 -12
  32. package/docs/README.md +2 -0
  33. package/docs/client.md +17 -0
  34. package/docs/collaborative-realtime.md +61 -0
  35. package/docs/durable-objects.md +84 -35
  36. package/docs/internals.md +1 -1
  37. package/docs/pushes.md +32 -2
  38. package/docs/react-durable-objects.md +96 -0
  39. package/docs/recipes.md +1 -1
  40. package/docs/reference.md +18 -5
  41. package/package.json +8 -8
  42. package/skills/socka/core-rpc/SKILL.md +1 -1
  43. package/skills/socka/do-session/SKILL.md +1 -1
  44. package/dist/chunk-5WQTYLIC.js.map +0 -1
  45. package/dist/chunk-LVVCHLNW.js.map +0 -1
  46. package/dist/chunk-P3JEEOJL.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/contract.ts"],"names":[],"mappings":";;;;AAkIO,SAAS,YACf,MAAA,EACmB;AACnB,EAAA,OAAO;AAAA,IACN,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,MAAA,EAAS,MAAA,CAAO,MAAA,IAAU;AAAC,GAC5B;AACD","file":"index.js","sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ReservedSockaProcedureName } from \"./reserved-procedure-names\";\n\n/**\n * Defines one client-initiated call: optional `input` and optional `output` schemas\n * (Standard Schema v1: Zod v4, Valibot, ArkType, etc.).\n *\n * - **`output` present** (including `z.void()`): request/response RPC; the server sends\n * a validated `serverResponse` on success.\n * - **`output` omitted**: fire-and-forget on success (no `serverResponse`); the client\n * `send` method resolves after the request is sent. Use `output: z.void()` when you\n * still want a correlated ack. See the package README and {@link defineSocka}.\n */\nexport type SockaProcedureDef = {\n\treadonly input?: StandardSchemaV1;\n\treadonly output?: StandardSchemaV1;\n};\n\n/** Configuration object accepted by {@link defineSocka}. */\nexport type SockaContractConfig = {\n\treadonly calls: Record<string, SockaProcedureDef>;\n\treadonly pushes?: Record<string, StandardSchemaV1>;\n};\n\n/**\n * When call keys are a **narrow** object type, rejects keys in\n * {@link ReservedSockaProcedureName} (thenable / `Object.prototype` hazards on\n * `session.send`). Wide `Record<string, SockaProcedureDef>` is unchanged so\n * dynamic maps still typecheck; use runtime validation (see {@link SockaSession}\n * `send`).\n */\nexport type ValidateSockaCallKeys<P extends Record<string, SockaProcedureDef>> =\n\tstring extends keyof P\n\t\t? P\n\t\t: keyof P & ReservedSockaProcedureName extends never\n\t\t\t? P\n\t\t\t: never;\n\n/** Runtime contract returned by {@link defineSocka}, preserving full generic types. */\nexport type SockaContract<T extends SockaContractConfig = SockaContractConfig> =\n\t{\n\t\treadonly calls: T[\"calls\"];\n\t\treadonly pushes: T extends { pushes: Record<string, StandardSchemaV1> }\n\t\t\t? T[\"pushes\"]\n\t\t\t: Record<string, never>;\n\t};\n\n/** Inferred client return type for a call: payload type or `void` when `output` is omitted. */\ntype InferSockaCallReturn<P extends SockaProcedureDef> =\n\tP[\"output\"] extends StandardSchemaV1\n\t\t? StandardSchemaV1.InferOutput<P[\"output\"]>\n\t\t: // biome-ignore lint/suspicious/noConfusingVoidType: This is correct\n\t\t\tvoid;\n\ntype CallFn<P extends SockaProcedureDef> = P extends {\n\tinput: infer I extends StandardSchemaV1;\n}\n\t? (input: StandardSchemaV1.InferInput<I>) => Promise<InferSockaCallReturn<P>>\n\t: () => Promise<InferSockaCallReturn<P>>;\n\n/**\n * Infers the typed `session.send.*` method map for a contract.\n */\nexport type InferSockaSend<C extends SockaContract> = {\n\t[K in keyof C[\"calls\"]]: CallFn<C[\"calls\"][K]>;\n};\n\ntype HandlerOut<P extends SockaProcedureDef> =\n\tP[\"output\"] extends StandardSchemaV1\n\t\t?\n\t\t\t\t| StandardSchemaV1.InferOutput<P[\"output\"]>\n\t\t\t\t| Promise<StandardSchemaV1.InferOutput<P[\"output\"]>>\n\t\t: void | Promise<void>;\n\ntype HandlerFn<P extends SockaProcedureDef, TSession> = P extends {\n\tinput: infer I extends StandardSchemaV1;\n}\n\t? (input: StandardSchemaV1.InferInput<I>, session: TSession) => HandlerOut<P>\n\t: (session: TSession) => HandlerOut<P>;\n\n/**\n * Infers the typed server handler map for a contract. Handlers with an input\n * schema take `(input, session)`; calls without input take `(session)` only.\n * When `output` is present, the return value is validated and sent as `serverResponse`.\n * When `output` is omitted (fire-and-forget), the handler should return `void`; the\n * server does not send a success response.\n */\nexport type InferSockaHandlers<C extends SockaContract, TSession> = {\n\t[K in keyof C[\"calls\"]]: HandlerFn<C[\"calls\"][K], TSession>;\n};\n\ntype InferPushPayload<S extends StandardSchemaV1> =\n\tStandardSchemaV1.InferOutput<S>;\n\n/**\n * Payload type for a contract push (output of the push's Standard Schema).\n */\nexport type InferSockaPushPayload<\n\tC extends SockaContract<SockaContractConfig>,\n\tK extends keyof C[\"pushes\"],\n> = C[\"pushes\"][K] extends StandardSchemaV1\n\t? InferPushPayload<C[\"pushes\"][K]>\n\t: never;\n\n/**\n * Infers the typed push subscription handler map for a contract's `pushes`.\n */\nexport type InferSockaPushHandlers<C extends SockaContract> = {\n\t[K in keyof C[\"pushes\"]]: C[\"pushes\"][K] extends StandardSchemaV1\n\t\t? (payload: InferPushPayload<C[\"pushes\"][K]>) => void | Promise<void>\n\t\t: never;\n};\n\n/**\n * Creates a socka contract from call and push definitions. Pass Zod, Valibot,\n * ArkType, or any Standard Schema v1 schemas directly — no adapters needed.\n *\n * ```ts\n * export const myContract = defineSocka({\n * calls: {\n * list: { output: z.array(itemSchema) },\n * insert: { input: z.object({ item: itemSchema }), output: z.void() },\n * notify: { input: z.object({ text: z.string() }) },\n * },\n * });\n * ```\n *\n * Call names must not be {@link ReservedSockaProcedureName} — they would make\n * `session.send` thenable or unsafe as a plain method bag.\n */\nexport function defineSocka<const T extends SockaContractConfig>(\n\tconfig: T & { calls: ValidateSockaCallKeys<T[\"calls\"]> },\n): SockaContract<T> {\n\treturn {\n\t\tcalls: config.calls,\n\t\tpushes: (config.pushes ?? {}) as SockaContract<T>[\"pushes\"],\n\t};\n}\n"]}
1
+ {"version":3,"sources":["../../src/core/contract.ts"],"names":[],"mappings":";;;;AAwJO,SAAS,YACf,MAAA,EACmB;AACnB,EAAA,OAAO;AAAA,IACN,OAAO,MAAA,CAAO,KAAA;AAAA,IACd,MAAA,EAAS,MAAA,CAAO,MAAA,IAAU;AAAC,GAC5B;AACD","file":"index.js","sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ReservedSockaProcedureName } from \"./reserved-procedure-names\";\n\n/**\n * Defines one client-initiated call: optional `input` and optional `output` schemas\n * (Standard Schema v1: Zod v4, Valibot, ArkType, etc.).\n *\n * - **`output` present** (including `z.void()`): request/response RPC; the server sends\n * a validated `serverResponse` on success.\n * - **`output` omitted**: fire-and-forget on success (no `serverResponse`); the client\n * `send` method resolves after the request is sent. Use `output: z.void()` when you\n * still want a correlated ack. See the package README and {@link defineSocka}.\n */\nexport type SockaProcedureDef = {\n\treadonly input?: StandardSchemaV1;\n\treadonly output?: StandardSchemaV1;\n};\n\n/** Configuration object accepted by {@link defineSocka}. */\nexport type SockaContractConfig = {\n\treadonly calls: Record<string, SockaProcedureDef>;\n\treadonly pushes?: Record<string, StandardSchemaV1>;\n};\n\n/** Runtime contract returned by {@link defineSocka}, preserving full generic types. */\nexport type SockaContract<T extends SockaContractConfig = SockaContractConfig> =\n\t{\n\t\treadonly calls: T[\"calls\"];\n\t\treadonly pushes: T extends { pushes: Record<string, StandardSchemaV1> }\n\t\t\t? T[\"pushes\"]\n\t\t\t: Record<string, never>;\n\t};\n\n/**\n * Wide config shape for generic *bounds* on APIs that must accept any `defineSocka`\n * result, including contracts with **server pushes**.\n *\n * `SockaContract` instantiated with {@link SockaContractConfig} alone resolves\n * `pushes` to `Record<string, never>` (because `pushes` is optional, so the\n * conditional in {@link SockaContract} does not keep concrete push fields). A generic\n * bound of `extends SockaContract<SockaContractConfig>` therefore **rejects** real\n * contracts with named server pushes. Use {@link SockaContractBound} (equivalently\n * `extends SockaContract<SockaContractConfigBound>`) for those constraints.\n */\nexport type SockaContractConfigBound = {\n\treadonly calls: Record<string, SockaProcedureDef>;\n\treadonly pushes: Record<string, StandardSchemaV1>;\n};\n\n/**\n * Widen any concrete `defineSocka` contract for use in `extends` constraints\n * (e.g. `SockaDoSession`, `SockaSession`, `useSockaSession`).\n */\nexport type SockaContractBound = SockaContract<SockaContractConfigBound>;\n\n/**\n * When call keys are a **narrow** object type, rejects keys in\n * {@link ReservedSockaProcedureName} (thenable / `Object.prototype` hazards on\n * `session.send`). Wide `Record<string, SockaProcedureDef>` is unchanged so\n * dynamic maps still typecheck; use runtime validation (see {@link SockaSession}\n * `send`).\n */\nexport type ValidateSockaCallKeys<P extends Record<string, SockaProcedureDef>> =\n\tstring extends keyof P\n\t\t? P\n\t\t: keyof P & ReservedSockaProcedureName extends never\n\t\t\t? P\n\t\t\t: never;\n\n/** Inferred client return type for a call: payload type or `void` when `output` is omitted. */\ntype InferSockaCallReturn<P extends SockaProcedureDef> =\n\tP[\"output\"] extends StandardSchemaV1\n\t\t? StandardSchemaV1.InferOutput<P[\"output\"]>\n\t\t: // biome-ignore lint/suspicious/noConfusingVoidType: This is correct\n\t\t\tvoid;\n\ntype CallFn<P extends SockaProcedureDef> = P extends {\n\tinput: infer I extends StandardSchemaV1;\n}\n\t? (input: StandardSchemaV1.InferInput<I>) => Promise<InferSockaCallReturn<P>>\n\t: () => Promise<InferSockaCallReturn<P>>;\n\n/**\n * Infers the typed `session.send.*` method map for a contract.\n */\nexport type InferSockaSend<C extends SockaContractBound> = {\n\t[K in keyof C[\"calls\"]]: CallFn<C[\"calls\"][K]>;\n};\n\ntype HandlerOut<P extends SockaProcedureDef> =\n\tP[\"output\"] extends StandardSchemaV1\n\t\t?\n\t\t\t\t| StandardSchemaV1.InferOutput<P[\"output\"]>\n\t\t\t\t| Promise<StandardSchemaV1.InferOutput<P[\"output\"]>>\n\t\t: void | Promise<void>;\n\ntype HandlerFn<P extends SockaProcedureDef, TSession> = P extends {\n\tinput: infer I extends StandardSchemaV1;\n}\n\t? (input: StandardSchemaV1.InferInput<I>, session: TSession) => HandlerOut<P>\n\t: (session: TSession) => HandlerOut<P>;\n\n/**\n * Infers the typed server handler map for a contract. Handlers with an input\n * schema take `(input, session)`; calls without input take `(session)` only.\n * When `output` is present, the return value is validated and sent as `serverResponse`.\n * When `output` is omitted (fire-and-forget), the handler should return `void`; the\n * server does not send a success response.\n */\nexport type InferSockaHandlers<C extends SockaContractBound, TSession> = {\n\t[K in keyof C[\"calls\"]]: HandlerFn<C[\"calls\"][K], TSession>;\n};\n\ntype InferPushPayload<S extends StandardSchemaV1> =\n\tStandardSchemaV1.InferOutput<S>;\n\n/**\n * Payload type for a contract push (output of the push's Standard Schema).\n */\nexport type InferSockaPushPayload<\n\tC extends SockaContractBound,\n\tK extends keyof C[\"pushes\"],\n> = C[\"pushes\"][K] extends StandardSchemaV1\n\t? InferPushPayload<C[\"pushes\"][K]>\n\t: never;\n\n/**\n * Infers the typed push subscription handler map for a contract's `pushes`.\n */\nexport type InferSockaPushHandlers<C extends SockaContractBound> = {\n\t[K in keyof C[\"pushes\"]]: C[\"pushes\"][K] extends StandardSchemaV1\n\t\t? (payload: InferPushPayload<C[\"pushes\"][K]>) => void | Promise<void>\n\t\t: never;\n};\n\n/**\n * Creates a socka contract from call and push definitions. Pass Zod, Valibot,\n * ArkType, or any Standard Schema v1 schemas directly — no adapters needed.\n *\n * ```ts\n * export const myContract = defineSocka({\n * calls: {\n * list: { output: z.array(itemSchema) },\n * insert: { input: z.object({ item: itemSchema }), output: z.void() },\n * notify: { input: z.object({ text: z.string() }) },\n * },\n * });\n * ```\n *\n * Call names must not be {@link ReservedSockaProcedureName} — they would make\n * `session.send` thenable or unsafe as a plain method bag.\n */\nexport function defineSocka<const T extends SockaContractConfig>(\n\tconfig: T & { calls: ValidateSockaCallKeys<T[\"calls\"]> },\n): SockaContract<T> {\n\treturn {\n\t\tcalls: config.calls,\n\t\tpushes: (config.pushes ?? {}) as SockaContract<T>[\"pushes\"],\n\t};\n}\n"]}
@@ -1,14 +1,29 @@
1
1
  import { Context } from 'hono';
2
2
  import { BaseSession, SessionEnv, BaseWebSocketDO } from '@firtoz/websocket-do';
3
- import { S as SockaContract, a as SockaContractConfig, w as SockaWireFormat, c as InferSockaHandlers, z as SockaReportError, f as InferSockaPushPayload } from '../socka-report-error-CXwpAUgl.js';
4
- import { d as SockaPushSession } from '../SockaWebSocketSession-B1w7RAid.js';
3
+ import { c as SockaContractBound, y as SockaWireFormat, f as InferSockaHandlers, B as SockaReportError, h as InferSockaPushPayload } from '../socka-report-error-nTXJIzNb.js';
4
+ import { i as SockaPushSession } from '../SockaWebSocketSession-BaGvSerM.js';
5
5
  import '@standard-schema/spec';
6
6
 
7
7
  /** Session data with no fields — `createData` may be omitted (defaults to `{}`). */
8
- type EmptySockaSessionData = Record<string, never>;
9
- type SockaDoOuterSession<TContract extends SockaContract<SockaContractConfig>, TData, TEnv extends object> = SockaDoSession<TContract, TData, TEnv>;
8
+ type EmptySockaSessionData$2 = Record<string, never>;
9
+ /**
10
+ * Durable Object (or test double) that owns the socka contract, shared
11
+ * {@link SockaDoSession} map, and per-connection config for
12
+ * {@link SockaDoSession}.
13
+ */
14
+ interface SockaDoHost<TContract extends SockaContractBound, TData = EmptySockaSessionData$2, TEnv extends object = Cloudflare.Env, TSession extends SockaDoSession<TContract, TData, TEnv> = SockaDoSession<TContract, TData, TEnv>> {
15
+ readonly contract: TContract;
16
+ readonly sessions: Map<WebSocket, TSession>;
17
+ buildSockaSessionConfig(ctx: Context<{
18
+ Bindings: TEnv;
19
+ }> | undefined): SockaDoSessionConfigInput<TContract, TData, TEnv>;
20
+ }
21
+
22
+ /** Session data with no fields — `createData` may be omitted (defaults to `{}`). */
23
+ type EmptySockaSessionData$1 = Record<string, never>;
24
+ type SockaDoOuterSession<TContract extends SockaContractBound, TData, TEnv extends object> = SockaDoSession<TContract, TData, TEnv>;
10
25
  type SockaDoSessionCreateData<TData, TEnv extends object> = [TData] extends [
11
- EmptySockaSessionData
26
+ EmptySockaSessionData$1
12
27
  ] ? {
13
28
  createData?: (ctx: Context<{
14
29
  Bindings: TEnv;
@@ -18,8 +33,7 @@ type SockaDoSessionCreateData<TData, TEnv extends object> = [TData] extends [
18
33
  Bindings: TEnv;
19
34
  }>) => TData;
20
35
  };
21
- type SockaDoSessionConfig<TContract extends SockaContract<SockaContractConfig>, TData, TEnv extends object> = {
22
- contract: TContract;
36
+ type SockaDoSessionConfigFields<TContract extends SockaContractBound, TData, TEnv extends object> = {
23
37
  /** Default `"json"`. Use `"msgpack"` for binary frames (must match client). */
24
38
  wireFormat?: SockaWireFormat;
25
39
  handlers: InferSockaHandlers<TContract, SockaDoOuterSession<TContract, TData, TEnv>>;
@@ -39,14 +53,22 @@ type SockaDoSessionConfig<TContract extends SockaContract<SockaContractConfig>,
39
53
  onAttached?: (session: SockaDoOuterSession<TContract, TData, TEnv>) => void | Promise<void>;
40
54
  serializeJson?: (value: unknown) => string;
41
55
  deserializeJson?: (raw: string) => unknown;
42
- } & SockaDoSessionCreateData<TData, TEnv>;
56
+ };
57
+ /** {@link SockaDoSessionConfig} without `contract` — supplied by the DO host. */
58
+ type SockaDoSessionConfigInput<TContract extends SockaContractBound, TData, TEnv extends object> = SockaDoSessionConfigFields<TContract, TData, TEnv> & SockaDoSessionCreateData<TData, TEnv>;
59
+ type SockaDoSessionConfig<TContract extends SockaContractBound, TData, TEnv extends object> = {
60
+ contract: TContract;
61
+ } & SockaDoSessionConfigInput<TContract, TData, TEnv>;
43
62
  /**
44
63
  * Durable Object WebSocket session driven by a socka contract.
45
64
  * Dispatches client requests to typed handler functions, validates
46
65
  * input/output via Standard Schema, and auto-sends response/error frames.
47
66
  */
48
- declare class SockaDoSession<TContract extends SockaContract<SockaContractConfig>, TData = EmptySockaSessionData, TEnv extends object = Cloudflare.Env> extends BaseSession<TData, unknown, unknown, TEnv> implements SockaPushSession<TContract> {
67
+ declare class SockaDoSession<TContract extends SockaContractBound, TData = EmptySockaSessionData$1, TEnv extends object = Cloudflare.Env> extends BaseSession<TData, unknown, unknown, TEnv> implements SockaPushSession<TContract> {
49
68
  private socka;
69
+ constructor(websocket: WebSocket, host: SockaDoHost<TContract, TData, TEnv, SockaDoSession<TContract, TData, TEnv>>, attachCtx?: Context<{
70
+ Bindings: TEnv;
71
+ }> | undefined);
50
72
  constructor(websocket: WebSocket, sessions: Map<WebSocket, SockaDoSession<TContract, TData, TEnv>>, config: SockaDoSessionConfig<TContract, TData, TEnv>);
51
73
  handleRawMessage(rawMessage: string): Promise<void>;
52
74
  emitWireEvent(event: string, body: unknown): void;
@@ -72,19 +94,62 @@ declare class SockaDoSession<TContract extends SockaContract<SockaContractConfig
72
94
  }): boolean;
73
95
  }
74
96
 
75
- type SockaWebSocketDOOptions<TEnv extends object, TSession extends SockaDoSession<any, any, TEnv>> = {
76
- createSockaSession: (ctx: Context<{
77
- Bindings: TEnv;
78
- }> | undefined, websocket: WebSocket) => TSession | Promise<TSession>;
97
+ /** Session data with no fields `createData` may be omitted (defaults to `{}`). */
98
+ type EmptySockaSessionData = Record<string, never>;
99
+ type SockaWebSocketDOOptions = {
79
100
  /** Same as `pairServerWebSocketAcceptOptions` on `BaseWebSocketDOOptions` from `@firtoz/websocket-do`. */
80
101
  pairServerWebSocketAcceptOptions?: WebSocketAcceptOptions;
81
102
  };
82
103
  /**
83
- * Durable Object base class for WebSocket apps using {@link SockaDoSession}
84
- * (Standard Schema contract-driven).
104
+ * Low-level DO base when {@link createSockaSession} returns a custom
105
+ * {@link SockaDoSession} subclass. Most apps extend {@link SockaWebSocketDO}
106
+ * instead (default session type, three type parameters).
107
+ */
108
+ declare abstract class SockaWebSocketDOBase<TContract extends SockaContractBound, TData = EmptySockaSessionData, TSession extends SockaDoSession<TContract, TData, any> = SockaDoSession<TContract, TData, Cloudflare.Env>, TEnv extends SessionEnv<TSession> = SessionEnv<TSession>> extends BaseWebSocketDO<TSession, TEnv> {
109
+ protected abstract readonly contract: TContract;
110
+ constructor(ctx: DurableObjectState, env: TEnv, options?: SockaWebSocketDOOptions);
111
+ /**
112
+ * Per-connection socka config (handlers, `createData`, lifecycle hooks).
113
+ * `contract` comes from {@link contract} on this host — do not repeat it here.
114
+ *
115
+ * Called on each new attach and on hibernation resume (`ctx` is `undefined`
116
+ * on resume). **`createData`** runs only on fresh upgrade via
117
+ * {@link BaseSession.startFresh}, not on {@link BaseSession.resume}.
118
+ */
119
+ protected abstract buildSockaSessionConfig(ctx: Context<{
120
+ Bindings: TEnv;
121
+ }> | undefined): SockaDoSessionConfigInput<TContract, TData, TEnv>;
122
+ /**
123
+ * Factory for each connected WebSocket. Default: {@link SockaDoSession} wired
124
+ * to this host. Override when you need a {@link SockaDoSession} subclass.
125
+ */
126
+ protected createSockaSession(ctx: Context<{
127
+ Bindings: TEnv;
128
+ }> | undefined, websocket: WebSocket): TSession | Promise<TSession>;
129
+ /**
130
+ * Broadcast a contract-typed push to **every** connected session in this DO.
131
+ *
132
+ * Use from HTTP handlers, alarms, or other code paths with no originating
133
+ * WebSocket session. No-op when the room is empty.
134
+ */
135
+ protected broadcastPushToAll<K extends keyof TContract["pushes"] & string>(name: K, body: InferSockaPushPayload<TContract, K>): Promise<void>;
136
+ }
137
+ /**
138
+ * Durable Object base class for socka WebSocket rooms.
139
+ *
140
+ * Subclasses declare {@link contract} and {@link buildSockaSessionConfig}; the
141
+ * base wires WebSocket upgrade and hibernation resume to
142
+ * `new SockaDoSession(websocket, host)`.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * class ChatRoomDO extends SockaWebSocketDO<typeof chatContract, SessionData, Env> {
147
+ * protected readonly contract = chatContract;
148
+ * protected buildSockaSessionConfig(ctx) { … }
149
+ * }
150
+ * ```
85
151
  */
86
- declare abstract class SockaWebSocketDO<TSession extends SockaDoSession<any, any, any> = SockaDoSession<SockaContract<SockaContractConfig>, any, any>, TEnv extends SessionEnv<TSession> = SessionEnv<TSession>> extends BaseWebSocketDO<TSession, TEnv> {
87
- constructor(ctx: DurableObjectState, env: TEnv, options: SockaWebSocketDOOptions<TEnv, TSession>);
152
+ declare abstract class SockaWebSocketDO<TContract extends SockaContractBound, TData, TEnv extends object> extends SockaWebSocketDOBase<TContract, TData, SockaDoSession<TContract, TData, TEnv>, TEnv> {
88
153
  }
89
154
 
90
155
  /**
@@ -97,4 +162,4 @@ declare function toErrorReply(id: string, error: string): {
97
162
  error: string;
98
163
  };
99
164
 
100
- export { SockaDoSession, type SockaDoSessionConfig, SockaWebSocketDO, type SockaWebSocketDOOptions, toErrorReply };
165
+ export { type SockaDoHost, SockaDoSession, type SockaDoSessionConfig, type SockaDoSessionConfigInput, SockaWebSocketDO, SockaWebSocketDOBase, type SockaWebSocketDOOptions, toErrorReply };
package/dist/do/index.js CHANGED
@@ -1,7 +1,16 @@
1
- import { SockaWebSocketSession } from '../chunk-LVVCHLNW.js';
1
+ import { SockaWebSocketSession, broadcastContractPushToAll } from '../chunk-TTXY7O5P.js';
2
2
  import { reportSockaError } from '../chunk-IFIGKR3W.js';
3
3
  import { BaseSession, BaseWebSocketDO } from '@firtoz/websocket-do';
4
4
 
5
+ // src/do/SockaDoHost.ts
6
+ function hasSockaDoHostShape(value) {
7
+ return "contract" in value && "sessions" in value && "buildSockaSessionConfig" in value && value.sessions instanceof Map && typeof value.buildSockaSessionConfig === "function";
8
+ }
9
+ function isSockaDoHost(value) {
10
+ return !(value instanceof Map) && hasSockaDoHostShape(value);
11
+ }
12
+
13
+ // src/do/SockaDoSession.ts
5
14
  function runSockaDoSessionOnAttached(config, session) {
6
15
  const cb = config.onAttached;
7
16
  if (!cb) return;
@@ -31,8 +40,28 @@ function wrapHandlersForInnerSockaEngine(contract, userHandlers, outer) {
31
40
  }
32
41
  return out;
33
42
  }
43
+ function resolveSockaDoSessionInit(sessionsOrHost, configOrAttachCtx) {
44
+ if (isSockaDoHost(sessionsOrHost)) {
45
+ const attachCtx = configOrAttachCtx;
46
+ return {
47
+ sessions: sessionsOrHost.sessions,
48
+ config: {
49
+ contract: sessionsOrHost.contract,
50
+ ...sessionsOrHost.buildSockaSessionConfig(attachCtx)
51
+ }
52
+ };
53
+ }
54
+ return {
55
+ sessions: sessionsOrHost,
56
+ config: configOrAttachCtx
57
+ };
58
+ }
34
59
  var SockaDoSession = class extends BaseSession {
35
- constructor(websocket, sessions, config) {
60
+ constructor(websocket, sessionsOrHost, configOrAttachCtx) {
61
+ const { sessions, config } = resolveSockaDoSessionInit(
62
+ sessionsOrHost,
63
+ configOrAttachCtx
64
+ );
36
65
  const wireFormat = config.wireFormat ?? "json";
37
66
  super(
38
67
  websocket,
@@ -126,13 +155,40 @@ var SockaDoSession = class extends BaseSession {
126
155
  return this.peerCount(options) > 0;
127
156
  }
128
157
  };
129
- var SockaWebSocketDO = class extends BaseWebSocketDO {
158
+ var SockaWebSocketDOBase = class extends BaseWebSocketDO {
130
159
  constructor(ctx, env, options) {
131
160
  super(ctx, env, {
132
- createSession: (sessionCtx, websocket) => options.createSockaSession(sessionCtx, websocket),
133
- pairServerWebSocketAcceptOptions: options.pairServerWebSocketAcceptOptions
161
+ createSession: (sessionCtx, websocket) => this.createSockaSession(sessionCtx, websocket),
162
+ pairServerWebSocketAcceptOptions: options?.pairServerWebSocketAcceptOptions
134
163
  });
135
164
  }
165
+ /**
166
+ * Factory for each connected WebSocket. Default: {@link SockaDoSession} wired
167
+ * to this host. Override when you need a {@link SockaDoSession} subclass.
168
+ */
169
+ createSockaSession(ctx, websocket) {
170
+ const host = {
171
+ contract: this.contract,
172
+ sessions: this.sessions,
173
+ buildSockaSessionConfig: (attachCtx) => this.buildSockaSessionConfig(attachCtx)
174
+ };
175
+ return new SockaDoSession(
176
+ websocket,
177
+ host,
178
+ ctx
179
+ );
180
+ }
181
+ /**
182
+ * Broadcast a contract-typed push to **every** connected session in this DO.
183
+ *
184
+ * Use from HTTP handlers, alarms, or other code paths with no originating
185
+ * WebSocket session. No-op when the room is empty.
186
+ */
187
+ broadcastPushToAll(name, body) {
188
+ return broadcastContractPushToAll(this.sessions, this.contract, name, body);
189
+ }
190
+ };
191
+ var SockaWebSocketDO = class extends SockaWebSocketDOBase {
136
192
  };
137
193
 
138
194
  // src/do/dispatch.ts
@@ -140,6 +196,6 @@ function toErrorReply(id, error) {
140
196
  return { type: "error", id, error };
141
197
  }
142
198
 
143
- export { SockaDoSession, SockaWebSocketDO, toErrorReply };
199
+ export { SockaDoSession, SockaWebSocketDO, SockaWebSocketDOBase, toErrorReply };
144
200
  //# sourceMappingURL=index.js.map
145
201
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/do/SockaDoSession.ts","../../src/do/SockaWebSocketDO.ts","../../src/do/dispatch.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Context } from \"hono\";\nimport { BaseSession } from \"@firtoz/websocket-do\";\nimport type {\n\tInferSockaPushPayload,\n\tSockaContract,\n\tSockaContractConfig,\n\tInferSockaHandlers,\n} from \"../core/contract\";\nimport {\n\tSockaWebSocketSession,\n\ttype SockaPushSession,\n\ttype SockaWebSocketSessionConfigLoose,\n} from \"../server/SockaWebSocketSession\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport type { SockaReportError } from \"../core/socka-report-error\";\nimport type { SockaWireFormat } from \"../core/wire-codec\";\n\n/** Session data with no fields — `createData` may be omitted (defaults to `{}`). */\ntype EmptySockaSessionData = Record<string, never>;\n\ntype SockaDoOuterSession<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTEnv extends object,\n> = import(\"./SockaDoSession\").SockaDoSession<TContract, TData, TEnv>;\n\ntype SockaDoSessionCreateData<TData, TEnv extends object> = [TData] extends [\n\tEmptySockaSessionData,\n]\n\t? {\n\t\t\tcreateData?: (ctx: Context<{ Bindings: TEnv }>) => TData;\n\t\t}\n\t: {\n\t\t\tcreateData: (ctx: Context<{ Bindings: TEnv }>) => TData;\n\t\t};\n\nexport type SockaDoSessionConfig<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTEnv extends object,\n> = {\n\tcontract: TContract;\n\t/** Default `\"json\"`. Use `\"msgpack\"` for binary frames (must match client). */\n\twireFormat?: SockaWireFormat;\n\thandlers: InferSockaHandlers<\n\t\tTContract,\n\t\tSockaDoOuterSession<TContract, TData, TEnv>\n\t>;\n\thandleClose: (\n\t\tsession: SockaDoOuterSession<TContract, TData, TEnv>,\n\t) => Promise<void>;\n\tonHandlerError?: (\n\t\terror: unknown,\n\t\trpcName: string,\n\t\tinput: unknown,\n\t\tsession: SockaDoOuterSession<TContract, TData, TEnv>,\n\t) => void;\n\tonValidationError?: (\n\t\terror: unknown,\n\t\toriginalMessage: unknown,\n\t) => Promise<void>;\n\t/**\n\t * Optional sink for non-RPC failures (e.g. `onAttached`). Defaults to\n\t * `console.error`; see `SockaReportError` in `@firtoz/socka/core`.\n\t */\n\treportError?: (event: SockaReportError) => void;\n\t/**\n\t * Called after this session is registered in the DO `sessions` map (next\n\t * microtask). Use for join broadcasts and other logic that must run only\n\t * when peers can see this connection.\n\t */\n\tonAttached?: (\n\t\tsession: SockaDoOuterSession<TContract, TData, TEnv>,\n\t) => void | Promise<void>;\n\tserializeJson?: (value: unknown) => string;\n\tdeserializeJson?: (raw: string) => unknown;\n} & SockaDoSessionCreateData<TData, TEnv>;\n\nfunction runSockaDoSessionOnAttached<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTEnv extends object,\n>(\n\tconfig: SockaDoSessionConfig<TContract, TData, TEnv>,\n\tsession: SockaDoSession<TContract, TData, TEnv>,\n): void {\n\tconst cb = config.onAttached;\n\tif (!cb) return;\n\ttry {\n\t\tconst result = cb(session);\n\t\tvoid Promise.resolve(result).catch((error: unknown) => {\n\t\t\treportSockaError(config.reportError, {\n\t\t\t\tkind: \"serverOnAttached\",\n\t\t\t\terror,\n\t\t\t});\n\t\t});\n\t} catch (error) {\n\t\treportSockaError(config.reportError, { kind: \"serverOnAttached\", error });\n\t}\n}\n\nfunction wrapHandlersForInnerSockaEngine<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTEnv extends object,\n>(\n\tcontract: TContract,\n\tuserHandlers: InferSockaHandlers<\n\t\tTContract,\n\t\tSockaDoSession<TContract, TData, TEnv>\n\t>,\n\touter: SockaDoSession<TContract, TData, TEnv>,\n): InferSockaHandlers<\n\tTContract,\n\tSockaWebSocketSession<TContract, EmptySockaSessionData>\n> {\n\tconst calls = contract.calls;\n\tconst out: Record<\n\t\tstring,\n\t\t| ((\n\t\t\t\tinput: unknown,\n\t\t\t\tinner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t ) => unknown | Promise<unknown>)\n\t\t| ((\n\t\t\t\tinner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t ) => unknown | Promise<unknown>)\n\t> = {};\n\n\tfor (const key of Object.keys(calls) as Array<keyof typeof calls & string>) {\n\t\tconst proc = calls[key];\n\t\tconst userFn = userHandlers[key as keyof typeof userHandlers];\n\t\tif (proc.input) {\n\t\t\tout[key] = (\n\t\t\t\tinput,\n\t\t\t\t_inner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t\t) =>\n\t\t\t\t(\n\t\t\t\t\tuserFn as (\n\t\t\t\t\t\ti: unknown,\n\t\t\t\t\t\ts: SockaDoSession<TContract, TData, TEnv>,\n\t\t\t\t\t) => unknown | Promise<unknown>\n\t\t\t\t)(input, outer);\n\t\t} else {\n\t\t\tout[key] = (\n\t\t\t\t_inner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t\t) =>\n\t\t\t\t(\n\t\t\t\t\tuserFn as (\n\t\t\t\t\t\ts: SockaDoSession<TContract, TData, TEnv>,\n\t\t\t\t\t) => unknown | Promise<unknown>\n\t\t\t\t)(outer);\n\t\t}\n\t}\n\n\treturn out as InferSockaHandlers<\n\t\tTContract,\n\t\tSockaWebSocketSession<TContract, EmptySockaSessionData>\n\t>;\n}\n\n/**\n * Durable Object WebSocket session driven by a socka contract.\n * Dispatches client requests to typed handler functions, validates\n * input/output via Standard Schema, and auto-sends response/error frames.\n */\nexport class SockaDoSession<\n\t\tTContract extends SockaContract<SockaContractConfig>,\n\t\tTData = EmptySockaSessionData,\n\t\tTEnv extends object = Cloudflare.Env,\n\t>\n\textends BaseSession<TData, unknown, unknown, TEnv>\n\timplements SockaPushSession<TContract>\n{\n\tprivate socka!: SockaWebSocketSession<TContract, EmptySockaSessionData>;\n\n\tconstructor(\n\t\twebsocket: WebSocket,\n\t\tsessions: Map<WebSocket, SockaDoSession<TContract, TData, TEnv>>,\n\t\tconfig: SockaDoSessionConfig<TContract, TData, TEnv>,\n\t) {\n\t\tconst wireFormat = config.wireFormat ?? \"json\";\n\t\tsuper(\n\t\t\twebsocket,\n\t\t\tsessions as Map<WebSocket, BaseSession<TData, unknown, unknown, TEnv>>,\n\t\t\t{\n\t\t\t\tcreateData:\n\t\t\t\t\tconfig.createData ??\n\t\t\t\t\t((_ctx: Context<{ Bindings: TEnv }>) => ({}) as TData),\n\t\t\t\thandleMessage: async () => {\n\t\t\t\t\t// Raw message handling goes through handleRawMessage / handleBufferMessage\n\t\t\t\t},\n\t\t\t\thandleBufferMessage: async (message) => {\n\t\t\t\t\tawait this.socka.handleBinaryMessage(message);\n\t\t\t\t},\n\t\t\t\thandleClose: async (baseSession) => {\n\t\t\t\t\tawait config.handleClose(\n\t\t\t\t\t\tbaseSession as SockaDoSession<TContract, TData, TEnv>,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t\tconst sockaConfig: SockaWebSocketSessionConfigLoose<\n\t\t\tTContract,\n\t\t\tEmptySockaSessionData\n\t\t> = {\n\t\t\tstrictUpgradeRequest: false,\n\t\t\tcontract: config.contract,\n\t\t\twireFormat,\n\t\t\thandlers: wrapHandlersForInnerSockaEngine(\n\t\t\t\tconfig.contract,\n\t\t\t\tconfig.handlers,\n\t\t\t\tthis,\n\t\t\t),\n\t\t\thandleClose: async () => {\n\t\t\t\t// Outer DO lifecycle uses SockaDoSessionConfig.handleClose; inner engine no-op.\n\t\t\t},\n\t\t\tonHandlerError: config.onHandlerError\n\t\t\t\t? (err, rpcName, input, _inner) => {\n\t\t\t\t\t\tconfig.onHandlerError?.(err, rpcName, input, this);\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tonValidationError: config.onValidationError,\n\t\t\tserializeJson: config.serializeJson,\n\t\t\tdeserializeJson: config.deserializeJson,\n\t\t};\n\t\tthis.socka = new SockaWebSocketSession(\n\t\t\twebsocket,\n\t\t\tsessions as unknown as Map<\n\t\t\t\tWebSocket,\n\t\t\t\tSockaWebSocketSession<TContract, EmptySockaSessionData>\n\t\t\t>,\n\t\t\tsockaConfig,\n\t\t);\n\t\t// Defer past the outer `await createSession()` continuation so\n\t\t// `BaseSession.startFresh` has run and `session.data` exists (single\n\t\t// `queueMicrotask` runs before that continuation).\n\t\tqueueMicrotask(() => {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\trunSockaDoSessionOnAttached(config, this);\n\t\t\t});\n\t\t});\n\t}\n\n\tpublic async handleRawMessage(rawMessage: string): Promise<void> {\n\t\treturn this.socka.handleRawMessage(rawMessage);\n\t}\n\n\tpublic emitWireEvent(event: string, body: unknown): void {\n\t\tthis.socka.emitWireEvent(event, body);\n\t}\n\n\tpublic emitPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t): Promise<void> {\n\t\treturn this.socka.emitPush(name, body);\n\t}\n\n\tpublic broadcastPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t\texcludeSelf = false,\n\t): Promise<void> {\n\t\treturn this.socka.broadcastPush(name, body, excludeSelf);\n\t}\n\n\t/**\n\t * {@link SockaWebSocketSession.listPeers} for this Durable Object room.\n\t */\n\tpublic listPeers(options?: { excludeSelf?: boolean }): TData[] {\n\t\tconst out: TData[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(s.data);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/**\n\t * Like {@link listPeers} but maps each peer {@link SockaDoSession}.\n\t */\n\tpublic listPeersWith<R>(\n\t\tmap: (session: SockaDoSession<TContract, TData, TEnv>) => R,\n\t\toptions?: { excludeSelf?: boolean },\n\t): R[] {\n\t\tconst out: R[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(map(s as SockaDoSession<TContract, TData, TEnv>));\n\t\t}\n\t\treturn out;\n\t}\n\n\tpublic peerCount(options?: { excludeSelf?: boolean }): number {\n\t\tlet n = 0;\n\t\tfor (const [ws] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tn += 1;\n\t\t}\n\t\treturn n;\n\t}\n\n\tpublic hasPeers(options?: { excludeSelf?: boolean }): boolean {\n\t\treturn this.peerCount(options) > 0;\n\t}\n}\n","import type { Context } from \"hono\";\nimport type { SessionEnv } from \"@firtoz/websocket-do\";\nimport { BaseWebSocketDO } from \"@firtoz/websocket-do\";\nimport type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport type { SockaDoSession } from \"./SockaDoSession\";\n\nexport type SockaWebSocketDOOptions<\n\tTEnv extends object,\n\t// `any` contract slot: concrete sessions use `defineSocka` contracts that do\n\t// not assign to `SockaContract<SockaContractConfig>` under strict generics.\n\tTSession extends SockaDoSession<\n\t\t// biome-ignore lint/suspicious/noExplicitAny: session family type erasure\n\t\tany,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: session family type erasure\n\t\tany,\n\t\tTEnv\n\t>,\n> = {\n\tcreateSockaSession: (\n\t\tctx: Context<{ Bindings: TEnv }> | undefined,\n\t\twebsocket: WebSocket,\n\t) => TSession | Promise<TSession>;\n\t/** Same as `pairServerWebSocketAcceptOptions` on `BaseWebSocketDOOptions` from `@firtoz/websocket-do`. */\n\tpairServerWebSocketAcceptOptions?: WebSocketAcceptOptions;\n};\n\n/**\n * Durable Object base class for WebSocket apps using {@link SockaDoSession}\n * (Standard Schema contract-driven).\n */\nexport abstract class SockaWebSocketDO<\n\tTSession extends SockaDoSession<\n\t\t// biome-ignore lint/suspicious/noExplicitAny: session family type erasure\n\t\tany,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: session family type erasure\n\t\tany,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: session family type erasure\n\t\tany\n\t> = SockaDoSession<\n\t\tSockaContract<SockaContractConfig>,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: session family type erasure\n\t\tany,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: session family type erasure\n\t\tany\n\t>,\n\tTEnv extends SessionEnv<TSession> = SessionEnv<TSession>,\n> extends BaseWebSocketDO<TSession, TEnv> {\n\tconstructor(\n\t\tctx: DurableObjectState,\n\t\tenv: TEnv,\n\t\toptions: SockaWebSocketDOOptions<TEnv, TSession>,\n\t) {\n\t\tsuper(ctx, env, {\n\t\t\tcreateSession: (sessionCtx, websocket) =>\n\t\t\t\toptions.createSockaSession(sessionCtx, websocket),\n\t\t\tpairServerWebSocketAcceptOptions:\n\t\t\t\toptions.pairServerWebSocketAcceptOptions,\n\t\t});\n\t}\n}\n","/**\n * Shared error reply shape for correlate-by-id RPC when the handler throws.\n * Narrow at the call site to your server message union if needed.\n */\nexport function toErrorReply(\n\tid: string,\n\terror: string,\n): {\n\ttype: \"error\";\n\tid: string;\n\terror: string;\n} {\n\treturn { type: \"error\", id, error };\n}\n"]}
1
+ {"version":3,"sources":["../../src/do/SockaDoHost.ts","../../src/do/SockaDoSession.ts","../../src/do/SockaWebSocketDO.ts","../../src/do/dispatch.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Context } from \"hono\";\nimport type { SockaContractBound } from \"../core/contract\";\nimport type {\n\tSockaDoSession,\n\tSockaDoSessionConfigInput,\n} from \"./SockaDoSession\";\n\n/** Session data with no fields — `createData` may be omitted (defaults to `{}`). */\ntype EmptySockaSessionData = Record<string, never>;\n\n/**\n * Durable Object (or test double) that owns the socka contract, shared\n * {@link SockaDoSession} map, and per-connection config for\n * {@link SockaDoSession}.\n */\nexport interface SockaDoHost<\n\tTContract extends SockaContractBound,\n\tTData = EmptySockaSessionData,\n\tTEnv extends object = Cloudflare.Env,\n\tTSession extends SockaDoSession<TContract, TData, TEnv> = SockaDoSession<\n\t\tTContract,\n\t\tTData,\n\t\tTEnv\n\t>,\n> {\n\treadonly contract: TContract;\n\treadonly sessions: Map<WebSocket, TSession>;\n\tbuildSockaSessionConfig(\n\t\tctx: Context<{ Bindings: TEnv }> | undefined,\n\t): SockaDoSessionConfigInput<TContract, TData, TEnv>;\n}\n\nfunction hasSockaDoHostShape(value: object): value is {\n\tcontract: SockaContractBound;\n\tsessions: Map<WebSocket, unknown>;\n\tbuildSockaSessionConfig: (ctx: unknown) => unknown;\n} {\n\treturn (\n\t\t\"contract\" in value &&\n\t\t\"sessions\" in value &&\n\t\t\"buildSockaSessionConfig\" in value &&\n\t\tvalue.sessions instanceof Map &&\n\t\ttypeof value.buildSockaSessionConfig === \"function\"\n\t);\n}\n\n/** @internal */\nexport function isSockaDoHost<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n\tTSession extends SockaDoSession<TContract, TData, TEnv>,\n>(\n\tvalue:\n\t\t| Map<WebSocket, SockaDoSession<TContract, TData, TEnv>>\n\t\t| SockaDoHost<TContract, TData, TEnv, TSession>,\n): value is SockaDoHost<TContract, TData, TEnv, TSession> {\n\treturn !(value instanceof Map) && hasSockaDoHostShape(value);\n}\n","import type { Context } from \"hono\";\nimport { BaseSession } from \"@firtoz/websocket-do\";\nimport type {\n\tInferSockaPushPayload,\n\tSockaContractBound,\n\tInferSockaHandlers,\n} from \"../core/contract\";\nimport {\n\tSockaWebSocketSession,\n\ttype SockaPushSession,\n\ttype SockaWebSocketSessionConfigLoose,\n} from \"../server/SockaWebSocketSession\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport type { SockaReportError } from \"../core/socka-report-error\";\nimport type { SockaWireFormat } from \"../core/wire-codec\";\nimport type { SockaDoHost } from \"./SockaDoHost\";\nimport { isSockaDoHost } from \"./SockaDoHost\";\n\n/** Session data with no fields — `createData` may be omitted (defaults to `{}`). */\ntype EmptySockaSessionData = Record<string, never>;\n\ntype SockaDoOuterSession<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n> = import(\"./SockaDoSession\").SockaDoSession<TContract, TData, TEnv>;\n\ntype SockaDoSessionCreateData<TData, TEnv extends object> = [TData] extends [\n\tEmptySockaSessionData,\n]\n\t? {\n\t\t\tcreateData?: (ctx: Context<{ Bindings: TEnv }>) => TData;\n\t\t}\n\t: {\n\t\t\tcreateData: (ctx: Context<{ Bindings: TEnv }>) => TData;\n\t\t};\n\ntype SockaDoSessionConfigFields<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n> = {\n\t/** Default `\"json\"`. Use `\"msgpack\"` for binary frames (must match client). */\n\twireFormat?: SockaWireFormat;\n\thandlers: InferSockaHandlers<\n\t\tTContract,\n\t\tSockaDoOuterSession<TContract, TData, TEnv>\n\t>;\n\thandleClose: (\n\t\tsession: SockaDoOuterSession<TContract, TData, TEnv>,\n\t) => Promise<void>;\n\tonHandlerError?: (\n\t\terror: unknown,\n\t\trpcName: string,\n\t\tinput: unknown,\n\t\tsession: SockaDoOuterSession<TContract, TData, TEnv>,\n\t) => void;\n\tonValidationError?: (\n\t\terror: unknown,\n\t\toriginalMessage: unknown,\n\t) => Promise<void>;\n\t/**\n\t * Optional sink for non-RPC failures (e.g. `onAttached`). Defaults to\n\t * `console.error`; see `SockaReportError` in `@firtoz/socka/core`.\n\t */\n\treportError?: (event: SockaReportError) => void;\n\t/**\n\t * Called after this session is registered in the DO `sessions` map (next\n\t * microtask). Use for join broadcasts and other logic that must run only\n\t * when peers can see this connection.\n\t */\n\tonAttached?: (\n\t\tsession: SockaDoOuterSession<TContract, TData, TEnv>,\n\t) => void | Promise<void>;\n\tserializeJson?: (value: unknown) => string;\n\tdeserializeJson?: (raw: string) => unknown;\n};\n\n/** {@link SockaDoSessionConfig} without `contract` — supplied by the DO host. */\nexport type SockaDoSessionConfigInput<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n> = SockaDoSessionConfigFields<TContract, TData, TEnv> &\n\tSockaDoSessionCreateData<TData, TEnv>;\n\nexport type SockaDoSessionConfig<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n> = {\n\tcontract: TContract;\n} & SockaDoSessionConfigInput<TContract, TData, TEnv>;\n\nfunction runSockaDoSessionOnAttached<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n>(\n\tconfig: SockaDoSessionConfig<TContract, TData, TEnv>,\n\tsession: SockaDoSession<TContract, TData, TEnv>,\n): void {\n\tconst cb = config.onAttached;\n\tif (!cb) return;\n\ttry {\n\t\tconst result = cb(session);\n\t\tvoid Promise.resolve(result).catch((error: unknown) => {\n\t\t\treportSockaError(config.reportError, {\n\t\t\t\tkind: \"serverOnAttached\",\n\t\t\t\terror,\n\t\t\t});\n\t\t});\n\t} catch (error) {\n\t\treportSockaError(config.reportError, { kind: \"serverOnAttached\", error });\n\t}\n}\n\nfunction wrapHandlersForInnerSockaEngine<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n>(\n\tcontract: TContract,\n\tuserHandlers: InferSockaHandlers<\n\t\tTContract,\n\t\tSockaDoSession<TContract, TData, TEnv>\n\t>,\n\touter: SockaDoSession<TContract, TData, TEnv>,\n): InferSockaHandlers<\n\tTContract,\n\tSockaWebSocketSession<TContract, EmptySockaSessionData>\n> {\n\tconst calls = contract.calls;\n\tconst out: Record<\n\t\tstring,\n\t\t| ((\n\t\t\t\tinput: unknown,\n\t\t\t\tinner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t ) => unknown | Promise<unknown>)\n\t\t| ((\n\t\t\t\tinner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t ) => unknown | Promise<unknown>)\n\t> = {};\n\n\tfor (const key of Object.keys(calls) as Array<keyof typeof calls & string>) {\n\t\tconst proc = calls[key];\n\t\tconst userFn = userHandlers[key as keyof typeof userHandlers];\n\t\tif (proc.input) {\n\t\t\tout[key] = (\n\t\t\t\tinput,\n\t\t\t\t_inner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t\t) =>\n\t\t\t\t(\n\t\t\t\t\tuserFn as (\n\t\t\t\t\t\ti: unknown,\n\t\t\t\t\t\ts: SockaDoSession<TContract, TData, TEnv>,\n\t\t\t\t\t) => unknown | Promise<unknown>\n\t\t\t\t)(input, outer);\n\t\t} else {\n\t\t\tout[key] = (\n\t\t\t\t_inner: SockaWebSocketSession<TContract, EmptySockaSessionData>,\n\t\t\t) =>\n\t\t\t\t(\n\t\t\t\t\tuserFn as (\n\t\t\t\t\t\ts: SockaDoSession<TContract, TData, TEnv>,\n\t\t\t\t\t) => unknown | Promise<unknown>\n\t\t\t\t)(outer);\n\t\t}\n\t}\n\n\treturn out as InferSockaHandlers<\n\t\tTContract,\n\t\tSockaWebSocketSession<TContract, EmptySockaSessionData>\n\t>;\n}\n\nfunction resolveSockaDoSessionInit<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n\tTSession extends SockaDoSession<TContract, TData, TEnv>,\n>(\n\tsessionsOrHost:\n\t\t| Map<WebSocket, SockaDoSession<TContract, TData, TEnv>>\n\t\t| SockaDoHost<TContract, TData, TEnv, TSession>,\n\tconfigOrAttachCtx?:\n\t\t| SockaDoSessionConfig<TContract, TData, TEnv>\n\t\t| Context<{ Bindings: TEnv }>,\n): {\n\tsessions: Map<WebSocket, SockaDoSession<TContract, TData, TEnv>>;\n\tconfig: SockaDoSessionConfig<TContract, TData, TEnv>;\n} {\n\tif (isSockaDoHost(sessionsOrHost)) {\n\t\tconst attachCtx = configOrAttachCtx as\n\t\t\t| Context<{ Bindings: TEnv }>\n\t\t\t| undefined;\n\t\treturn {\n\t\t\tsessions: sessionsOrHost.sessions as Map<\n\t\t\t\tWebSocket,\n\t\t\t\tSockaDoSession<TContract, TData, TEnv>\n\t\t\t>,\n\t\t\tconfig: {\n\t\t\t\tcontract: sessionsOrHost.contract,\n\t\t\t\t...sessionsOrHost.buildSockaSessionConfig(attachCtx),\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tsessions: sessionsOrHost,\n\t\tconfig: configOrAttachCtx as SockaDoSessionConfig<TContract, TData, TEnv>,\n\t};\n}\n\n/**\n * Durable Object WebSocket session driven by a socka contract.\n * Dispatches client requests to typed handler functions, validates\n * input/output via Standard Schema, and auto-sends response/error frames.\n */\nexport class SockaDoSession<\n\t\tTContract extends SockaContractBound,\n\t\tTData = EmptySockaSessionData,\n\t\tTEnv extends object = Cloudflare.Env,\n\t>\n\textends BaseSession<TData, unknown, unknown, TEnv>\n\timplements SockaPushSession<TContract>\n{\n\tprivate socka!: SockaWebSocketSession<TContract, EmptySockaSessionData>;\n\n\tconstructor(\n\t\twebsocket: WebSocket,\n\t\thost: SockaDoHost<\n\t\t\tTContract,\n\t\t\tTData,\n\t\t\tTEnv,\n\t\t\tSockaDoSession<TContract, TData, TEnv>\n\t\t>,\n\t\tattachCtx?: Context<{ Bindings: TEnv }> | undefined,\n\t);\n\tconstructor(\n\t\twebsocket: WebSocket,\n\t\tsessions: Map<WebSocket, SockaDoSession<TContract, TData, TEnv>>,\n\t\tconfig: SockaDoSessionConfig<TContract, TData, TEnv>,\n\t);\n\tconstructor(\n\t\twebsocket: WebSocket,\n\t\tsessionsOrHost:\n\t\t\t| Map<WebSocket, SockaDoSession<TContract, TData, TEnv>>\n\t\t\t| SockaDoHost<\n\t\t\t\t\tTContract,\n\t\t\t\t\tTData,\n\t\t\t\t\tTEnv,\n\t\t\t\t\tSockaDoSession<TContract, TData, TEnv>\n\t\t\t >,\n\t\tconfigOrAttachCtx?:\n\t\t\t| SockaDoSessionConfig<TContract, TData, TEnv>\n\t\t\t| Context<{ Bindings: TEnv }>,\n\t) {\n\t\tconst { sessions, config } = resolveSockaDoSessionInit(\n\t\t\tsessionsOrHost,\n\t\t\tconfigOrAttachCtx,\n\t\t);\n\t\tconst wireFormat = config.wireFormat ?? \"json\";\n\t\tsuper(\n\t\t\twebsocket,\n\t\t\tsessions as Map<WebSocket, BaseSession<TData, unknown, unknown, TEnv>>,\n\t\t\t{\n\t\t\t\tcreateData:\n\t\t\t\t\tconfig.createData ??\n\t\t\t\t\t((_ctx: Context<{ Bindings: TEnv }>) => ({}) as TData),\n\t\t\t\thandleMessage: async () => {\n\t\t\t\t\t// Raw message handling goes through handleRawMessage / handleBufferMessage\n\t\t\t\t},\n\t\t\t\thandleBufferMessage: async (message) => {\n\t\t\t\t\tawait this.socka.handleBinaryMessage(message);\n\t\t\t\t},\n\t\t\t\thandleClose: async (baseSession) => {\n\t\t\t\t\tawait config.handleClose(\n\t\t\t\t\t\tbaseSession as SockaDoSession<TContract, TData, TEnv>,\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t\tconst sockaConfig: SockaWebSocketSessionConfigLoose<\n\t\t\tTContract,\n\t\t\tEmptySockaSessionData\n\t\t> = {\n\t\t\tstrictUpgradeRequest: false,\n\t\t\tcontract: config.contract,\n\t\t\twireFormat,\n\t\t\thandlers: wrapHandlersForInnerSockaEngine(\n\t\t\t\tconfig.contract,\n\t\t\t\tconfig.handlers,\n\t\t\t\tthis,\n\t\t\t),\n\t\t\thandleClose: async () => {\n\t\t\t\t// Outer DO lifecycle uses SockaDoSessionConfig.handleClose; inner engine no-op.\n\t\t\t},\n\t\t\tonHandlerError: config.onHandlerError\n\t\t\t\t? (err, rpcName, input, _inner) => {\n\t\t\t\t\t\tconfig.onHandlerError?.(err, rpcName, input, this);\n\t\t\t\t\t}\n\t\t\t\t: undefined,\n\t\t\tonValidationError: config.onValidationError,\n\t\t\tserializeJson: config.serializeJson,\n\t\t\tdeserializeJson: config.deserializeJson,\n\t\t};\n\t\tthis.socka = new SockaWebSocketSession(\n\t\t\twebsocket,\n\t\t\tsessions as unknown as Map<\n\t\t\t\tWebSocket,\n\t\t\t\tSockaWebSocketSession<TContract, EmptySockaSessionData>\n\t\t\t>,\n\t\t\tsockaConfig,\n\t\t);\n\t\t// Defer past the outer `await createSession()` continuation so\n\t\t// `BaseSession.startFresh` has run and `session.data` exists (single\n\t\t// `queueMicrotask` runs before that continuation).\n\t\tqueueMicrotask(() => {\n\t\t\tqueueMicrotask(() => {\n\t\t\t\trunSockaDoSessionOnAttached(config, this);\n\t\t\t});\n\t\t});\n\t}\n\n\tpublic async handleRawMessage(rawMessage: string): Promise<void> {\n\t\treturn this.socka.handleRawMessage(rawMessage);\n\t}\n\n\tpublic emitWireEvent(event: string, body: unknown): void {\n\t\tthis.socka.emitWireEvent(event, body);\n\t}\n\n\tpublic emitPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t): Promise<void> {\n\t\treturn this.socka.emitPush(name, body);\n\t}\n\n\tpublic broadcastPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t\texcludeSelf = false,\n\t): Promise<void> {\n\t\treturn this.socka.broadcastPush(name, body, excludeSelf);\n\t}\n\n\t/**\n\t * {@link SockaWebSocketSession.listPeers} for this Durable Object room.\n\t */\n\tpublic listPeers(options?: { excludeSelf?: boolean }): TData[] {\n\t\tconst out: TData[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(s.data);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/**\n\t * Like {@link listPeers} but maps each peer {@link SockaDoSession}.\n\t */\n\tpublic listPeersWith<R>(\n\t\tmap: (session: SockaDoSession<TContract, TData, TEnv>) => R,\n\t\toptions?: { excludeSelf?: boolean },\n\t): R[] {\n\t\tconst out: R[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(map(s as SockaDoSession<TContract, TData, TEnv>));\n\t\t}\n\t\treturn out;\n\t}\n\n\tpublic peerCount(options?: { excludeSelf?: boolean }): number {\n\t\tlet n = 0;\n\t\tfor (const [ws] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tn += 1;\n\t\t}\n\t\treturn n;\n\t}\n\n\tpublic hasPeers(options?: { excludeSelf?: boolean }): boolean {\n\t\treturn this.peerCount(options) > 0;\n\t}\n}\n","import type { Context } from \"hono\";\nimport type { SessionEnv } from \"@firtoz/websocket-do\";\nimport { BaseWebSocketDO } from \"@firtoz/websocket-do\";\nimport type {\n\tInferSockaPushPayload,\n\tSockaContractBound,\n} from \"../core/contract\";\nimport { broadcastContractPushToAll } from \"../server/SockaWebSocketSession\";\nimport type { SockaDoHost } from \"./SockaDoHost\";\nimport {\n\tSockaDoSession,\n\ttype SockaDoSessionConfigInput,\n} from \"./SockaDoSession\";\n\n/** Session data with no fields — `createData` may be omitted (defaults to `{}`). */\ntype EmptySockaSessionData = Record<string, never>;\n\nexport type SockaWebSocketDOOptions = {\n\t/** Same as `pairServerWebSocketAcceptOptions` on `BaseWebSocketDOOptions` from `@firtoz/websocket-do`. */\n\tpairServerWebSocketAcceptOptions?: WebSocketAcceptOptions;\n};\n\n/**\n * Low-level DO base when {@link createSockaSession} returns a custom\n * {@link SockaDoSession} subclass. Most apps extend {@link SockaWebSocketDO}\n * instead (default session type, three type parameters).\n */\nexport abstract class SockaWebSocketDOBase<\n\tTContract extends SockaContractBound,\n\tTData = EmptySockaSessionData,\n\tTSession extends SockaDoSession<\n\t\tTContract,\n\t\tTData,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: TEnv inferred from TSession via SessionEnv\n\t\tany\n\t> = SockaDoSession<TContract, TData, Cloudflare.Env>,\n\tTEnv extends SessionEnv<TSession> = SessionEnv<TSession>,\n> extends BaseWebSocketDO<TSession, TEnv> {\n\tprotected abstract readonly contract: TContract;\n\n\tconstructor(\n\t\tctx: DurableObjectState,\n\t\tenv: TEnv,\n\t\toptions?: SockaWebSocketDOOptions,\n\t) {\n\t\tsuper(ctx, env, {\n\t\t\tcreateSession: (sessionCtx, websocket) =>\n\t\t\t\tthis.createSockaSession(sessionCtx, websocket),\n\t\t\tpairServerWebSocketAcceptOptions:\n\t\t\t\toptions?.pairServerWebSocketAcceptOptions,\n\t\t});\n\t}\n\n\t/**\n\t * Per-connection socka config (handlers, `createData`, lifecycle hooks).\n\t * `contract` comes from {@link contract} on this host — do not repeat it here.\n\t *\n\t * Called on each new attach and on hibernation resume (`ctx` is `undefined`\n\t * on resume). **`createData`** runs only on fresh upgrade via\n\t * {@link BaseSession.startFresh}, not on {@link BaseSession.resume}.\n\t */\n\tprotected abstract buildSockaSessionConfig(\n\t\tctx: Context<{ Bindings: TEnv }> | undefined,\n\t): SockaDoSessionConfigInput<TContract, TData, TEnv>;\n\n\t/**\n\t * Factory for each connected WebSocket. Default: {@link SockaDoSession} wired\n\t * to this host. Override when you need a {@link SockaDoSession} subclass.\n\t */\n\tprotected createSockaSession(\n\t\tctx: Context<{ Bindings: TEnv }> | undefined,\n\t\twebsocket: WebSocket,\n\t): TSession | Promise<TSession> {\n\t\tconst host: SockaDoHost<TContract, TData, TEnv, TSession> = {\n\t\t\tcontract: this.contract,\n\t\t\tsessions: this.sessions,\n\t\t\tbuildSockaSessionConfig: (attachCtx) =>\n\t\t\t\tthis.buildSockaSessionConfig(attachCtx),\n\t\t};\n\t\treturn new SockaDoSession<TContract, TData, TEnv>(\n\t\t\twebsocket,\n\t\t\thost,\n\t\t\tctx,\n\t\t) as TSession;\n\t}\n\n\t/**\n\t * Broadcast a contract-typed push to **every** connected session in this DO.\n\t *\n\t * Use from HTTP handlers, alarms, or other code paths with no originating\n\t * WebSocket session. No-op when the room is empty.\n\t */\n\tprotected broadcastPushToAll<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t): Promise<void> {\n\t\treturn broadcastContractPushToAll(this.sessions, this.contract, name, body);\n\t}\n}\n\n/**\n * Durable Object base class for socka WebSocket rooms.\n *\n * Subclasses declare {@link contract} and {@link buildSockaSessionConfig}; the\n * base wires WebSocket upgrade and hibernation resume to\n * `new SockaDoSession(websocket, host)`.\n *\n * @example\n * ```ts\n * class ChatRoomDO extends SockaWebSocketDO<typeof chatContract, SessionData, Env> {\n * protected readonly contract = chatContract;\n * protected buildSockaSessionConfig(ctx) { … }\n * }\n * ```\n */\nexport abstract class SockaWebSocketDO<\n\tTContract extends SockaContractBound,\n\tTData,\n\tTEnv extends object,\n> extends SockaWebSocketDOBase<\n\tTContract,\n\tTData,\n\tSockaDoSession<TContract, TData, TEnv>,\n\tTEnv\n> {}\n","/**\n * Shared error reply shape for correlate-by-id RPC when the handler throws.\n * Narrow at the call site to your server message union if needed.\n */\nexport function toErrorReply(\n\tid: string,\n\terror: string,\n): {\n\ttype: \"error\";\n\tid: string;\n\terror: string;\n} {\n\treturn { type: \"error\", id, error };\n}\n"]}
@@ -1,10 +1,10 @@
1
1
  import { Context } from 'hono';
2
2
  import { WSEvents } from 'hono/ws';
3
- import { S as SockaContract, a as SockaContractConfig } from '../socka-report-error-CXwpAUgl.js';
4
- import { a as SockaWebSocketSession, c as SockaWebSocketInit, b as SockaWebSocketSessionConfigUnion } from '../SockaWebSocketSession-B1w7RAid.js';
3
+ import { c as SockaContractBound } from '../socka-report-error-nTXJIzNb.js';
4
+ import { a as SockaWebSocketSession, c as SockaWebSocketInit, b as SockaWebSocketSessionConfigUnion } from '../SockaWebSocketSession-BaGvSerM.js';
5
5
  import '@standard-schema/spec';
6
6
 
7
- type SockaHonoCloudflareOptions<TContract extends SockaContract<SockaContractConfig>, TData> = {
7
+ type SockaHonoCloudflareOptions<TContract extends SockaContractBound, TData> = {
8
8
  sessions?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
9
9
  sockaInit?: (c: Context) => SockaWebSocketInit | undefined;
10
10
  resolveScope?: (c: Context) => {
@@ -16,6 +16,6 @@ type SockaHonoCloudflareOptions<TContract extends SockaContract<SockaContractCon
16
16
  * Callback for `upgradeWebSocket` from `hono/cloudflare-workers` (no `onOpen`;
17
17
  * the session is created on first `onMessage`).
18
18
  */
19
- declare function sockaHonoCloudflare<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfigUnion<TContract, TData>, options?: SockaHonoCloudflareOptions<TContract, TData>): (c: Context) => Omit<WSEvents<WebSocket>, "onOpen">;
19
+ declare function sockaHonoCloudflare<TContract extends SockaContractBound, TData>(config: SockaWebSocketSessionConfigUnion<TContract, TData>, options?: SockaHonoCloudflareOptions<TContract, TData>): (c: Context) => Omit<WSEvents<WebSocket>, "onOpen">;
20
20
 
21
21
  export { type SockaHonoCloudflareOptions, sockaHonoCloudflare };
@@ -1,6 +1,6 @@
1
1
  import { sockaHonoStrictInitFromContext } from '../chunk-QGURL3DJ.js';
2
- import { dispatchSockaInboundMessage } from '../chunk-5WQTYLIC.js';
3
- import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-LVVCHLNW.js';
2
+ import { dispatchSockaInboundMessage } from '../chunk-JR2GENNT.js';
3
+ import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-TTXY7O5P.js';
4
4
  import { reportSockaError } from '../chunk-IFIGKR3W.js';
5
5
 
6
6
  // src/hono/cloudflare-workers.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hono/cloudflare-workers.ts"],"names":[],"mappings":";;;;;;AA8BO,SAAS,mBAAA,CAIf,QACA,OAAA,EACsD;AACtD,EAAA,MAAM,cAAA,GACL,OAAA,EAAS,QAAA,oBACT,IAAI,GAAA,EAAwD;AAC7D,EAAA,MAAM,YAAA,GAAe,MAAA;AACrB,EAAA,MAAM,eAAe,OAAA,EAAS,YAAA;AAE9B,EAAA,OAAO,CAAC,CAAA,MAAgB;AAAA,IACvB,SAAA,CAAU,KAAK,KAAA,EAAO;AACrB,MAAA,MAAM,MAAM,KAAA,CAAM,GAAA;AAClB,MAAA,IAAI,CAAC,GAAA,EAAK;AACV,MAAA,MAAM,KAAA,GAAQ,GAAA;AACd,MAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,WAAA,EAAY,GAAI,YAAA,GACvC,YAAA,CAAa,CAAC,CAAA,GACd,EAAE,QAAA,EAAU,cAAA,EAAgB,QAAQ,YAAA,EAAa;AACpD,MAAA,IAAI,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAChC,MAAA,MAAM,GAAA,GAAM,WAAA;AAIZ,MAAA,IAAI,CAAC,OAAA,EAAS;AACb,QAAA,MAAM,OACL,OAAA,EAAS,SAAA,GAAY,CAAC,CAAA,IAAK,+BAA+B,CAAC,CAAA;AAC5D,QAAA,OAAA,GAAU,IAAI,qBAAA,CAAsB,KAAA,EAAO,QAAA,EAAU,KAAK,IAAI,CAAA;AAC9D,QAAA,QAAA,CAAS,GAAA,CAAI,OAAO,OAAO,CAAA;AAC3B,QAAA,yBAAA,CAA0B,KAAK,OAAO,CAAA;AAAA,MACvC;AACA,MAAA,MAAM,UAAA,GAA8B,IAAI,UAAA,IAAc,MAAA;AACtD,MAAA,KAAK,2BAAA,CAA4B,OAAA,EAAS,UAAA,EAAY,GAAA,CAAI,IAAI,CAAA,CAAE,KAAA;AAAA,QAC/D,CAAC,KAAA,KAAmB;AACnB,UAAA,gBAAA,CAAiB,IAAI,WAAA,EAAa;AAAA,YACjC,IAAA,EAAM,sBAAA;AAAA,YACN,OAAA,EAAS,MAAA;AAAA,YACT;AAAA,WACA,CAAA;AAAA,QACF;AAAA,OACD;AAAA,IACD,CAAA;AAAA,IACA,OAAA,CAAQ,MAAM,KAAA,EAAO;AACpB,MAAA,MAAM,MAAM,KAAA,CAAM,GAAA;AAClB,MAAA,IAAI,CAAC,GAAA,EAAK;AACV,MAAA,MAAM,KAAA,GAAQ,GAAA;AACd,MAAA,KAAA,CAAM,YAA2B;AAChC,QAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,WAAA,EAAY,GAAI,YAAA,GACvC,YAAA,CAAa,CAAC,CAAA,GACd,EAAE,QAAA,EAAU,cAAA,EAAgB,QAAQ,YAAA,EAAa;AACpD,QAAA,MAAM,GAAA,GAAM,WAAA;AAIZ,QAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAClC,QAAA,IAAI;AACH,UAAA,IAAI,OAAA,EAAS;AACZ,YAAA,MAAM,QAAQ,iBAAA,EAAkB;AAAA,UACjC;AAAA,QACD,SAAS,KAAA,EAAO;AACf,UAAA,gBAAA,CAAiB,IAAI,WAAA,EAAa;AAAA,YACjC,IAAA,EAAM,mBAAA;AAAA,YACN;AAAA,WACA,CAAA;AAAA,QACF,CAAA,SAAE;AACD,UAAA,QAAA,CAAS,OAAO,KAAK,CAAA;AAAA,QACtB;AAAA,MACD,CAAA,GAAG,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AAC9B,QAAA,gBAAA,CAAiB,aAAa,WAAA,EAAa;AAAA,UAC1C,IAAA,EAAM,gBAAA;AAAA,UACN,OAAA,EAAS,MAAA;AAAA,UACT;AAAA,SACA,CAAA;AAAA,MACF,CAAC,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACD","file":"cloudflare-workers.js","sourcesContent":["import type { Context } from \"hono\";\nimport type { WSEvents } from \"hono/ws\";\nimport type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport type { SockaWireFormat } from \"../core/wire-codec\";\nimport { dispatchSockaInboundMessage } from \"../server/dispatchSockaInboundMessage\";\nimport {\n\tSockaWebSocketSession,\n\trunSockaSessionOnAttached,\n\ttype SockaWebSocketInit,\n\ttype SockaWebSocketSessionConfigUnion,\n} from \"../server/SockaWebSocketSession\";\nimport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport type SockaHonoCloudflareOptions<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n> = {\n\tsessions?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\tsockaInit?: (c: Context) => SockaWebSocketInit | undefined;\n\tresolveScope?: (c: Context) => {\n\t\tsessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>;\n\t};\n};\n\n/**\n * Callback for `upgradeWebSocket` from `hono/cloudflare-workers` (no `onOpen`;\n * the session is created on first `onMessage`).\n */\nexport function sockaHonoCloudflare<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\toptions?: SockaHonoCloudflareOptions<TContract, TData>,\n): (c: Context) => Omit<WSEvents<WebSocket>, \"onOpen\"> {\n\tconst staticSessions =\n\t\toptions?.sessions ??\n\t\tnew Map<WebSocket, SockaWebSocketSession<TContract, TData>>();\n\tconst staticConfig = config;\n\tconst resolveScope = options?.resolveScope;\n\n\treturn (c: Context) => ({\n\t\tonMessage(evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tlet session = sessions.get(domWs);\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tif (!session) {\n\t\t\t\tconst init: SockaWebSocketInit | undefined =\n\t\t\t\t\toptions?.sockaInit?.(c) ?? sockaHonoStrictInitFromContext(c);\n\t\t\t\tsession = new SockaWebSocketSession(domWs, sessions, cfg, init);\n\t\t\t\tsessions.set(domWs, session);\n\t\t\t\trunSockaSessionOnAttached(cfg, session);\n\t\t\t}\n\t\t\tconst wireFormat: SockaWireFormat = cfg.wireFormat ?? \"json\";\n\t\t\tvoid dispatchSockaInboundMessage(session, wireFormat, evt.data).catch(\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\tonClose(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as WebSocket;\n\t\t\tvoid (async (): Promise<void> => {\n\t\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t\t? resolveScope(c)\n\t\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\t\tTContract,\n\t\t\t\t\tTData\n\t\t\t\t>;\n\t\t\t\tconst session = sessions.get(domWs);\n\t\t\t\ttry {\n\t\t\t\t\tif (session) {\n\t\t\t\t\t\tawait session.invokeHandleClose();\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverHandleClose\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\tsessions.delete(domWs);\n\t\t\t\t}\n\t\t\t})().catch((error: unknown) => {\n\t\t\t\treportSockaError(staticConfig.reportError, {\n\t\t\t\t\tkind: \"serverShutdown\",\n\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"sources":["../../src/hono/cloudflare-workers.ts"],"names":[],"mappings":";;;;;;AA8BO,SAAS,mBAAA,CAIf,QACA,OAAA,EACsD;AACtD,EAAA,MAAM,cAAA,GACL,OAAA,EAAS,QAAA,oBACT,IAAI,GAAA,EAAwD;AAC7D,EAAA,MAAM,YAAA,GAAe,MAAA;AACrB,EAAA,MAAM,eAAe,OAAA,EAAS,YAAA;AAE9B,EAAA,OAAO,CAAC,CAAA,MAAgB;AAAA,IACvB,SAAA,CAAU,KAAK,KAAA,EAAO;AACrB,MAAA,MAAM,MAAM,KAAA,CAAM,GAAA;AAClB,MAAA,IAAI,CAAC,GAAA,EAAK;AACV,MAAA,MAAM,KAAA,GAAQ,GAAA;AACd,MAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,WAAA,EAAY,GAAI,YAAA,GACvC,YAAA,CAAa,CAAC,CAAA,GACd,EAAE,QAAA,EAAU,cAAA,EAAgB,QAAQ,YAAA,EAAa;AACpD,MAAA,IAAI,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAChC,MAAA,MAAM,GAAA,GAAM,WAAA;AAIZ,MAAA,IAAI,CAAC,OAAA,EAAS;AACb,QAAA,MAAM,OACL,OAAA,EAAS,SAAA,GAAY,CAAC,CAAA,IAAK,+BAA+B,CAAC,CAAA;AAC5D,QAAA,OAAA,GAAU,IAAI,qBAAA,CAAsB,KAAA,EAAO,QAAA,EAAU,KAAK,IAAI,CAAA;AAC9D,QAAA,QAAA,CAAS,GAAA,CAAI,OAAO,OAAO,CAAA;AAC3B,QAAA,yBAAA,CAA0B,KAAK,OAAO,CAAA;AAAA,MACvC;AACA,MAAA,MAAM,UAAA,GAA8B,IAAI,UAAA,IAAc,MAAA;AACtD,MAAA,KAAK,2BAAA,CAA4B,OAAA,EAAS,UAAA,EAAY,GAAA,CAAI,IAAI,CAAA,CAAE,KAAA;AAAA,QAC/D,CAAC,KAAA,KAAmB;AACnB,UAAA,gBAAA,CAAiB,IAAI,WAAA,EAAa;AAAA,YACjC,IAAA,EAAM,sBAAA;AAAA,YACN,OAAA,EAAS,MAAA;AAAA,YACT;AAAA,WACA,CAAA;AAAA,QACF;AAAA,OACD;AAAA,IACD,CAAA;AAAA,IACA,OAAA,CAAQ,MAAM,KAAA,EAAO;AACpB,MAAA,MAAM,MAAM,KAAA,CAAM,GAAA;AAClB,MAAA,IAAI,CAAC,GAAA,EAAK;AACV,MAAA,MAAM,KAAA,GAAQ,GAAA;AACd,MAAA,KAAA,CAAM,YAA2B;AAChC,QAAA,MAAM,EAAE,QAAA,EAAU,MAAA,EAAQ,WAAA,EAAY,GAAI,YAAA,GACvC,YAAA,CAAa,CAAC,CAAA,GACd,EAAE,QAAA,EAAU,cAAA,EAAgB,QAAQ,YAAA,EAAa;AACpD,QAAA,MAAM,GAAA,GAAM,WAAA;AAIZ,QAAA,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAClC,QAAA,IAAI;AACH,UAAA,IAAI,OAAA,EAAS;AACZ,YAAA,MAAM,QAAQ,iBAAA,EAAkB;AAAA,UACjC;AAAA,QACD,SAAS,KAAA,EAAO;AACf,UAAA,gBAAA,CAAiB,IAAI,WAAA,EAAa;AAAA,YACjC,IAAA,EAAM,mBAAA;AAAA,YACN;AAAA,WACA,CAAA;AAAA,QACF,CAAA,SAAE;AACD,UAAA,QAAA,CAAS,OAAO,KAAK,CAAA;AAAA,QACtB;AAAA,MACD,CAAA,GAAG,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AAC9B,QAAA,gBAAA,CAAiB,aAAa,WAAA,EAAa;AAAA,UAC1C,IAAA,EAAM,gBAAA;AAAA,UACN,OAAA,EAAS,MAAA;AAAA,UACT;AAAA,SACA,CAAA;AAAA,MACF,CAAC,CAAA;AAAA,IACF;AAAA,GACD,CAAA;AACD","file":"cloudflare-workers.js","sourcesContent":["import type { Context } from \"hono\";\nimport type { WSEvents } from \"hono/ws\";\nimport type { SockaContractBound } from \"../core/contract\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport type { SockaWireFormat } from \"../core/wire-codec\";\nimport { dispatchSockaInboundMessage } from \"../server/dispatchSockaInboundMessage\";\nimport {\n\tSockaWebSocketSession,\n\trunSockaSessionOnAttached,\n\ttype SockaWebSocketInit,\n\ttype SockaWebSocketSessionConfigUnion,\n} from \"../server/SockaWebSocketSession\";\nimport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport type SockaHonoCloudflareOptions<\n\tTContract extends SockaContractBound,\n\tTData,\n> = {\n\tsessions?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\tsockaInit?: (c: Context) => SockaWebSocketInit | undefined;\n\tresolveScope?: (c: Context) => {\n\t\tsessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>;\n\t};\n};\n\n/**\n * Callback for `upgradeWebSocket` from `hono/cloudflare-workers` (no `onOpen`;\n * the session is created on first `onMessage`).\n */\nexport function sockaHonoCloudflare<\n\tTContract extends SockaContractBound,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\toptions?: SockaHonoCloudflareOptions<TContract, TData>,\n): (c: Context) => Omit<WSEvents<WebSocket>, \"onOpen\"> {\n\tconst staticSessions =\n\t\toptions?.sessions ??\n\t\tnew Map<WebSocket, SockaWebSocketSession<TContract, TData>>();\n\tconst staticConfig = config;\n\tconst resolveScope = options?.resolveScope;\n\n\treturn (c: Context) => ({\n\t\tonMessage(evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tlet session = sessions.get(domWs);\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tif (!session) {\n\t\t\t\tconst init: SockaWebSocketInit | undefined =\n\t\t\t\t\toptions?.sockaInit?.(c) ?? sockaHonoStrictInitFromContext(c);\n\t\t\t\tsession = new SockaWebSocketSession(domWs, sessions, cfg, init);\n\t\t\t\tsessions.set(domWs, session);\n\t\t\t\trunSockaSessionOnAttached(cfg, session);\n\t\t\t}\n\t\t\tconst wireFormat: SockaWireFormat = cfg.wireFormat ?? \"json\";\n\t\t\tvoid dispatchSockaInboundMessage(session, wireFormat, evt.data).catch(\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\tonClose(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as WebSocket;\n\t\t\tvoid (async (): Promise<void> => {\n\t\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t\t? resolveScope(c)\n\t\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\t\tTContract,\n\t\t\t\t\tTData\n\t\t\t\t>;\n\t\t\t\tconst session = sessions.get(domWs);\n\t\t\t\ttry {\n\t\t\t\t\tif (session) {\n\t\t\t\t\t\tawait session.invokeHandleClose();\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverHandleClose\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\tsessions.delete(domWs);\n\t\t\t\t}\n\t\t\t})().catch((error: unknown) => {\n\t\t\t\treportSockaError(staticConfig.reportError, {\n\t\t\t\t\tkind: \"serverShutdown\",\n\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t});\n}\n"]}
@@ -1,8 +1,8 @@
1
1
  import { Context } from 'hono';
2
- import { S as SockaStrictWebSocketInit, b as SockaWebSocketSessionConfigUnion, a as SockaWebSocketSession, c as SockaWebSocketInit } from '../SockaWebSocketSession-B1w7RAid.js';
2
+ import { S as SockaStrictWebSocketInit, b as SockaWebSocketSessionConfigUnion, a as SockaWebSocketSession, c as SockaWebSocketInit } from '../SockaWebSocketSession-BaGvSerM.js';
3
3
  import { WSEvents } from 'hono/ws';
4
4
  import { WebSocket as WebSocket$1 } from 'ws';
5
- import { S as SockaContract, a as SockaContractConfig } from '../socka-report-error-CXwpAUgl.js';
5
+ import { c as SockaContractBound } from '../socka-report-error-nTXJIzNb.js';
6
6
  import '@standard-schema/spec';
7
7
 
8
8
  /**
@@ -18,7 +18,7 @@ import '@standard-schema/spec';
18
18
  */
19
19
  declare function sockaHonoStrictInitFromContext(c: Context): SockaStrictWebSocketInit;
20
20
 
21
- type SockaHonoNodeWsOptions<TContract extends SockaContract<SockaContractConfig>, TData> = {
21
+ type SockaHonoNodeWsOptions<TContract extends SockaContractBound, TData> = {
22
22
  /** Shared map; default is a new `Map`. */
23
23
  sessions?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
24
24
  /**
@@ -41,6 +41,6 @@ type SockaHonoNodeWsOptions<TContract extends SockaContract<SockaContractConfig>
41
41
  * {@link https://github.com/honojs/middleware/tree/main/packages/node-ws @hono/node-ws}
42
42
  * `createNodeWebSocket({ app }).upgradeWebSocket`.
43
43
  */
44
- declare function sockaHonoNodeWs<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfigUnion<TContract, TData>, options?: SockaHonoNodeWsOptions<TContract, TData>): (c: Context) => WSEvents<WebSocket$1>;
44
+ declare function sockaHonoNodeWs<TContract extends SockaContractBound, TData>(config: SockaWebSocketSessionConfigUnion<TContract, TData>, options?: SockaHonoNodeWsOptions<TContract, TData>): (c: Context) => WSEvents<WebSocket$1>;
45
45
 
46
46
  export { type SockaHonoNodeWsOptions, sockaHonoNodeWs, sockaHonoStrictInitFromContext };
@@ -1,7 +1,7 @@
1
1
  import { sockaHonoStrictInitFromContext } from '../chunk-QGURL3DJ.js';
2
2
  export { sockaHonoStrictInitFromContext } from '../chunk-QGURL3DJ.js';
3
- import { dispatchSockaInboundMessage } from '../chunk-5WQTYLIC.js';
4
- import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-LVVCHLNW.js';
3
+ import { dispatchSockaInboundMessage } from '../chunk-JR2GENNT.js';
4
+ import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-TTXY7O5P.js';
5
5
  import { reportSockaError } from '../chunk-IFIGKR3W.js';
6
6
 
7
7
  // src/hono/node-ws.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/hono/node-ws.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Context } from \"hono\";\nimport type { WSEvents } from \"hono/ws\";\nimport type { WebSocket as NodeWebSocket } from \"ws\";\nimport type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport type { SockaWireFormat } from \"../core/wire-codec\";\nimport { dispatchSockaInboundMessage } from \"../server/dispatchSockaInboundMessage\";\nimport {\n\tSockaWebSocketSession,\n\trunSockaSessionOnAttached,\n\ttype SockaWebSocketInit,\n\ttype SockaWebSocketSessionConfigUnion,\n} from \"../server/SockaWebSocketSession\";\nimport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport type SockaHonoNodeWsOptions<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n> = {\n\t/** Shared map; default is a new `Map`. */\n\tsessions?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t/**\n\t * Per-upgrade init for `createData`. When omitted, defaults to\n\t * {@link sockaHonoStrictInitFromContext} so `Request` is always available.\n\t */\n\tsockaInit?: (c: Context) => SockaWebSocketInit | undefined;\n\t/**\n\t * Resolve the session map and config from this upgrade’s Hono context (e.g. multi-room\n\t * from `c.req.param(\"roomId\")`). When set, overrides the outer `config` / static `sessions`\n\t * for each connection.\n\t */\n\tresolveScope?: (c: Context) => {\n\t\tsessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>;\n\t};\n};\n\n/**\n * Returns the callback passed to `upgradeWebSocket` from\n * {@link https://github.com/honojs/middleware/tree/main/packages/node-ws @hono/node-ws}\n * `createNodeWebSocket({ app }).upgradeWebSocket`.\n */\nexport function sockaHonoNodeWs<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\toptions?: SockaHonoNodeWsOptions<TContract, TData>,\n): (c: Context) => WSEvents<NodeWebSocket> {\n\tconst staticSessions =\n\t\toptions?.sessions ??\n\t\tnew Map<WebSocket, SockaWebSocketSession<TContract, TData>>();\n\tconst staticConfig = config;\n\tconst resolveScope = options?.resolveScope;\n\n\treturn (c: Context) => ({\n\t\tonOpen(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tconst init: SockaWebSocketInit | undefined =\n\t\t\t\toptions?.sockaInit?.(c) ?? sockaHonoStrictInitFromContext(c);\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tconst session = new SockaWebSocketSession(domWs, sessions, cfg, init);\n\t\t\tsessions.set(domWs, session);\n\t\t\trunSockaSessionOnAttached(cfg, session);\n\t\t},\n\t\tonMessage(evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tconst session = sessions.get(domWs);\n\t\t\tif (!session) return;\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tconst wireFormat: SockaWireFormat = cfg.wireFormat ?? \"json\";\n\t\t\tvoid dispatchSockaInboundMessage(session, wireFormat, evt.data).catch(\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\tonClose(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tvoid (async (): Promise<void> => {\n\t\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t\t? resolveScope(c)\n\t\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\t\tTContract,\n\t\t\t\t\tTData\n\t\t\t\t>;\n\t\t\t\tconst session = sessions.get(domWs);\n\t\t\t\ttry {\n\t\t\t\t\tif (session) {\n\t\t\t\t\t\tawait session.invokeHandleClose();\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverHandleClose\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\tsessions.delete(domWs);\n\t\t\t\t}\n\t\t\t})().catch((error: unknown) => {\n\t\t\t\treportSockaError(staticConfig.reportError, {\n\t\t\t\t\tkind: \"serverShutdown\",\n\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"sources":["../../src/hono/node-ws.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Context } from \"hono\";\nimport type { WSEvents } from \"hono/ws\";\nimport type { WebSocket as NodeWebSocket } from \"ws\";\nimport type { SockaContractBound } from \"../core/contract\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport type { SockaWireFormat } from \"../core/wire-codec\";\nimport { dispatchSockaInboundMessage } from \"../server/dispatchSockaInboundMessage\";\nimport {\n\tSockaWebSocketSession,\n\trunSockaSessionOnAttached,\n\ttype SockaWebSocketInit,\n\ttype SockaWebSocketSessionConfigUnion,\n} from \"../server/SockaWebSocketSession\";\nimport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport type SockaHonoNodeWsOptions<\n\tTContract extends SockaContractBound,\n\tTData,\n> = {\n\t/** Shared map; default is a new `Map`. */\n\tsessions?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t/**\n\t * Per-upgrade init for `createData`. When omitted, defaults to\n\t * {@link sockaHonoStrictInitFromContext} so `Request` is always available.\n\t */\n\tsockaInit?: (c: Context) => SockaWebSocketInit | undefined;\n\t/**\n\t * Resolve the session map and config from this upgrade’s Hono context (e.g. multi-room\n\t * from `c.req.param(\"roomId\")`). When set, overrides the outer `config` / static `sessions`\n\t * for each connection.\n\t */\n\tresolveScope?: (c: Context) => {\n\t\tsessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>;\n\t};\n};\n\n/**\n * Returns the callback passed to `upgradeWebSocket` from\n * {@link https://github.com/honojs/middleware/tree/main/packages/node-ws @hono/node-ws}\n * `createNodeWebSocket({ app }).upgradeWebSocket`.\n */\nexport function sockaHonoNodeWs<TContract extends SockaContractBound, TData>(\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\toptions?: SockaHonoNodeWsOptions<TContract, TData>,\n): (c: Context) => WSEvents<NodeWebSocket> {\n\tconst staticSessions =\n\t\toptions?.sessions ??\n\t\tnew Map<WebSocket, SockaWebSocketSession<TContract, TData>>();\n\tconst staticConfig = config;\n\tconst resolveScope = options?.resolveScope;\n\n\treturn (c: Context) => ({\n\t\tonOpen(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tconst init: SockaWebSocketInit | undefined =\n\t\t\t\toptions?.sockaInit?.(c) ?? sockaHonoStrictInitFromContext(c);\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tconst session = new SockaWebSocketSession(domWs, sessions, cfg, init);\n\t\t\tsessions.set(domWs, session);\n\t\t\trunSockaSessionOnAttached(cfg, session);\n\t\t},\n\t\tonMessage(evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tconst session = sessions.get(domWs);\n\t\t\tif (!session) return;\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tconst wireFormat: SockaWireFormat = cfg.wireFormat ?? \"json\";\n\t\t\tvoid dispatchSockaInboundMessage(session, wireFormat, evt.data).catch(\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\tonClose(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tvoid (async (): Promise<void> => {\n\t\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t\t? resolveScope(c)\n\t\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\t\tTContract,\n\t\t\t\t\tTData\n\t\t\t\t>;\n\t\t\t\tconst session = sessions.get(domWs);\n\t\t\t\ttry {\n\t\t\t\t\tif (session) {\n\t\t\t\t\t\tawait session.invokeHandleClose();\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverHandleClose\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\tsessions.delete(domWs);\n\t\t\t\t}\n\t\t\t})().catch((error: unknown) => {\n\t\t\t\treportSockaError(staticConfig.reportError, {\n\t\t\t\t\tkind: \"serverShutdown\",\n\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t});\n}\n"]}