@owlmeans/client-socket 0.1.18-rc.5 → 0.1.18-rc.50

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 (65) hide show
  1. package/README.md +22 -16
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/client-socket/SKILL.md +173 -8
  4. package/build/consts.d.ts +8 -0
  5. package/build/consts.d.ts.map +1 -0
  6. package/build/consts.js +17 -0
  7. package/build/consts.js.map +1 -0
  8. package/build/consts.local.d.ts +3 -0
  9. package/build/consts.local.d.ts.map +1 -0
  10. package/build/consts.local.js +2 -0
  11. package/build/consts.local.js.map +1 -0
  12. package/build/helper/types.d.ts +22 -0
  13. package/build/helper/types.d.ts.map +1 -0
  14. package/build/helper/types.js +2 -0
  15. package/build/helper/types.js.map +1 -0
  16. package/build/helper.d.ts +8 -4
  17. package/build/helper.d.ts.map +1 -1
  18. package/build/helper.js +79 -49
  19. package/build/helper.js.map +1 -1
  20. package/build/hooks.d.ts +18 -0
  21. package/build/hooks.d.ts.map +1 -0
  22. package/build/hooks.js +66 -0
  23. package/build/hooks.js.map +1 -0
  24. package/build/index.d.ts +4 -0
  25. package/build/index.d.ts.map +1 -1
  26. package/build/index.js +3 -0
  27. package/build/index.js.map +1 -1
  28. package/build/status.d.ts +30 -0
  29. package/build/status.d.ts.map +1 -0
  30. package/build/status.js +151 -0
  31. package/build/status.js.map +1 -0
  32. package/build/types.d.ts +75 -0
  33. package/build/types.d.ts.map +1 -1
  34. package/build/utils/connection.d.ts +12 -3
  35. package/build/utils/connection.d.ts.map +1 -1
  36. package/build/utils/connection.js +264 -28
  37. package/build/utils/connection.js.map +1 -1
  38. package/build/utils/consts.local.d.ts +6 -0
  39. package/build/utils/consts.local.d.ts.map +1 -0
  40. package/build/utils/consts.local.js +6 -0
  41. package/build/utils/consts.local.js.map +1 -0
  42. package/build/utils/index.d.ts +1 -0
  43. package/build/utils/index.d.ts.map +1 -1
  44. package/build/utils/types.d.ts +31 -0
  45. package/build/utils/types.d.ts.map +1 -0
  46. package/build/utils/types.js +2 -0
  47. package/build/utils/types.js.map +1 -0
  48. package/package.json +15 -11
  49. package/src/consts.local.ts +3 -0
  50. package/src/consts.ts +20 -0
  51. package/src/helper/types.ts +20 -0
  52. package/src/helper.ts +95 -53
  53. package/src/hooks.ts +75 -0
  54. package/src/index.ts +4 -0
  55. package/src/status.ts +162 -0
  56. package/src/types.ts +84 -1
  57. package/src/utils/connection.ts +256 -33
  58. package/src/utils/consts.local.ts +5 -0
  59. package/src/utils/index.ts +1 -0
  60. package/src/utils/types.ts +33 -0
  61. package/tests/context.ts +20 -0
  62. package/tests/reconnect.spec.ts +372 -0
  63. package/tests/tsconfig.json +12 -0
  64. package/tsconfig.json +1 -1
  65. package/build/.gitkeep +0 -0
package/README.md CHANGED
@@ -1,29 +1,33 @@
1
1
  # @owlmeans/client-socket
2
2
 
3
- React hook and factory for WebSocket connections via OwlMeans module routing.
3
+ React hook and factory for WebSocket connections via OwlMeans entrypoint routing.
4
4
 
5
5
  ## Overview
6
6
 
7
- - `ws(module, request?)` — creates a `Connection` by resolving the module URL and opening a WebSocket
8
- - `useWs(module, request?)` — React hook wrapping `ws()` with lifecycle management
7
+ - `socketClientHelper.ws(entrypoint, request?)` — creates a `Connection` from the entrypoint's URL and opens a WebSocket
8
+ - `useWs(entrypoint, request?)` — React hook wrapping `ws()` with lifecycle management; pass a
9
+ protocol-bound entrypoint directly
9
10
  - The returned `Connection` implements `@owlmeans/socket`'s `Connection` interface
10
11
 
11
12
  ## Installation
12
13
 
13
14
  ```bash
14
- bun add @owlmeans/client-socket
15
+ bun add @owlmeans/client-socket@^0.1.18-rc.50
15
16
  ```
16
17
 
17
18
  ## Usage
18
19
 
19
- Connect to a WebSocket module and observe events:
20
+ Connect to a WebSocket entrypoint and observe events:
20
21
 
