@firtoz/socka 2.1.0 → 3.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 (52) hide show
  1. package/README.md +10 -8
  2. package/dist/{SockaWebSocketSession-Cza7Fti-.d.ts → SockaWebSocketSession-B1w7RAid.d.ts} +61 -41
  3. package/dist/bun/index.d.ts +8 -8
  4. package/dist/bun/index.js +2 -2
  5. package/dist/bun/index.js.map +1 -1
  6. package/dist/{chunk-2FNWVCP3.js → chunk-IFIGKR3W.js} +20 -2
  7. package/dist/chunk-IFIGKR3W.js.map +1 -0
  8. package/dist/{chunk-JVLUA3Q5.js → chunk-LVVCHLNW.js} +21 -14
  9. package/dist/chunk-LVVCHLNW.js.map +1 -0
  10. package/dist/{chunk-H3S3435J.js → chunk-P3JEEOJL.js} +70 -10
  11. package/dist/chunk-P3JEEOJL.js.map +1 -0
  12. package/dist/{chunk-KQO5AVKA.js → chunk-QGURL3DJ.js} +2 -2
  13. package/dist/chunk-QGURL3DJ.js.map +1 -0
  14. package/dist/client/index.d.ts +2 -2
  15. package/dist/client/index.js +2 -2
  16. package/dist/core/index.d.ts +2 -25
  17. package/dist/core/index.js +1 -1
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/do/index.d.ts +2 -2
  20. package/dist/do/index.js +3 -2
  21. package/dist/do/index.js.map +1 -1
  22. package/dist/hono/cloudflare-workers.d.ts +4 -4
  23. package/dist/hono/cloudflare-workers.js +3 -3
  24. package/dist/hono/cloudflare-workers.js.map +1 -1
  25. package/dist/hono/index.d.ts +5 -5
  26. package/dist/hono/index.js +4 -4
  27. package/dist/hono/index.js.map +1 -1
  28. package/dist/react/index.d.ts +1 -1
  29. package/dist/react/index.js +2 -2
  30. package/dist/server/index.d.ts +5 -5
  31. package/dist/server/index.js +3 -3
  32. package/dist/server/index.js.map +1 -1
  33. package/dist/{socka-report-error-ixTynx4w.d.ts → socka-report-error-CXwpAUgl.d.ts} +72 -7
  34. package/docs/README.md +1 -1
  35. package/docs/auth.md +1 -1
  36. package/docs/client.md +4 -0
  37. package/docs/comparison.md +1 -1
  38. package/docs/durable-objects.md +1 -1
  39. package/docs/internals.md +3 -3
  40. package/docs/recipes.md +0 -1
  41. package/docs/reference.md +9 -5
  42. package/docs/server.md +9 -6
  43. package/docs/wire-format.md +4 -0
  44. package/package.json +1 -1
  45. package/roadmap.md +2 -2
  46. package/skills/socka/core-rpc/SKILL.md +2 -2
  47. package/skills/socka/do-session/SKILL.md +2 -2
  48. package/skills/socka/standard-schema/SKILL.md +1 -1
  49. package/dist/chunk-2FNWVCP3.js.map +0 -1
  50. package/dist/chunk-H3S3435J.js.map +0 -1
  51. package/dist/chunk-JVLUA3Q5.js.map +0 -1
  52. package/dist/chunk-KQO5AVKA.js.map +0 -1
@@ -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 SockaWebSocketSessionConfig,\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: SockaWebSocketSessionConfig<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: SockaWebSocketSessionConfig<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 SockaWebSocketSessionConfig<TContract, TData>;\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 SockaWebSocketSessionConfig<TContract, TData>;\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 SockaWebSocketSessionConfig<\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":";;;;;;;AA4CO,SAAS,eAAA,CAIf,QACA,OAAA,EAC0C;AAC1C,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,MAAA,CAAO,MAAM,KAAA,EAAO;AACnB,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,MAAM,OACL,OAAA,EAAS,SAAA,GAAY,CAAC,CAAA,IAAK,+BAA+B,CAAC,CAAA;AAC5D,MAAA,MAAM,GAAA,GAAM,WAAA;AAIZ,MAAA,MAAM,UAAU,IAAI,qBAAA,CAAsB,KAAA,EAAO,QAAA,EAAU,KAAK,IAAI,CAAA;AACpE,MAAA,QAAA,CAAS,GAAA,CAAI,OAAO,OAAO,CAAA;AAC3B,MAAA,yBAAA,CAA0B,KAAK,OAAO,CAAA;AAAA,IACvC,CAAA;AAAA,IACA,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,MAAM,OAAA,GAAU,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA;AAClC,MAAA,IAAI,CAAC,OAAA,EAAS;AACd,MAAA,MAAM,GAAA,GAAM,WAAA;AAIZ,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":"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,5 +1,5 @@
1
1
  import { DependencyList, RefObject, ReactNode, ReactElement } from 'react';
2
- import { S as SockaContract, a as SockaContractConfig, f as InferSockaPushHandlers, I as InferSockaSend, g as InferSockaPushPayload } from '../socka-report-error-ixTynx4w.js';
2
+ import { S as SockaContract, a as SockaContractConfig, e as InferSockaPushHandlers, I as InferSockaSend, f as InferSockaPushPayload } from '../socka-report-error-CXwpAUgl.js';
3
3
  import { SockaSessionOptions, SockaSession, SockaConnectionStatus } from '../client/index.js';
4
4
  import '@standard-schema/spec';
5
5
 
@@ -1,6 +1,6 @@
1
- import { SockaSession } from '../chunk-H3S3435J.js';
1
+ import { SockaSession } from '../chunk-P3JEEOJL.js';
2
2
  import '../chunk-YMT4HAH7.js';
3
- import '../chunk-2FNWVCP3.js';
3
+ import '../chunk-IFIGKR3W.js';
4
4
  import { createContext, useRef, useState, useEffect, useMemo, useContext } from 'react';
5
5
  import { jsx } from 'react/jsx-runtime';
6
6
 
@@ -1,6 +1,6 @@
1
- import { a as SockaWebSocketSession, b as SockaWebSocketSessionConfig, c as SockaWebSocketInit } from '../SockaWebSocketSession-Cza7Fti-.js';
2
- export { f as SockaEmitCapable, d as SockaPushSession, S as SockaStrictWebSocketInit, e as broadcastSockaEventToPeers, r as runSockaSessionOnAttached } from '../SockaWebSocketSession-Cza7Fti-.js';
3
- import { S as SockaContract, a as SockaContractConfig, b as SockaWireFormat } from '../socka-report-error-ixTynx4w.js';
1
+ import { a as SockaWebSocketSession, b as SockaWebSocketSessionConfigUnion, c as SockaWebSocketInit, e as SockaWebSocketSessionConfig } from '../SockaWebSocketSession-B1w7RAid.js';
2
+ export { g as SockaEmitCapable, d as SockaPushSession, S as SockaStrictWebSocketInit, h as SockaWebSocketSessionConfigLoose, f as broadcastSockaEventToPeers, r as runSockaSessionOnAttached } from '../SockaWebSocketSession-B1w7RAid.js';
3
+ import { S as SockaContract, a as SockaContractConfig, w as SockaWireFormat } from '../socka-report-error-CXwpAUgl.js';
4
4
  import '@standard-schema/spec';
5
5
 
