@firtoz/socka 2.1.0 → 3.0.1
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.
- package/README.md +10 -8
- package/dist/{SockaWebSocketSession-Cza7Fti-.d.ts → SockaWebSocketSession-B1w7RAid.d.ts} +61 -41
- package/dist/bun/index.d.ts +8 -8
- package/dist/bun/index.js +2 -2
- package/dist/bun/index.js.map +1 -1
- package/dist/{chunk-2FNWVCP3.js → chunk-IFIGKR3W.js} +20 -2
- package/dist/chunk-IFIGKR3W.js.map +1 -0
- package/dist/{chunk-JVLUA3Q5.js → chunk-LVVCHLNW.js} +21 -14
- package/dist/chunk-LVVCHLNW.js.map +1 -0
- package/dist/{chunk-H3S3435J.js → chunk-P3JEEOJL.js} +70 -10
- package/dist/chunk-P3JEEOJL.js.map +1 -0
- package/dist/{chunk-KQO5AVKA.js → chunk-QGURL3DJ.js} +2 -2
- package/dist/chunk-QGURL3DJ.js.map +1 -0
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.js +2 -2
- package/dist/core/index.d.ts +2 -25
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/do/index.d.ts +2 -2
- package/dist/do/index.js +3 -2
- package/dist/do/index.js.map +1 -1
- package/dist/hono/cloudflare-workers.d.ts +4 -4
- package/dist/hono/cloudflare-workers.js +3 -3
- package/dist/hono/cloudflare-workers.js.map +1 -1
- package/dist/hono/index.d.ts +5 -5
- package/dist/hono/index.js +4 -4
- package/dist/hono/index.js.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +2 -2
- package/dist/server/index.d.ts +5 -5
- package/dist/server/index.js +3 -3
- package/dist/server/index.js.map +1 -1
- package/dist/{socka-report-error-ixTynx4w.d.ts → socka-report-error-CXwpAUgl.d.ts} +72 -7
- package/docs/README.md +1 -1
- package/docs/auth.md +1 -1
- package/docs/client.md +4 -0
- package/docs/comparison.md +1 -1
- package/docs/durable-objects.md +1 -1
- package/docs/internals.md +3 -3
- package/docs/recipes.md +0 -1
- package/docs/reference.md +9 -5
- package/docs/server.md +9 -6
- package/docs/wire-format.md +4 -0
- package/package.json +8 -8
- package/roadmap.md +2 -2
- package/skills/socka/core-rpc/SKILL.md +2 -2
- package/skills/socka/do-session/SKILL.md +2 -2
- package/skills/socka/standard-schema/SKILL.md +1 -1
- package/dist/chunk-2FNWVCP3.js.map +0 -1
- package/dist/chunk-H3S3435J.js.map +0 -1
- package/dist/chunk-JVLUA3Q5.js.map +0 -1
- package/dist/chunk-KQO5AVKA.js.map +0 -1
package/dist/hono/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"sources":["../../src/hono/node-ws.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { Context } from \"hono\";\nimport type { WSEvents } from \"hono/ws\";\nimport type { WebSocket as NodeWebSocket } from \"ws\";\nimport type { SockaContract, SockaContractConfig } from \"../core/contract\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport type { SockaWireFormat } from \"../core/wire-codec\";\nimport { dispatchSockaInboundMessage } from \"../server/dispatchSockaInboundMessage\";\nimport {\n\tSockaWebSocketSession,\n\trunSockaSessionOnAttached,\n\ttype SockaWebSocketInit,\n\ttype SockaWebSocketSessionConfigUnion,\n} from \"../server/SockaWebSocketSession\";\nimport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport { sockaHonoStrictInitFromContext } from \"./strict-init-context\";\n\nexport type SockaHonoNodeWsOptions<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n> = {\n\t/** Shared map; default is a new `Map`. */\n\tsessions?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t/**\n\t * Per-upgrade init for `createData`. When omitted, defaults to\n\t * {@link sockaHonoStrictInitFromContext} so `Request` is always available.\n\t */\n\tsockaInit?: (c: Context) => SockaWebSocketInit | undefined;\n\t/**\n\t * Resolve the session map and config from this upgrade’s Hono context (e.g. multi-room\n\t * from `c.req.param(\"roomId\")`). When set, overrides the outer `config` / static `sessions`\n\t * for each connection.\n\t */\n\tresolveScope?: (c: Context) => {\n\t\tsessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>;\n\t};\n};\n\n/**\n * Returns the callback passed to `upgradeWebSocket` from\n * {@link https://github.com/honojs/middleware/tree/main/packages/node-ws @hono/node-ws}\n * `createNodeWebSocket({ app }).upgradeWebSocket`.\n */\nexport function sockaHonoNodeWs<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\toptions?: SockaHonoNodeWsOptions<TContract, TData>,\n): (c: Context) => WSEvents<NodeWebSocket> {\n\tconst staticSessions =\n\t\toptions?.sessions ??\n\t\tnew Map<WebSocket, SockaWebSocketSession<TContract, TData>>();\n\tconst staticConfig = config;\n\tconst resolveScope = options?.resolveScope;\n\n\treturn (c: Context) => ({\n\t\tonOpen(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tconst init: SockaWebSocketInit | undefined =\n\t\t\t\toptions?.sockaInit?.(c) ?? sockaHonoStrictInitFromContext(c);\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tconst session = new SockaWebSocketSession(domWs, sessions, cfg, init);\n\t\t\tsessions.set(domWs, session);\n\t\t\trunSockaSessionOnAttached(cfg, session);\n\t\t},\n\t\tonMessage(evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t? resolveScope(c)\n\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\tconst session = sessions.get(domWs);\n\t\t\tif (!session) return;\n\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\tTContract,\n\t\t\t\tTData\n\t\t\t>;\n\t\t\tconst wireFormat: SockaWireFormat = cfg.wireFormat ?? \"json\";\n\t\t\tvoid dispatchSockaInboundMessage(session, wireFormat, evt.data).catch(\n\t\t\t\t(error: unknown) => {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t\t);\n\t\t},\n\t\tonClose(_evt, wsCtx) {\n\t\t\tconst raw = wsCtx.raw;\n\t\t\tif (!raw) return;\n\t\t\tconst domWs = raw as unknown as WebSocket;\n\t\t\tvoid (async (): Promise<void> => {\n\t\t\t\tconst { sessions, config: scopeConfig } = resolveScope\n\t\t\t\t\t? resolveScope(c)\n\t\t\t\t\t: { sessions: staticSessions, config: staticConfig };\n\t\t\t\tconst cfg = scopeConfig as SockaWebSocketSessionConfigUnion<\n\t\t\t\t\tTContract,\n\t\t\t\t\tTData\n\t\t\t\t>;\n\t\t\t\tconst session = sessions.get(domWs);\n\t\t\t\ttry {\n\t\t\t\t\tif (session) {\n\t\t\t\t\t\tawait session.invokeHandleClose();\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\treportSockaError(cfg.reportError, {\n\t\t\t\t\t\tkind: \"serverHandleClose\",\n\t\t\t\t\t\terror,\n\t\t\t\t\t});\n\t\t\t\t} finally {\n\t\t\t\t\tsessions.delete(domWs);\n\t\t\t\t}\n\t\t\t})().catch((error: unknown) => {\n\t\t\t\treportSockaError(staticConfig.reportError, {\n\t\t\t\t\tkind: \"serverShutdown\",\n\t\t\t\t\tadapter: \"hono\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t});\n\t\t},\n\t});\n}\n"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DependencyList, RefObject, ReactNode, ReactElement } from 'react';
|
|
2
|
-
import { S as SockaContract, a as SockaContractConfig,
|
|
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
|
|
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { SockaSession } from '../chunk-
|
|
1
|
+
import { SockaSession } from '../chunk-P3JEEOJL.js';
|
|
2
2
|
import '../chunk-YMT4HAH7.js';
|
|
3
|
-
import '../chunk-
|
|
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
|
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as SockaWebSocketSession, b as
|
|
2
|
-
export {
|
|
3
|
-
import { S as SockaContract, a as SockaContractConfig,
|
|
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:
|
|
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 };
|
package/dist/server/index.js
CHANGED
|
@@ -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-
|
|
4
|
-
export { SockaWebSocketSession, broadcastSockaEventToPeers, runSockaSessionOnAttached } from '../chunk-
|
|
5
|
-
import { reportSockaError } from '../chunk-
|
|
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) {
|
package/dist/server/index.js.map
CHANGED
|
@@ -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
|
|
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 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:
|
|
15
|
-
*
|
|
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
|
|
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<
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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`**
|
|
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
|
package/docs/comparison.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Compared to
|
|
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
|
|
package/docs/durable-objects.md
CHANGED
|
@@ -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
|
|
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`,
|
|
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`**
|
|
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"`**.
|
|
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:
|
|
60
|
-
| **`
|
|
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. |
|
|
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
|
-
**`
|
|
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) | **`
|
|
61
|
-
| **`
|
|
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`**
|
|
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) =>
|
|
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 }),
|
package/docs/wire-format.md
CHANGED
|
@@ -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": "
|
|
3
|
+
"version": "3.0.1",
|
|
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",
|
|
@@ -98,11 +98,11 @@
|
|
|
98
98
|
"dependencies": {
|
|
99
99
|
"@firtoz/maybe-error": "^1.6.1",
|
|
100
100
|
"@standard-schema/spec": "^1.1.0",
|
|
101
|
-
"msgpackr": "^1.11.
|
|
101
|
+
"msgpackr": "^1.11.10"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
|
-
"@cloudflare/workers-types": "^4.
|
|
105
|
-
"@firtoz/websocket-do": "^13.0.
|
|
104
|
+
"@cloudflare/workers-types": "^4.20260422.1",
|
|
105
|
+
"@firtoz/websocket-do": "^13.0.1",
|
|
106
106
|
"@hono/node-server": "^1.19.2",
|
|
107
107
|
"@hono/node-ws": "^1.3.0",
|
|
108
108
|
"hono": "^4.12.9",
|
|
@@ -133,19 +133,19 @@
|
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
135
|
"devDependencies": {
|
|
136
|
-
"@cloudflare/workers-types": "^4.
|
|
136
|
+
"@cloudflare/workers-types": "^4.20260422.1",
|
|
137
137
|
"@happy-dom/global-registrator": "^20.9.0",
|
|
138
|
-
"@hono/node-server": "^
|
|
138
|
+
"@hono/node-server": "^2.0.0",
|
|
139
139
|
"@hono/node-ws": "^1.3.0",
|
|
140
140
|
"@tanstack/intent": "^0.0.29",
|
|
141
141
|
"@testing-library/react": "^16.3.2",
|
|
142
142
|
"@types/react": "^19.2.14",
|
|
143
143
|
"@types/ws": "^8.18.1",
|
|
144
|
-
"bun-types": "^1.3.
|
|
144
|
+
"bun-types": "^1.3.13",
|
|
145
145
|
"happy-dom": "^20.9.0",
|
|
146
146
|
"react-dom": "19.2.5",
|
|
147
147
|
"tsup": "^8.5.1",
|
|
148
|
-
"typescript": "^6.0.
|
|
148
|
+
"typescript": "^6.0.3",
|
|
149
149
|
"valibot": "^1.3.1",
|
|
150
150
|
"zod": "^4.3.6"
|
|
151
151
|
}
|
package/roadmap.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Roadmap
|
|
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
|
|
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`
|
|
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
|
|
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>`**).
|
|
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
|
|
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`
|
|
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
|
|