21
22
  ```typescript
22
23
  import { useWs } from '@owlmeans/client-socket'
23
24
  import { MessageType } from '@owlmeans/socket'
25
+ import { useContext } from '@owlmeans/web-client'
26
+ import { appProtocols } from 'my-app-common'
24
27
 
25
28
  function ThinkingPanel({ storyId }: { storyId: string }) {
26
- const conn = useWs('story-thinking', { params: { id: storyId } })
29
+ const context = useContext()
30
+ const conn = useWs(context.entrypoint(appProtocols.api.storyThinking), { params: { id: storyId } })
27
31
 
28
32
  useEffect(() => {
29
33
  if (!conn) return
@@ -38,28 +42,30 @@ function ThinkingPanel({ storyId }: { storyId: string }) {
38
42
  Direct connection (non-hook):
39
43
 
40
44
  ```typescript
41
- import { ws } from '@owlmeans/client-socket'
42
- import type { ClientModule } from '@owlmeans/client-module'
45
+ import { socketClientHelper } from '@owlmeans/client-socket'
46
+ import { appProtocols } from 'my-app-common'
43
47
 
44
- const wsModule = context.module<ClientModule<string>>('story-thinking')
45
- const connection = await ws(wsModule, { params: { id: storyId } })
48
+ const wsEntrypoint = context.entrypoint(appProtocols.api.storyThinking)
49
+ const connection = await socketClientHelper.ws(wsEntrypoint, { params: { id: storyId } })
46
50
  ```
47
51
 
48
52
  ## API
49
53
 
50
- ### `ws(module, request?): Promise<Connection>`
54
+ ### `socketClientHelper.ws(entrypoint, request?): Promise<Connection>`
51
55
 
52
- Resolves the module URL, opens a WebSocket, and returns a `Connection` once the socket opens.
56
+ Builds the entrypoint's URL, opens a WebSocket, and returns a `Connection` once the socket opens.
53
57
 
54
- ### `useWs(module, request?): Connection | null`
58
+ ### `useWs(entrypoint, request?): Connection | null`
55
59
 
56
- React hook version of `ws()`. Returns `null` while connecting. Manages connection lifecycle (opens on mount, closes on unmount).
60
+ React hook version of `ws()`. Returns `null` while connecting. Manages connection lifecycle (opens
61
+ on mount, closes on unmount). Alias input is reserved for a dynamic external boundary; application
62
+ code calls `context.entrypoint(protocol)` first.
57
63
 
58
64
  ## Related Packages
59
65
 
60
66
  - [`@owlmeans/socket`](../socket) — `Connection` interface with `notify`, `observe`, `call` methods
61
67
  - [`@owlmeans/server-socket`](../server-socket) — server-side connection handler
62
- - [`@owlmeans/client-module`](../client-module) — `ClientModule` passed to `ws()`
68
+ - [`@owlmeans/client-entrypoint`](../client-entrypoint) — protocol bindings passed to `ws()`
63
69
 
64
70
  <!-- owlmeans:agent-guidance:start -->
65
71
  ## Agent guidance
@@ -69,7 +75,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
69
75
  your project's skill store (`.agents/skills/`):
70
76
 
71
77
  ```sh
