@firtoz/socka 2.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 (55) hide show
  1. package/README.md +120 -0
  2. package/assets/banner.png +0 -0
  3. package/dist/SockaWebSocketSession-Bru8yFcK.d.ts +107 -0
  4. package/dist/bun/index.d.ts +38 -0
  5. package/dist/bun/index.js +121 -0
  6. package/dist/bun/index.js.map +1 -0
  7. package/dist/chunk-45D4T232.js +236 -0
  8. package/dist/chunk-45D4T232.js.map +1 -0
  9. package/dist/chunk-5WQTYLIC.js +46 -0
  10. package/dist/chunk-5WQTYLIC.js.map +1 -0
  11. package/dist/chunk-AM7PB26G.js +421 -0
  12. package/dist/chunk-AM7PB26G.js.map +1 -0
  13. package/dist/chunk-MZCQHJXY.js +158 -0
  14. package/dist/chunk-MZCQHJXY.js.map +1 -0
  15. package/dist/chunk-YMT4HAH7.js +20 -0
  16. package/dist/chunk-YMT4HAH7.js.map +1 -0
  17. package/dist/client/index.d.ts +119 -0
  18. package/dist/client/index.js +5 -0
  19. package/dist/client/index.js.map +1 -0
  20. package/dist/core/index.d.ts +29 -0
  21. package/dist/core/index.js +14 -0
  22. package/dist/core/index.js.map +1 -0
  23. package/dist/do/index.d.ts +80 -0
  24. package/dist/do/index.js +110 -0
  25. package/dist/do/index.js.map +1 -0
  26. package/dist/hono/cloudflare-workers.d.ts +21 -0
  27. package/dist/hono/cloudflare-workers.js +68 -0
  28. package/dist/hono/cloudflare-workers.js.map +1 -0
  29. package/dist/hono/index.d.ts +30 -0
  30. package/dist/hono/index.js +74 -0
  31. package/dist/hono/index.js.map +1 -0
  32. package/dist/react/index.d.ts +72 -0
  33. package/dist/react/index.js +126 -0
  34. package/dist/react/index.js.map +1 -0
  35. package/dist/server/index.d.ts +27 -0
  36. package/dist/server/index.js +63 -0
  37. package/dist/server/index.js.map +1 -0
  38. package/dist/socka-report-error-DzFI2Tr7.d.ts +206 -0
  39. package/docs/README.md +18 -0
  40. package/docs/client.md +85 -0
  41. package/docs/comparison.md +36 -0
  42. package/docs/durable-objects.md +74 -0
  43. package/docs/events.md +48 -0
  44. package/docs/getting-started.md +138 -0
  45. package/docs/lifecycle.md +31 -0
  46. package/docs/multi-room.md +31 -0
  47. package/docs/peers.md +85 -0
  48. package/docs/reference.md +123 -0
  49. package/docs/server.md +124 -0
  50. package/examples/minimal-socka.ts +31 -0
  51. package/package.json +148 -0
  52. package/roadmap.md +8 -0
  53. package/skills/socka/core-rpc/SKILL.md +36 -0
  54. package/skills/socka/do-session/SKILL.md +33 -0
  55. package/skills/socka/standard-schema/SKILL.md +26 -0
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # @firtoz/socka
2
+
3
+ [![npm version](https://img.shields.io/npm/v/%40firtoz%2Fsocka.svg)](https://www.npmjs.com/package/@firtoz/socka)
4
+ [![npm downloads](https://img.shields.io/npm/dm/%40firtoz%2Fsocka.svg)](https://www.npmjs.com/package/@firtoz/socka)
5
+ [![license](https://img.shields.io/npm/l/%40firtoz%2Fsocka.svg)](https://github.com/firtoz/fullstack-toolkit/blob/main/LICENSE)
6
+
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
8
+ [![WebSocket](https://img.shields.io/badge/WebSocket-RPC-6366f1)](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
9
+ [![Standard Schema](https://img.shields.io/badge/Standard_Schema-v1-1e293b)](https://standardschema.dev)
10
+
11
+ ![Socka — WebSocket RPC, Standard Schema](./assets/banner.png)
12
+
13
+ **Typed WebSocket RPC for TypeScript.** Define one contract, get **`session.send.*`** in the client and **`handlers`** on the server—validated, correlated, done.
14
+
15
+ **npm:** [`@firtoz/socka`](https://www.npmjs.com/package/@firtoz/socka). *Socka* is the project name in prose; **install and `import` paths always use `@firtoz/socka` or `@firtoz/socka/...`**. The published artifact is **compiled ESM + `.d.ts` in `dist/`** (see `package.json` `exports`).
16
+
17
+ ## 30-second example (Bun)
18
+
19
+ **`contract.ts`** (shared):
20
+
21
+ ```ts
22
+ import { defineSocka } from "@firtoz/socka/core";
23
+ import * as z from "zod";
24
+
25
+ export const myContract = defineSocka({
26
+ calls: {
27
+ echo: {
28
+ input: z.object({ text: z.string() }),
29
+ output: z.object({ text: z.string() }),
30
+ },
31
+ },
32
+ });
33
+ ```
34
+
35
+ **`server.ts`**:
36
+
37
+ ```ts
38
+ import { createSockaBunWebSocketHandlers } from "@firtoz/socka/bun";
39
+ import { myContract } from "./contract";
40
+
41
+ const { websocket } = createSockaBunWebSocketHandlers({
42
+ contract: myContract,
43
+ handlers: {
44
+ echo: async (input) => ({ text: input.text }),
45
+ },
46
+ handleClose: async () => {},
47
+ });
48
+
49
+ Bun.serve({
50
+ port: 3450,
51
+ fetch(req, server) {
52
+ if (new URL(req.url).pathname === "/ws") {
53
+ if (server.upgrade(req)) return undefined;
54
+ return new Response("WebSocket upgrade failed", { status: 400 });
55
+ }
56
+ return new Response("OK");
57
+ },
58
+ websocket,
59
+ });
60
+ ```
61
+
62
+ **`client.ts`** (browser or Bun):
63
+
64
+ ```ts
65
+ import { SockaSession } from "@firtoz/socka/client";
66
+ import { myContract } from "./contract";
67
+
68
+ const session = new SockaSession({
69
+ contract: myContract,
70
+ url: "ws://localhost:3450/ws",
71
+ });
72
+ const { text } = await session.send.echo({ text: "hello" });
73
+ console.log(text);
74
+ ```
75
+
76
+ Run **`bun run server.ts`**, then point the client at **`ws://localhost:3450/ws`**.
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ npm install @firtoz/socka
82
+ ```
83
+
84
+ Also: `pnpm add @firtoz/socka` · `bun add @firtoz/socka`
85
+
86
+ Optional peers depend on which subpath you import—see **[Peers](./docs/peers.md)**.
87
+
88
+ ## Other runtimes
89
+
90
+ | Runtime | Subpath | Guide |
91
+ |--------|---------|--------|
92
+ | **Node** + [`ws`](https://github.com/websockets/ws), or any standard **`WebSocket`** | `@firtoz/socka/server` | **[Server](./docs/server.md)** — `attachSockaWebSocket` |
93
+ | **Bun** `Bun.serve` / `ServerWebSocket` | `@firtoz/socka/bun` | **[Server](./docs/server.md)** |
94
+ | **Hono** on Node (`@hono/node-ws`) | `@firtoz/socka/hono` | **[Server](./docs/server.md)** |
95
+ | **Hono** on Cloudflare Workers | `@firtoz/socka/hono/cloudflare` | **[Server](./docs/server.md)** |
96
+ | **Cloudflare Durable Objects** | `@firtoz/socka/do` | **[Durable Objects](./docs/durable-objects.md)** |
97
+
98
+ ## Why not socket.io, tRPC, or DIY?
99
+
100
+ - **Schema-first RPC + push** — one contract; no parallel “event” protocol for server pushes.
101
+ - **Correlated envelopes** — request/response IDs and validation hooks are built in.
102
+ - **Same contract** across Bun, Hono, Node `ws`, and Durable Objects (see **[Comparison](./docs/comparison.md)** for socket.io / tRPC / hand-rolled).
103
+
104
+ ## Documentation
105
+
106
+ Hub: **[`docs/README.md`](./docs/README.md)** (getting started, peers, lifecycle, multi-room, reference).
107
+
108
+ **Roadmap:** [post–v1 and deferred work](./roadmap.md). Agent skills: [`skills/`](./skills/).
109
+
110
+ ## Full-stack examples
111
+
112
+ Self-contained **tic-tac-toe** apps in the monorepo [`examples/`](../../examples/) (same game, different servers):
113
+
114
+ | Stack | Folder | Port |
115
+ |--------|--------|------|
116
+ | **Bun** (`@firtoz/socka/bun`) | [`tic-tac-toe-bun`](../../examples/tic-tac-toe-bun) | **3461** |
117
+ | **Hono + Node** (`@firtoz/socka/hono`) | [`tic-tac-toe-hono`](../../examples/tic-tac-toe-hono) | **3462** |
118
+ | **Cloudflare DO** (`@firtoz/socka/do`) | [`tic-tac-toe-do`](../../examples/tic-tac-toe-do) | **3463** |
119
+
120
+ Each app: **`bun run dev`** (or **`wrangler dev`** for the DO example).
Binary file
@@ -0,0 +1,107 @@
1
+ import { S as SockaContract, a as SockaContractConfig, b as SockaWireFormat, e as InferSockaHandlers, y as SockaReportError, g as InferSockaPushPayload } from './socka-report-error-DzFI2Tr7.js';
2
+
3
+ /** Session data with no fields — `createData` may be omitted (defaults to `{}`). */
4
+ type EmptySockaSessionData$1 = Record<string, never>;
5
+ /** Optional upgrade context for {@link SockaWebSocketSession}. */
6
+ type SockaWebSocketInit = {
7
+ /** Original HTTP request for the WebSocket upgrade, when available. */
8
+ request?: Request;
9
+ };
10
+ type SockaWebSocketCreateData<TData> = [TData] extends [EmptySockaSessionData$1] ? {
11
+ createData?: (init: SockaWebSocketInit) => TData;
12
+ } : {
13
+ createData: (init: SockaWebSocketInit) => TData;
14
+ };
15
+ type SockaSessionForHandlers<TContract extends SockaContract<SockaContractConfig>, TData> = SockaWebSocketSession<TContract, TData>;
16
+ /**
17
+ * Configuration for {@link SockaWebSocketSession}. Handlers receive the session
18
+ * instance as the second argument (or the only argument when the procedure has no input).
19
+ */
20
+ type SockaWebSocketSessionConfig<TContract extends SockaContract<SockaContractConfig>, TData = EmptySockaSessionData$1> = {
21
+ contract: TContract;
22
+ /** Default `"json"`. Use `"msgpack"` for binary frames (must match client). */
23
+ wireFormat?: SockaWireFormat;
24
+ handlers: InferSockaHandlers<TContract, SockaSessionForHandlers<TContract, TData>>;
25
+ /** Called when this WebSocket closes, before the socket is removed from `sessions`. */
26
+ handleClose: (session: SockaSessionForHandlers<TContract, TData>) => Promise<void>;
27
+ onHandlerError?: (error: unknown, rpcName: string, input: unknown, session: SockaSessionForHandlers<TContract, TData>) => void;
28
+ onValidationError?: (error: unknown, originalMessage: unknown) => Promise<void>;
29
+ /**
30
+ * Optional sink for non-RPC failures (onAttached, adapter I/O). Defaults to
31
+ * `console.error` with `socka:` prefixes; see `SockaReportError` in `@firtoz/socka/core`.
32
+ */
33
+ reportError?: (event: SockaReportError) => void;
34
+ serializeJson?: (value: unknown) => string;
35
+ deserializeJson?: (raw: string) => unknown;
36
+ /**
37
+ * Called once after this session is registered in the shared `sessions` map
38
+ * (safe to broadcast to peers). Sync or async; async rejections are logged.
39
+ */
40
+ onAttached?: (session: SockaSessionForHandlers<TContract, TData>) => void | Promise<void>;
41
+ } & SockaWebSocketCreateData<TData>;
42
+
43
+ /** Session data with no fields — `createData` may be omitted (defaults to `{}`). */
44
+ type EmptySockaSessionData = Record<string, never>;
45
+
46
+ /** Session that can send a wire-level server event (already validated). */
47
+ type SockaEmitCapable = {
48
+ emitWireEvent(event: string, body: unknown): void;
49
+ };
50
+ /**
51
+ * Contract-typed session surface for handlers that push to clients.
52
+ */
53
+ interface SockaPushSession<TContract extends SockaContract<SockaContractConfig>> {
54
+ emitPush<K extends keyof TContract["pushes"] & string>(name: K, body: InferSockaPushPayload<TContract, K>): Promise<void>;
55
+ broadcastPush<K extends keyof TContract["pushes"] & string>(name: K, body: InferSockaPushPayload<TContract, K>, excludeSelf?: boolean): Promise<void>;
56
+ }
57
+ /**
58
+ * Broadcast a socka server event to every session in the map (optionally
59
+ * excluding the caller). Payload must already be contract-validated.
60
+ *
61
+ * Exclusion uses the **WebSocket** identity (`self.websocket`), not the session
62
+ * object reference, so the same `sessions` map can hold `SockaDoSession` while
63
+ * `broadcastPush` runs on `this.socka` (inner {@link SockaWebSocketSession}).
64
+ */
65
+ declare function broadcastSockaEventToPeers(sessions: Map<WebSocket, SockaEmitCapable>, self: SockaEmitCapable & {
66
+ readonly websocket: WebSocket;
67
+ }, event: string, body: unknown, excludeSelf?: boolean): void;
68
+ /**
69
+ * Runtime-agnostic socka server session: standard {@link WebSocket} wire
70
+ * dispatch without Cloudflare Durable Object APIs.
71
+ */
72
+ declare class SockaWebSocketSession<TContract extends SockaContract<SockaContractConfig>, TData = EmptySockaSessionData> implements SockaPushSession<TContract> {
73
+ readonly websocket: WebSocket;
74
+ protected readonly sessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
75
+ private readonly config;
76
+ private readonly wireFormat;
77
+ private _data;
78
+ constructor(websocket: WebSocket, sessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>, config: SockaWebSocketSessionConfig<TContract, TData>, init?: SockaWebSocketInit);
79
+ get data(): TData;
80
+ /**
81
+ * Invokes the user {@link typeof SockaWebSocketSessionConfig.handleClose} callback.
82
+ * Server adapters should call this when the WebSocket closes, **before** deleting
83
+ * this session from the shared `sessions` map.
84
+ */
85
+ invokeHandleClose(): Promise<void>;
86
+ handleRawMessage(rawMessage: string): Promise<void>;
87
+ handleBinaryMessage(buffer: ArrayBuffer): Promise<void>;
88
+ private dispatchAfterParsed;
89
+ private dispatchClientRequest;
90
+ private encodeOutgoing;
91
+ private sendWireFrame;
92
+ /**
93
+ * Send a server event frame (wire). Prefer {@link emitPush} so
94
+ * payloads are validated against the contract.
95
+ */
96
+ emitWireEvent(event: string, body: unknown): void;
97
+ emitPush<K extends keyof TContract["pushes"] & string>(name: K, body: InferSockaPushPayload<TContract, K>): Promise<void>;
98
+ broadcastPush<K extends keyof TContract["pushes"] & string>(name: K, body: InferSockaPushPayload<TContract, K>, excludeSelf?: boolean): Promise<void>;
99
+ private reportValidationError;
100
+ }
101
+ /**
102
+ * Invoke {@link SockaWebSocketSessionConfig.onAttached} after the session is
103
+ * registered in the shared map.
104
+ */
105
+ declare function runSockaSessionOnAttached<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfig<TContract, TData>, session: SockaWebSocketSession<TContract, TData>): void;
106
+
107
+ export { SockaWebSocketSession as S, type SockaWebSocketSessionConfig as a, type SockaWebSocketInit as b, broadcastSockaEventToPeers as c, type SockaEmitCapable as d, type SockaPushSession as e, runSockaSessionOnAttached as r };
@@ -0,0 +1,38 @@
1
+ import { ServerWebSocket } from 'bun';
2
+ import { S as SockaContract, a as SockaContractConfig, b as SockaWireFormat } from '../socka-report-error-DzFI2Tr7.js';
3
+ import { S as SockaWebSocketSession, a as SockaWebSocketSessionConfig } from '../SockaWebSocketSession-Bru8yFcK.js';
4
+ import '@standard-schema/spec';
5
+
6
+ type SockaBunResolveScope<TContract extends SockaContract<SockaContractConfig>, TData, TWsData = undefined> = (ws: ServerWebSocket<TWsData>) => {
7
+ sessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
8
+ config: SockaWebSocketSessionConfig<TContract, TData>;
9
+ };
10
+ type SockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConfig>, TData, TWsData = undefined> = {
11
+ sessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
12
+ /** Pass into `Bun.serve({ ..., websocket })`. */
13
+ websocket: {
14
+ open: (ws: ServerWebSocket<TWsData>) => void;
15
+ message: (ws: ServerWebSocket<TWsData>, message: unknown) => void | Promise<void>;
16
+ close: (ws: ServerWebSocket<TWsData>) => void | Promise<void>;
17
+ };
18
+ wireFormat: SockaWireFormat;
19
+ };
20
+ /**
21
+ * WebSocket handlers for `Bun.serve` when using `ServerWebSocket` (no
22
+ * `addEventListener`). Inbound frames are dispatched with the same logic as
23
+ * `attachSockaWebSocket`.
24
+ *
25
+ * **Single-room:** pass a {@link SockaWebSocketSessionConfig} and optional shared `sessionMap`.
26
+ *
27
+ * **Multi-room:** pass `{ resolveScope }` where `resolveScope(ws)` returns the
28
+ * `sessionMap` and `config` for that socket’s scope (e.g. from `ws.data.roomId`).
29
+ * The returned `sessionMap` is an empty placeholder; real maps come from `resolveScope`.
30
+ */
31
+ declare function createSockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfig<TContract, TData>, options?: {
32
+ sessionMap?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
33
+ }): SockaBunWebSocketHandlers<TContract, TData, undefined>;
34
+ declare function createSockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConfig>, TData, TWsData>(options: {
35
+ resolveScope: SockaBunResolveScope<TContract, TData, TWsData>;
36
+ }): SockaBunWebSocketHandlers<TContract, TData, TWsData>;
37
+
38
+ export { type SockaBunResolveScope, type SockaBunWebSocketHandlers, createSockaBunWebSocketHandlers };
@@ -0,0 +1,121 @@
1
+ import { dispatchSockaInboundMessage } from '../chunk-5WQTYLIC.js';
2
+ import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-45D4T232.js';
3
+ import { reportSockaError } from '../chunk-MZCQHJXY.js';
4
+
5
+ // src/bun/index.ts
6
+ function bunHandlersFromResolveScope(resolveScope) {
7
+ const websocket = {
8
+ open(ws) {
9
+ const { sessionMap, config } = resolveScope(ws);
10
+ const domWs = ws;
11
+ const session = new SockaWebSocketSession(domWs, sessionMap, config);
12
+ sessionMap.set(domWs, session);
13
+ runSockaSessionOnAttached(config, session);
14
+ },
15
+ async message(ws, message) {
16
+ const { sessionMap, config } = resolveScope(ws);
17
+ const domWs = ws;
18
+ const session = sessionMap.get(domWs);
19
+ if (!session) return;
20
+ const wireFormat = config.wireFormat ?? "json";
21
+ try {
22
+ await dispatchSockaInboundMessage(
23
+ session,
24
+ wireFormat,
25
+ message
26
+ );
27
+ } catch (error) {
28
+ reportSockaError(config.reportError, {
29
+ kind: "serverInboundMessage",
30
+ adapter: "bun",
31
+ error
32
+ });
33
+ }
34
+ },
35
+ async close(ws) {
36
+ const { sessionMap, config } = resolveScope(ws);
37
+ const domWs = ws;
38
+ const session = sessionMap.get(domWs);
39
+ try {
40
+ if (session) {
41
+ await session.invokeHandleClose();
42
+ }
43
+ } catch (error) {
44
+ reportSockaError(config.reportError, {
45
+ kind: "serverHandleClose",
46
+ error
47
+ });
48
+ } finally {
49
+ sessionMap.delete(domWs);
50
+ }
51
+ }
52
+ };
53
+ return {
54
+ sessionMap: /* @__PURE__ */ new Map(),
55
+ websocket,
56
+ wireFormat: "json"
57
+ };
58
+ }
59
+ function bunHandlersFromConfig(config, maybeOptions) {
60
+ const sessionMap = maybeOptions?.sessionMap ?? /* @__PURE__ */ new Map();
61
+ const wireFormat = config.wireFormat ?? "json";
62
+ const websocket = {
63
+ open(ws) {
64
+ const domWs = ws;
65
+ const session = new SockaWebSocketSession(domWs, sessionMap, config);
66
+ sessionMap.set(domWs, session);
67
+ runSockaSessionOnAttached(config, session);
68
+ },
69
+ async message(ws, message) {
70
+ const domWs = ws;
71
+ const session = sessionMap.get(domWs);
72
+ if (!session) return;
73
+ try {
74
+ await dispatchSockaInboundMessage(
75
+ session,
76
+ wireFormat,
77
+ message
78
+ );
79
+ } catch (error) {
80
+ reportSockaError(config.reportError, {
81
+ kind: "serverInboundMessage",
82
+ adapter: "bun",
83
+ error
84
+ });
85
+ }
86
+ },
87
+ async close(ws) {
88
+ const domWs = ws;
89
+ const session = sessionMap.get(domWs);
90
+ try {
91
+ if (session) {
92
+ await session.invokeHandleClose();
93
+ }
94
+ } catch (error) {
95
+ reportSockaError(config.reportError, {
96
+ kind: "serverHandleClose",
97
+ error
98
+ });
99
+ } finally {
100
+ sessionMap.delete(domWs);
101
+ }
102
+ }
103
+ };
104
+ return { sessionMap, websocket, wireFormat };
105
+ }
106
+ function createSockaBunWebSocketHandlers(configOrOptions, maybeOptions) {
107
+ const isResolveScope = typeof configOrOptions === "object" && configOrOptions !== null && "resolveScope" in configOrOptions && typeof configOrOptions.resolveScope === "function";
108
+ if (isResolveScope) {
109
+ return bunHandlersFromResolveScope(
110
+ configOrOptions.resolveScope
111
+ );
112
+ }
113
+ return bunHandlersFromConfig(
114
+ configOrOptions,
115
+ maybeOptions
116
+ );
117
+ }
118
+
119
+ export { createSockaBunWebSocketHandlers };
120
+ //# sourceMappingURL=index.js.map
121
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bun/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ServerWebSocket } from \"bun\";\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 SockaWebSocketSessionConfig,\n} from \"../server/SockaWebSocketSession\";\n\nexport type SockaBunResolveScope<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTWsData = undefined,\n> = (ws: ServerWebSocket<TWsData>) => {\n\tsessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>;\n};\n\nexport type SockaBunWebSocketHandlers<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTWsData = undefined,\n> = {\n\tsessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t/** Pass into `Bun.serve({ ..., websocket })`. */\n\twebsocket: {\n\t\topen: (ws: ServerWebSocket<TWsData>) => void;\n\t\tmessage: (\n\t\t\tws: ServerWebSocket<TWsData>,\n\t\t\tmessage: unknown,\n\t\t) => void | Promise<void>;\n\t\tclose: (ws: ServerWebSocket<TWsData>) => void | Promise<void>;\n\t};\n\twireFormat: SockaWireFormat;\n};\n\nfunction bunHandlersFromResolveScope<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTWsData,\n>(\n\tresolveScope: SockaBunResolveScope<TContract, TData, TWsData>,\n): SockaBunWebSocketHandlers<TContract, TData, TWsData> {\n\tconst websocket: SockaBunWebSocketHandlers<\n\t\tTContract,\n\t\tTData,\n\t\tTWsData\n\t>[\"websocket\"] = {\n\t\topen(ws: ServerWebSocket<TWsData>) {\n\t\t\tconst { sessionMap, config } = resolveScope(ws);\n\t\t\tconst domWs = ws as unknown as WebSocket;\n\t\t\tconst session = new SockaWebSocketSession(domWs, sessionMap, config);\n\t\t\tsessionMap.set(domWs, session);\n\t\t\trunSockaSessionOnAttached(config, session);\n\t\t},\n\t\tasync message(ws: ServerWebSocket<TWsData>, message: unknown) {\n\t\t\tconst { sessionMap, config } = resolveScope(ws);\n\t\t\tconst domWs = ws as unknown as WebSocket;\n\t\t\tconst session = sessionMap.get(domWs);\n\t\t\tif (!session) return;\n\t\t\tconst wireFormat = config.wireFormat ?? \"json\";\n\t\t\ttry {\n\t\t\t\tawait dispatchSockaInboundMessage(\n\t\t\t\t\tsession,\n\t\t\t\t\twireFormat,\n\t\t\t\t\tmessage as MessageEvent[\"data\"],\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\treportSockaError(config.reportError, {\n\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\tadapter: \"bun\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tasync close(ws: ServerWebSocket<TWsData>) {\n\t\t\tconst { sessionMap, config } = resolveScope(ws);\n\t\t\tconst domWs = ws as unknown as WebSocket;\n\t\t\tconst session = sessionMap.get(domWs);\n\t\t\ttry {\n\t\t\t\tif (session) {\n\t\t\t\t\tawait session.invokeHandleClose();\n\t\t\t\t}\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\tsessionMap.delete(domWs);\n\t\t\t}\n\t\t},\n\t};\n\n\treturn {\n\t\tsessionMap: new Map(),\n\t\twebsocket,\n\t\twireFormat: \"json\",\n\t};\n}\n\nfunction bunHandlersFromConfig<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>,\n\tmaybeOptions?: {\n\t\tsessionMap?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t},\n): SockaBunWebSocketHandlers<TContract, TData, undefined> {\n\tconst sessionMap =\n\t\tmaybeOptions?.sessionMap ??\n\t\tnew Map<WebSocket, SockaWebSocketSession<TContract, TData>>();\n\tconst wireFormat = config.wireFormat ?? \"json\";\n\n\tconst websocket: SockaBunWebSocketHandlers<\n\t\tTContract,\n\t\tTData,\n\t\tundefined\n\t>[\"websocket\"] = {\n\t\topen(ws: ServerWebSocket<undefined>) {\n\t\t\tconst domWs = ws as unknown as WebSocket;\n\t\t\tconst session = new SockaWebSocketSession(domWs, sessionMap, config);\n\t\t\tsessionMap.set(domWs, session);\n\t\t\trunSockaSessionOnAttached(config, session);\n\t\t},\n\t\tasync message(ws: ServerWebSocket<undefined>, message: unknown) {\n\t\t\tconst domWs = ws as unknown as WebSocket;\n\t\t\tconst session = sessionMap.get(domWs);\n\t\t\tif (!session) return;\n\t\t\ttry {\n\t\t\t\tawait dispatchSockaInboundMessage(\n\t\t\t\t\tsession,\n\t\t\t\t\twireFormat,\n\t\t\t\t\tmessage as MessageEvent[\"data\"],\n\t\t\t\t);\n\t\t\t} catch (error) {\n\t\t\t\treportSockaError(config.reportError, {\n\t\t\t\t\tkind: \"serverInboundMessage\",\n\t\t\t\t\tadapter: \"bun\",\n\t\t\t\t\terror,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tasync close(ws: ServerWebSocket<undefined>) {\n\t\t\tconst domWs = ws as unknown as WebSocket;\n\t\t\tconst session = sessionMap.get(domWs);\n\t\t\ttry {\n\t\t\t\tif (session) {\n\t\t\t\t\tawait session.invokeHandleClose();\n\t\t\t\t}\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\tsessionMap.delete(domWs);\n\t\t\t}\n\t\t},\n\t};\n\n\treturn { sessionMap, websocket, wireFormat };\n}\n\n/**\n * WebSocket handlers for `Bun.serve` when using `ServerWebSocket` (no\n * `addEventListener`). Inbound frames are dispatched with the same logic as\n * `attachSockaWebSocket`.\n *\n * **Single-room:** pass a {@link SockaWebSocketSessionConfig} and optional shared `sessionMap`.\n *\n * **Multi-room:** pass `{ resolveScope }` where `resolveScope(ws)` returns the\n * `sessionMap` and `config` for that socket’s scope (e.g. from `ws.data.roomId`).\n * The returned `sessionMap` is an empty placeholder; real maps come from `resolveScope`.\n */\nexport function createSockaBunWebSocketHandlers<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfig<TContract, TData>,\n\toptions?: {\n\t\tsessionMap?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t},\n): SockaBunWebSocketHandlers<TContract, TData, undefined>;\n\nexport function createSockaBunWebSocketHandlers<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n\tTWsData,\n>(options: {\n\tresolveScope: SockaBunResolveScope<TContract, TData, TWsData>;\n}): SockaBunWebSocketHandlers<TContract, TData, TWsData>;\n\nexport function createSockaBunWebSocketHandlers<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfigOrOptions:\n\t\t| SockaWebSocketSessionConfig<TContract, TData>\n\t\t| { resolveScope: SockaBunResolveScope<TContract, TData, unknown> },\n\tmaybeOptions?: {\n\t\tsessionMap?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;\n\t},\n):\n\t| SockaBunWebSocketHandlers<TContract, TData, undefined>\n\t| SockaBunWebSocketHandlers<TContract, TData, unknown> {\n\tconst isResolveScope =\n\t\ttypeof configOrOptions === \"object\" &&\n\t\tconfigOrOptions !== null &&\n\t\t\"resolveScope\" in configOrOptions &&\n\t\ttypeof (configOrOptions as { resolveScope: unknown }).resolveScope ===\n\t\t\t\"function\";\n\n\tif (isResolveScope) {\n\t\treturn bunHandlersFromResolveScope(\n\t\t\t(\n\t\t\t\tconfigOrOptions as {\n\t\t\t\t\tresolveScope: SockaBunResolveScope<TContract, TData, unknown>;\n\t\t\t\t}\n\t\t\t).resolveScope,\n\t\t);\n\t}\n\treturn bunHandlersFromConfig(\n\t\tconfigOrOptions as SockaWebSocketSessionConfig<TContract, TData>,\n\t\tmaybeOptions,\n\t);\n}\n"]}
@@ -0,0 +1,236 @@
1
+ import { parseWirePayload, decodeSockaWire, SockaWireError, encodeServerError, parseStandardSchema, SockaError, encodeServerResponse, encodeSockaWire, encodeServerEvent, reportSockaError } from './chunk-MZCQHJXY.js';
2
+ import { exhaustiveGuard } from '@firtoz/maybe-error';
3
+
4
+ function broadcastSockaEventToPeers(sessions, self, event, body, excludeSelf = false) {
5
+ for (const [ws, session] of sessions) {
6
+ if (excludeSelf && ws === self.websocket) continue;
7
+ session.emitWireEvent(event, body);
8
+ }
9
+ }
10
+ var SockaWebSocketSession = class {
11
+ constructor(websocket, sessions, config, init) {
12
+ this.websocket = websocket;
13
+ this.sessions = sessions;
14
+ this.config = config;
15
+ this.wireFormat = config.wireFormat ?? "json";
16
+ const create = config.createData ?? ((_i) => ({}));
17
+ this._data = create(init ?? {});
18
+ }
19
+ get data() {
20
+ return this._data;
21
+ }
22
+ /**
23
+ * Invokes the user {@link typeof SockaWebSocketSessionConfig.handleClose} callback.
24
+ * Server adapters should call this when the WebSocket closes, **before** deleting
25
+ * this session from the shared `sessions` map.
26
+ */
27
+ async invokeHandleClose() {
28
+ await this.config.handleClose(this);
29
+ }
30
+ async handleRawMessage(rawMessage) {
31
+ if (this.wireFormat !== "json") {
32
+ await this.reportValidationError(
33
+ new Error("socka: unexpected JSON frame in msgpack mode"),
34
+ rawMessage
35
+ );
36
+ return;
37
+ }
38
+ const deserialize = this.config.deserializeJson ?? JSON.parse;
39
+ let parsed;
40
+ try {
41
+ parsed = deserialize(rawMessage);
42
+ } catch {
43
+ await this.reportValidationError(
44
+ new Error("socka: invalid JSON"),
45
+ rawMessage
46
+ );
47
+ return;
48
+ }
49
+ await this.dispatchAfterParsed(parsed, rawMessage);
50
+ }
51
+ async handleBinaryMessage(buffer) {
52
+ if (this.wireFormat !== "msgpack") {
53
+ await this.reportValidationError(
54
+ new Error("socka: unexpected binary frame in JSON mode"),
55
+ buffer
56
+ );
57
+ return;
58
+ }
59
+ let parsed;
60
+ try {
61
+ parsed = parseWirePayload(buffer, "msgpack");
62
+ } catch (err) {
63
+ await this.reportValidationError(
64
+ err instanceof Error ? err : new Error("socka: msgpack decode failed"),
65
+ buffer
66
+ );
67
+ return;
68
+ }
69
+ await this.dispatchAfterParsed(parsed, buffer);
70
+ }
71
+ async dispatchAfterParsed(parsed, originalWire) {
72
+ let decoded;
73
+ try {
74
+ decoded = decodeSockaWire(parsed);
75
+ } catch (err) {
76
+ if (err instanceof SockaWireError) {
77
+ await this.reportValidationError(err, originalWire);
78
+ return;
79
+ }
80
+ throw err;
81
+ }
82
+ switch (decoded.kind) {
83
+ case "clientRequest":
84
+ await this.dispatchClientRequest(decoded.frame, originalWire);
85
+ return;
86
+ case "serverResponse":
87
+ case "serverError":
88
+ case "serverEvent":
89
+ await this.reportValidationError(
90
+ new Error("socka: unexpected server-originated frame from client"),
91
+ parsed
92
+ );
93
+ return;
94
+ default:
95
+ exhaustiveGuard(decoded);
96
+ }
97
+ }
98
+ async dispatchClientRequest(frame, _originalWire) {
99
+ const rpcName = frame.rpc;
100
+ const procedure = this.config.contract.calls[rpcName];
101
+ if (!procedure) {
102
+ const errorFrame = encodeServerError(
103
+ frame.id,
104
+ `Unknown call: ${rpcName}`
105
+ );
106
+ this.sendWireFrame(errorFrame);
107
+ return;
108
+ }
109
+ let validatedInput;
110
+ if (procedure.input) {
111
+ try {
112
+ validatedInput = await parseStandardSchema(procedure.input, frame.body);
113
+ } catch (err) {
114
+ const msg = err instanceof Error ? err.message : "Input validation failed";
115
+ const errorFrame = encodeServerError(frame.id, msg);
116
+ this.sendWireFrame(errorFrame);
117
+ return;
118
+ }
119
+ }
120
+ let result;
121
+ try {
122
+ if (procedure.input) {
123
+ const handler = this.config.handlers[rpcName];
124
+ result = await handler(validatedInput, this);
125
+ } else {
126
+ const handler = this.config.handlers[rpcName];
127
+ result = await handler(this);
128
+ }
129
+ } catch (err) {
130
+ this.config.onHandlerError?.(err, rpcName, validatedInput, this);
131
+ const sockaErr = err instanceof SockaError ? err : new SockaError(
132
+ err instanceof Error ? err.message : "Handler failed"
133
+ );
134
+ const errorFrame = encodeServerError(frame.id, sockaErr.message);
135
+ this.sendWireFrame(errorFrame);
136
+ return;
137
+ }
138
+ let validatedOutput;
139
+ try {
140
+ validatedOutput = await parseStandardSchema(procedure.output, result);
141
+ } catch (err) {
142
+ const msg = err instanceof Error ? err.message : "Output validation failed";
143
+ const errorFrame = encodeServerError(frame.id, msg);
144
+ this.sendWireFrame(errorFrame);
145
+ return;
146
+ }
147
+ const responseFrame = encodeServerResponse(
148
+ frame.id,
149
+ rpcName,
150
+ validatedOutput
151
+ );
152
+ this.sendWireFrame(responseFrame);
153
+ }
154
+ encodeOutgoing(frame) {
155
+ return encodeSockaWire(
156
+ frame,
157
+ this.wireFormat,
158
+ this.config.serializeJson ?? JSON.stringify
159
+ );
160
+ }
161
+ sendWireFrame(frame) {
162
+ if (this.websocket.readyState !== WebSocket.OPEN) {
163
+ return;
164
+ }
165
+ const encoded = this.encodeOutgoing(frame);
166
+ if (typeof encoded === "string") {
167
+ this.websocket.send(encoded);
168
+ return;
169
+ }
170
+ const copy = new Uint8Array(encoded.byteLength);
171
+ copy.set(encoded);
172
+ this.websocket.send(copy.buffer);
173
+ }
174
+ /**
175
+ * Send a server event frame (wire). Prefer {@link emitPush} so
176
+ * payloads are validated against the contract.
177
+ */
178
+ emitWireEvent(event, body) {
179
+ const frame = encodeServerEvent(event, body);
180
+ this.sendWireFrame(frame);
181
+ }
182
+ async emitPush(name, body) {
183
+ const schema = this.config.contract.pushes[name];
184
+ if (!schema) {
185
+ throw new Error(`socka: unknown push ${String(name)}`);
186
+ }
187
+ const validated = await parseStandardSchema(
188
+ schema,
189
+ body
190
+ );
191
+ this.emitWireEvent(name, validated);
192
+ }
193
+ async broadcastPush(name, body, excludeSelf = false) {
194
+ const schema = this.config.contract.pushes[name];
195
+ if (!schema) {
196
+ throw new Error(`socka: unknown push ${String(name)}`);
197
+ }
198
+ const validated = await parseStandardSchema(
199
+ schema,
200
+ body
201
+ );
202
+ broadcastSockaEventToPeers(
203
+ this.sessions,
204
+ this,
205
+ name,
206
+ validated,
207
+ excludeSelf
208
+ );
209
+ }
210
+ async reportValidationError(error, originalMessage) {
211
+ if (this.config.onValidationError) {
212
+ await this.config.onValidationError(error, originalMessage);
213
+ } else {
214
+ console.error("socka: validation error:", error, originalMessage);
215
+ }
216
+ }
217
+ };
218
+ function runSockaSessionOnAttached(config, session) {
219
+ const cb = config.onAttached;
220
+ if (!cb) return;
221
+ try {
222
+ const result = cb(session);
223
+ void Promise.resolve(result).catch((error) => {
224
+ reportSockaError(config.reportError, {
225
+ kind: "serverOnAttached",
226
+ error
227
+ });
228
+ });
229
+ } catch (error) {
230
+ reportSockaError(config.reportError, { kind: "serverOnAttached", error });
231
+ }
232
+ }
233
+
234
+ export { SockaWebSocketSession, broadcastSockaEventToPeers, runSockaSessionOnAttached };
235
+ //# sourceMappingURL=chunk-45D4T232.js.map
236
+ //# sourceMappingURL=chunk-45D4T232.js.map