@owlmeans/client-socket 0.1.18-rc.47 → 0.1.18-rc.49

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 (58) hide show
  1. package/README.md +6 -6
  2. package/agent-meta/manifest.json +2 -2
  3. package/agent-meta/skills/client-socket/SKILL.md +1 -1
  4. package/build/consts.d.ts +1 -0
  5. package/build/consts.d.ts.map +1 -1
  6. package/build/consts.js +1 -0
  7. package/build/consts.js.map +1 -1
  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 +7 -28
  17. package/build/helper.d.ts.map +1 -1
  18. package/build/helper.js +74 -141
  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 +2 -0
  25. package/build/index.d.ts.map +1 -1
  26. package/build/index.js +1 -0
  27. package/build/index.js.map +1 -1
  28. package/build/status.d.ts +0 -1
  29. package/build/status.d.ts.map +1 -1
  30. package/build/status.js +2 -2
  31. package/build/status.js.map +1 -1
  32. package/build/utils/connection.d.ts +1 -30
  33. package/build/utils/connection.d.ts.map +1 -1
  34. package/build/utils/connection.js +1 -5
  35. package/build/utils/connection.js.map +1 -1
  36. package/build/utils/consts.local.d.ts +6 -0
  37. package/build/utils/consts.local.d.ts.map +1 -0
  38. package/build/utils/consts.local.js +6 -0
  39. package/build/utils/consts.local.js.map +1 -0
  40. package/build/utils/index.d.ts +1 -0
  41. package/build/utils/index.d.ts.map +1 -1
  42. package/build/utils/types.d.ts +31 -0
  43. package/build/utils/types.d.ts.map +1 -0
  44. package/build/utils/types.js +2 -0
  45. package/build/utils/types.js.map +1 -0
  46. package/package.json +13 -13
  47. package/src/consts.local.ts +3 -0
  48. package/src/consts.ts +2 -0
  49. package/src/helper/types.ts +20 -0
  50. package/src/helper.ts +90 -160
  51. package/src/hooks.ts +75 -0
  52. package/src/index.ts +2 -0
  53. package/src/status.ts +2 -4
  54. package/src/utils/connection.ts +3 -43
  55. package/src/utils/consts.local.ts +5 -0
  56. package/src/utils/index.ts +1 -0
  57. package/src/utils/types.ts +33 -0
  58. package/tests/reconnect.spec.ts +11 -11
package/README.md CHANGED
@@ -4,7 +4,7 @@ React hook and factory for WebSocket connections via OwlMeans entrypoint routing
4
4
 
5
5
  ## Overview
6
6
 
7
- - `ws(entrypoint, request?)` — creates a `Connection` from the entrypoint's URL and opens a WebSocket
7
+ - `socketClientHelper.ws(entrypoint, request?)` — creates a `Connection` from the entrypoint's URL and opens a WebSocket
8
8
  - `useWs(entrypoint, request?)` — React hook wrapping `ws()` with lifecycle management; pass a
9
9
  protocol-bound entrypoint directly
10
10
  - The returned `Connection` implements `@owlmeans/socket`'s `Connection` interface
@@ -12,7 +12,7 @@ React hook and factory for WebSocket connections via OwlMeans entrypoint routing
12
12
  ## Installation
13
13
 
14
14
  ```bash
15
- bun add @owlmeans/client-socket@^0.1.18-rc.47
15
+ bun add @owlmeans/client-socket@^0.1.18-rc.49
16
16
  ```
17
17
 
18
18
  ## Usage