72
- npx @owlmeans/agent-skills
78
+ npx @owlmeans/agent-skills@^0.1.18-rc.51
73
79
  ```
74
80
 
75
81
  The embedded files are version-matched to this package release. Do not edit them
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
3
  "package": "@owlmeans/client-socket",
4
- "version": "0.1.18-rc.0",
5
- "generatedAt": "2026-08-16T22:20:50.502Z",
4
+ "version": "0.1.18-rc.50",
5
+ "generatedAt": "2026-10-06T11:45:57.661Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: client-socket
3
- description: How to use @owlmeans/client-socket — client-side WebSocket connection helpers for browsers and native clients. Auto-invoked when importing client socket primitives.
3
+ description: How to use @owlmeans/client-socket — opening a self-restoring WebSocket Connection to a socket entrypoint from browsers and native clients, the reconnect policy and its options, the socket-status aggregator, the useWs hook and the system frames a carrier emits. Auto-invoked when importing client socket primitives or wiring a browser subscription.
4
4
  user-invocable: false
5
5
  ---
6
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
@@ -8,23 +8,188 @@ user-invocable: false
8
8
  # @owlmeans/client-socket
9
9
 
10
10
  **Layer:** Client
11
- **Install:** `"@owlmeans/client-socket": "^0.1.18-rc.0"` in `dependencies`
11
+ **Install:** `"@owlmeans/client-socket": "^0.1.18-rc.50"` in `dependencies`
12
+
13
+ The browser carrier for `@owlmeans/socket`. It supplies the four members the connection model
14
+ leaves abstract — `send`, `close`, `prepare`, `authenticate` — and hands back a plain `Connection`,
15
+ so everything a screen does with the socket is the vocabulary of that package — `observe`,
16
+ `notify`, `call`, `listen`. It also restores a dropped WebSocket on its own, on the SAME
17
+ `Connection` model, so nothing a caller registered with `observe`/`listen` needs to be
18
+ re-registered after a network blip.
12
19
 
13
20
  ## Key Exports
14
21
 
15
22
  | Export | Description |
16
23
  |--------|-------------|
17
- | `Socket` types | Client socket connection shape |
18
- | Helpers | Connect, send, subscribe to channels |
24
+ | `ws(entrypoint, request?, options?)` | Open a `Connection` to a socket entrypoint. Resolves once the FIRST attempt opens; rejects with `SocketConnectionError('lost')` if the retry budget elapses first |
25
+ | `useWs(protocol \| alias, request?, options?)` | React hook — `Connection \| null` until it opens (or gives up); re-opens on `alias`/`AUTH_QUERY`/`params` change and closes on unmount |
26
+ | `connect(address, ctx, options?)` | The same machinery with no entrypoint behind it — an `address` callback returning the URL, for an adapter or a test |
27
+ | `appendSocketStatus(ctx, alias?)` / `useSocketStatus()` / `useSocketRetry()` | The status aggregator and its retry — see below |
28
+ | `Config` / `Context` | The client config (`socket?: SocketClientSettings`) and context types this package expects |
29
+ | `ReconnectPolicy` / `SocketClientSettings` / `ConnectOptions` / `WsOptions` | The reconnect policy shape and where it can be set — config, or per-call `options` |
30
+
31
+ ## How the address is built
32
+
33
+ A socket entrypoint is **addressed, not called**: `entrypointUrl` from
34
+ `@owlmeans/client-entrypoint/utils` turns the declaration plus the asking context into the `wss://`
35
+ URL — `:params` filled in, query appended, protocol and TLS taken from the entrypoint's address —
36
+ and the connection is opened on that. Nothing is hand-concatenated, so the same declaration the
37
+ server binds is the one the client dials. It is re-resolved on EVERY attempt, first one included,
38
+ so a `beforeConnect` hook that mutates the request's query (a refreshed token, most often) reaches
39
+ every reconnect too, not just the first handshake.
40
+
41
+ Authentication rides on the query, because a WebSocket handshake carries no Authorization header a
42
+ browser can set: the token goes under `AUTH_QUERY`, and the server derives the connection's subject
43
+ from it.
44
+
45
+ **Take the authenticated hook, not this one.** `@owlmeans/client-auth` exports its own `useWs` that
46
+ wraps this one, fills `AUTH_QUERY` from `ctx.auth().token`, and refreshes it via `beforeConnect` on
47
+ every reconnect — unless the request already carried its own token, in which case that caller's
48
+ value is left alone across reconnects too. Import `useWs` from there for anything a guard
49
+ protects, and from here only for an entrypoint that is open to everyone.
50
+
51
+ The in-band auth sequence is a different thing from the query token, and only half of it is here.
52
+ `connection.auth(stage, payload)` sends the frame and resolves on the server's reply — that is the
53
+ client-initiated exchange, and it works. This carrier's own `authenticate` is a stub answering an
54
+ empty tuple, so an `Auth` frame the SERVER opens finds no stage to answer with and is dropped
55
+ without a reply: a browser can start an exchange, never answer one.
56
+
57
+ **A reconnect is a brand-new server-side connection.** If a server sets up subscriptions only
58
+ after an in-band `authenticate` frame (as this platform's own handlers do — see the `socket`
59
+ memory in `viable`), the carrier does NOT replay that frame by itself; it only reopens the pipe.
60
+ A caller that authenticates in-band must listen for `SocketSystemEvent.Reconnected` and resend it
61
+ — `viable`'s `sources/manager-web/src/lib/ws.ts` `useAuthWs` is the worked example.
19
62
 
20
63
  ## Usage
21
64
 