6
6
  type AttachedSockaWebSocket<TContract extends SockaContract<SockaContractConfig>, TData> = {
@@ -14,7 +14,7 @@ type AttachedSockaWebSocket<TContract extends SockaContract<SockaContractConfig>
14
14
  * {@link SockaWebSocketSession.invokeHandleClose} once, then removes listeners
15
15
  * (also triggered by `close`).
16
16
  */
17
- declare function attachSockaWebSocket<TContract extends SockaContract<SockaContractConfig>, TData>(websocket: WebSocket, sessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>, config: SockaWebSocketSessionConfig<TContract, TData>, init?: SockaWebSocketInit): AttachedSockaWebSocket<TContract, TData>;
17
+ declare function attachSockaWebSocket<TContract extends SockaContract<SockaContractConfig>, TData>(websocket: WebSocket, sessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>, config: SockaWebSocketSessionConfigUnion<TContract, TData>, init?: SockaWebSocketInit): AttachedSockaWebSocket<TContract, TData>;
18
18
 
19
19
  /**
20
20
  * Decode a WebSocket `message` payload and dispatch it to the session (same
@@ -37,4 +37,4 @@ declare function createSockaRoomRegistry<TContract extends SockaContract<SockaCo
37
37
  readonly rooms: ReadonlyMap<string, SockaRoomBundle<TContract, TData>>;
38
38
  };
39
39
 
40
- export { type AttachedSockaWebSocket, type SockaRoomBundle, SockaWebSocketInit, SockaWebSocketSession, SockaWebSocketSessionConfig, attachSockaWebSocket, createSockaRoomRegistry, dispatchSockaInboundMessage };
40
+ export { type AttachedSockaWebSocket, type SockaRoomBundle, SockaWebSocketInit, SockaWebSocketSession, SockaWebSocketSessionConfig, SockaWebSocketSessionConfigUnion, attachSockaWebSocket, createSockaRoomRegistry, dispatchSockaInboundMessage };
@@ -1,8 +1,8 @@
1
1
  import { dispatchSockaInboundMessage } from '../chunk-5WQTYLIC.js';
2
2
  export { dispatchSockaInboundMessage } from '../chunk-5WQTYLIC.js';
3
- import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-JVLUA3Q5.js';
4
- export { SockaWebSocketSession, broadcastSockaEventToPeers, runSockaSessionOnAttached } from '../chunk-JVLUA3Q5.js';
5
- import { reportSockaError } from '../chunk-2FNWVCP3.js';
3
+ import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-LVVCHLNW.js';
4
+ export { SockaWebSocketSession, broadcastSockaEventToPeers, runSockaSessionOnAttached } from '../chunk-LVVCHLNW.js';
5
+ import { reportSockaError } from '../chunk-IFIGKR3W.js';
6
6
 
7
7
  // src/server/attachSockaWebSocket.ts
8
8
  function attachSockaWebSocket(websocket, sessions, config, init) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/server/attachSockaWebSocket.ts","../../src/server/room-registry.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport { dispatchSockaInboundMessage } from \"./dispatchSockaInboundMessage\";\nimport {\n\tSockaWebSocketSession,\n\trunSockaSessionOnAttached,\n\ttype SockaWebSocketInit,\n\ttype SockaWebSocketSessionConfig,\n} from \"./SockaWebSocketSession\";\n\nexport type AttachedSockaWebSocket<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n> = {\n\tsession: SockaWebSocketSession<TContract, TData>;\n\t/** Remove listeners and delete this session from the map (idempotent). */\n\tdispose: () => void;\n};\n\n/**\n * Register WebSocket `message` / `close` handlers, insert the session into\n * `sessions`, and return `{ session, dispose }`. `dispose` runs\n * {@link SockaWebSocketSession.invokeHandleClose} once, then removes listeners\n * (also triggered by `close`).\n */\nexport function attachSockaWebSocket<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\twebsocket: WebSocket,\n\tsessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>,\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>,\n\tinit?: SockaWebSocketInit,\n): AttachedSockaWebSocket<TContract, TData> {\n\tconst session = new SockaWebSocketSession(websocket, sessions, config, init);\n\tsessions.set(websocket, session);\n\trunSockaSessionOnAttached(config, session);\n\n\tlet shuttingDown = false;\n\n\tconst finalize = (): void => {\n\t\twebsocket.removeEventListener(\"message\", onMessage);\n\t\twebsocket.removeEventListener(\"close\", onClose);\n\t\tsessions.delete(websocket);\n\t};\n\n\tconst shutdown = (): void => {\n\t\tif (shuttingDown) return;\n\t\tshuttingDown = true;\n\t\tvoid (async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tawait session.invokeHandleClose();\n\t\t\t} catch (error) {\n\t\t\t\treportSockaError(config.reportError, {\n\t\t\t\t\tkind: \"serverHandleClose\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tfinalize();\n\t\t\t}\n\t\t})().catch((error: unknown) => {\n\t\t\treportSockaError(config.reportError, {\n\t\t\t\tkind: \"serverShutdown\",\n\t\t\t\tadapter: \"attach\",\n\t\t\t\terror,\n\t\t\t});\n\t\t\tfinalize();\n\t\t});\n\t};\n\n\tconst onMessage = (ev: MessageEvent): void => {\n\t\tconst wf = config.wireFormat ?? \"json\";\n\t\tvoid dispatchSockaInboundMessage(session, wf, ev.data).catch(\n\t\t\t(error: unknown) => {\n\t\t\t\treportSockaError(config.reportError, {\n\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\tadapter: \"attach\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t};\n\n\tconst onClose = (): void => {\n\t\tshutdown();\n\t};\n\n\twebsocket.addEventListener(\"message\", onMessage);\n\twebsocket.addEventListener(\"close\", onClose);\n\n\treturn { session, dispose: shutdown };\n}\n","import type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport type { SockaWebSocketSession } from \"./SockaWebSocketSession\";\nimport type { SockaWebSocketSessionConfig } from \"./SockaWebSocketSessionConfig\";\n\nexport type SockaRoomBundle<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n> = {\n\tsessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>;\n};\n\n/**\n * Per-room {@link SockaWebSocketSession} maps and configs for Bun/Hono multi-room\n * apps (one bundle per `roomId`).\n */\nexport function createSockaRoomRegistry<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tmakeConfig: (\n\t\troomId: string,\n\t\tsessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>,\n\t) => SockaWebSocketSessionConfig<TContract, TData>,\n): {\n\tget(roomId: string): SockaRoomBundle<TContract, TData>;\n\treadonly rooms: ReadonlyMap<string, SockaRoomBundle<TContract, TData>>;\n} {\n\tconst rooms = new Map<string, SockaRoomBundle<TContract, TData>>();\n\treturn {\n\t\tget(roomId: string): SockaRoomBundle<TContract, TData> {\n\t\t\tlet r = rooms.get(roomId);\n\t\t\tif (!r) {\n\t\t\t\tconst sessionMap = new Map<\n\t\t\t\t\tWebSocket,\n\t\t\t\t\tSockaWebSocketSession<TContract, TData>\n\t\t\t\t>();\n\t\t\t\tconst config = makeConfig(roomId, sessionMap);\n\t\t\t\tr = { sessionMap, config };\n\t\t\t\trooms.set(roomId, r);\n\t\t\t}\n\t\t\treturn r;\n\t\t},\n\t\tget rooms(): ReadonlyMap<string, SockaRoomBundle<TContract, TData>> {\n\t\t\treturn rooms;\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"sources":["../../src/server/attachSockaWebSocket.ts","../../src/server/room-registry.ts"],"names":[],"mappings":";;;;;;;AAyBO,SAAS,oBAAA,CAIf,SAAA,EACA,QAAA,EACA,MAAA,EACA,IAAA,EAC2C;AAC3C,EAAA,MAAM,UAAU,IAAI,qBAAA,CAAsB,SAAA,EAAW,QAAA,EAAU,QAAQ,IAAI,CAAA;AAC3E,EAAA,QAAA,CAAS,GAAA,CAAI,WAAW,OAAO,CAAA;AAC/B,EAAA,yBAAA,CAA0B,QAAQ,OAAO,CAAA;AAEzC,EAAA,IAAI,YAAA,GAAe,KAAA;AAEnB,EAAA,MAAM,WAAW,MAAY;AAC5B,IAAA,SAAA,CAAU,mBAAA,CAAoB,WAAW,SAAS,CAAA;AAClD,IAAA,SAAA,CAAU,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAC9C,IAAA,QAAA,CAAS,OAAO,SAAS,CAAA;AAAA,EAC1B,CAAA;AAEA,EAAA,MAAM,WAAW,MAAY;AAC5B,IAAA,IAAI,YAAA,EAAc;AAClB,IAAA,YAAA,GAAe,IAAA;AACf,IAAA,KAAA,CAAM,YAA2B;AAChC,MAAA,IAAI;AACH,QAAA,MAAM,QAAQ,iBAAA,EAAkB;AAAA,MACjC,SAAS,KAAA,EAAO;AACf,QAAA,gBAAA,CAAiB,OAAO,WAAA,EAAa;AAAA,UACpC,IAAA,EAAM,mBAAA;AAAA,UACN;AAAA,SACA,CAAA;AAAA,MACF,CAAA,SAAE;AACD,QAAA,QAAA,EAAS;AAAA,MACV;AAAA,IACD,CAAA,GAAG,CAAE,KAAA,CAAM,CAAC,KAAA,KAAmB;AAC9B,MAAA,gBAAA,CAAiB,OAAO,WAAA,EAAa;AAAA,QACpC,IAAA,EAAM,gBAAA;AAAA,QACN,OAAA,EAAS,QAAA;AAAA,QACT;AAAA,OACA,CAAA;AACD,MAAA,QAAA,EAAS;AAAA,IACV,CAAC,CAAA;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,EAAA,KAA2B;AAC7C,IAAA,MAAM,EAAA,GAAK,OAAO,UAAA,IAAc,MAAA;AAChC,IAAA,KAAK,2BAAA,CAA4B,OAAA,EAAS,EAAA,EAAI,EAAA,CAAG,IAAI,CAAA,CAAE,KAAA;AAAA,MACtD,CAAC,KAAA,KAAmB;AACnB,QAAA,gBAAA,CAAiB,OAAO,WAAA,EAAa;AAAA,UACpC,IAAA,EAAM,sBAAA;AAAA,UACN,OAAA,EAAS,QAAA;AAAA,UACT;AAAA,SACA,CAAA;AAAA,MACF;AAAA,KACD;AAAA,EACD,CAAA;AAEA,EAAA,MAAM,UAAU,MAAY;AAC3B,IAAA,QAAA,EAAS;AAAA,EACV,CAAA;AAEA,EAAA,SAAA,CAAU,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC/C,EAAA,SAAA,CAAU,gBAAA,CAAiB,SAAS,OAAO,CAAA;AAE3C,EAAA,OAAO,EAAE,OAAA,EAAS,OAAA,EAAS,QAAA,EAAS;AACrC;;;AC3EO,SAAS,wBAIf,UAAA,EAOC;AACD,EAAA,MAAM,KAAA,uBAAY,GAAA,EAA+C;AACjE,EAAA,OAAO;AAAA,IACN,IAAI,MAAA,EAAmD;AACtD,MAAA,IAAI,CAAA,GAAI,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA;AACxB,MAAA,IAAI,CAAC,CAAA,EAAG;AACP,QAAA,MAAM,UAAA,uBAAiB,GAAA,EAGrB;AACF,QAAA,MAAM,MAAA,GAAS,UAAA,CAAW,MAAA,EAAQ,UAAU,CAAA;AAC5C,QAAA,CAAA,GAAI,EAAE,YAAY,MAAA,EAAO;AACzB,QAAA,KAAA,CAAM,GAAA,CAAI,QAAQ,CAAC,CAAA;AAAA,MACpB;AACA,MAAA,OAAO,CAAA;AAAA,IACR,CAAA;AAAA,IACA,IAAI,KAAA,GAAgE;AACnE,MAAA,OAAO,KAAA;AAAA,IACR;AAAA,GACD;AACD","file":"index.js","sourcesContent":["import type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport { dispatchSockaInboundMessage } from \"./dispatchSockaInboundMessage\";\nimport {\n\tSockaWebSocketSession,\n\trunSockaSessionOnAttached,\n\ttype SockaWebSocketInit,\n\ttype SockaWebSocketSessionConfigUnion,\n} from \"./SockaWebSocketSession\";\n\nexport type AttachedSockaWebSocket<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n> = {\n\tsession: SockaWebSocketSession<TContract, TData>;\n\t/** Remove listeners and delete this session from the map (idempotent). */\n\tdispose: () => void;\n};\n\n/**\n * Register WebSocket `message` / `close` handlers, insert the session into\n * `sessions`, and return `{ session, dispose }`. `dispose` runs\n * {@link SockaWebSocketSession.invokeHandleClose} once, then removes listeners\n * (also triggered by `close`).\n */\nexport function attachSockaWebSocket<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\twebsocket: WebSocket,\n\tsessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>,\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\tinit?: SockaWebSocketInit,\n): AttachedSockaWebSocket<TContract, TData> {\n\tconst session = new SockaWebSocketSession(websocket, sessions, config, init);\n\tsessions.set(websocket, session);\n\trunSockaSessionOnAttached(config, session);\n\n\tlet shuttingDown = false;\n\n\tconst finalize = (): void => {\n\t\twebsocket.removeEventListener(\"message\", onMessage);\n\t\twebsocket.removeEventListener(\"close\", onClose);\n\t\tsessions.delete(websocket);\n\t};\n\n\tconst shutdown = (): void => {\n\t\tif (shuttingDown) return;\n\t\tshuttingDown = true;\n\t\tvoid (async (): Promise<void> => {\n\t\t\ttry {\n\t\t\t\tawait session.invokeHandleClose();\n\t\t\t} catch (error) {\n\t\t\t\treportSockaError(config.reportError, {\n\t\t\t\t\tkind: \"serverHandleClose\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t} finally {\n\t\t\t\tfinalize();\n\t\t\t}\n\t\t})().catch((error: unknown) => {\n\t\t\treportSockaError(config.reportError, {\n\t\t\t\tkind: \"serverShutdown\",\n\t\t\t\tadapter: \"attach\",\n\t\t\t\terror,\n\t\t\t});\n\t\t\tfinalize();\n\t\t});\n\t};\n\n\tconst onMessage = (ev: MessageEvent): void => {\n\t\tconst wf = config.wireFormat ?? \"json\";\n\t\tvoid dispatchSockaInboundMessage(session, wf, ev.data).catch(\n\t\t\t(error: unknown) => {\n\t\t\t\treportSockaError(config.reportError, {\n\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\tadapter: \"attach\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t},\n\t\t);\n\t};\n\n\tconst onClose = (): void => {\n\t\tshutdown();\n\t};\n\n\twebsocket.addEventListener(\"message\", onMessage);\n\twebsocket.addEventListener(\"close\", onClose);\n\n\treturn { session, dispose: shutdown };\n}\n","import type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport type { SockaWebSocketSession } from \"./SockaWebSocketSession\";\nimport type { SockaWebSocketSessionConfig } from \"./SockaWebSocketSessionConfig\";\n\nexport type SockaRoomBundle<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n> = {\n\tsessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>;\n};\n\n/**\n * Per-room {@link SockaWebSocketSession} maps and configs for Bun/Hono multi-room\n * apps (one bundle per `roomId`).\n */\nexport function createSockaRoomRegistry<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tmakeConfig: (\n\t\troomId: string,\n\t\tsessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>,\n\t) => SockaWebSocketSessionConfig<TContract, TData>,\n): {\n\tget(roomId: string): SockaRoomBundle<TContract, TData>;\n\treadonly rooms: ReadonlyMap<string, SockaRoomBundle<TContract, TData>>;\n} {\n\tconst rooms = new Map<string, SockaRoomBundle<TContract, TData>>();\n\treturn {\n\t\tget(roomId: string): SockaRoomBundle<TContract, TData> {\n\t\t\tlet r = rooms.get(roomId);\n\t\t\tif (!r) {\n\t\t\t\tconst sessionMap = new Map<\n\t\t\t\t\tWebSocket,\n\t\t\t\t\tSockaWebSocketSession<TContract, TData>\n\t\t\t\t>();\n\t\t\t\tconst config = makeConfig(roomId, sessionMap);\n\t\t\t\tr = { sessionMap, config };\n\t\t\t\trooms.set(roomId, r);\n\t\t\t}\n\t\t\treturn r;\n\t\t},\n\t\tget rooms(): ReadonlyMap<string, SockaRoomBundle<TContract, TData>> {\n\t\t\treturn rooms;\n\t\t},\n\t};\n}\n"]}
@@ -11,12 +11,18 @@ declare const RESERVED_SOCKA_PROCEDURE_NAMES: readonly ["then", "catch", "finall
11
11
  type ReservedSockaProcedureName = (typeof RESERVED_SOCKA_PROCEDURE_NAMES)[number];
12
12
 
13
13
  /**
14
- * Defines one client-initiated call: an optional input schema and a required output schema.
15
- * Both must be Standard Schema v1 compliant (Zod v4, Valibot, ArkType, etc.).
14
+ * Defines one client-initiated call: optional `input` and optional `output` schemas
15
+ * (Standard Schema v1: Zod v4, Valibot, ArkType, etc.).
16
+ *
17
+ * - **`output` present** (including `z.void()`): request/response RPC; the server sends
18
+ * a validated `serverResponse` on success.
19
+ * - **`output` omitted**: fire-and-forget on success (no `serverResponse`); the client
20
+ * `send` method resolves after the request is sent. Use `output: z.void()` when you
21
+ * still want a correlated ack. See the package README and {@link defineSocka}.
16
22
  */
17
23
  type SockaProcedureDef = {
18
24
  readonly input?: StandardSchemaV1;
19
- readonly output: StandardSchemaV1;
25
+ readonly output?: StandardSchemaV1;
20
26
  };
21
27
  /** Configuration object accepted by {@link defineSocka}. */
22
28
  type SockaContractConfig = {
@@ -38,23 +44,27 @@ type SockaContract<T extends SockaContractConfig = SockaContractConfig> = {
38
44
  pushes: Record<string, StandardSchemaV1>;
39
45
  } ? T["pushes"] : Record<string, never>;
40
46
  };
47
+ /** Inferred client return type for a call: payload type or `void` when `output` is omitted. */
48
+ type InferSockaCallReturn<P extends SockaProcedureDef> = P["output"] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<P["output"]> : void;
41
49
  type CallFn<P extends SockaProcedureDef> = P extends {
42
50
  input: infer I extends StandardSchemaV1;
43
- } ? (input: StandardSchemaV1.InferInput<I>) => Promise<StandardSchemaV1.InferOutput<P["output"]>> : () => Promise<StandardSchemaV1.InferOutput<P["output"]>>;
51
+ } ? (input: StandardSchemaV1.InferInput<I>) => Promise<InferSockaCallReturn<P>> : () => Promise<InferSockaCallReturn<P>>;
44
52
  /**
45
53
  * Infers the typed `session.send.*` method map for a contract.
46
54
  */
47
55
  type InferSockaSend<C extends SockaContract> = {
48
56
  [K in keyof C["calls"]]: CallFn<C["calls"][K]>;
49
57
  };
50
- type HandlerOut<P extends SockaProcedureDef> = StandardSchemaV1.InferOutput<P["output"]> | Promise<StandardSchemaV1.InferOutput<P["output"]>>;
58
+ type HandlerOut<P extends SockaProcedureDef> = P["output"] extends StandardSchemaV1 ? StandardSchemaV1.InferOutput<P["output"]> | Promise<StandardSchemaV1.InferOutput<P["output"]>> : void | Promise<void>;
51
59
  type HandlerFn<P extends SockaProcedureDef, TSession> = P extends {
52
60
  input: infer I extends StandardSchemaV1;
53
61
  } ? (input: StandardSchemaV1.InferInput<I>, session: TSession) => HandlerOut<P> : (session: TSession) => HandlerOut<P>;
54
62
  /**
55
63
  * Infers the typed server handler map for a contract. Handlers with an input
56
64
  * schema take `(input, session)`; calls without input take `(session)` only.
57
- * Each handler returns the output that will be validated before sending.
65
+ * When `output` is present, the return value is validated and sent as `serverResponse`.
66
+ * When `output` is omitted (fire-and-forget), the handler should return `void`; the
67
+ * server does not send a success response.
58
68
  */
59
69
  type InferSockaHandlers<C extends SockaContract, TSession> = {
60
70
  [K in keyof C["calls"]]: HandlerFn<C["calls"][K], TSession>;
@@ -79,6 +89,7 @@ type InferSockaPushHandlers<C extends SockaContract> = {
79
89
  * calls: {
80
90
  * list: { output: z.array(itemSchema) },
81
91
  * insert: { input: z.object({ item: itemSchema }), output: z.void() },
92
+ * notify: { input: z.object({ text: z.string() }) },
82
93
  * },
83
94
  * });
84
95
  * ```
@@ -90,6 +101,33 @@ declare function defineSocka<const T extends SockaContractConfig>(config: T & {
90
101
  calls: ValidateSockaCallKeys<T["calls"]>;
91
102
  }): SockaContract<T>;
92
103
 
104
+ /**
105
+ * Thrown on the server and surfaced on the client when the wire uses a shared
106
+ * `{ type: "error", id, error }` envelope for correlated RPC failures.
107
+ */
108
+ declare class SockaError extends Error {
109
+ readonly requestId?: string;
110
+ /** Procedure name when provided on the wire (`serverError.rpc`). */
111
+ readonly rpc?: string;
112
+ readonly code?: string;
113
+ readonly data?: unknown;
114
+ constructor(message: string, options?: {
115
+ requestId?: string;
116
+ rpc?: string;
117
+ code?: string;
118
+ data?: unknown;
119
+ cause?: unknown;
120
+ });
121
+ /** Builds a {@link SockaError} from a standard RPC error envelope. */
122
+ static fromWire(msg: {
123
+ id: string;
124
+ error: string;
125
+ rpc?: string;
126
+ code?: string;
127
+ data?: unknown;
128
+ }): SockaError;
129
+ }
130
+
93
131
  /**
94
132
  * Versioned socka wire framing. After JSON parse or msgpack unpack, every frame
95
133
  * must satisfy {@link decodeSockaWire}; procedure bodies are validated with Standard Schema on each side.
@@ -117,6 +155,8 @@ type SockaServerErrorFrame = {
117
155
  readonly v: typeof SOCKA_WIRE_VERSION;
118
156
  readonly id: string;
119
157
  readonly error: string;
158
+ /** Procedure name when the error relates to a client RPC (helps clients without a pending entry). */
159
+ readonly rpc?: string;
120
160
  /** Optional machine-readable code (e.g. `FORBIDDEN`). */
121
161
  readonly code?: string;
122
162
  /** Optional structured detail for clients; keep small and JSON-serializable. */
@@ -155,6 +195,7 @@ declare function encodeServerResponse(id: string, rpc: string, body: unknown): S
155
195
  declare function encodeServerError(id: string, error: string, extra?: {
156
196
  readonly code?: string;
157
197
  readonly data?: unknown;
198
+ readonly rpc?: string;
158
199
  }): SockaServerErrorFrame;
159
200
  /** Builds a socka v1 server event frame. */
160
201
  declare function encodeServerEvent(event: string, body: unknown): SockaServerEventFrame;
@@ -189,6 +230,30 @@ type SockaReportError = {
189
230
  kind: "clientEventValidation";
190
231
  eventName: string;
191
232
  error: unknown;
233
+ }
234
+ /**
235
+ * `serverError` for a fire-and-forget call (no pending client promise). Prefer
236
+ * setting `reportError` on the session when using output-less procedures.
237
+ */
238
+ | {
239
+ kind: "clientFireAndForgetRpcError";
240
+ error: SockaError;
241
+ }
242
+ /**
243
+ * `serverError` with no matching pending entry for a call that expects a response
244
+ * (e.g. stale id after reconnect or duplicate frame).
245
+ */
246
+ | {
247
+ kind: "clientOrphanServerError";
248
+ error: SockaError;
249
+ }
250
+ /**
251
+ * Server sent `serverResponse` for a procedure with no `output` (misbehaving server).
252
+ */
253
+ | {
254
+ kind: "clientUnexpectedServerResponse";
255
+ rpc: string;
256
+ requestId: string;
192
257
  } | {
193
258
  kind: "serverOnAttached";
194
259
  error: unknown;
@@ -210,4 +275,4 @@ declare function defaultReportError(event: SockaReportError): void;
210
275
  /** Invokes the optional `reportError` callback when provided, otherwise `defaultReportError`. */
211
276
  declare function reportSockaError(reportError: ((event: SockaReportError) => void) | undefined, event: SockaReportError): void;
212
277
 
213
- export { type DecodedSockaWire as D, type InferSockaSend as I, RESERVED_SOCKA_PROCEDURE_NAMES as R, type SockaContract as S, type ValidateSockaCallKeys as V, type SockaContractConfig as a, type SockaWireFormat as b, type SockaProcedureDef as c, defineSocka as d, type InferSockaHandlers as e, type InferSockaPushHandlers as f, type InferSockaPushPayload as g, type ReservedSockaProcedureName as h, SOCKA_WIRE_VERSION as i, SockaWireError as j, type SockaClientRequestFrame as k, type SockaServerErrorFrame as l, type SockaServerEventFrame as m, type SockaServerResponseFrame as n, type SockaWireFrame as o, decodeSockaWire as p, encodeClientRequest as q, encodeServerResponse as r, encodeServerError as s, encodeServerEvent as t, encodeSockaWire as u, parseWirePayload as v, defaultReportError as w, reportSockaError as x, type SockaReportError as y };
278
+ export { type DecodedSockaWire as D, type InferSockaSend as I, RESERVED_SOCKA_PROCEDURE_NAMES as R, type SockaContract as S, type ValidateSockaCallKeys as V, type SockaContractConfig as a, type SockaProcedureDef as b, type InferSockaHandlers as c, defineSocka as d, type InferSockaPushHandlers as e, type InferSockaPushPayload as f, type ReservedSockaProcedureName as g, SockaError as h, SOCKA_WIRE_VERSION as i, SockaWireError as j, type SockaClientRequestFrame as k, type SockaServerErrorFrame as l, type SockaServerEventFrame as m, type SockaServerResponseFrame as n, type SockaWireFrame as o, decodeSockaWire as p, encodeClientRequest as q, encodeServerResponse as r, encodeServerError as s, encodeServerEvent as t, encodeSockaWire as u, parseWirePayload as v, type SockaWireFormat as w, defaultReportError as x, reportSockaError as y, type SockaReportError as z };
package/docs/README.md CHANGED
@@ -24,4 +24,4 @@ In-repo guides for the **[Socka](../README.md)** library (**npm** [`@firtoz/sock
24
24
  | [Internals](./internals.md) | Wire protocol details, frame kinds, source file links (contributors & curious readers) |
25
25
  | [Comparison](./comparison.md) | vs DIY WS, **socket.io**, **tRPC** |
26
26
 
27
- **Roadmap** — [Deferred and post–v1 ideas](../roadmap.md).
27
+ **Roadmap** — [Deferred ideas and future work](../roadmap.md).
package/docs/auth.md CHANGED
@@ -4,7 +4,7 @@ Socka does not ship a built-in auth layer: you decide **who** may open a WebSock
4
4
 
5
5
  ## Read credentials on upgrade
6
6
 
7
- - **`@firtoz/socka/server`** with **`strictUpgradeRequest: true`** in **`createData`**, read **`init.request`** (cookies via **`Cookie`**, **`Authorization`**, URL query, path segments).
7
+ - **`@firtoz/socka/server`** by default **`createData`** receives **`SockaStrictWebSocketInit`**; read **`init.request`** (cookies via **`Cookie`**, **`Authorization`**, URL query, path segments).
8
8
  - **`SockaDoSession`** with **`createData: (ctx) => …`** — use Hono **`ctx.req`**, **`ctx.get("…")`**, or **`ctx.req.raw.headers`**.
9
9
 
10
10
  Reject before returning session data: throw **`SockaError`** with **`{ code, data }`** so the client receives a correlated **`serverError`** frame (see **[Reference — RPC handler errors](./reference.md#rpc-handler-errors)**).
package/docs/client.md CHANGED
@@ -16,6 +16,10 @@ Outside React, construct **`SockaSession`** from **`@firtoz/socka/client`** with
16
16
 
17
17
  Full list: **[Reference — Client configuration](./reference.md#client-configuration)**.
18
18
 
19
+ ### Fire-and-forget
20
+
21
+ Calls that **omit** **`output`** resolve **`await session.send.*`** as soon as the request is sent; they do **not** wait for **`serverResponse`**. If the server returns **`serverError`**, handle it with **`reportError`** (see **[Reference — Errors](./reference.md#errors-and-observability)**) — there is no rejected promise for that call. Prefer **`output: z.void()`** when you still want **`await`** to track success or failure through the returned promise.
22
+
19
23
  **Call names** — For literal `calls` objects, **`defineSocka`** rejects names that would make **`session.send`** Promise-like or clash with object shape (e.g. **`then`**, **`toString`**). If you use a wide **`Record<string, SockaProcedureDef>`**, TypeScript cannot apply that check; **`SockaSession`** still validates at construction (see **`RESERVED_SOCKA_PROCEDURE_NAMES`** in **`@firtoz/socka/core`**).
20
24
 
21
25
  ```ts
@@ -1,4 +1,4 @@
1
- # Compared to hand-rolled WebSocket RPC (and common alternatives)
1
+ # Compared to custom WebSocket RPC (and common alternatives)
2
2
 
3
3
  Most apps model messages as large discriminated unions (`type` + `id`), validate twice (once on the wire, once in the handler), and maintain a **pending `Map<string, Deferred>`** for every RPC. That works, but types drift between client and server, correlation IDs are easy to get wrong, and pushing **server events** becomes a second, parallel protocol.
4
4
 
@@ -39,7 +39,7 @@ new SockaDoSession(websocket, sessions, {
39
39
  });
40
40
  ```
41
41
 
42
- Handler types use **`InferSockaHandlers<typeof myContract, SockaDoSession<typeof myContract, …>>`**. Throw **`SockaError`** for expected domain failures (bad move, permission denied) so the client receives a structured **`serverError`** frame; see **[Reference](./reference.md)** for other failure paths.
42
+ Handler types use **`InferSockaHandlers<typeof myContract, SockaDoSession<typeof myContract, …>>`**. Same semantics as **`SockaWebSocketSession`**: calls **with** **`output`** get a validated **`serverResponse`**; calls **without** **`output`** are fire-and-forget on success (no success frame). Throw **`SockaError`** for expected domain failures so the client receives a structured **`serverError`** frame (with optional **`rpc`** on the wire). For client-side **`reportError`** when using output-less calls, see **[Reference](./reference.md#optional-output-fire-and-forget)** and **[Client](./client.md#fire-and-forget)**.
43
43
 
44
44
  **`createData`** — If your session needs typed **`session.data`**, provide **`createData: (ctx) => …`** where **`ctx`** is a Hono **`Context`** (bindings, request, etc.). That runs when the DO accepts the socket; data participates in **hibernation** via **`@firtoz/websocket-do`** **`BaseSession`** (see **`session.update()`** below).
45
45
 
package/docs/internals.md CHANGED
@@ -35,11 +35,11 @@ Every decoded payload is one logical socka **v1** object. **`decodeSockaWire`**
35
35
  | Kind | Role |
36
36
  |------|------|
37
37
  | `clientRequest` | Client → server RPC (`id`, `rpc`, `body`) |
38
- | `serverResponse` | Success reply (correlated by `id`) |
39
- | `serverError` | Correlated failure (`id`, `error` message string) |
38
+ | `serverResponse` | Success reply (correlated by `id`) — **omitted** when the contract call has **no** **`output`** (fire-and-forget success); see **[Reference](./reference.md)** |
39
+ | `serverError` | Correlated failure (`id`, **`error`** string; optional **`code`**, **`data`**, **`rpc`**) — **`rpc`** names the procedure when the failure is tied to an RPC |
40
40
  | `serverEvent` | Server push (`event`, `body`) — **not** tied to an RPC `id` |
41
41
 
42
- Clients generate **`id`** strings per request; servers echo them on **`serverResponse`** and **`serverError`** so concurrent RPCs never mix results. **`serverEvent`** uses the contract **`pushes`** map and **`session.subscribe`** on the client.
42
+ Clients generate **`id`** strings per request; servers echo them on **`serverResponse`** (when the call declares **`output`**) and on **`serverError`**. **`serverEvent`** uses the contract **`pushes`** map and **`session.subscribe`** on the client.
43
43
 
44
44
  ---
45
45
 
package/docs/recipes.md CHANGED
@@ -12,7 +12,6 @@ import { createSockaRoomRegistry } from "@firtoz/socka/server";
12
12
 
13
13
  const rooms = createSockaRoomRegistry((roomId, _sessionMap) => ({
14
14
  contract: myContract,
15
- strictUpgradeRequest: true,
16
15
  createData: (init) => { /* parse init.request */ return { roomId: "…" }; },
17
16
  handlers: { /* … */ },
18
17
  handleClose: async () => {},
package/docs/reference.md CHANGED
@@ -16,15 +16,19 @@ type Handlers = InferSockaHandlers<
16
16
 
17
17
  **`InferSockaSend`** — Call names become methods on **`session.send`**; inputs/outputs follow the contract. **`InferSockaHandlers`** — Server handler arity matches **`calls`** (with or without `input`).
18
18
 
19
+ ### Optional output (fire-and-forget)
20
+
21
+ If a call omits **`output`**, the server sends **no** **`serverResponse`** on success, and the client **`send`** method returns **`Promise<void>`** that resolves after the request is queued to the socket (not after the server runs the handler). **`output: z.void()`** keeps full request/response: the server still sends **`serverResponse`** and the client **`await`** waits for it. For output-less calls, server **`serverError`** frames include an optional **`rpc`** field so **`reportError`** can attribute failures when there is no pending promise.
22
+
19
23
  ## Errors and observability
20
24
 
21
25
  | Concern | Hook |
22
26
  |--------|------|
23
27
  | Exceptions inside **RPC handlers** | `onHandlerError` on `SockaWebSocketSessionConfig` / `SockaDoSessionConfig` |
24
28
  | Invalid **inbound wire** payloads (before your handler runs) | `onValidationError` on the same config |
25
- | Everything else ( **`onAttached`** failures, adapter I/O, **client** push listener throws, **client** push payload validation) | Optional **`reportError(event)`** on `SockaWebSocketSessionConfig`, `SockaDoSessionConfig`, or `SockaSession` / `useSockaSession` options |
29
+ | Everything else ( **`onAttached`** failures, adapter I/O, **client** push listener throws, **client** push payload validation, **fire-and-forget RPC errors** without a pending promise) | Optional **`reportError(event)`** on `SockaWebSocketSessionConfig`, `SockaDoSessionConfig`, or `SockaSession` / `useSockaSession` options |
26
30
 
27
- Each **`event`** is **`SockaReportError`**: one discriminated union (`kind` narrows context; **`error`** is the thrown/rejected value; **`eventName`** / **`adapter`** where relevant). Export: **`@firtoz/socka/core`** (`defaultReportError`, `reportSockaError`). If you omit **`reportError`**, socka uses **`console.error`** with the same **`socka:`**-prefixed messages as before.
31
+ Each **`event`** is **`SockaReportError`**: one discriminated union (`kind` narrows context; **`error`** is the thrown/rejected value where applicable; **`eventName`** / **`adapter`** where relevant). Kinds include **`clientFireAndForgetRpcError`**, **`clientOrphanServerError`**, and **`clientUnexpectedServerResponse`** for client-side RPC edge cases. Export: **`@firtoz/socka/core`** (`defaultReportError`, `reportSockaError`). If you omit **`reportError`**, socka uses **`console.error`** with the same **`socka:`**-prefixed messages as before.
28
32
 
29
33
  ## Wire encoding: JSON and msgpack
30
34
 
@@ -44,7 +48,7 @@ Throw **`SockaError`** from handlers when you control the **message** sent on th
44
48
  throw new SockaError("Not allowed", { code: "FORBIDDEN", data: { reason: "…" } });
45
49
  ```
46
50
 
47
- Any other thrown value is wrapped in **`SockaError`** using the original **`Error.message`** when possible, otherwise **`"Handler failed"`**. The client rejects the matching RPC with **`SockaError`**; the wire carries **`error`** (string) plus optional **`code`** and **`data`**. Older peers that only read **`error`** are unchanged.
51
+ Any other thrown value is wrapped in **`SockaError`** using the original **`Error.message`** when possible, otherwise **`"Handler failed"`**. For calls **with** **`output`**, the client rejects the matching RPC with **`SockaError`**. For **output-less** (fire-and-forget) calls, there is no pending promise; the client surfaces **`SockaError`** via **`reportError`** (`kind`: **`clientFireAndForgetRpcError`**). The wire carries **`error`** (string) plus optional **`code`**, **`data`**, and **`rpc`** (procedure name). Older peers that only read **`error`** are unchanged.
48
52
 
49
53
  ## Server session configuration
50
54
 
@@ -56,8 +60,8 @@ Any other thrown value is wrapped in **`SockaError`** using the original **`Erro
56
60
  | **`wireFormat`** | `"json"` (default) or `"msgpack"` — must match clients. |
57
61
  | **`handlers`** | Typed call implementations; arity follows input schema (see [Getting started](./getting-started.md)). |
58
62
  | **`handleClose`** | Async per-socket teardown; runs **before** removal from `sessions` (see [Lifecycle](./lifecycle.md)). |
59
- | **`createData`** | Builds **`session.data`**. **`SockaWebSocketSession`**: **`(init: SockaWebSocketInit) => T`** or, with **`strictUpgradeRequest: true`**, **`(init: SockaStrictWebSocketInit) => T`** so **`init.request`** is always set — see **[Server](./server.md)**. **`SockaDoSession`**: **`(ctx: Context) => T`** — see **[Durable Objects](./durable-objects.md)**. |
60
- | **`strictUpgradeRequest`** | When **`true`**, **`createData`** receives **`SockaStrictWebSocketInit`** ( **`init.request` required** ). Omitted = **`SockaWebSocketInit`** with optional **`request`**. See **[Server — Strict upgrade request](./server.md#strict-upgrade-request)**. |
63
+ | **`createData`** | Builds **`session.data`**. **`SockaWebSocketSession`**: **`SockaWebSocketSessionConfig`** uses **`(init: SockaStrictWebSocketInit) => T`**; **`SockaWebSocketSessionConfigLoose`** uses **`(init: SockaWebSocketInit) => T`** — see **[Server](./server.md)**. **`SockaDoSession`**: **`(ctx: Context) => T`** — see **[Durable Objects](./durable-objects.md)**. |
64
+ | **`SockaWebSocketSessionConfig` / `Loose` / `Union`** | Default **`SockaWebSocketSessionConfig`** is strict ( **`init.request` required** ). **`SockaWebSocketSessionConfigLoose`** sets **`strictUpgradeRequest: false`**. **`SockaWebSocketSessionConfigUnion`** is the union of strict and loose (what **`attachSockaWebSocket`** and Bun/Hono helpers accept). See **[Server — Strict upgrade request](./server.md#strict-upgrade-request)**. |
61
65
  | **`onAttached`** | Optional: after registration in `sessions` (safe for broadcasts). |
62
66
  | **`onHandlerError`** | Observes thrown errors in handlers (after optional `SockaError` wrapping for the wire). |
63
67
  | **`onValidationError`** | Inbound frame failed schema / wire decode before your handler. |
package/docs/server.md CHANGED
@@ -48,21 +48,23 @@ Optional fourth argument **`{ request }`** is passed to **`createData`** when yo
48
48
 
49
49
  | | |
50
50
  |---:|---|
51
- | **`createData`** runs in the **`SockaWebSocketSession`** constructor. | You receive **`SockaWebSocketInit`** (e.g. **`{ request }`** from **`attachSockaWebSocket`**). |
51
+ | **`createData`** runs in the **`SockaWebSocketSession`** constructor. | By default you receive **`SockaStrictWebSocketInit`** ( **`init.request`** is the upgrade **`Request`** ). Set **`strictUpgradeRequest: false`** for **`SockaWebSocketInit`** when **`request`** may be missing. |
52
52
  | **Result** is stored in **`session.data`**. | Lives in **process memory** unless you persist it yourself. |
53
53
 
54
54
  ## Strict upgrade request
55
55
 
56
- **`strictUpgradeRequest`** is an optional field on **`SockaWebSocketSessionConfig`**. It does not change the wire protocol only how **`createData`** is typed and what happens at runtime if the upgrade **`Request`** is missing.
56
+ **Strict vs loose:** **`SockaWebSocketSessionConfig`** (default) requires the upgrade **`Request`**. Use **`SockaWebSocketSessionConfigLoose`** with **`strictUpgradeRequest: false`** when **`init.request`** may be missing — it does not change the wire protocol, only **`createData`** typing and runtime checks.
57
57
 
58
58
  | Mode | Type passed to **`createData`** | When to use it |
59
59
  |------|----------------------------------|----------------|
60
- | **Omitted** (default) | **`SockaWebSocketInit`** — **`init.request` may be `undefined`** | Custom **`attachSockaWebSocket`** call sites, tests, or any adapter that might not attach an HTTP **`Request`**. You handle a missing URL yourself (optional chaining, fallback URL). |
61
- | **`true`** | **`SockaStrictWebSocketInit`** — **`init.request` is always a `Request`** | Normal **Bun** / **Hono** upgrades where you always have the incoming request and want to read query params, cookies, or path without `init.request?.url ?? "http://_/"` placeholders. TypeScript catches mistakes; if the adapter omits **`request`**, socka throws a clear error at session construction. |
60
+ | **Omitted** (default) | **`SockaStrictWebSocketInit`** — **`init.request` is always a `Request`** | Normal **Bun** / **Hono** upgrades and **`attachSockaWebSocket(..., { request })`**. **`createData`** can use **`new URL(init.request.url)`** and read headers. If the adapter omits **`request`**, socka throws at session construction. |
61
+ | **`false`** | **`SockaWebSocketInit`** — **`init.request` may be `undefined`** | Tests, **Node `ws`** without a **`Request`**, or adapters that only have a bare **`WebSocket`**. Handle a missing **`request`** in **`createData`** or omit **`createData`** usage of **`init`**. |
62
62
 
63
- **Typical wiring:** Bun stores **`request`** on **`ServerWebSocket` `data`**; use **`sockaBunInitFromWsData`** with **`strictUpgradeRequest: true`**. Hono **`sockaHonoNodeWs`** can omit **`sockaInit`** — the default builds a **`Request`** from the Hono context. See JSDoc on **`SockaWebSocketSessionConfig`**, **`SockaWebSocketInit`**, and **`SockaStrictWebSocketInit`** in **`@firtoz/socka/server`**.
63
+ **Typical wiring:** Bun stores **`request`** on **`ServerWebSocket` `data`**; use **`sockaBunInitFromWsData`** (strict is the default). Hono **`sockaHonoNodeWs`** can omit **`sockaInit`** — the default builds a **`Request`** from the Hono context. See JSDoc on **`SockaWebSocketSessionConfig`**, **`SockaWebSocketInit`**, and **`SockaStrictWebSocketInit`** in **`@firtoz/socka/server`**.
64
64
 
65
- Calls **with** an input schema use **`(input, session) => output`**. Calls **without** input use **`(session) => output`** only (no `undefined` first argument). The **`session`** argument is the **`SockaWebSocketSession`** instance: read **`session.data`**, call **`await session.emitPush`**, **`await session.broadcastPush`** (payloads are validated against the contract **`pushes`** schemas before send).
65
+ Calls **with** an input schema use **`(input, session) => …`**. Calls **without** input use **`(session) => …`** only (no `undefined` first argument). When the call has **`output`** in the contract, the handler return value is validated and sent as **`serverResponse`**. When **`output` is omitted** (fire-and-forget), the handler should return **`void`**; socka sends **no** success **`serverResponse`** (failures still become **`serverError`**). See **[Reference — Optional output (fire-and-forget)](./reference.md#optional-output-fire-and-forget)** and **[Client](./client.md)**.
66
+
67
+ The **`session`** argument is the **`SockaWebSocketSession`** instance: read **`session.data`**, call **`await session.emitPush`**, **`await session.broadcastPush`** (payloads are validated against the contract **`pushes`** schemas before send).
66
68
 
67
69
  **`onAttached`** — optional. Runs after the session is registered in the shared **`sessions`** map (safe to broadcast to peers).
68
70
 
@@ -89,6 +91,7 @@ type GameData = { health: number };
89
91
 
90
92
  const session = new SockaWebSocketSession(websocket, sessions, {
91
93
  contract: gameContract,
94
+ strictUpgradeRequest: false,
92
95
  createData: () => ({ health: 100 }),
93
96
  handlers: {
94
97
  getHealth: async (s) => ({ health: s.data.health }),
@@ -23,3 +23,7 @@ If you send **raw binary** application data, msgpack mode is a natural fit; keep
23
23
  Set **`wireFormat: "msgpack"`** on **`SockaSession`** / **`SockaWebSocketClient`** and on **`SockaWebSocketSessionConfig`** / **`SockaDoSessionConfig`** for every session that speaks to that client.
24
24
 
25
25
  **Details:** **[Reference — Wire encoding](./reference.md#wire-encoding-json-and-msgpack)** · **[Internals](./internals.md)**.
26
+
27
+ ## `serverError` and `rpc`
28
+
29
+ On **`serverError`** frames, **`rpc`** is an optional string naming the procedure when the failure is tied to a client RPC. Servers built on **`SockaWebSocketSession`** include it on correlated errors so clients can attribute failures without relying on a pending **`Map`** entry (needed for fire-and-forget calls that omit **`output`**). Older servers may omit **`rpc`**; clients still receive **`id`** and **`error`**.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@firtoz/socka",
3
- "version": "2.1.0",
3
+ "version": "3.0.0",
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",
package/roadmap.md CHANGED
@@ -1,8 +1,8 @@
1
- # Roadmap (post–socka v1)
1
+ # Roadmap
2
2
 
3
3
  Deferred work and ideas—not a commitment order.
4
4
 
5
5
  - **npm metadata** — Keywords, README polish, and release notes that match positioning (WebSocket RPC, Standard Schema, Durable Objects).
6
- - **Release communication** — Short announcement (optional blog or dev.to), comparison vs hand-rolled protocols and adjacent tools.
6
+ - **Release communication** — Short announcement (optional blog or dev.to), comparison vs custom protocols and adjacent tools.
7
7
  - **`create-socka-app` / `bun create`** — After the public API is stable enough, a scaffold for contract + client + server/DO choice.
8
8
  - **Ecosystem** — Tutorials, Stack Overflow presence, example repos as the community asks.
@@ -7,7 +7,7 @@ description: Standard Schema socka contracts (defineSocka), v1 wire envelopes, S
7
7
 
8
8
  ## Contract
9
9
 
10
- - **`defineSocka`** in **`@firtoz/socka/core`**: pass **`calls`** (and optional **`pushes`**) with **`StandardSchemaV1`** `input` / `output` per call. Types flow from **`InferSockaSend`**, **`InferSockaHandlers`**, **`InferSockaPushHandlers`**.
10
+ - **`defineSocka`** in **`@firtoz/socka/core`**: pass **`calls`** (and optional **`pushes`**) with **`StandardSchemaV1`** `input` and optional **`output`** per call. Omit **`output`** for fire-and-forget (no **`serverResponse`** on success; client **`send`** resolves after send; failures use **`serverError`** + **`reportError`**). Use **`output: z.void()`** for a correlated ACK. Types flow from **`InferSockaSend`**, **`InferSockaHandlers`**, **`InferSockaPushHandlers`**.
11
11
  - There is **no** `defineSockaProtocol` / `defineSockaRpcSpec` in socka—those names belong to other stacks; use **`defineSocka`** only.
12
12
 
13
13
  ## Browser client
@@ -24,7 +24,7 @@ description: Standard Schema socka contracts (defineSocka), v1 wire envelopes, S
24
24
  ## Wire
25
25
 
26
26
  - Every frame is a **socka v1** object validated by **`decodeSockaWire`** (`socka`, **`v`**, discriminators, **`id`**, **`rpc`**, **`body`**, …). Invalid payloads become **`SockaWireError`** (or **`onValidationError`** on the client).
27
- - RPC success → **`serverResponse`**; RPC failure → **`serverError`**; server pushes → **`serverEvent`** with event name + **`body`**.
27
+ - RPC success → **`serverResponse`** (unless the call omits **`output`**, then no success frame); RPC failure → **`serverError`** (optional **`rpc`** field for procedure name); server pushes → **`serverEvent`** with event name + **`body`**.
28
28
  - **JSON vs msgpack** is a transport choice only; the logical shape is identical. **Client and DO must use the same `wireFormat`.**
29
29
 
30
30
  ## Durable Objects
@@ -7,7 +7,7 @@ description: SockaDoSession and SockaWebSocketDO on Cloudflare Durable Objects
7
7
 
8
8
  ## Components
9
9
 
10
- - **`SockaDoSession`** (**`@firtoz/socka/do`**): extends **`BaseSession`** from **`@firtoz/websocket-do`**. Incoming messages are decoded with **`decodeSockaWire`** after JSON parse (text) or **`parseWirePayload`** (msgpack). Valid **`clientRequest`** frames are dispatched to **`handlers`** (typed **`InferSockaHandlers<typeof contract>`**). Responses use **`encodeServerResponse`** / **`encodeServerError`**; optional **`encodeServerEvent`** for contract pushes.
10
+ - **`SockaDoSession`** (**`@firtoz/socka/do`**): extends **`BaseSession`** from **`@firtoz/websocket-do`**. Incoming messages are decoded with **`decodeSockaWire`** after JSON parse (text) or **`parseWirePayload`** (msgpack). Valid **`clientRequest`** frames are dispatched to **`handlers`** (typed **`InferSockaHandlers<typeof contract>`**). Calls **with** **`output`** get **`encodeServerResponse`** on success; calls **without** **`output`** send **`encodeServerError`** only on failure; optional **`encodeServerEvent`** for contract pushes.
11
11
  - **`SockaWebSocketDO`**: thin **`BaseWebSocketDO`** wrapper; you supply **`createSockaSession(ctx, websocket)`** returning a **`SockaDoSession`** (or subclass).
12
12
 
13
13
  ## Session config (`SockaDoSessionConfig`)
@@ -15,7 +15,7 @@ description: SockaDoSession and SockaWebSocketDO on Cloudflare Durable Objects
15
15
  - **`contract`**: from **`defineSocka`**.
16
16
  - **`wireFormat`**: **`"json"`** (default) or **`"msgpack"`**—must match the browser **`SockaWebSocketClient`/`SockaSession`** **`wireFormat`**.
17
17
  - **`createData`**: optional when session **`TData`** is empty (**`Record<string, never>`**); defaults to **`{}`**. Otherwise Hono **`Context`** → per-connection state.
18
- - **`handlers`**: call name → async/sync handler; inputs/outputs validated with Standard Schema via **`parseStandardSchema`**.
18
+ - **`handlers`**: call name → async/sync handler; inputs and optional outputs validated with Standard Schema via **`parseStandardSchema`** (omit **`output`** in the contract for fire-and-forget success; handler still runs on the server).
19
19
  - **`handleClose`**: cleanup when the socket closes.
20
20
  - **`onHandlerError`**, **`onValidationError`**: optional hooks (validation and handler failures are also mapped to **`serverError`** frames).
21
21
  - **`serializeJson`**, **`deserializeJson`**: optional; default **`JSON.stringify`/`JSON.parse`** for JSON mode.
@@ -7,7 +7,7 @@ description: Standard Schema v1 for socka contracts and wire validation—Zod, V
7
7
 
8
8
  ## When to use
9
9
 
10
- You define **call** `input` / `output` (and optional **push** payloads) for **`@firtoz/socka/core`**, **`@firtoz/socka/client`**, **`@firtoz/socka/react`**, and **`@firtoz/socka/do`**. Every schema slot expects **`StandardSchemaV1`** from **`@standard-schema/spec`** so callers can use **Zod**, **Valibot**, **ArkType**, etc., without socka-specific adapters.
10
+ You define **call** `input` and optional **`output`** (and optional **push** payloads) for **`@firtoz/socka/core`**, **`@firtoz/socka/client`**, **`@firtoz/socka/react`**, and **`@firtoz/socka/do`**. Omit **`output`** only for intentional fire-and-forget success semantics; **`output`** slots that are present use **`StandardSchemaV1`** from **`@standard-schema/spec`** so callers can use **Zod**, **Valibot**, **ArkType**, etc., without socka-specific adapters.
11
11
 
12
12
  ## Rules
13
13
 
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/validate.ts","../src/core/socka-error.ts","../src/core/envelope.ts","../src/core/wire-codec.ts","../src/core/socka-report-error.ts"],"names":[],"mappings":"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","file":"chunk-2FNWVCP3.js","sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Validates {@link value} with a Standard Schema v1 schema and returns the output,\n * or throws an {@link Error} whose message aggregates issue messages.\n */\nexport async function parseStandardSchema<T>(\n\tschema: StandardSchemaV1<unknown, T>,\n\tvalue: unknown,\n): Promise<T> {\n\tconst result = await schema[\"~standard\"].validate(value);\n\tif (result.issues) {\n\t\tconst messages = result.issues.map((issue) => issue.message).join(\"; \");\n\t\tthrow new Error(messages || \"Validation failed\");\n\t}\n\treturn result.value;\n}\n","/**\n * Thrown on the server and surfaced on the client when the wire uses a shared\n * `{ type: \"error\", id, error }` envelope for correlated RPC failures.\n */\nexport class SockaError extends Error {\n\treadonly requestId?: string;\n\treadonly code?: string;\n\treadonly data?: unknown;\n\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: {\n\t\t\trequestId?: string;\n\t\t\tcode?: string;\n\t\t\tdata?: unknown;\n\t\t\tcause?: unknown;\n\t\t},\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"SockaError\";\n\t\tthis.requestId = options?.requestId;\n\t\tthis.code = options?.code;\n\t\tthis.data = options?.data;\n\t\tif (options?.cause !== undefined) {\n\t\t\tObject.defineProperty(this, \"cause\", {\n\t\t\t\tvalue: options.cause,\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: false,\n\t\t\t\twritable: true,\n\t\t\t});\n\t\t}\n\t\tObject.setPrototypeOf(this, SockaError.prototype);\n\t}\n\n\t/** Builds a {@link SockaError} from a standard RPC error envelope. */\n\tstatic fromWire(msg: {\n\t\tid: string;\n\t\terror: string;\n\t\tcode?: string;\n\t\tdata?: unknown;\n\t}): SockaError {\n\t\treturn new SockaError(msg.error, {\n\t\t\trequestId: msg.id,\n\t\t\tcode: msg.code,\n\t\t\tdata: msg.data,\n\t\t});\n\t}\n}\n","/**\n * Versioned socka wire framing. After JSON parse or msgpack unpack, every frame\n * must satisfy {@link decodeSockaWire}; procedure bodies are validated with Standard Schema on each side.\n */\n\nexport const SOCKA_WIRE_VERSION = 1 as const;\n\nexport class SockaWireError extends Error {\n\toverride readonly name = \"SockaWireError\";\n}\n\nexport type SockaClientRequestFrame = {\n\treadonly socka: \"clientRequest\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly id: string;\n\treadonly rpc: string;\n\treadonly body: Record<string, unknown>;\n};\n\nexport type SockaServerResponseFrame = {\n\treadonly socka: \"serverResponse\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly id: string;\n\treadonly rpc: string;\n\treadonly body: unknown;\n};\n\nexport type SockaServerErrorFrame = {\n\treadonly socka: \"serverError\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly id: string;\n\treadonly error: string;\n\t/** Optional machine-readable code (e.g. `FORBIDDEN`). */\n\treadonly code?: string;\n\t/** Optional structured detail for clients; keep small and JSON-serializable. */\n\treadonly data?: unknown;\n};\n\nexport type SockaServerEventFrame = {\n\treadonly socka: \"serverEvent\";\n\treadonly v: typeof SOCKA_WIRE_VERSION;\n\treadonly event: string;\n\treadonly body: unknown;\n};\n\nexport type SockaWireFrame =\n\t| SockaClientRequestFrame\n\t| SockaServerResponseFrame\n\t| SockaServerErrorFrame\n\t| SockaServerEventFrame;\n\nexport type DecodedSockaWire =\n\t| { readonly kind: \"clientRequest\"; readonly frame: SockaClientRequestFrame }\n\t| {\n\t\t\treadonly kind: \"serverResponse\";\n\t\t\treadonly frame: SockaServerResponseFrame;\n\t }\n\t| { readonly kind: \"serverError\"; readonly frame: SockaServerErrorFrame }\n\t| { readonly kind: \"serverEvent\"; readonly frame: SockaServerEventFrame };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Decodes a parsed wire object (from JSON or msgpack). Throws {@link SockaWireError}\n * if the payload is not a valid socka v1 frame.\n */\nexport function decodeSockaWire(parsed: unknown): DecodedSockaWire {\n\tif (!isRecord(parsed)) {\n\t\tthrow new SockaWireError(\"socka: expected a JSON object\");\n\t}\n\tif (parsed.socka === undefined) {\n\t\tthrow new SockaWireError('socka: missing \"socka\" discriminator');\n\t}\n\tif (parsed.v !== SOCKA_WIRE_VERSION) {\n\t\tthrow new SockaWireError(\"socka: unsupported wire version\");\n\t}\n\tconst socka = parsed.socka;\n\tif (\n\t\tsocka === \"clientRequest\" &&\n\t\ttypeof parsed.id === \"string\" &&\n\t\ttypeof parsed.rpc === \"string\" &&\n\t\tisRecord(parsed.body)\n\t) {\n\t\treturn {\n\t\t\tkind: \"clientRequest\",\n\t\t\tframe: parsed as SockaClientRequestFrame,\n\t\t};\n\t}\n\tif (\n\t\tsocka === \"serverResponse\" &&\n\t\ttypeof parsed.id === \"string\" &&\n\t\ttypeof parsed.rpc === \"string\"\n\t) {\n\t\treturn {\n\t\t\tkind: \"serverResponse\",\n\t\t\tframe: parsed as SockaServerResponseFrame,\n\t\t};\n\t}\n\tif (\n\t\tsocka === \"serverError\" &&\n\t\ttypeof parsed.id === \"string\" &&\n\t\ttypeof parsed.error === \"string\"\n\t) {\n\t\tconst code =\n\t\t\t\"code\" in parsed && typeof parsed.code === \"string\"\n\t\t\t\t? parsed.code\n\t\t\t\t: undefined;\n\t\tconst data = \"data\" in parsed ? parsed.data : undefined;\n\t\tconst frame: SockaServerErrorFrame = {\n\t\t\tsocka: \"serverError\",\n\t\t\tv: SOCKA_WIRE_VERSION,\n\t\t\tid: parsed.id,\n\t\t\terror: parsed.error,\n\t\t\t...(code !== undefined ? { code } : {}),\n\t\t\t...(data !== undefined ? { data } : {}),\n\t\t};\n\t\treturn { kind: \"serverError\", frame };\n\t}\n\tif (socka === \"serverEvent\" && typeof parsed.event === \"string\") {\n\t\treturn {\n\t\t\tkind: \"serverEvent\",\n\t\t\tframe: parsed as SockaServerEventFrame,\n\t\t};\n\t}\n\tthrow new SockaWireError(\n\t\t`socka: unknown or invalid frame kind ${String(socka)}`,\n\t);\n}\n\n/** Builds a socka v1 client request frame. */\nexport function encodeClientRequest(\n\tid: string,\n\trpc: string,\n\tbody: Record<string, unknown>,\n): SockaClientRequestFrame {\n\treturn { socka: \"clientRequest\", v: SOCKA_WIRE_VERSION, id, rpc, body };\n}\n\n/** Builds a socka v1 server response frame. */\nexport function encodeServerResponse(\n\tid: string,\n\trpc: string,\n\tbody: unknown,\n): SockaServerResponseFrame {\n\treturn { socka: \"serverResponse\", v: SOCKA_WIRE_VERSION, id, rpc, body };\n}\n\n/** Builds a socka v1 server error frame. */\nexport function encodeServerError(\n\tid: string,\n\terror: string,\n\textra?: { readonly code?: string; readonly data?: unknown },\n): SockaServerErrorFrame {\n\treturn {\n\t\tsocka: \"serverError\",\n\t\tv: SOCKA_WIRE_VERSION,\n\t\tid,\n\t\terror,\n\t\t...(extra?.code !== undefined ? { code: extra.code } : {}),\n\t\t...(extra?.data !== undefined ? { data: extra.data } : {}),\n\t};\n}\n\n/** Builds a socka v1 server event frame. */\nexport function encodeServerEvent(\n\tevent: string,\n\tbody: unknown,\n): SockaServerEventFrame {\n\treturn { socka: \"serverEvent\", v: SOCKA_WIRE_VERSION, event, body };\n}\n","/**\n * JSON text frames vs msgpack binary frames for the same socka v1 object graph.\n * Matches {@link decodeSockaWire} after parse/unpack.\n */\n\nimport { pack, unpack } from \"msgpackr\";\nimport type { SockaWireFrame } from \"./envelope\";\n\n/** Wire encoding: UTF-8 JSON strings (default) or msgpack `ArrayBuffer` frames. */\nexport type SockaWireFormat = \"json\" | \"msgpack\";\n\n/**\n * Encodes a socka frame for the wire. JSON returns a string; msgpack returns bytes\n * suitable for `WebSocket.send`.\n */\nexport function encodeSockaWire(\n\tframe: SockaWireFrame,\n\tformat: SockaWireFormat,\n\tserializeJson: (value: unknown) => string = JSON.stringify,\n): string | Uint8Array {\n\tif (format === \"json\") {\n\t\treturn serializeJson(frame);\n\t}\n\treturn pack(frame) as Uint8Array;\n}\n\n/**\n * Decodes a wire payload to a plain object before {@link decodeSockaWire}.\n * Msgpack mode accepts `ArrayBuffer` or `Uint8Array` (e.g. from `msgpackr` / `WebSocket`).\n */\nexport function parseWirePayload(\n\tdata: string | ArrayBuffer | Uint8Array,\n\tformat: SockaWireFormat,\n\tdeserializeJson: (raw: string) => unknown = JSON.parse,\n): unknown {\n\tif (format === \"json\") {\n\t\tif (typeof data !== \"string\") {\n\t\t\tthrow new Error(\"socka: expected a JSON text frame\");\n\t\t}\n\t\treturn deserializeJson(data);\n\t}\n\tif (data instanceof Uint8Array) {\n\t\treturn unpack(data);\n\t}\n\tif (data instanceof ArrayBuffer) {\n\t\treturn unpack(new Uint8Array(data));\n\t}\n\tthrow new Error(\"socka: expected an ArrayBuffer or Uint8Array msgpack frame\");\n}\n","import { exhaustiveGuard } from \"@firtoz/maybe-error\";\n\n/**\n * Single discriminated union for optional `reportError` on session config and\n * `SockaSession` options: `kind` narrows context; `error` is what was thrown or rejected.\n */\nexport type SockaReportError =\n\t| { kind: \"clientEventListener\"; eventName: string; error: unknown }\n\t| { kind: \"clientEventValidation\"; eventName: string; error: unknown }\n\t| { kind: \"serverOnAttached\"; error: unknown }\n\t| {\n\t\t\tkind: \"serverInboundMessage\";\n\t\t\t/** `hono` uses the same log line as the Hono adapters; others use attach-style. */\n\t\t\tadapter: \"attach\" | \"hono\" | \"bun\";\n\t\t\terror: unknown;\n\t }\n\t| { kind: \"serverHandleClose\"; error: unknown }\n\t| {\n\t\t\tkind: \"serverShutdown\";\n\t\t\tadapter: \"attach\" | \"hono\";\n\t\t\terror: unknown;\n\t };\n\n/** Default `console.error` behavior; same messages as pre–`reportError` socka. */\nexport function defaultReportError(event: SockaReportError): void {\n\tswitch (event.kind) {\n\t\tcase \"clientEventListener\":\n\t\t\tconsole.error(\"socka: event listener error\", event.error);\n\t\t\treturn;\n\t\tcase \"clientEventValidation\":\n\t\t\tconsole.error(\"socka: event validation error\", event.error);\n\t\t\treturn;\n\t\tcase \"serverOnAttached\":\n\t\t\tconsole.error(\"socka: onAttached error:\", event.error);\n\t\t\treturn;\n\t\tcase \"serverInboundMessage\":\n\t\t\tif (event.adapter === \"hono\") {\n\t\t\t\tconsole.error(\"socka: onMessage error:\", event.error);\n\t\t\t} else {\n\t\t\t\tconsole.error(\"socka: message handler error:\", event.error);\n\t\t\t}\n\t\t\treturn;\n\t\tcase \"serverHandleClose\":\n\t\t\tconsole.error(\"socka: handleClose error:\", event.error);\n\t\t\treturn;\n\t\tcase \"serverShutdown\":\n\t\t\tif (event.adapter === \"hono\") {\n\t\t\t\tconsole.error(\"socka: onClose error:\", event.error);\n\t\t\t} else {\n\t\t\t\tconsole.error(\"socka: shutdown error:\", event.error);\n\t\t\t}\n\t\t\treturn;\n\t\tdefault:\n\t\t\texhaustiveGuard(event);\n\t}\n}\n\n/** Invokes the optional `reportError` callback when provided, otherwise `defaultReportError`. */\nexport function reportSockaError(\n\treportError: ((event: SockaReportError) => void) | undefined,\n\tevent: SockaReportError,\n): void {\n\t(reportError ?? defaultReportError)(event);\n}\n"]}