@@ -42,16 +42,16 @@ function ThinkingPanel({ storyId }: { storyId: string }) {
42
42
  Direct connection (non-hook):
43
43
 
44
44
  ```typescript
45
- import { ws } from '@owlmeans/client-socket'
45
+ import { socketClientHelper } from '@owlmeans/client-socket'
46
46
  import { appProtocols } from 'my-app-common'
47
47
 
48
48
  const wsEntrypoint = context.entrypoint(appProtocols.api.storyThinking)
49
- const connection = await ws(wsEntrypoint, { params: { id: storyId } })
49
+ const connection = await socketClientHelper.ws(wsEntrypoint, { params: { id: storyId } })
50
50
  ```
51
51
 
52
52
  ## API
53
53
 
54
- ### `ws(entrypoint, request?): Promise<Connection>`
54
+ ### `socketClientHelper.ws(entrypoint, request?): Promise<Connection>`
55
55
 
56
56
  Builds the entrypoint's URL, opens a WebSocket, and returns a `Connection` once the socket opens.
57
57
 
@@ -75,7 +75,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
75
75
  your project's skill store (`.agents/skills/`):
76
76
 
77
77
  ```sh
78
- npx @owlmeans/agent-skills@^0.1.18-rc.47
78
+ npx @owlmeans/agent-skills@^0.1.18-rc.49
79
79
  ```
80
80
 
81
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.47",
5
- "generatedAt": "2026-10-03T20:05:07.590Z",
4
+ "version": "0.1.18-rc.49",
5
+ "generatedAt": "2026-10-05T21:03:06.385Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
8
  {
@@ -8,7 +8,7 @@ user-invocable: false
8
8
  # @owlmeans/client-socket
9
9
 
10
10
  **Layer:** Client
11
- **Install:** `"@owlmeans/client-socket": "^0.1.18-rc.47"` in `dependencies`
11
+ **Install:** `"@owlmeans/client-socket": "^0.1.18-rc.49"` in `dependencies`
12
12
 
13
13
  The browser carrier for `@owlmeans/socket`. It supplies the four members the connection model
14
14
  leaves abstract — `send`, `close`, `prepare`, `authenticate` — and hands back a plain `Connection`,
package/build/consts.d.ts CHANGED
@@ -4,4 +4,5 @@ import type { ReconnectPolicy } from './types.js';
4
4
  * reports the connection lost. See the client-socket skill's "Disconnects" section.
5
5
  */
6
6
  export declare const DEFAULT_RECONNECT_POLICY: ReconnectPolicy;
7
+ export declare const SOCKET_STATUS = "socket-status";
7
8
  //# sourceMappingURL=consts.d.ts.map
@@ -1 +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"}
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"}
package/build/consts.js CHANGED
@@ -13,4 +13,5 @@ export const DEFAULT_RECONNECT_POLICY = {
13
13
  heartbeat: 30_000,
14
14
  pongTimeout: 10_000,
15
15
  };
16
+ export const SOCKET_STATUS = 'socket-status';
16
17
  //# sourceMappingURL=consts.js.map
@@ -1 +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"}
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,33 +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';
4
- import type { EntrypointReference } from '@owlmeans/context';
5
- import type { Context, ConnectOptions, WsOptions } from './types.js';
6
- /**
7
- * Open a socket with no declared entrypoint behind it — the primitive `ws()` builds on, and what
8
- * a test drives directly against a bare WebSocket server.
9
- */
10
- export declare const connect: (address: () => Promise<string> | string, ctx: Context, options?: ConnectOptions) => Promise<Connection>;
11
- /**
12
- * Open a socket through a declared entrypoint protocol, restoring it on its own after a network
13
- * failure — see the client-socket skill's "Disconnects" section for the full policy. Resolves
14
- * once the first attempt opens; rejects with `SocketConnectionError('lost')` if the retry budget
15
- * elapses before that ever happens (or the single attempt fails, with `reconnect: false`).
16
- */
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(…)` */
17
9
  export declare const ws: (module: ClientEntrypoint<string>, request?: AbstractRequest<{
18
10
  token?: string;
19
11
  }>, options?: WsOptions) => Promise<Connection>;
20
- /**
21
- * Open a socket through a declared entrypoint protocol.
22
- *
23
- * String aliases remain an adapter input for dynamically addressed integrations, while
24
- * application callers pass their immutable protocol declaration and receive its bound client
25
- * entrypoint here at the transport boundary.
26
- *
27
- * Re-opens on its own after a network drop (see `ws()`). `null` until the first attempt opens,
28
- * and again once the retry budget elapses without one ever opening — dialed afresh on the next
29
- * `SocketStatusService.retry()`. A screen that must tell those two apart reads
30
- * `useSocketStatus()` (from `./status.js`) alongside this.
31
- */
32
- export declare const useWs: (module: EntrypointReference | string, request?: Partial<AbstractRequest<any>>, options?: WsOptions) => Connection | null;
33
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;AAE3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAIlD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,KAAK,EAAU,OAAO,EAAE,cAAc,EAA8C,SAAS,EAAE,MAAM,YAAY,CAAA;AA6ExH;;;GAGG;AACH,eAAO,MAAM,OAAO,YACT,MAAM,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,OAAO,OAAO,YAAY,cAAc,KAC9E,OAAO,CAAC,UAAU,CAGpB,CAAA;AAED;;;;;GAKG;AACH,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,CAUpB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,KAAK,WACR,mBAAmB,GAAG,MAAM,YAAY,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,YAAY,SAAS,KACjG,UAAU,GAAG,IAiDf,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,151 +1,84 @@
1
- import { provideRequest } from '@owlmeans/client-entrypoint';
1
+ import { clientRequestHelper } from '@owlmeans/client-entrypoint';
2
2
  import { SocketConnectionError } from '@owlmeans/socket';
3
3
  import { makeConnection } from './utils/connection.js';
4
4
  import { assertContext } from '@owlmeans/context';
5
- import { useContext, useValue } from '@owlmeans/client';
6
- import { AUTH_QUERY } from '@owlmeans/auth';
7
- import { entrypointUrl } from '@owlmeans/client-entrypoint/utils';
8
- import { useEffect, useMemo, useState } from 'react';
5
+ import { makeApiCallHelper } from '@owlmeans/client-entrypoint/utils';
9
6
  import { createIdOfLength } from '@owlmeans/basic-ids';
10
- import { DEFAULT_RECONNECT_POLICY } from './consts.js';
11
- import { SOCKET_STATUS } from './status.js';
12
- const resolvePolicy = (ctx, options) => {
13
- const requested = options?.reconnect !== undefined ? options.reconnect : ctx.cfg.socket?.reconnect;
14
- const retry = requested !== false;
15
- const overrides = requested === false || requested == null ? {} : requested;
16
- return { policy: { ...DEFAULT_RECONNECT_POLICY, ...overrides }, retry };
17
- };
18
- const openRawSocket = async (url) => {
19
- const socket = new WebSocket(url);
20
- return await new Promise((resolve, reject) => {
21
- const cleanup = () => {
22
- socket.removeEventListener('open', onOpen);
23
- socket.removeEventListener('error', onFail);
24
- socket.removeEventListener('close', onFail);
25
- };
26
- const onOpen = () => { cleanup(); resolve(socket); };
27
- const onFail = () => { cleanup(); reject(new SocketConnectionError('handshake')); };
28
- socket.addEventListener('open', onOpen);
29
- socket.addEventListener('error', onFail);
30
- socket.addEventListener('close', onFail);
31
- });
32
- };
33
- const establish = async (ctx, policy, retry, open) => {
34
- const statusId = createIdOfLength(8);
35
- const status = ctx.hasService(SOCKET_STATUS)
36
- ? ctx.socketStatus() : null;
37
- let opened = false;
38
- let lost = false;
39
- const managed = makeConnection({
40
- policy, retry, open, onStatus: state => {
41
- lost = state === 'lost';
42
- status?.report(statusId, state, state === 'online' ? undefined : revive);
43
- }
44
- });
45
- const { connection, ready } = managed;
46
- const baseClose = connection.close;
47
- connection.close = async () => {
48
- await baseClose();
49
- status?.release(statusId);
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 };
50
14
  };
51
- const revive = () => {
52
- if (opened || !lost) {
53
- managed.revive();
54
- return;
55
- }
56
- // Never opened, so `ws()` already rejected and nobody holds this connection to hand a revived
57
- // socket to: drop it for good and leave re-dialing to its owner (`useWs` does, on `onRetry`).
58
- void baseClose();
59
- status?.release(statusId);
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);
22
+ };
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
+ });
60
29
  };
61
- // A rejection leaves this id reporting `'lost'` rather than releasing it — there is no
62
- // `Connection` to hand back for a caller to close, and the aggregate is what puts
63
- // `SocketReloadDialog` up. It is released by `revive` above, on the next `retry()`.
64
- await ready;
65
- opened = true;
66
- return connection;
67
- };
68
- /**
69
- * Open a socket with no declared entrypoint behind it — the primitive `ws()` builds on, and what
70
- * a test drives directly against a bare WebSocket server.
71
- */
72
- export const connect = async (address, ctx, options) => {
73
- const { policy, retry } = resolvePolicy(ctx, options);
74
- return await establish(ctx, policy, retry, async () => openRawSocket(await address()));
75
- };
76
- /**
77
- * Open a socket through a declared entrypoint protocol, restoring it on its own after a network
78
- * failure — see the client-socket skill's "Disconnects" section for the full policy. Resolves
79
- * once the first attempt opens; rejects with `SocketConnectionError('lost')` if the retry budget
80
- * elapses before that ever happens (or the single attempt fails, with `reconnect: false`).
81
- */
82
- export const ws = async (module, request, options) => {
83
- const ctx = assertContext(module.ctx, 'client-ws');
84
- request = request ?? provideRequest(module.alias, module.path());
85
- const { policy, retry } = resolvePolicy(ctx, options);
86
- return await establish(ctx, policy, retry, async () => {
87
- await options?.beforeConnect?.(request);
88
- const url = await entrypointUrl({ ref: module }, request);
89
- return await openRawSocket(url);
90
- });
91
- };
92
- /**
93
- * Open a socket through a declared entrypoint protocol.
94
- *
95
- * String aliases remain an adapter input for dynamically addressed integrations, while
96
- * application callers pass their immutable protocol declaration and receive its bound client
97
- * entrypoint here at the transport boundary.
98
- *
99
- * Re-opens on its own after a network drop (see `ws()`). `null` until the first attempt opens,
100
- * and again once the retry budget elapses without one ever opening — dialed afresh on the next
101
- * `SocketStatusService.retry()`. A screen that must tell those two apart reads
102
- * `useSocketStatus()` (from `./status.js`) alongside this.
103
- */
104
- export const useWs = (module, request, options) => {
105
- const ctx = useContext();
106
- const mod = useMemo(() => ctx.entrypoint(module), [module]);
107
- const [failed, setFailed] = useState(false);
108
- const [dial, setDial] = useState(0);
109
- const connection = useValue(async (cancel) => {
110
- const _request = provideRequest(mod.alias, mod.path());
111
- Object.assign(_request, request);
112
- try {
113
- const conn = await ws(mod, _request, options);
114
- if (cancel?.current) {
115
- // The component unmounted while the handshake (or its retries) were still in flight —
116
- // there is no `useEffect` cleanup coming for this value, so close it here instead of
117
- // leaving a live socket and its retry timers attached to nothing.
118
- void conn.close();
119
- return null;
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);
120
40
  }
121
- return conn;
122
- }
123
- catch {
124
- if (!cancel?.current)
125
- setFailed(true);
126
- return null;
127
- }
128
- }, [
129
- mod.alias,
130
- request?.query?.[AUTH_QUERY],
131
- request?.params ? JSON.stringify(request.params) : undefined,
132
- dial
133
- ]);
134
- useEffect(() => {
135
- if (!failed || !ctx.hasService(SOCKET_STATUS))
136
- return;
137
- return ctx.socketStatus().onRetry(() => {
138
- setFailed(false);
139
- setDial(n => n + 1);
140
41
  });
141
- }, [failed]);
142
- useEffect(() => {
143
- if (connection != null) {
144
- return () => {
145
- void connection.close();
146
- };
147
- }
148
- }, [connection]);
149
- return connection;
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 };
150
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);
151
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"}
package/build/hooks.js ADDED
@@ -0,0 +1,66 @@
1
+ import { clientRequestHelper } from '@owlmeans/client-entrypoint';
2
+ import { useContext, useValue } from '@owlmeans/client';
3
+ import { AUTH_QUERY } from '@owlmeans/auth';
4
+ import { useEffect, useMemo, useState } from 'react';
5
+ import { SOCKET_STATUS } from './consts.js';
6
+ import { socketClientHelper } from './helper.js';
7
+ /**
8
+ * Open a socket through a declared entrypoint protocol.
9
+ *
10
+ * String aliases remain an adapter input for dynamically addressed integrations, while
11
+ * application callers pass their immutable protocol declaration and receive its bound client
12
+ * entrypoint here at the transport boundary.
13
+ *
14
+ * Re-opens on its own after a network drop (see `ws()`). `null` until the first attempt opens,
15
+ * and again once the retry budget elapses without one ever opening — dialed afresh on the next
16
+ * `SocketStatusService.retry()`. A screen that must tell those two apart reads
17
+ * `useSocketStatus()` (from `./status.js`) alongside this.
18
+ */
19
+ export const useWs = (module, request, options) => {
20
+ const ctx = useContext();
21
+ const mod = useMemo(() => ctx.entrypoint(module), [module]);
22
+ const [failed, setFailed] = useState(false);
23
+ const [dial, setDial] = useState(0);
24
+ const connection = useValue(async (cancel) => {
25
+ const _request = clientRequestHelper.provideRequest(mod.alias, mod.path());
26
+ Object.assign(_request, request);
27
+ try {
28
+ const conn = await socketClientHelper.ws(mod, _request, options);
29
+ if (cancel?.current) {
30
+ // The component unmounted while the handshake (or its retries) were still in flight —
31
+ // there is no `useEffect` cleanup coming for this value, so close it here instead of
32
+ // leaving a live socket and its retry timers attached to nothing.
33
+ void conn.close();
34
+ return null;
35
+ }
36
+ return conn;
37
+ }
38
+ catch {
39
+ if (!cancel?.current)
40
+ setFailed(true);
41
+ return null;
42
+ }
43
+ }, [
44
+ mod.alias,
45
+ request?.query?.[AUTH_QUERY],
46
+ request?.params ? JSON.stringify(request.params) : undefined,
47
+ dial
48
+ ]);
49
+ useEffect(() => {
50
+ if (!failed || !ctx.hasService(SOCKET_STATUS))
51
+ return;
52
+ return ctx.socketStatus().onRetry(() => {
53
+ setFailed(false);
54
+ setDial(n => n + 1);
55
+ });
56
+ }, [failed]);
57
+ useEffect(() => {
58
+ if (connection != null) {
59
+ return () => {
60
+ void connection.close();
61
+ };
62
+ }
63
+ }, [connection]);
64
+ return connection;
65
+ };
66
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,mBAAmB,EAAE,MAAM,6BAA6B,CAAA;AAKxF,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,MAAoC,EAAE,OAAuC,EAAE,OAAmB,EAC/E,EAAE;IACrB,MAAM,GAAG,GAAG,UAAU,EAAE,CAAA;IACxB,MAAM,GAAG,GAAG,OAAO,CACjB,GAAG,EAAE,CAAC,GAAG,CAAC,UAAU,CAAmB,MAAM,CAAC,EAC9C,CAAC,MAAM,CAAC,CACT,CAAA;IACD,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC3C,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAA;IACnC,MAAM,UAAU,GAAG,QAAQ,CAAoB,KAAK,EAAE,MAAM,EAAE,EAAE;QAC9D,MAAM,QAAQ,GAAG,mBAAmB,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;QAC1E,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAChC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;YAChE,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,sFAAsF;gBACtF,qFAAqF;gBACrF,kEAAkE;gBAClE,KAAK,IAAI,CAAC,KAAK,EAAE,CAAA;gBACjB,OAAO,IAAI,CAAA;YACb,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,MAAM,EAAE,OAAO;gBAAE,SAAS,CAAC,IAAI,CAAC,CAAA;YACrC,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC,EAAE;QACD,GAAG,CAAC,KAAK;QACT,OAAO,EAAE,KAAK,EAAE,CAAC,UAAU,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;QAC5D,IAAI;KACL,CAAC,CAAA;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC;YAAE,OAAM;QACrD,OAAQ,GAA4C,CAAC,YAAY,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YAC/E,SAAS,CAAC,KAAK,CAAC,CAAA;YAChB,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;QACrB,CAAC,CAAC,CAAA;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IAEZ,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;YACvB,OAAO,GAAG,EAAE;gBACV,KAAK,UAAU,CAAC,KAAK,EAAE,CAAA;YACzB,CAAC,CAAA;QACH,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAA;IAEhB,OAAO,UAAU,CAAA;AACnB,CAAC,CAAA"}
package/build/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export type * from './types.js';
2
+ export type * from './helper/types.js';
2
3
  export * from './consts.js';
3
4
  export * from './helper.js';
5
+ export * from './hooks.js';
4
6
  export * from './status.js';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,mBAAmB,mBAAmB,CAAA;AACtC,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA"}
package/build/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './consts.js';
2
2
  export * from './helper.js';
3
+ export * from './hooks.js';
3
4
  export * from './status.js';
4
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA"}
package/build/status.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import type { Config, Context, SocketConnectionState, SocketStatusService, SocketStatusServiceAppend } from './types.js';
2
- export declare const SOCKET_STATUS = "socket-status";
3
2
  /**
4
3
  * Aggregates the coarse health of every `ws()`/`useWs()` connection an app has open into one
5
4
  * worst-of-all state. A page with several sockets (thinking stream, file watch, notifications…)
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,yBAAyB,EACvF,MAAM,YAAY,CAAA;AAEnB,eAAO,MAAM,aAAa,kBAAkB,CAAA;AAI5C;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,WAAW,MAAM,KAAmB,mBA6DzE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,OACxF,CAAC,UAAS,MAAM,KACpB,CAAC,GAAG,yBAON,CAAA;AAgCD;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAO,qBAWlC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAcvE,CAAA"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,MAAM,EAAE,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,yBAAyB,EACvF,MAAM,YAAY,CAAA;AAInB;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,WAAW,MAAM,KAAmB,mBA6DzE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,OACxF,CAAC,UAAS,MAAM,KACpB,CAAC,GAAG,yBAON,CAAA;AAgCD;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAO,qBAWlC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAO;IAAE,KAAK,EAAE,MAAM,IAAI,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAcvE,CAAA"}
package/build/status.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { createService } from '@owlmeans/context';
2
2
  import { useContext } from '@owlmeans/client';
3
3
  import { useCallback, useEffect, useState } from 'react';
4
- export const SOCKET_STATUS = 'socket-status';
5
- const RANK = { online: 0, reconnecting: 1, lost: 2 };
4
+ import { RANK } from './consts.local.js';
5
+ import { SOCKET_STATUS } from './consts.js';
6
6
  /**
7
7
  * Aggregates the coarse health of every `ws()`/`useWs()` connection an app has open into one
8
8
  * worst-of-all state. A page with several sockets (thinking stream, file watch, notifications…)