22
65
  ```typescript
23
- import { connectSocket } from '@owlmeans/client-socket'
24
- const socket = await connectSocket(context, { url: 'wss://api/ws' })
25
- socket.subscribe('projects', msg => console.log(msg))
66
+ // A guarded entrypoint: this useWs fills AUTH_QUERY from the current session and keeps it fresh
67
+ // across reconnects.
68
+ import { useWs } from '@owlmeans/client-auth'
69
+
70
+ // An open one: this useWs sends whatever query it is given, and nothing more.
71
+ // import { useWs } from '@owlmeans/client-socket'
72
+
73
+ const connection = useWs(app.api.project.stream, { params: { id } })
74
+
75
+ useEffect(() => {
76
+ if (connection == null) {
77
+ return
78
+ }
79
+ const stop = connection.observe<Update>('update', async msg => apply(msg.payload))
80
+
81
+ return () => stop()
82
+ }, [connection])
83
+ ```
84
+
85
+ Pass the immutable socket protocol declaration in application code. A string alias is reserved
86
+ for an adapter whose remote declaration is unavailable to import. Both hooks re-open when the
87
+ protocol, `AUTH_QUERY` value, or params change — the params are compared by content, so a fresh
88
+ object literal each render does not re-open. Both close the connection when the component unmounts,
89
+ and both answer `null` until the socket is open, so every effect that touches one guards on that.
90
+
91
+ ## Disconnects and reconnects
92
+
93
+ The carrier keeps the same `Connection` MODEL for the life of the hook/call and swaps only the
94
+ underlying `WebSocket` underneath it — every `observe`/`listen` a caller registered survives a
95
+ reconnect, because it was registered on the model, not on the socket.
96
+
97
+ **The retry policy** (`ReconnectPolicy`, defaults in `DEFAULT_RECONNECT_POLICY`):
98
+
99
+ | Field | Default | Meaning |
100
+ |---|---|---|
101
+ | `minDelay` | 200ms | First retry delay |
102
+ | `maxDelay` | 3000ms | Delay ceiling — backoff never grows past this |
103
+ | `factor` | 2 | Geometric growth per failed attempt |
104
+ | `jitter` | 0.1 | ±10% randomization on each computed delay |
105
+ | `budget` | 600000ms (10 min) | Total retry time before the carrier gives up |
106
+ | `reviveBudget` | 15000ms | Retry window `retry()` gives a lost socket (and the minimum it leaves one still retrying) — short, so a retry that cannot succeed reports `'lost'` again while someone is still looking |
107
+ | `stableAfter` | 10000ms | How long a reopened socket must stay up before the attempt count, the outage clock and the budget (back to `budget`) reset |
108
+ | `heartbeat` | 30000ms | Ping interval — runs even with `reconnect: false` |
109
+ | `pongTimeout` | 10000ms | Checked at the next heartbeat tick: the ping still unanswered this long ⇒ the carrier force-closes the socket itself (code 4000, `SOCKET_HEARTBEAT_TIMEOUT_CODE`) — the only way a silently half-open TCP connection is noticed before the OS would, minutes later. Measured from the PING, never from the last frame: a hidden tab's timers are throttled to roughly one wake-up a minute, and "nothing for a whole interval" is what throttling looks like, not what a dead socket looks like. Any inbound frame answers the ping; `server-socket` replies `{"type":"pong"}` |
110
+
111
+ Set it on `ctx.cfg.socket.reconnect` (app-wide) or per call via `options.reconnect` (which wins).
112
+ `reconnect: false` disables retries — a drop is reported once and stays dropped, the
113
+ pre-reconnect-support behaviour — while the heartbeat/liveness check above still runs. Use it for
114
+ a stateful one-shot handshake a reconnect could never resume correctly (the wallet-tunnel rely
115
+ session in `client-auth` sets it for exactly this reason).
116
+
117
+ **System frames** (`SocketSystemEvent`, from `@owlmeans/socket`) reach every `connection.listen`
118
+ subscriber:
119
+
120
+ | Event | When |
121
+ |---|---|
122
+ | `disconnected` | The socket dropped and a retry IS scheduled — `{ code }` |
123
+ | `reconnecting` | Before each retry attempt — `{ attempt, delay }` (`delay: 0` for a revive's immediate attempt) |
124
+ | `reconnected` | A retry succeeded, a revived one included — `{ attempts }` |
125
+ | `lost` | The retry budget elapsed with no success — no payload. The connection is NOT closed: it stays revivable (below) until its owner closes it |
126
+ | `close` | The connection is gone for GOOD: a client-initiated close, a terminal server code (1000/1008), or `reconnect: false` and the one attempt failed. Never follows `lost` by itself |
127
+
128
+ ```typescript
129
+ connection.listen(async message => {
130
+ const msg = message as EventMessage<unknown>
131
+ if (typeof message !== 'object' || msg.type !== 'system') return
132
+ if (msg.event === SocketSystemEvent.Reconnected) await resubscribe()
133
+ if (msg.event === SocketSystemEvent.Close) await release()
134
+ })
26
135
  ```
27
136
 
137
+ **The status aggregator.** `appendSocketStatus(ctx)` registers a shared service every
138
+ `ws()`/`useWs()` connection reports its state into; `useSocketStatus()` reads the WORST state
139
+ across all of them (`'online' | 'reconnecting' | 'lost'`). A `'lost'` connection stays in the
140
+ aggregate — that is what `@owlmeans/web-panel`'s `SocketReloadDialog` reads to put up its global
141
+ prompt (`cfg.socket.reloadDialog`) — until something revives or closes it. Calling
142
+ `appendSocketStatus` unconditionally is cheap and safe for a host package — nothing reports into
143
+ it unless something calls `ws()`/`useWs()`, so an app that never does behaves exactly as before.
144
+
145
+ **Retry.** `socketStatus().retry()` revives every `'lost'` connection and hurries every
146
+ `'reconnecting'` one (attempt at once instead of after the backoff, and at least `reviveBudget`
147
+ left) — without that, a sibling whose older budget runs out a moment later reports `'lost'` right
148
+ after the retry and puts the dialog straight back. It fires `onRetry` listeners and returns `true`
149
+ only when something was lost, so calling it freely is safe.
150
+ The service calls it by itself when the tab becomes visible, the window gains focus, or the
151
+ browser goes back `online` — a background tab is where sockets die unnoticed (throttled timers, a
152
+ sleeping machine), so the moment it is looked at again is when to retry. How a lost connection
153
+ comes back depends on whether it ever opened:
154
+
155
+ - **Opened before** — revived in place: one attempt at once, then the normal backoff within
156
+ `reviveBudget`. Same `Connection` model, and success emits `reconnected`, so the re-auth / re-sync
157
+ listeners every caller already has for a network blip cover this too. Failing again reports
158
+ `lost` again.
159
+ - **Never opened** — `ws()` already rejected and nobody holds it, so it is closed for good and
160
+ released. `useWs` re-dials by itself on `onRetry` (a fresh `ws()`, full `budget`); a direct
161
+ `ws()` caller re-opens on its own terms.
162
+ - A terminal close (1000/1008, `reconnect: false`) is never revived.
163
+
164
+ `useSocketRetry()` gives a UI `{ retry, retrying }` — `retrying` is true from any retry (the
165
+ button, or the service's own on activation) until the aggregate settles on `'online'` or `'lost'`.
166
+
167
+ `useSocketStatus()` tolerates being called before the context's own `configure()`/`init()` has
168
+ run (it defaults to `'online'` and re-attaches once `ctx.waitForInitialized()` resolves) — a
169
+ component mounted as a sibling of the router, as `SocketReloadDialog` is, renders on React's
170
+ first commit, before the router's effect has had a chance to initialize the context.
171
+
172
+ Outbound frames are stamped with `dt` and dropped rather than queued while the socket is not
173
+ `OPEN`, so a `notify` issued while the socket is down is lost rather than delivered late. Nothing
174
+ here replays events the SERVER sent while the socket was down either — a caller that cannot
175
+ tolerate a gap re-syncs its own state on `reconnected` (viable's file-watch editor re-lists files;
176
+ its thinking/slot socket relies on a separate periodic API push for the same reason).
177
+
28
178
  ## Depends On
29
179
 
30
- - `@owlmeans/socket`, `@owlmeans/client-context`
180
+ - `@owlmeans/socket` — `createBasicConnection`, `MessageType`, `SocketSystemEvent`,
181
+ `SOCKET_HEARTBEAT_TIMEOUT_CODE`, `SocketConnectionError`, the `Connection` contract
182
+ - `@owlmeans/client-entrypoint` — `entrypointUrl`, `provideRequest`
183
+ - `@owlmeans/client` — `useContext`, `useValue`; `@owlmeans/client-context` — the config type
184
+ - `@owlmeans/auth` — `AUTH_QUERY`, `AuthenticationStage`
185
+ - `@owlmeans/basic-ids` — `createIdOfLength`, for the per-connection status-tracking id
186
+ - `react` (peer)
187
+
188
+ ## Related
189
+
190
+ - `socket` — the message model every verb here belongs to, and the `SocketSystemEvent` vocabulary
191
+ - `server-socket` — the far side: guard enforcement, and what it stamps on a frame
192
+ - `client-auth` — the `useWs` that carries the token and refreshes it across reconnects;
193
+ `client-job` — `useJobFeed`, a worked subscription built on it
194
+ - `web-panel` — `SocketReloadDialog`, the global blocking prompt built on `useSocketStatus()` /
195
+ `useSocketRetry()`
@@ -0,0 +1,8 @@
1
+ import type { ReconnectPolicy } from './types.js';
2
+ /**
3
+ * 0.2s → 3s geometric backoff, retried for up to 10 minutes before the carrier gives up and
4
+ * reports the connection lost. See the client-socket skill's "Disconnects" section.
5
+ */
6
+ export declare const DEFAULT_RECONNECT_POLICY: ReconnectPolicy;
7
+ export declare const SOCKET_STATUS = "socket-status";
8
+ //# sourceMappingURL=consts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,EAAE,eAUtC,CAAA;AAED,eAAO,MAAM,aAAa,kBAAkB,CAAA"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 0.2s → 3s geometric backoff, retried for up to 10 minutes before the carrier gives up and
3
+ * reports the connection lost. See the client-socket skill's "Disconnects" section.
4
+ */
5
+ export const DEFAULT_RECONNECT_POLICY = {
6
+ minDelay: 200,
7
+ maxDelay: 3_000,
8
+ factor: 2,
9
+ jitter: 0.1,
10
+ budget: 600_000,
11
+ reviveBudget: 15_000,
12
+ stableAfter: 10_000,
13
+ heartbeat: 30_000,
14
+ pongTimeout: 10_000,
15
+ };
16
+ export const SOCKET_STATUS = 'socket-status';
17
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAoB;IACvD,QAAQ,EAAE,GAAG;IACb,QAAQ,EAAE,KAAK;IACf,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,GAAG;IACX,MAAM,EAAE,OAAO;IACf,YAAY,EAAE,MAAM;IACpB,WAAW,EAAE,MAAM;IACnB,SAAS,EAAE,MAAM;IACjB,WAAW,EAAE,MAAM;CACpB,CAAA;AAED,MAAM,CAAC,MAAM,aAAa,GAAG,eAAe,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { SocketConnectionState } from './types.js';
2
+ export declare const RANK: Record<SocketConnectionState, number>;
3
+ //# sourceMappingURL=consts.local.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.d.ts","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAA2C,CAAA"}
@@ -0,0 +1,2 @@
1
+ export const RANK = { online: 0, reconnecting: 1, lost: 2 };
2
+ //# sourceMappingURL=consts.local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.local.js","sourceRoot":"","sources":["../src/consts.local.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,IAAI,GAA0C,EAAE,MAAM,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAA"}
@@ -0,0 +1,22 @@
1
+ import type { ClientEntrypoint } from '@owlmeans/client-entrypoint';
2
+ import type { AbstractRequest } from '@owlmeans/entrypoint';
3
+ import type { Connection } from '@owlmeans/socket';
4
+ import type { ConnectOptions, Context, WsOptions } from '../types.js';
5
+ /** Opens managed sockets that restore themselves after a network failure. */
6
+ export interface SocketClientHelper {
7
+ /**
8
+ * Open a socket with no declared entrypoint behind it — the primitive `ws()` builds on, and what
9
+ * a test drives directly against a bare WebSocket server.
10
+ */
11
+ connect: (address: () => Promise<string> | string, ctx: Context, options?: ConnectOptions) => Promise<Connection>;
12
+ /**
13
+ * Open a socket through a declared entrypoint protocol, restoring it on its own after a network
14
+ * failure — see the client-socket skill's "Disconnects" section for the full policy. Resolves
15
+ * once the first attempt opens; rejects with `SocketConnectionError('lost')` if the retry budget
16
+ * elapses before that ever happens (or the single attempt fails, with `reconnect: false`).
17
+ */
18
+ ws: (module: ClientEntrypoint<string>, request?: AbstractRequest<{
19
+ token?: string;
20
+ }>, options?: WsOptions) => Promise<Connection>;
21
+ }
22
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/helper/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAErE,6EAA6E;AAC7E,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IACjH;;;;;OAKG;IACH,EAAE,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,EAAE,eAAe,CAAC;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,OAAO,CAAC,EAAE,SAAS,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;CAClI"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/helper/types.ts"],"names":[],"mappings":""}
package/build/helper.d.ts CHANGED
@@ -1,8 +1,12 @@
1
- import type { ClientEntrypoint } from '@owlmeans/client-entrypoint';
1
+ import { type ClientEntrypoint } from '@owlmeans/client-entrypoint';
2
2
  import type { AbstractRequest } from '@owlmeans/entrypoint';
