@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
package/README.md CHANGED
@@ -12,6 +12,8 @@
12
12
 
13
13
  **Typed WebSocket RPC and pushes for TypeScript.** One **`defineSocka`** contract gives you **`session.send.*`** for RPCs and **`session.subscribe`** for **typed server pushes**—validated, correlated, same schema on client and server.
14
14
 
15
+ **Validation is [Standard Schema](https://standardschema.dev) v1**, not Zod-specific: any compliant library works (e.g. **Zod**, **Valibot**, or others). Examples below use **Zod** for familiarity.
16
+
15
17
  **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
18
 
17
19
  ## Minimal example: multi-room chat (Bun)
@@ -77,14 +79,15 @@ export const chatContract = defineSocka({
77
79
  });
78
80
  ```
79
81
 
80
- **`server.ts`** — **`createSockaRoomRegistry`** holds one **`sessionMap` + config per room**; **`strictUpgradeRequest: true`** types **`createData`** with a real **`Request`** (no `http://_/` placeholder). **`session.listPeers()`** replaces hand-rolled **`sessionMap`** walks for presence.
82
+ **Fire-and-forget vs `output: z.void()`** — Omit **`output`** on a call when you want one-way success semantics: the server does not send a **`serverResponse`**, and **`await session.send.*` resolves after the frame is sent** (it does not wait for server processing). Server failures still return a correlated **`serverError`**; use **`reportError`** on **`SockaSession`** / **`useSockaSession`** to observe those when using output-less calls. Use **`output: z.void()`** when you still want a normal request/response **`await`** that completes only after the server acknowledges.
83
+
84
+ **`server.ts`** — **`createSockaRoomRegistry`** gives each room its own **`sessionMap`** and **config**. By default **`createData`** receives **`SockaStrictWebSocketInit`**: **`init.request`** is the upgrade **`Request`** (Bun/Hono/adapters pass it through; see **[Server — Strict upgrade request](./docs/server.md#strict-upgrade-request)**). Set **`strictUpgradeRequest: false`** when you have no **`Request`**. **`session.listPeers()`** returns **`session.data`** for other sockets in the same room—use it to implement **`listPresence`**-style calls (see **[Presence](./docs/presence.md)**).
81
85
 
82
86
  ```ts
83
87
  import type { ServerWebSocket } from "bun";
84
88
  import { createSockaBunWebSocketHandlers } from "@firtoz/socka/bun";
85
89
  import {
86
90
  createSockaRoomRegistry,
87
- type SockaStrictWebSocketInit,
88
91
  type SockaWebSocketSessionConfig,
89
92
  } from "@firtoz/socka/server";
90
93
  import { type ChatMessageRow, chatContract } from "./contract";
@@ -97,8 +100,7 @@ const history = new Map<string, ChatMessageRow[]>();
97
100
  const registry = createSockaRoomRegistry(
98
101
  (roomId): SockaWebSocketSessionConfig<typeof chatContract, SessionData> => ({
99
102
  contract: chatContract,
100
- strictUpgradeRequest: true,
101
- createData: (init: SockaStrictWebSocketInit) => {
103
+ createData: (init) => {
102
104
  const u = new URL(init.request.url);
103
105
  const displayName = u.searchParams.get("name")?.trim() || "anon";
104
106
  return { roomId, userId: crypto.randomUUID(), displayName };
@@ -247,15 +249,15 @@ Pick how the socket is upgraded, then use the matching import path and guide:
247
249
 
248
250
  - **Schema-first RPC + push** — one contract; no parallel “event” protocol for server pushes.
249
251
  - **Correlated envelopes** — request/response IDs and validation hooks are built in.
250
- - **Same contract** across Bun, Hono, Node `ws`, and Durable Objects (see **[Comparison](./docs/comparison.md)** for socket.io / tRPC / hand-rolled).
251
- - **Room registry + presence helpers** — **`createSockaRoomRegistry`** for per-room **`sessionMap`** / config; **`session.listPeers()`** for who is in the room without walking maps by hand.
252
- - **Strict upgrade typing + optional reconnect** — Bun/Hono can set **`strictUpgradeRequest: true`** so **`createData`** sees **`init.request`**; **`SockaWebSocketClient`** / **`SockaSession`** can **`reconnect`** with exponential backoff (see **[Reconnection](./docs/reconnection.md)**).
252
+ - **Same contract** across Bun, Hono, Node `ws`, and Durable Objects (see **[Comparison](./docs/comparison.md)** for socket.io, tRPC, and custom WebSocket stacks).
253
+ - **Room registry + presence helpers** — **`createSockaRoomRegistry`** for per-room **`sessionMap`** / config; **`session.listPeers()`** to list other peers in the room (see **[Presence](./docs/presence.md)**).
254
+ - **Strict upgrade typing + optional reconnect** — by default **`createData`** sees **`init.request`** on the upgrade; **`SockaWebSocketClient`** / **`SockaSession`** can **`reconnect`** with exponential backoff (see **[Reconnection](./docs/reconnection.md)**).
253
255
 
254
256
  ## Documentation
255
257
 
256
258
  Hub: **[`docs/README.md`](./docs/README.md)** (getting started, peers, lifecycle, multi-room, reference).
257
259
 
258
- **Roadmap:** [post–v1 and deferred work](./roadmap.md). Agent skills: [`skills/`](./skills/).
260
+ **Roadmap:** [deferred ideas and future work](./roadmap.md). Agent skills: [`skills/`](./skills/).
259
261
 
260
262
  ## Full-stack examples
261
263
 
@@ -1,15 +1,16 @@
1
- import { S as SockaContract, a as SockaContractConfig, b as SockaWireFormat, e as InferSockaHandlers, y as SockaReportError, g as InferSockaPushPayload } from './socka-report-error-ixTynx4w.js';
1
+ import { S as SockaContract, a as SockaContractConfig, w as SockaWireFormat, c as InferSockaHandlers, z as SockaReportError, f as InferSockaPushPayload } from './socka-report-error-CXwpAUgl.js';
2
2
 
3
3
  /** Session data with no fields — `createData` may be omitted (defaults to `{}`). */
4
4
  type EmptySockaSessionData$1 = Record<string, never>;
5
5
  /**
6
6
  * Upgrade context passed into `createData` for {@link SockaWebSocketSession} when
7
- * **`strictUpgradeRequest` is not set to `true`** (the default).
7
+ * **`strictUpgradeRequest: false`** is set (opt out of the default strict upgrade).
8
8
  *
9
9
  * **`request` is optional** because some call sites attach a socket without an HTTP upgrade
10
- * (custom tests, unusual adapters). If you read query params or headers from the upgrade,
11
- * you must handle a missing `request` (e.g. optional chaining and a fallback URL), or set
12
- * **`strictUpgradeRequest: true`** instead so {@link SockaStrictWebSocketInit} applies.
10
+ * (custom tests, unusual adapters, Node **`ws`** without a **`Request`**, inner DO engine).
11
+ * If you read query params or headers from the upgrade, either handle a missing
12
+ * **`request`** here, or use the default strict mode (omit **`strictUpgradeRequest: false`**)
13
+ * so {@link SockaStrictWebSocketInit} applies.
13
14
  */
14
15
  type SockaWebSocketInit = {
15
16
  /**
@@ -19,13 +20,12 @@ type SockaWebSocketInit = {
19
20
  request?: Request;
20
21
  };
21
22
  /**
22
- * Upgrade context when **`strictUpgradeRequest: true`** is set on
23
- * {@link SockaWebSocketSessionConfig}.
23
+ * Upgrade context for {@link SockaWebSocketSession} when using the default strict config
24
+ * ({@link SockaWebSocketSessionConfig} — no `strictUpgradeRequest` field).
24
25
  *
25
26
  * **What this enables:** `createData` is typed so **`init.request` is always defined**.
26
- * You can use **`new URL(init.request.url)`** and read search params without
27
- * `init.request?.url ?? "http://_/"` placeholders, and TypeScript will catch mistakes if you
28
- * treat the request as optional.
27
+ * You can use **`new URL(init.request.url)`**, read search params, and use **`Request`**
28
+ * headers without optional chaining or a dummy base URL for **`URL`** parsing.
29
29
  *
30
30
  * **Runtime behavior:** If the adapter does not pass a `Request` while strict mode is on,
31
31
  * socka throws an error explaining how to wire the upgrade (e.g. Bun `data: { request: req }`,
@@ -51,29 +51,7 @@ type SockaWebSocketCreateDataStrict<TData> = [TData] extends [
51
51
  createData: (init: SockaStrictWebSocketInit) => TData;
52
52
  };
53
53
  type SockaSessionForHandlers<TContract extends SockaContract<SockaContractConfig>, TData> = SockaWebSocketSession<TContract, TData>;
54
- /**
55
- * Configuration for {@link SockaWebSocketSession}. Handlers receive the session
56
- * instance as the second argument (or the only argument when the procedure has no input).
57
- *
58
- * ## `strictUpgradeRequest` (optional flag)
59
- *
60
- * Controls how **`createData`** is typed and validated for the **HTTP upgrade**:
61
- *
62
- * - **Omitted or `undefined` (default)** — `createData` receives {@link SockaWebSocketInit}.
63
- * **`init.request` may be missing.** Use this for adapters or tests that construct sessions
64
- * without a real upgrade request, or when you intentionally support both cases and handle
65
- * optional `request` in code.
66
- *
67
- * - **`true`** — Opt in to **strict** upgrade typing: `createData` receives
68
- * {@link SockaStrictWebSocketInit}, so **`init.request` is required** in TypeScript and
69
- * enforced at runtime. Prefer this for normal Bun/Hono apps that always have an upgrade
70
- * `Request`, so you avoid placeholder URLs and get clearer errors if wiring is wrong.
71
- *
72
- * Bun and Hono helpers document how they populate init (e.g. **`sockaBunInitFromWsData`**,
73
- * default **`sockaInit`** from Hono context). The published **Server** guide includes a
74
- * **Strict upgrade request** section with examples.
75
- */
76
- type SockaWebSocketSessionConfig<TContract extends SockaContract<SockaContractConfig>, TData = EmptySockaSessionData$1> = {
54
+ type SockaWebSocketSessionConfigBase<TContract extends SockaContract<SockaContractConfig>, TData> = {
77
55
  contract: TContract;
78
56
  /** Default `"json"`. Use `"msgpack"` for binary frames (must match client). */
79
57
  wireFormat?: SockaWireFormat;
@@ -94,11 +72,53 @@ type SockaWebSocketSessionConfig<TContract extends SockaContract<SockaContractCo
94
72
  * (safe to broadcast to peers). Sync or async; async rejections are logged.
95
73
  */
96
74
  onAttached?: (session: SockaSessionForHandlers<TContract, TData>) => void | Promise<void>;
97
- } & (({
98
- strictUpgradeRequest?: undefined;
99
- } & SockaWebSocketCreateData<TData>) | ({
100
- strictUpgradeRequest: true;
101
- } & SockaWebSocketCreateDataStrict<TData>));
75
+ };
76
+ /**
77
+ * Configuration for {@link SockaWebSocketSession} **default (strict upgrade)**.
78
+ *
79
+ * Handlers receive the session as the second argument, or the only argument when the call
80
+ * has no input schema.
81
+ *
82
+ * **`createData`** receives {@link SockaStrictWebSocketInit}: **`init.request`** is the
83
+ * HTTP upgrade **`Request`**, required in TypeScript and enforced at runtime. Use for
84
+ * normal Bun/Hono apps and **`attachSockaWebSocket(websocket, sessions, config, { request })`**.
85
+ *
86
+ * Bun helpers: store **`request`** on **`ServerWebSocket`** `data` and use
87
+ * **`sockaBunInitFromWsData`** (see **`@firtoz/socka/bun`**). Hono: **`sockaHonoNodeWs`** /
88
+ * **`sockaHonoCloudflare`** can supply **`sockaInit`** from the context so **`createData`**
89
+ * still sees a **`Request`**.
90
+ *
91
+ * To allow a missing upgrade **`Request`** (tests, Node **`ws`**, inner DO engine), use
92
+ * {@link SockaWebSocketSessionConfigLoose} instead. See the **Server** guide section
93
+ * **Strict upgrade request** in the package docs.
94
+ */
95
+ type SockaWebSocketSessionConfig<TContract extends SockaContract<SockaContractConfig>, TData = EmptySockaSessionData$1> = SockaWebSocketSessionConfigBase<TContract, TData> & SockaWebSocketCreateDataStrict<TData>;
96
+ /**
97
+ * Configuration for {@link SockaWebSocketSession} — **loose upgrade** (opt out of strict).
98
+ *
99
+ * Sets **`strictUpgradeRequest: false`**. **`createData`** receives {@link SockaWebSocketInit};
100
+ * **`init.request` may be `undefined`**.
101
+ *
102
+ * Use when:
103
+ *
104
+ * - Custom **`attachSockaWebSocket`** call sites or tests that do not attach an HTTP
105
+ * **`Request`**
106
+ * - Node **`ws`** or other adapters where you only have a **`WebSocket`**
107
+ * - The inner **`SockaWebSocketSession`** constructed inside **`SockaDoSession`** (socka sets
108
+ * this mode for you)
109
+ *
110
+ * If you read query params or headers from the upgrade, guard for a missing **`request`**
111
+ * or switch to {@link SockaWebSocketSessionConfig} and wire the real **`Request`** through.
112
+ */
113
+ type SockaWebSocketSessionConfigLoose<TContract extends SockaContract<SockaContractConfig>, TData = EmptySockaSessionData$1> = SockaWebSocketSessionConfigBase<TContract, TData> & {
114
+ strictUpgradeRequest: false;
115
+ } & SockaWebSocketCreateData<TData>;
116
+ /**
117
+ * Union of {@link SockaWebSocketSessionConfig} (strict) and {@link SockaWebSocketSessionConfigLoose}.
118
+ * This is the type accepted by {@link SockaWebSocketSession}'s constructor,
119
+ * **`attachSockaWebSocket`**, **`createSockaBunWebSocketHandlers`**, and Hono socka helpers.
120
+ */
121
+ type SockaWebSocketSessionConfigUnion<TContract extends SockaContract<SockaContractConfig>, TData = EmptySockaSessionData$1> = SockaWebSocketSessionConfig<TContract, TData> | SockaWebSocketSessionConfigLoose<TContract, TData>;
102
122
 
103
123
  /** Session data with no fields — `createData` may be omitted (defaults to `{}`). */
104
124
  type EmptySockaSessionData = Record<string, never>;
@@ -135,7 +155,7 @@ declare class SockaWebSocketSession<TContract extends SockaContract<SockaContrac
135
155
  private readonly config;
136
156
  private readonly wireFormat;
137
157
  private _data;
138
- constructor(websocket: WebSocket, sessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>, config: SockaWebSocketSessionConfig<TContract, TData>, init?: SockaWebSocketInit);
158
+ constructor(websocket: WebSocket, sessions: Map<WebSocket, SockaWebSocketSession<TContract, TData>>, config: SockaWebSocketSessionConfigUnion<TContract, TData>, init?: SockaWebSocketInit);
139
159
  get data(): TData;
140
160
  /**
141
161
  * Session data for every connection in the same {@link sessions} map (same room),
@@ -184,6 +204,6 @@ declare class SockaWebSocketSession<TContract extends SockaContract<SockaContrac
184
204
  * Invoke {@link SockaWebSocketSessionConfig.onAttached} after the session is
185
205
  * registered in the shared map.
186
206
  */
187
- declare function runSockaSessionOnAttached<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfig<TContract, TData>, session: SockaWebSocketSession<TContract, TData>): void;
207
+ declare function runSockaSessionOnAttached<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfigUnion<TContract, TData>, session: SockaWebSocketSession<TContract, TData>): void;
188
208
 
189
- export { type SockaStrictWebSocketInit as S, SockaWebSocketSession as a, type SockaWebSocketSessionConfig as b, type SockaWebSocketInit as c, type SockaPushSession as d, broadcastSockaEventToPeers as e, type SockaEmitCapable as f, runSockaSessionOnAttached as r };
209
+ export { type SockaStrictWebSocketInit as S, SockaWebSocketSession as a, type SockaWebSocketSessionConfigUnion as b, type SockaWebSocketInit as c, type SockaPushSession as d, type SockaWebSocketSessionConfig as e, broadcastSockaEventToPeers as f, type SockaEmitCapable as g, type SockaWebSocketSessionConfigLoose as h, runSockaSessionOnAttached as r };
@@ -1,17 +1,17 @@
1
1
  import { ServerWebSocket } from 'bun';
2
- import { S as SockaContract, a as SockaContractConfig, b as SockaWireFormat } from '../socka-report-error-ixTynx4w.js';
3
- import { S as SockaStrictWebSocketInit, a as SockaWebSocketSession, b as SockaWebSocketSessionConfig } from '../SockaWebSocketSession-Cza7Fti-.js';
2
+ import { S as SockaContract, a as SockaContractConfig, w as SockaWireFormat } from '../socka-report-error-CXwpAUgl.js';
3
+ import { S as SockaStrictWebSocketInit, a as SockaWebSocketSession, b as SockaWebSocketSessionConfigUnion } from '../SockaWebSocketSession-B1w7RAid.js';
4
4
  import '@standard-schema/spec';
5
5
 
6
6
  /**
7
7
  * Reads the upgrade {@link Request} from Bun **`ServerWebSocket.data`** when your
8
8
  * **`fetch`** handler stored it there (e.g. **`server.upgrade(req, { data: { roomId, request: req } })`**).
9
9
  *
10
- * Pair with **`strictUpgradeRequest: true`** on {@link SockaWebSocketSessionConfig} so
10
+ * Pair with the default strict upgrade on {@link SockaWebSocketSessionConfig} so
11
11
  * **`createData`** is typed with {@link SockaStrictWebSocketInit} and **`init.request`**
12
12
  * is always defined. If **`request`** is missing from **`data`**, this returns **`undefined`**
13
- * and strict mode will throw when constructing the session — that usually means you forgot
14
- * to pass **`request`** on upgrade.
13
+ * and the session constructor will throw — that usually means you forgot
14
+ * to pass **`request`** on upgrade (or set **`strictUpgradeRequest: false`**).
15
15
  */
16
16
  declare function sockaBunInitFromWsData(ws: ServerWebSocket<unknown>): SockaStrictWebSocketInit | undefined;
17
17
  /** `ServerWebSocket.data` shape after {@link sockaBunUpgrade}. */
@@ -20,7 +20,7 @@ type SockaBunUpgradeData<TExtra> = TExtra & {
20
20
  };
21
21
  /**
22
22
  * Calls {@link Bun.Server.upgrade} with **`request: req`** merged into **`data`**, so
23
- * {@link sockaBunInitFromWsData} and **`strictUpgradeRequest: true`** always see the HTTP
23
+ * {@link sockaBunInitFromWsData} and the default strict upgrade always see the HTTP
24
24
  * upgrade request (query params, cookies path).
25
25
  */
26
26
  declare function sockaBunUpgrade<TExtra extends Record<string, unknown>>(server: {
@@ -30,7 +30,7 @@ declare function sockaBunUpgrade<TExtra extends Record<string, unknown>>(server:
30
30
  }, req: Request, data?: TExtra): boolean;
31
31
  type SockaBunResolveScope<TContract extends SockaContract<SockaContractConfig>, TData, TWsData = undefined> = (ws: ServerWebSocket<TWsData>) => {
32
32
  sessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
33
- config: SockaWebSocketSessionConfig<TContract, TData>;
33
+ config: SockaWebSocketSessionConfigUnion<TContract, TData>;
34
34
  };
35
35
  type SockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConfig>, TData, TWsData = undefined> = {
36
36
  sessionMap: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
@@ -53,7 +53,7 @@ type SockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConf
53
53
  * `sessionMap` and `config` for that socket’s scope (e.g. from `ws.data.roomId`).
54
54
  * The returned `sessionMap` is an empty placeholder; real maps come from `resolveScope`.
55
55
  */
56
- declare function createSockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfig<TContract, TData>, options?: {
56
+ declare function createSockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConfig>, TData>(config: SockaWebSocketSessionConfigUnion<TContract, TData>, options?: {
57
57
  sessionMap?: Map<WebSocket, SockaWebSocketSession<TContract, TData>>;
58
58
  }): SockaBunWebSocketHandlers<TContract, TData, undefined>;
59
59
  declare function createSockaBunWebSocketHandlers<TContract extends SockaContract<SockaContractConfig>, TData, TWsData>(options: {
package/dist/bun/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { dispatchSockaInboundMessage } from '../chunk-5WQTYLIC.js';
2
- import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-JVLUA3Q5.js';
3
- import { reportSockaError } from '../chunk-2FNWVCP3.js';
2
+ import { SockaWebSocketSession, runSockaSessionOnAttached } from '../chunk-LVVCHLNW.js';
3
+ import { reportSockaError } from '../chunk-IFIGKR3W.js';
4
4
 
5
5
  // src/bun/index.ts
6
6
  function sockaBunInitFromWsData(ws) {
@@ -1 +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 SockaStrictWebSocketInit,\n\ttype SockaWebSocketSessionConfig,\n} from \"../server/SockaWebSocketSession\";\n\n/**\n * Reads the upgrade {@link Request} from Bun **`ServerWebSocket.data`** when your\n * **`fetch`** handler stored it there (e.g. **`server.upgrade(req, { data: { roomId, request: req } })`**).\n *\n * Pair with **`strictUpgradeRequest: true`** on {@link SockaWebSocketSessionConfig} so\n * **`createData`** is typed with {@link SockaStrictWebSocketInit} and **`init.request`**\n * is always defined. If **`request`** is missing from **`data`**, this returns **`undefined`**\n * and strict mode will throw when constructing the session — that usually means you forgot\n * to pass **`request`** on upgrade.\n */\nexport function sockaBunInitFromWsData(\n\tws: ServerWebSocket<unknown>,\n): SockaStrictWebSocketInit | undefined {\n\tconst d = ws.data as Record<string, unknown> | undefined;\n\tif (d && \"request\" in d && d.request instanceof Request) {\n\t\treturn { request: d.request };\n\t}\n\treturn undefined;\n}\n\n/** `ServerWebSocket.data` shape after {@link sockaBunUpgrade}. */\nexport type SockaBunUpgradeData<TExtra> = TExtra & { request: Request };\n\n/**\n * Calls {@link Bun.Server.upgrade} with **`request: req`** merged into **`data`**, so\n * {@link sockaBunInitFromWsData} and **`strictUpgradeRequest: true`** always see the HTTP\n * upgrade request (query params, cookies path).\n */\nexport function sockaBunUpgrade<TExtra extends Record<string, unknown>>(\n\tserver: {\n\t\tupgrade: (\n\t\t\treq: Request,\n\t\t\topts: { data: SockaBunUpgradeData<TExtra> },\n\t\t) => boolean;\n\t},\n\treq: Request,\n\tdata?: TExtra,\n): boolean {\n\tconst extra = (data ?? ({} as TExtra)) as TExtra;\n\treturn server.upgrade(req, { data: { ...extra, request: req } });\n}\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 init = sockaBunInitFromWsData(ws);\n\t\t\tconst session = new SockaWebSocketSession(\n\t\t\t\tdomWs,\n\t\t\t\tsessionMap,\n\t\t\t\tconfig,\n\t\t\t\tinit,\n\t\t\t);\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 init = sockaBunInitFromWsData(ws);\n\t\t\tconst session = new SockaWebSocketSession(\n\t\t\t\tdomWs,\n\t\t\t\tsessionMap,\n\t\t\t\tconfig,\n\t\t\t\tinit,\n\t\t\t);\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"]}
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 SockaStrictWebSocketInit,\n\ttype SockaWebSocketSessionConfigUnion,\n} from \"../server/SockaWebSocketSession\";\n\n/**\n * Reads the upgrade {@link Request} from Bun **`ServerWebSocket.data`** when your\n * **`fetch`** handler stored it there (e.g. **`server.upgrade(req, { data: { roomId, request: req } })`**).\n *\n * Pair with the default strict upgrade on {@link SockaWebSocketSessionConfig} so\n * **`createData`** is typed with {@link SockaStrictWebSocketInit} and **`init.request`**\n * is always defined. If **`request`** is missing from **`data`**, this returns **`undefined`**\n * and the session constructor will throw — that usually means you forgot\n * to pass **`request`** on upgrade (or set **`strictUpgradeRequest: false`**).\n */\nexport function sockaBunInitFromWsData(\n\tws: ServerWebSocket<unknown>,\n): SockaStrictWebSocketInit | undefined {\n\tconst d = ws.data as Record<string, unknown> | undefined;\n\tif (d && \"request\" in d && d.request instanceof Request) {\n\t\treturn { request: d.request };\n\t}\n\treturn undefined;\n}\n\n/** `ServerWebSocket.data` shape after {@link sockaBunUpgrade}. */\nexport type SockaBunUpgradeData<TExtra> = TExtra & { request: Request };\n\n/**\n * Calls {@link Bun.Server.upgrade} with **`request: req`** merged into **`data`**, so\n * {@link sockaBunInitFromWsData} and the default strict upgrade always see the HTTP\n * upgrade request (query params, cookies path).\n */\nexport function sockaBunUpgrade<TExtra extends Record<string, unknown>>(\n\tserver: {\n\t\tupgrade: (\n\t\t\treq: Request,\n\t\t\topts: { data: SockaBunUpgradeData<TExtra> },\n\t\t) => boolean;\n\t},\n\treq: Request,\n\tdata?: TExtra,\n): boolean {\n\tconst extra = (data ?? ({} as TExtra)) as TExtra;\n\treturn server.upgrade(req, { data: { ...extra, request: req } });\n}\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: SockaWebSocketSessionConfigUnion<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 init = sockaBunInitFromWsData(ws);\n\t\t\tconst session = new SockaWebSocketSession(\n\t\t\t\tdomWs,\n\t\t\t\tsessionMap,\n\t\t\t\tconfig,\n\t\t\t\tinit,\n\t\t\t);\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: SockaWebSocketSessionConfigUnion<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 init = sockaBunInitFromWsData(ws);\n\t\t\tconst session = new SockaWebSocketSession(\n\t\t\t\tdomWs,\n\t\t\t\tsessionMap,\n\t\t\t\tconfig,\n\t\t\t\tinit,\n\t\t\t);\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: SockaWebSocketSessionConfigUnion<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| SockaWebSocketSessionConfigUnion<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 SockaWebSocketSessionConfigUnion<TContract, TData>,\n\t\tmaybeOptions,\n\t);\n}\n"]}
@@ -17,6 +17,7 @@ var SockaError = class _SockaError extends Error {
17
17
  super(message);
18
18
  this.name = "SockaError";
19
19
  this.requestId = options?.requestId;
20
+ this.rpc = options?.rpc;
20
21
  this.code = options?.code;
21
22
  this.data = options?.data;
22
23
  if (options?.cause !== void 0) {
@@ -33,6 +34,7 @@ var SockaError = class _SockaError extends Error {
33
34
  static fromWire(msg) {
34
35
  return new _SockaError(msg.error, {
35
36
  requestId: msg.id,
37
+ rpc: msg.rpc,
36
38
  code: msg.code,
37
39
  data: msg.data
38
40
  });
@@ -76,11 +78,13 @@ function decodeSockaWire(parsed) {
76
78
  if (socka === "serverError" && typeof parsed.id === "string" && typeof parsed.error === "string") {
77
79
  const code = "code" in parsed && typeof parsed.code === "string" ? parsed.code : void 0;
78
80
  const data = "data" in parsed ? parsed.data : void 0;
81
+ const rpc = "rpc" in parsed && typeof parsed.rpc === "string" ? parsed.rpc : void 0;
79
82
  const frame = {
80
83
  socka: "serverError",
81
84
  v: SOCKA_WIRE_VERSION,
82
85
  id: parsed.id,
83
86
  error: parsed.error,
87
+ ...rpc !== void 0 ? { rpc } : {},
84
88
  ...code !== void 0 ? { code } : {},
85
89
  ...data !== void 0 ? { data } : {}
86
90
  };
@@ -108,6 +112,7 @@ function encodeServerError(id, error, extra) {
108
112
  v: SOCKA_WIRE_VERSION,
109
113
  id,
110
114
  error,
115
+ ...extra?.rpc !== void 0 ? { rpc: extra.rpc } : {},
111
116
  ...extra?.code !== void 0 ? { code: extra.code } : {},
112
117
  ...extra?.data !== void 0 ? { data: extra.data } : {}
113
118
  };
@@ -144,6 +149,19 @@ function defaultReportError(event) {
144
149
  case "clientEventValidation":
145
150
  console.error("socka: event validation error", event.error);
146
151
  return;
152
+ case "clientFireAndForgetRpcError":
153
+ console.error("socka: fire-and-forget RPC error", event.error);
154
+ return;
155
+ case "clientOrphanServerError":
156
+ console.error("socka: orphan serverError (no pending RPC)", event.error);
157
+ return;
158
+ case "clientUnexpectedServerResponse":
159
+ console.error(
160
+ "socka: unexpected serverResponse for fire-and-forget RPC",
161
+ event.rpc,
162
+ event.requestId
163
+ );
164
+ return;
147
165
  case "serverOnAttached":
148
166
  console.error("socka: onAttached error:", event.error);
149
167
  return;
@@ -173,5 +191,5 @@ function reportSockaError(reportError, event) {
173
191
  }
174
192
 
175
193
  export { SOCKA_WIRE_VERSION, SockaError, SockaWireError, decodeSockaWire, defaultReportError, encodeClientRequest, encodeServerError, encodeServerEvent, encodeServerResponse, encodeSockaWire, parseStandardSchema, parseWirePayload, reportSockaError };
176
- //# sourceMappingURL=chunk-2FNWVCP3.js.map
177
- //# sourceMappingURL=chunk-2FNWVCP3.js.map
194
+ //# sourceMappingURL=chunk-IFIGKR3W.js.map
195
+ //# sourceMappingURL=chunk-IFIGKR3W.js.map
@@ -0,0 +1 @@
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-IFIGKR3W.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\t/** Procedure name when provided on the wire (`serverError.rpc`). */\n\treadonly rpc?: 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\trpc?: 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.rpc = options?.rpc;\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\trpc?: 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\trpc: msg.rpc,\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/** Procedure name when the error relates to a client RPC (helps clients without a pending entry). */\n\treadonly rpc?: 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 rpc =\n\t\t\t\"rpc\" in parsed && typeof parsed.rpc === \"string\"\n\t\t\t\t? parsed.rpc\n\t\t\t\t: 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...(rpc !== undefined ? { rpc } : {}),\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?: {\n\t\treadonly code?: string;\n\t\treadonly data?: unknown;\n\t\treadonly rpc?: string;\n\t},\n): SockaServerErrorFrame {\n\treturn {\n\t\tsocka: \"serverError\",\n\t\tv: SOCKA_WIRE_VERSION,\n\t\tid,\n\t\terror,\n\t\t...(extra?.rpc !== undefined ? { rpc: extra.rpc } : {}),\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\";\nimport type { SockaError } from \"./socka-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/**\n\t * `serverError` for a fire-and-forget call (no pending client promise). Prefer\n\t * setting `reportError` on the session when using output-less procedures.\n\t */\n\t| { kind: \"clientFireAndForgetRpcError\"; error: SockaError }\n\t/**\n\t * `serverError` with no matching pending entry for a call that expects a response\n\t * (e.g. stale id after reconnect or duplicate frame).\n\t */\n\t| { kind: \"clientOrphanServerError\"; error: SockaError }\n\t/**\n\t * Server sent `serverResponse` for a procedure with no `output` (misbehaving server).\n\t */\n\t| {\n\t\t\tkind: \"clientUnexpectedServerResponse\";\n\t\t\trpc: string;\n\t\t\trequestId: string;\n\t }\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 \"clientFireAndForgetRpcError\":\n\t\t\tconsole.error(\"socka: fire-and-forget RPC error\", event.error);\n\t\t\treturn;\n\t\tcase \"clientOrphanServerError\":\n\t\t\tconsole.error(\"socka: orphan serverError (no pending RPC)\", event.error);\n\t\t\treturn;\n\t\tcase \"clientUnexpectedServerResponse\":\n\t\t\tconsole.error(\n\t\t\t\t\"socka: unexpected serverResponse for fire-and-forget RPC\",\n\t\t\t\tevent.rpc,\n\t\t\t\tevent.requestId,\n\t\t\t);\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"]}
@@ -1,8 +1,8 @@
1
- import { parseWirePayload, decodeSockaWire, SockaWireError, encodeServerError, parseStandardSchema, SockaError, encodeServerResponse, encodeSockaWire, encodeServerEvent, reportSockaError } from './chunk-2FNWVCP3.js';
1
+ import { parseWirePayload, decodeSockaWire, SockaWireError, encodeServerError, parseStandardSchema, SockaError, encodeServerResponse, encodeSockaWire, encodeServerEvent, reportSockaError } from './chunk-IFIGKR3W.js';
2
2
  import { exhaustiveGuard } from '@firtoz/maybe-error';
3
3
 
4
- function isStrictUpgradeConfig(config) {
5
- return config.strictUpgradeRequest === true;
4
+ function isLooseUpgradeConfig(config) {
5
+ return "strictUpgradeRequest" in config && config.strictUpgradeRequest === false;
6
6
  }
7
7
  function broadcastSockaEventToPeers(sessions, self, event, body, excludeSelf = false) {
8
8
  for (const [ws, session] of sessions) {
@@ -16,10 +16,14 @@ var SockaWebSocketSession = class {
16
16
  this.sessions = sessions;
17
17
  this.config = config;
18
18
  this.wireFormat = config.wireFormat ?? "json";
19
- if (isStrictUpgradeConfig(config)) {
19
+ if (isLooseUpgradeConfig(config)) {
20
+ const createData = config.createData;
21
+ const create = createData ?? ((_i) => ({}));
22
+ this._data = create(init ?? {});
23
+ } else {
20
24
  if (!init?.request) {
21
25
  throw new Error(
22
- "socka: strictUpgradeRequest requires a Request on the upgrade init (e.g. Bun upgrade with `data: { \u2026, request: req }`, or Hono default sockaInit)"
26
+ "socka: strict upgrade (default) requires a Request on the upgrade init (e.g. Bun upgrade with `data: { \u2026, request: req }`, or Hono default sockaInit), or use SockaWebSocketSessionConfigLoose with strictUpgradeRequest: false"
23
27
  );
24
28
  }
25
29
  const strictInit = { request: init.request };
@@ -28,10 +32,6 @@ var SockaWebSocketSession = class {
28
32
  } else {
29
33
  this._data = {};
30
34
  }
31
- } else {
32
- const createData = config.createData;
33
- const create = createData ?? ((_i) => ({}));
34
- this._data = create(init ?? {});
35
35
  }
36
36
  }
37
37
  get data() {
@@ -156,7 +156,8 @@ var SockaWebSocketSession = class {
156
156
  if (!procedure) {
157
157
  const errorFrame = encodeServerError(
158
158
  frame.id,
159
- `Unknown call: ${rpcName}`
159
+ `Unknown call: ${rpcName}`,
160
+ { rpc: rpcName }
160
161
  );
161
162
  this.sendWireFrame(errorFrame);
162
163
  return;
@@ -167,7 +168,9 @@ var SockaWebSocketSession = class {
167
168
  validatedInput = await parseStandardSchema(procedure.input, frame.body);
168
169
  } catch (err) {
169
170
  const msg = err instanceof Error ? err.message : "Input validation failed";
170
- const errorFrame = encodeServerError(frame.id, msg);
171
+ const errorFrame = encodeServerError(frame.id, msg, {
172
+ rpc: rpcName
173
+ });
171
174
  this.sendWireFrame(errorFrame);
172
175
  return;
173
176
  }
@@ -187,18 +190,22 @@ var SockaWebSocketSession = class {
187
190
  err instanceof Error ? err.message : "Handler failed"
188
191
  );
189
192
  const errorFrame = encodeServerError(frame.id, sockaErr.message, {
193
+ rpc: rpcName,
190
194
  code: sockaErr.code,
191
195
  data: sockaErr.data
192
196
  });
193
197
  this.sendWireFrame(errorFrame);
194
198
  return;
195
199
  }
200
+ if (procedure.output === void 0) {
201
+ return;
202
+ }
196
203
  let validatedOutput;
197
204
  try {
198
205
  validatedOutput = await parseStandardSchema(procedure.output, result);
199
206
  } catch (err) {
200
207
  const msg = err instanceof Error ? err.message : "Output validation failed";
201
- const errorFrame = encodeServerError(frame.id, msg);
208
+ const errorFrame = encodeServerError(frame.id, msg, { rpc: rpcName });
202
209
  this.sendWireFrame(errorFrame);
203
210
  return;
204
211
  }
@@ -290,5 +297,5 @@ function runSockaSessionOnAttached(config, session) {
290
297
  }
291
298
 
292
299
  export { SockaWebSocketSession, broadcastSockaEventToPeers, runSockaSessionOnAttached };
293
- //# sourceMappingURL=chunk-JVLUA3Q5.js.map
294
- //# sourceMappingURL=chunk-JVLUA3Q5.js.map
300
+ //# sourceMappingURL=chunk-LVVCHLNW.js.map
301
+ //# sourceMappingURL=chunk-LVVCHLNW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server/SockaWebSocketSession.ts"],"names":[],"mappings":"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","file":"chunk-LVVCHLNW.js","sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { exhaustiveGuard } from \"@firtoz/maybe-error\";\nimport type {\n\tInferSockaPushPayload,\n\tSockaContract,\n\tSockaContractConfig,\n} from \"../core/contract\";\nimport {\n\tSockaWireError,\n\tdecodeSockaWire,\n\tencodeServerResponse,\n\tencodeServerError,\n\tencodeServerEvent,\n\ttype SockaClientRequestFrame,\n\ttype SockaWireFrame,\n} from \"../core/envelope\";\nimport {\n\tencodeSockaWire,\n\tparseWirePayload,\n\ttype SockaWireFormat,\n} from \"../core/wire-codec\";\nimport { reportSockaError } from \"../core/socka-report-error\";\nimport { parseStandardSchema } from \"../core/validate\";\nimport { SockaError } from \"../core/socka-error\";\nimport type {\n\tSockaStrictWebSocketInit,\n\tSockaWebSocketInit,\n\tSockaWebSocketSessionConfig,\n\tSockaWebSocketSessionConfigLoose,\n\tSockaWebSocketSessionConfigUnion,\n} from \"./SockaWebSocketSessionConfig\";\n\n/** Session data with no fields — `createData` may be omitted (defaults to `{}`). */\ntype EmptySockaSessionData = Record<string, never>;\n\nexport type {\n\tSockaStrictWebSocketInit,\n\tSockaWebSocketInit,\n\tSockaWebSocketSessionConfig,\n\tSockaWebSocketSessionConfigLoose,\n\tSockaWebSocketSessionConfigUnion,\n};\n\nfunction isLooseUpgradeConfig<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n): config is SockaWebSocketSessionConfigLoose<TContract, TData> {\n\treturn (\n\t\t\"strictUpgradeRequest\" in config && config.strictUpgradeRequest === false\n\t);\n}\n\n/** Session that can send a wire-level server event (already validated). */\nexport type SockaEmitCapable = {\n\temitWireEvent(event: string, body: unknown): void;\n};\n\n/**\n * Contract-typed session surface for handlers that push to clients.\n */\nexport interface SockaPushSession<\n\tTContract extends SockaContract<SockaContractConfig>,\n> {\n\temitPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t): Promise<void>;\n\tbroadcastPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t\texcludeSelf?: boolean,\n\t): Promise<void>;\n}\n\n/**\n * Broadcast a socka server event to every session in the map (optionally\n * excluding the caller). Payload must already be contract-validated.\n *\n * Exclusion uses the **WebSocket** identity (`self.websocket`), not the session\n * object reference, so the same `sessions` map can hold `SockaDoSession` while\n * `broadcastPush` runs on `this.socka` (inner {@link SockaWebSocketSession}).\n */\nexport function broadcastSockaEventToPeers(\n\tsessions: Map<WebSocket, SockaEmitCapable>,\n\tself: SockaEmitCapable & { readonly websocket: WebSocket },\n\tevent: string,\n\tbody: unknown,\n\texcludeSelf = false,\n): void {\n\tfor (const [ws, session] of sessions) {\n\t\tif (excludeSelf && ws === self.websocket) continue;\n\t\tsession.emitWireEvent(event, body);\n\t}\n}\n\n/**\n * Runtime-agnostic socka server session: standard {@link WebSocket} wire\n * dispatch without Cloudflare Durable Object APIs.\n */\nexport class SockaWebSocketSession<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData = EmptySockaSessionData,\n> implements SockaPushSession<TContract>\n{\n\tprivate readonly config: SockaWebSocketSessionConfigUnion<TContract, TData>;\n\tprivate readonly wireFormat: SockaWireFormat;\n\tprivate _data!: TData;\n\n\tpublic constructor(\n\t\tpublic readonly websocket: WebSocket,\n\t\tprotected readonly sessions: Map<\n\t\t\tWebSocket,\n\t\t\tSockaWebSocketSession<TContract, TData>\n\t\t>,\n\t\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\t\tinit?: SockaWebSocketInit,\n\t) {\n\t\tthis.config = config;\n\t\tthis.wireFormat = config.wireFormat ?? \"json\";\n\t\tif (isLooseUpgradeConfig(config)) {\n\t\t\tconst createData = config.createData as\n\t\t\t\t| ((init: SockaWebSocketInit) => TData)\n\t\t\t\t| undefined;\n\t\t\tconst create = createData ?? ((_i: SockaWebSocketInit) => ({}) as TData);\n\t\t\tthis._data = create(init ?? {});\n\t\t} else {\n\t\t\tif (!init?.request) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"socka: strict upgrade (default) requires a Request on the upgrade init (e.g. Bun upgrade with `data: { …, request: req }`, or Hono default sockaInit), or use SockaWebSocketSessionConfigLoose with strictUpgradeRequest: false\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst strictInit: SockaStrictWebSocketInit = { request: init.request };\n\t\t\tif (config.createData) {\n\t\t\t\tthis._data = config.createData(strictInit);\n\t\t\t} else {\n\t\t\t\tthis._data = {} as TData;\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic get data(): TData {\n\t\treturn this._data;\n\t}\n\n\t/**\n\t * Session data for every connection in the same {@link sessions} map (same room),\n\t * optionally excluding this socket.\n\t */\n\tpublic listPeers(options?: { excludeSelf?: boolean }): TData[] {\n\t\tconst out: TData[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(s.data);\n\t\t}\n\t\treturn out;\n\t}\n\n\t/**\n\t * Like {@link listPeers} but maps each peer {@link SockaWebSocketSession}\n\t * (e.g. when you need more than {@link #data}).\n\t */\n\tpublic listPeersWith<R>(\n\t\tmap: (session: SockaWebSocketSession<TContract, TData>) => R,\n\t\toptions?: { excludeSelf?: boolean },\n\t): R[] {\n\t\tconst out: R[] = [];\n\t\tfor (const [ws, s] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tout.push(map(s));\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Count of sessions in this room (same {@link sessions} map), optionally excluding self. */\n\tpublic peerCount(options?: { excludeSelf?: boolean }): number {\n\t\tlet n = 0;\n\t\tfor (const [ws] of this.sessions) {\n\t\t\tif (options?.excludeSelf && ws === this.websocket) continue;\n\t\t\tn += 1;\n\t\t}\n\t\treturn n;\n\t}\n\n\t/** Whether any peer sessions exist (optionally excluding self). */\n\tpublic hasPeers(options?: { excludeSelf?: boolean }): boolean {\n\t\treturn this.peerCount(options) > 0;\n\t}\n\n\t/**\n\t * Invokes the user {@link typeof SockaWebSocketSessionConfig.handleClose} callback.\n\t * Server adapters should call this when the WebSocket closes, **before** deleting\n\t * this session from the shared `sessions` map.\n\t */\n\tpublic async invokeHandleClose(): Promise<void> {\n\t\tawait this.config.handleClose(this);\n\t}\n\n\tpublic async handleRawMessage(rawMessage: string): Promise<void> {\n\t\tif (this.wireFormat !== \"json\") {\n\t\t\tawait this.reportValidationError(\n\t\t\t\tnew Error(\"socka: unexpected JSON frame in msgpack mode\"),\n\t\t\t\trawMessage,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tconst deserialize = this.config.deserializeJson ?? JSON.parse;\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = deserialize(rawMessage);\n\t\t} catch {\n\t\t\tawait this.reportValidationError(\n\t\t\t\tnew Error(\"socka: invalid JSON\"),\n\t\t\t\trawMessage,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tawait this.dispatchAfterParsed(parsed, rawMessage);\n\t}\n\n\tpublic async handleBinaryMessage(buffer: ArrayBuffer): Promise<void> {\n\t\tif (this.wireFormat !== \"msgpack\") {\n\t\t\tawait this.reportValidationError(\n\t\t\t\tnew Error(\"socka: unexpected binary frame in JSON mode\"),\n\t\t\t\tbuffer,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = parseWirePayload(buffer, \"msgpack\");\n\t\t} catch (err) {\n\t\t\tawait this.reportValidationError(\n\t\t\t\terr instanceof Error ? err : new Error(\"socka: msgpack decode failed\"),\n\t\t\t\tbuffer,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\t\tawait this.dispatchAfterParsed(parsed, buffer);\n\t}\n\n\tprivate async dispatchAfterParsed(\n\t\tparsed: unknown,\n\t\toriginalWire: unknown,\n\t): Promise<void> {\n\t\tlet decoded: ReturnType<typeof decodeSockaWire>;\n\t\ttry {\n\t\t\tdecoded = decodeSockaWire(parsed);\n\t\t} catch (err) {\n\t\t\tif (err instanceof SockaWireError) {\n\t\t\t\tawait this.reportValidationError(err, originalWire);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\n\t\tswitch (decoded.kind) {\n\t\t\tcase \"clientRequest\":\n\t\t\t\tawait this.dispatchClientRequest(decoded.frame, originalWire);\n\t\t\t\treturn;\n\t\t\tcase \"serverResponse\":\n\t\t\tcase \"serverError\":\n\t\t\tcase \"serverEvent\":\n\t\t\t\tawait this.reportValidationError(\n\t\t\t\t\tnew Error(\"socka: unexpected server-originated frame from client\"),\n\t\t\t\t\tparsed,\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\tdefault:\n\t\t\t\texhaustiveGuard(decoded);\n\t\t}\n\t}\n\n\tprivate async dispatchClientRequest(\n\t\tframe: SockaClientRequestFrame,\n\t\t_originalWire: unknown,\n\t): Promise<void> {\n\t\tconst rpcName = frame.rpc;\n\t\tconst procedure = this.config.contract.calls[rpcName];\n\n\t\tif (!procedure) {\n\t\t\tconst errorFrame = encodeServerError(\n\t\t\t\tframe.id,\n\t\t\t\t`Unknown call: ${rpcName}`,\n\t\t\t\t{ rpc: rpcName },\n\t\t\t);\n\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\treturn;\n\t\t}\n\n\t\tlet validatedInput: unknown;\n\t\tif (procedure.input) {\n\t\t\ttry {\n\t\t\t\tvalidatedInput = await parseStandardSchema(procedure.input, frame.body);\n\t\t\t} catch (err) {\n\t\t\t\tconst msg =\n\t\t\t\t\terr instanceof Error ? err.message : \"Input validation failed\";\n\t\t\t\tconst errorFrame = encodeServerError(frame.id, msg, {\n\t\t\t\t\trpc: rpcName,\n\t\t\t\t});\n\t\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\n\t\tlet result: unknown;\n\t\ttry {\n\t\t\tif (procedure.input) {\n\t\t\t\tconst handler = this.config.handlers[rpcName] as (\n\t\t\t\t\tinput: unknown,\n\t\t\t\t\ts: SockaWebSocketSession<TContract, TData>,\n\t\t\t\t) => unknown | Promise<unknown>;\n\t\t\t\tresult = await handler(validatedInput, this);\n\t\t\t} else {\n\t\t\t\tconst handler = this.config.handlers[rpcName] as (\n\t\t\t\t\ts: SockaWebSocketSession<TContract, TData>,\n\t\t\t\t) => unknown | Promise<unknown>;\n\t\t\t\tresult = await handler(this);\n\t\t\t}\n\t\t} catch (err) {\n\t\t\tthis.config.onHandlerError?.(err, rpcName, validatedInput, this);\n\t\t\tconst sockaErr =\n\t\t\t\terr instanceof SockaError\n\t\t\t\t\t? err\n\t\t\t\t\t: new SockaError(\n\t\t\t\t\t\t\terr instanceof Error ? err.message : \"Handler failed\",\n\t\t\t\t\t\t);\n\t\t\tconst errorFrame = encodeServerError(frame.id, sockaErr.message, {\n\t\t\t\trpc: rpcName,\n\t\t\t\tcode: sockaErr.code,\n\t\t\t\tdata: sockaErr.data,\n\t\t\t});\n\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\treturn;\n\t\t}\n\n\t\tif (procedure.output === undefined) {\n\t\t\treturn;\n\t\t}\n\n\t\tlet validatedOutput: unknown;\n\t\ttry {\n\t\t\tvalidatedOutput = await parseStandardSchema(procedure.output, result);\n\t\t} catch (err) {\n\t\t\tconst msg =\n\t\t\t\terr instanceof Error ? err.message : \"Output validation failed\";\n\t\t\tconst errorFrame = encodeServerError(frame.id, msg, { rpc: rpcName });\n\t\t\tthis.sendWireFrame(errorFrame);\n\t\t\treturn;\n\t\t}\n\n\t\tconst responseFrame = encodeServerResponse(\n\t\t\tframe.id,\n\t\t\trpcName,\n\t\t\tvalidatedOutput,\n\t\t);\n\t\tthis.sendWireFrame(responseFrame);\n\t}\n\n\tprivate encodeOutgoing(frame: SockaWireFrame): string | Uint8Array {\n\t\treturn encodeSockaWire(\n\t\t\tframe,\n\t\t\tthis.wireFormat,\n\t\t\tthis.config.serializeJson ?? JSON.stringify,\n\t\t);\n\t}\n\n\tprivate sendWireFrame(frame: SockaWireFrame): void {\n\t\tif (this.websocket.readyState !== WebSocket.OPEN) {\n\t\t\treturn;\n\t\t}\n\t\tconst encoded = this.encodeOutgoing(frame);\n\t\tif (typeof encoded === \"string\") {\n\t\t\tthis.websocket.send(encoded);\n\t\t\treturn;\n\t\t}\n\t\tconst copy = new Uint8Array(encoded.byteLength);\n\t\tcopy.set(encoded);\n\t\tthis.websocket.send(copy.buffer);\n\t}\n\n\t/**\n\t * Send a server event frame (wire). Prefer {@link emitPush} so\n\t * payloads are validated against the contract.\n\t */\n\tpublic emitWireEvent(event: string, body: unknown): void {\n\t\tconst frame = encodeServerEvent(event, body);\n\t\tthis.sendWireFrame(frame);\n\t}\n\n\tpublic async emitPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t): Promise<void> {\n\t\tconst schema = this.config.contract.pushes[name];\n\t\tif (!schema) {\n\t\t\tthrow new Error(`socka: unknown push ${String(name)}`);\n\t\t}\n\t\tconst validated = await parseStandardSchema(\n\t\t\tschema as StandardSchemaV1<unknown, InferSockaPushPayload<TContract, K>>,\n\t\t\tbody,\n\t\t);\n\t\tthis.emitWireEvent(name, validated);\n\t}\n\n\tpublic async broadcastPush<K extends keyof TContract[\"pushes\"] & string>(\n\t\tname: K,\n\t\tbody: InferSockaPushPayload<TContract, K>,\n\t\texcludeSelf = false,\n\t): Promise<void> {\n\t\tconst schema = this.config.contract.pushes[name];\n\t\tif (!schema) {\n\t\t\tthrow new Error(`socka: unknown push ${String(name)}`);\n\t\t}\n\t\tconst validated = await parseStandardSchema(\n\t\t\tschema as StandardSchemaV1<unknown, InferSockaPushPayload<TContract, K>>,\n\t\t\tbody,\n\t\t);\n\t\tbroadcastSockaEventToPeers(\n\t\t\tthis.sessions,\n\t\t\tthis,\n\t\t\tname,\n\t\t\tvalidated,\n\t\t\texcludeSelf,\n\t\t);\n\t}\n\n\tprivate async reportValidationError(\n\t\terror: unknown,\n\t\toriginalMessage: unknown,\n\t): Promise<void> {\n\t\tif (this.config.onValidationError) {\n\t\t\tawait this.config.onValidationError(error, originalMessage);\n\t\t} else {\n\t\t\tconsole.error(\"socka: validation error:\", error, originalMessage);\n\t\t}\n\t}\n}\n\n/**\n * Invoke {@link SockaWebSocketSessionConfig.onAttached} after the session is\n * registered in the shared map.\n */\nexport function runSockaSessionOnAttached<\n\tTContract extends SockaContract<SockaContractConfig>,\n\tTData,\n>(\n\tconfig: SockaWebSocketSessionConfigUnion<TContract, TData>,\n\tsession: SockaWebSocketSession<TContract, TData>,\n): void {\n\tconst cb = config.onAttached;\n\tif (!cb) return;\n\ttry {\n\t\tconst result = cb(session);\n\t\tvoid Promise.resolve(result).catch((error: unknown) => {\n\t\t\treportSockaError(config.reportError, {\n\t\t\t\tkind: \"serverOnAttached\",\n\t\t\t\terror,\n\t\t\t});\n\t\t});\n\t} catch (error) {\n\t\treportSockaError(config.reportError, { kind: \"serverOnAttached\", error });\n\t}\n}\n"]}