@firtoz/socka 3.0.1 → 3.0.2

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.
@@ -76,6 +76,8 @@ type SockaWebSocketDOOptions<TEnv extends object, TSession extends SockaDoSessio
76
76
  createSockaSession: (ctx: Context<{
77
77
  Bindings: TEnv;
78
78
  }> | undefined, websocket: WebSocket) => TSession | Promise<TSession>;
79
+ /** Same as `pairServerWebSocketAcceptOptions` on `BaseWebSocketDOOptions` from `@firtoz/websocket-do`. */
80
+ pairServerWebSocketAcceptOptions?: WebSocketAcceptOptions;
79
81
  };
80
82
  /**
81
83
  * Durable Object base class for WebSocket apps using {@link SockaDoSession}
package/dist/do/index.js CHANGED
@@ -129,7 +129,8 @@ var SockaDoSession = class extends BaseSession {
129
129
  var SockaWebSocketDO = class extends BaseWebSocketDO {
130
130
  constructor(ctx, env, options) {
131
131
  super(ctx, env, {
132
- createSession: (sessionCtx, websocket) => options.createSockaSession(sessionCtx, websocket)
132
+ createSession: (sessionCtx, websocket) => options.createSockaSession(sessionCtx, websocket),
133
+ pairServerWebSocketAcceptOptions: options.pairServerWebSocketAcceptOptions
133
134
  });
134
135
  }
135
136
  };
@@ -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};\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});\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/SockaDoSession.ts","../../src/do/SockaWebSocketDO.ts","../../src/do/dispatch.ts"],"names":[],"mappings":";;;;AA8EA,SAAS,2BAAA,CAKR,QACA,OAAA,EACO;AACP,EAAA,MAAM,KAAK,MAAA,CAAO,UAAA;AAClB,EAAA,IAAI,CAAC,EAAA,EAAI;AACT,EAAA,IAAI;AACH,IAAA,MAAM,MAAA,GAAS,GAAG,OAAO,CAAA;AACzB,IAAA,KAAK,QAAQ,OAAA,CAAQ,MAAM,CAAA,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AACtD,MAAA,gBAAA,CAAiB,OAAO,WAAA,EAAa;AAAA,QACpC,IAAA,EAAM,kBAAA;AAAA,QACN;AAAA,OACA,CAAA;AAAA,IACF,CAAC,CAAA;AAAA,EACF,SAAS,KAAA,EAAO;AACf,IAAA,gBAAA,CAAiB,OAAO,WAAA,EAAa,EAAE,IAAA,EAAM,kBAAA,EAAoB,OAAO,CAAA;AAAA,EACzE;AACD;AAEA,SAAS,+BAAA,CAKR,QAAA,EACA,YAAA,EAIA,KAAA,EAIC;AACD,EAAA,MAAM,QAAQ,QAAA,CAAS,KAAA;AACvB,EAAA,MAAM,MASF,EAAC;AAEL,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,KAAK,CAAA,EAAyC;AAC3E,IAAA,MAAM,IAAA,GAAO,MAAM,GAAG,CAAA;AACtB,IAAA,MAAM,MAAA,GAAS,aAAa,GAAgC,CAAA;AAC5D,IAAA,IAAI,KAAK,KAAA,EAAO;AACf,MAAA,GAAA,CAAI,GAAG,CAAA,GAAI,CACV,OACA,MAAA,KAGC,MAAA,CAIC,OAAO,KAAK,CAAA;AAAA,IAChB,CAAA,MAAO;AACN,MAAA,GAAA,CAAI,GAAG,CAAA,GAAI,CACV,MAAA,KAGC,OAGC,KAAK,CAAA;AAAA,IACT;AAAA,EACD;AAEA,EAAA,OAAO,GAAA;AAIR;AAOO,IAAM,cAAA,GAAN,cAKE,WAAA,CAET;AAAA,EAGC,WAAA,CACC,SAAA,EACA,QAAA,EACA,MAAA,EACC;AACD,IAAA,MAAM,UAAA,GAAa,OAAO,UAAA,IAAc,MAAA;AACxC,IAAA,KAAA;AAAA,MACC,SAAA;AAAA,MACA,QAAA;AAAA,MACA;AAAA,QACC,UAAA,EACC,MAAA,CAAO,UAAA,KACN,CAAC,UAAuC,EAAC,CAAA,CAAA;AAAA,QAC3C,eAAe,YAAY;AAAA,QAE3B,CAAA;AAAA,QACA,mBAAA,EAAqB,OAAO,OAAA,KAAY;AACvC,UAAA,MAAM,IAAA,CAAK,KAAA,CAAM,mBAAA,CAAoB,OAAO,CAAA;AAAA,QAC7C,CAAA;AAAA,QACA,WAAA,EAAa,OAAO,WAAA,KAAgB;AACnC,UAAA,MAAM,MAAA,CAAO,WAAA;AAAA,YACZ;AAAA,WACD;AAAA,QACD;AAAA;AACD,KACD;AACA,IAAA,MAAM,WAAA,GAGF;AAAA,MACH,oBAAA,EAAsB,KAAA;AAAA,MACtB,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,UAAA;AAAA,MACA,QAAA,EAAU,+BAAA;AAAA,QACT,MAAA,CAAO,QAAA;AAAA,QACP,MAAA,CAAO,QAAA;AAAA,QACP;AAAA,OACD;AAAA,MACA,aAAa,YAAY;AAAA,MAEzB,CAAA;AAAA,MACA,gBAAgB,MAAA,CAAO,cAAA,GACpB,CAAC,GAAA,EAAK,OAAA,EAAS,OAAO,MAAA,KAAW;AACjC,QAAA,MAAA,CAAO,cAAA,GAAiB,GAAA,EAAK,OAAA,EAAS,KAAA,EAAO,IAAI,CAAA;AAAA,MAClD,CAAA,GACC,MAAA;AAAA,MACH,mBAAmB,MAAA,CAAO,iBAAA;AAAA,MAC1B,eAAe,MAAA,CAAO,aAAA;AAAA,MACtB,iBAAiB,MAAA,CAAO;AAAA,KACzB;AACA,IAAA,IAAA,CAAK,QAAQ,IAAI,qBAAA;AAAA,MAChB,SAAA;AAAA,MACA,QAAA;AAAA,MAIA;AAAA,KACD;AAIA,IAAA,cAAA,CAAe,MAAM;AACpB,MAAA,cAAA,CAAe,MAAM;AACpB,QAAA,2BAAA,CAA4B,QAAQ,IAAI,CAAA;AAAA,MACzC,CAAC,CAAA;AAAA,IACF,CAAC,CAAA;AAAA,EACF;AAAA,EAEA,MAAa,iBAAiB,UAAA,EAAmC;AAChE,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,gBAAA,CAAiB,UAAU,CAAA;AAAA,EAC9C;AAAA,EAEO,aAAA,CAAc,OAAe,IAAA,EAAqB;AACxD,IAAA,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,KAAA,EAAO,IAAI,CAAA;AAAA,EACrC;AAAA,EAEO,QAAA,CACN,MACA,IAAA,EACgB;AAChB,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,IAAA,EAAM,IAAI,CAAA;AAAA,EACtC;AAAA,EAEO,aAAA,CACN,IAAA,EACA,IAAA,EACA,WAAA,GAAc,KAAA,EACE;AAChB,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,aAAA,CAAc,IAAA,EAAM,MAAM,WAAW,CAAA;AAAA,EACxD;AAAA;AAAA;AAAA;AAAA,EAKO,UAAU,OAAA,EAA8C;AAC9D,IAAA,MAAM,MAAe,EAAC;AACtB,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,CAAC,CAAA,IAAK,KAAK,QAAA,EAAU;AACpC,MAAA,IAAI,OAAA,EAAS,WAAA,IAAe,EAAA,KAAO,IAAA,CAAK,SAAA,EAAW;AACnD,MAAA,GAAA,CAAI,IAAA,CAAK,EAAE,IAAI,CAAA;AAAA,IAChB;AACA,IAAA,OAAO,GAAA;AAAA,EACR;AAAA;AAAA;AAAA;AAAA,EAKO,aAAA,CACN,KACA,OAAA,EACM;AACN,IAAA,MAAM,MAAW,EAAC;AAClB,IAAA,KAAA,MAAW,CAAC,EAAA,EAAI,CAAC,CAAA,IAAK,KAAK,QAAA,EAAU;AACpC,MAAA,IAAI,OAAA,EAAS,WAAA,IAAe,EAAA,KAAO,IAAA,CAAK,SAAA,EAAW;AACnD,MAAA,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,CAA2C,CAAC,CAAA;AAAA,IAC1D;AACA,IAAA,OAAO,GAAA;AAAA,EACR;AAAA,EAEO,UAAU,OAAA,EAA6C;AAC7D,IAAA,IAAI,CAAA,GAAI,CAAA;AACR,IAAA,KAAA,MAAW,CAAC,EAAE,CAAA,IAAK,IAAA,CAAK,QAAA,EAAU;AACjC,MAAA,IAAI,OAAA,EAAS,WAAA,IAAe,EAAA,KAAO,IAAA,CAAK,SAAA,EAAW;AACnD,MAAA,CAAA,IAAK,CAAA;AAAA,IACN;AACA,IAAA,OAAO,CAAA;AAAA,EACR;AAAA,EAEO,SAAS,OAAA,EAA8C;AAC7D,IAAA,OAAO,IAAA,CAAK,SAAA,CAAU,OAAO,CAAA,GAAI,CAAA;AAAA,EAClC;AACD;ACnRO,IAAe,gBAAA,GAAf,cAgBG,eAAA,CAAgC;AAAA,EACzC,WAAA,CACC,GAAA,EACA,GAAA,EACA,OAAA,EACC;AACD,IAAA,KAAA,CAAM,KAAK,GAAA,EAAK;AAAA,MACf,eAAe,CAAC,UAAA,EAAY,cAC3B,OAAA,CAAQ,kBAAA,CAAmB,YAAY,SAAS,CAAA;AAAA,MACjD,kCACC,OAAA,CAAQ;AAAA,KACT,CAAA;AAAA,EACF;AACD;;;ACvDO,SAAS,YAAA,CACf,IACA,KAAA,EAKC;AACD,EAAA,OAAO,EAAE,IAAA,EAAM,OAAA,EAAS,EAAA,EAAI,KAAA,EAAM;AACnC","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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firtoz/socka",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "type": "module",
5
5
  "description": "Standard Schema–first WebSocket RPC for TypeScript — Bun, Hono, Node ws, Cloudflare Workers, Durable Objects",
6
6
  "main": "./dist/core/index.js",
@@ -101,8 +101,8 @@
101
101
  "msgpackr": "^1.11.10"
102
102
  },
103
103
  "peerDependencies": {
104
- "@cloudflare/workers-types": "^4.20260422.1",
105
- "@firtoz/websocket-do": "^13.0.1",
104
+ "@cloudflare/workers-types": "^4.20260423.1",
105
+ "@firtoz/websocket-do": "^13.0.2",
106
106
  "@hono/node-server": "^1.19.2",
107
107
  "@hono/node-ws": "^1.3.0",
108
108
  "hono": "^4.12.9",
@@ -133,11 +133,11 @@
133
133
  }
134
134
  },
135
135
  "devDependencies": {
136
- "@cloudflare/workers-types": "^4.20260422.1",
136
+ "@cloudflare/workers-types": "^4.20260423.1",
137
137
  "@happy-dom/global-registrator": "^20.9.0",
138
138
  "@hono/node-server": "^2.0.0",
139
139
  "@hono/node-ws": "^1.3.0",
140
- "@tanstack/intent": "^0.0.29",
140
+ "@tanstack/intent": "^0.0.32",
141
141
  "@testing-library/react": "^16.3.2",
142
142
  "@types/react": "^19.2.14",
143
143
  "@types/ws": "^8.18.1",