3
- import type { Connection } from '@owlmeans/socket';
3
+ import { type Connection } from '@owlmeans/socket';
4
+ import type { WsOptions } from './types.js';
5
+ import type { SocketClientHelper } from './helper/types.js';
6
+ export declare const createSocketClientHelper: () => SocketClientHelper;
7
+ export declare const socketClientHelper: SocketClientHelper;
8
+ /** @deprecated compat:factory-refactor — use `socketClientHelper.ws(…)` */
4
9
  export declare const ws: (module: ClientEntrypoint<string>, request?: AbstractRequest<{
5
10
  token?: string;
6
- }>) => Promise<Connection>;
7
- export declare const useWs: (module: string | ClientEntrypoint<any>, request?: Partial<AbstractRequest<any>>) => Connection | null;
11
+ }>, options?: WsOptions) => Promise<Connection>;
8
12
  //# sourceMappingURL=helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAG3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAUlD,eAAO,MAAM,EAAE,WAAkB,gBAAgB,CAAC,MAAM,CAAC,YAAY,eAAe,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,KAAG,OAAO,CAAC,UAAU,CA0B5H,CAAA;AAED,eAAO,MAAM,KAAK,WAAY,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,YAAY,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,KAAG,UAAU,GAAG,IAyBpH,CAAA"}
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAAuB,MAAM,6BAA6B,CAAA;AACxF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,EAAE,KAAK,UAAU,EAAyB,MAAM,kBAAkB,CAAA;AAGzE,OAAO,KAAK,EAA+E,SAAS,EAAE,MAAM,YAAY,CAAA;AACxH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAK3D,eAAO,MAAM,wBAAwB,QAAO,kBA2F3C,CAAA;AAED,eAAO,MAAM,kBAAkB,oBAA6B,CAAA;AAE5D,2EAA2E;AAC3E,eAAO,MAAM,EAAE,WACL,gBAAgB,CAAC,MAAM,CAAC,YAAY,eAAe,CAAC;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,YAAY,SAAS,KACnG,OAAO,CAAC,UAAU,CAAoD,CAAA"}
package/build/helper.js CHANGED
@@ -1,54 +1,84 @@
1
- import { provideRequest } from '@owlmeans/client-entrypoint';
2
- import { EntrypointOutcome } from '@owlmeans/entrypoint';
3
- import { SocketInitializationError } from '@owlmeans/socket';
1
+ import { clientRequestHelper } from '@owlmeans/client-entrypoint';
2
+ import { SocketConnectionError } from '@owlmeans/socket';
4
3
  import { makeConnection } from './utils/connection.js';
5
4
  import { assertContext } from '@owlmeans/context';
6
- import { useContext, useValue } from '@owlmeans/client';
7
- import { AUTH_QUERY } from '@owlmeans/auth';
8
- import { urlCall } from '@owlmeans/client-entrypoint/utils';
9
- import { useEffect, useMemo } from 'react';
10
- export const ws = async (module, request) => {
11
- const ctx = assertContext(module.ctx, 'client-ws');
12
- request = request ?? provideRequest(module.getAlias(), module.getPath());
13
- const [url, state] = await urlCall({ ref: module })(request);
14
- if (state !== EntrypointOutcome.Ok) {
15
- throw new SocketInitializationError;
16
- }
17
- const socket = new WebSocket(url);
18
- return new Promise(resolve => {
19
- socket.onopen = () => {
20
- const heartbeat = setInterval(() => {
21
- if (socket.readyState === WebSocket.OPEN) {
22
- socket.send(JSON.stringify({ type: 'ping' }));
23
- }
24
- }, 30_000);
25
- socket.addEventListener('close', () => {
26
- console.info('WebSocket connection closed, clearing heartbeat interval.');
27
- clearInterval(heartbeat);
28
- });
29
- resolve(makeConnection(socket, ctx));
30
- };
31
- });
32
- };
33
- export const useWs = (module, request) => {
34
- const ctx = useContext();
35
- const mod = useMemo(() => typeof module === 'string' ? ctx.entrypoint(module) : module, [module]);
36
- const connection = useValue(async () => {
37
- const _request = provideRequest(mod.getAlias(), mod.getPath());
38
- Object.assign(_request, request);
39
- return await ws(mod, _request);
40
- }, [
41
- mod.getAlias(),
42
- request?.query?.[AUTH_QUERY],
43
- request?.params ? JSON.stringify(request.params) : undefined
44
- ]);
45
- useEffect(() => {
46
- if (connection != null) {
47
- return () => {
48
- void connection.close();
5
+ import { makeApiCallHelper } from '@owlmeans/client-entrypoint/utils';
6
+ import { createIdOfLength } from '@owlmeans/basic-ids';
7
+ import { DEFAULT_RECONNECT_POLICY, SOCKET_STATUS } from './consts.js';
8
+ export const createSocketClientHelper = () => {
9
+ const resolvePolicy = (ctx, options) => {
10
+ const requested = options?.reconnect !== undefined ? options.reconnect : ctx.cfg.socket?.reconnect;
11
+ const retry = requested !== false;
12
+ const overrides = requested === false || requested == null ? {} : requested;
13
+ return { policy: { ...DEFAULT_RECONNECT_POLICY, ...overrides }, retry };
14
+ };
15
+ const openRawSocket = async (url) => {
16
+ const socket = new WebSocket(url);
17
+ return await new Promise((resolve, reject) => {
18
+ const cleanup = () => {
19
+ socket.removeEventListener('open', onOpen);
20
+ socket.removeEventListener('error', onFail);
21
+ socket.removeEventListener('close', onFail);
49
22
  };
50
- }
51
- }, [connection]);
52
- return connection;
23
+ const onOpen = () => { cleanup(); resolve(socket); };
24
+ const onFail = () => { cleanup(); reject(new SocketConnectionError('handshake')); };
25
+ socket.addEventListener('open', onOpen);
26
+ socket.addEventListener('error', onFail);
27
+ socket.addEventListener('close', onFail);
28
+ });
29
+ };
30
+ const establish = async (ctx, policy, retry, open) => {
31
+ const statusId = createIdOfLength(8);
32
+ const status = ctx.hasService(SOCKET_STATUS)
33
+ ? ctx.socketStatus() : null;
34
+ let opened = false;
35
+ let lost = false;
36
+ const managed = makeConnection({
37
+ policy, retry, open, onStatus: state => {
38
+ lost = state === 'lost';
39
+ status?.report(statusId, state, state === 'online' ? undefined : revive);
40
+ }
41
+ });
42
+ const { connection, ready } = managed;
43
+ const baseClose = connection.close;
44
+ connection.close = async () => {
45
+ await baseClose();
46
+ status?.release(statusId);
47
+ };
48
+ const revive = () => {
49
+ if (opened || !lost) {
50
+ managed.revive();
51
+ return;
52
+ }
53
+ // Never opened, so `ws()` already rejected and nobody holds this connection to hand a revived
54
+ // socket to: drop it for good and leave re-dialing to its owner (`useWs` does, on `onRetry`).
55
+ void baseClose();
56
+ status?.release(statusId);
57
+ };
58
+ // A rejection leaves this id reporting `'lost'` rather than releasing it — there is no
59
+ // `Connection` to hand back for a caller to close, and the aggregate is what puts
60
+ // `SocketReloadDialog` up. It is released by `revive` above, on the next `retry()`.
61
+ await ready;
62
+ opened = true;
63
+ return connection;
64
+ };
65
+ const connect = async (address, ctx, options) => {
66
+ const { policy, retry } = resolvePolicy(ctx, options);
67
+ return await establish(ctx, policy, retry, async () => openRawSocket(await address()));
68
+ };
69
+ const ws = async (module, request, options) => {
70
+ const ctx = assertContext(module.ctx, 'client-ws');
71
+ request = request ?? clientRequestHelper.provideRequest(module.alias, module.path());
72
+ const { policy, retry } = resolvePolicy(ctx, options);
73
+ return await establish(ctx, policy, retry, async () => {
74
+ await options?.beforeConnect?.(request);
75
+ const url = await makeApiCallHelper({ ref: module }).entrypointUrl(request);
76
+ return await openRawSocket(url);
77
+ });
78
+ };
79
+ return { connect, ws };
53
80
  };
81
+ export const socketClientHelper = createSocketClientHelper();
82
+ /** @deprecated compat:factory-refactor — use `socketClientHelper.ws(…)` */
83
+ export const ws = (module, request, options) => socketClientHelper.ws(module, request, options);
54
84
  //# sourceMappingURL=helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"helper.js","sourceRoot":"","sources":["../src/helper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { AbstractRequest } from '@owlmeans/entrypoint';
2
+ import type { Connection } from '@owlmeans/socket';
3
+ import type { EntrypointReference } from '@owlmeans/context';
4
+ import type { WsOptions } from './types.js';
5
+ /**
6
+ * Open a socket through a declared entrypoint protocol.
7
+ *
8
+ * String aliases remain an adapter input for dynamically addressed integrations, while
9
+ * application callers pass their immutable protocol declaration and receive its bound client
10
+ * entrypoint here at the transport boundary.
11
+ *
12
+ * Re-opens on its own after a network drop (see `ws()`). `null` until the first attempt opens,
13
+ * and again once the retry budget elapses without one ever opening — dialed afresh on the next
14
+ * `SocketStatusService.retry()`. A screen that must tell those two apart reads
15
+ * `useSocketStatus()` (from `./status.js`) alongside this.
16
+ */
17
+ export declare const useWs: (module: EntrypointReference | string, request?: Partial<AbstractRequest<any>>, options?: WsOptions) => Connection | null;
18
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAClD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,KAAK,EAA6B,SAAS,EAAE,MAAM,YAAY,CAAA;AAOtE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,WACR,mBAAmB,GAAG,MAAM,YAAY,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,YAAY,SAAS,KACjG,UAAU,GAAG,IAiDf,CAAA"}