homebridge-plugin-utils 1.35.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/README.md +130 -2
  2. package/build/eslint-plugin/README.md +164 -0
  3. package/build/eslint-plugin/config.mjs +308 -0
  4. package/build/eslint-plugin/index.mjs +7 -0
  5. package/build/eslint-plugin/plugin.mjs +46 -0
  6. package/build/eslint-plugin/rules/blank-line-after-open-brace.mjs +137 -0
  7. package/build/eslint-plugin/rules/blank-line-after-open-brace.test.mjs +112 -0
  8. package/build/eslint-plugin/rules/comment-style.mjs +190 -0
  9. package/build/eslint-plugin/rules/comment-style.test.mjs +190 -0
  10. package/build/eslint-plugin/rules/enforce-node-protocol.mjs +114 -0
  11. package/build/eslint-plugin/rules/enforce-node-protocol.test.mjs +116 -0
  12. package/build/eslint-plugin/rules/paren-comparisons-in-logical.mjs +94 -0
  13. package/build/eslint-plugin/rules/paren-comparisons-in-logical.test.mjs +107 -0
  14. package/build/eslint-plugin/rules/split-type-imports.mjs +354 -0
  15. package/build/eslint-plugin/rules/split-type-imports.test.mjs +268 -0
  16. package/build/eslint-plugin/test-setup.mjs +21 -0
  17. package/build/fs-ops.mjs +184 -0
  18. package/build/tsconfig.json +18 -3
  19. package/dist/backpressure.d.ts +94 -45
  20. package/dist/backpressure.js +229 -93
  21. package/dist/backpressure.js.map +1 -1
  22. package/dist/cli/index.d.ts +95 -0
  23. package/dist/cli/index.js +355 -0
  24. package/dist/cli/index.js.map +1 -0
  25. package/dist/clock-double.d.ts +93 -0
  26. package/dist/clock-double.js +141 -0
  27. package/dist/clock-double.js.map +1 -0
  28. package/dist/clock.d.ts +39 -0
  29. package/dist/clock.js +34 -0
  30. package/dist/clock.js.map +1 -0
  31. package/dist/eslint-plugin/config.d.mts +193 -0
  32. package/dist/eslint-plugin/index.d.mts +2 -0
  33. package/dist/eslint-plugin/plugin.d.mts +87 -0
  34. package/dist/eslint-plugin/rules/blank-line-after-open-brace.d.mts +19 -0
  35. package/dist/eslint-plugin/rules/comment-style.d.mts +15 -0
  36. package/dist/eslint-plugin/rules/enforce-node-protocol.d.mts +18 -0
  37. package/dist/eslint-plugin/rules/paren-comparisons-in-logical.d.mts +15 -0
  38. package/dist/eslint-plugin/rules/split-type-imports.d.mts +16 -0
  39. package/dist/featureOptions-docs.d.ts +96 -0
  40. package/dist/featureOptions-docs.js +193 -0
  41. package/dist/featureOptions-docs.js.map +1 -0
  42. package/dist/featureOptions.d.ts +674 -0
  43. package/dist/featureOptions.js +870 -0
  44. package/dist/featureOptions.js.map +1 -0
  45. package/dist/ffmpeg/codecs.d.ts +256 -72
  46. package/dist/ffmpeg/codecs.js +477 -262
  47. package/dist/ffmpeg/codecs.js.map +1 -1
  48. package/dist/ffmpeg/dgram-util.d.ts +46 -0
  49. package/dist/ffmpeg/dgram-util.js +38 -0
  50. package/dist/ffmpeg/dgram-util.js.map +1 -0
  51. package/dist/ffmpeg/exec.d.ts +83 -64
  52. package/dist/ffmpeg/exec.js +77 -86
  53. package/dist/ffmpeg/exec.js.map +1 -1
  54. package/dist/ffmpeg/fmp4.d.ts +49 -2
  55. package/dist/ffmpeg/fmp4.js +47 -11
  56. package/dist/ffmpeg/fmp4.js.map +1 -1
  57. package/dist/ffmpeg/hap-enums.d.ts +214 -0
  58. package/dist/ffmpeg/hap-enums.js +92 -0
  59. package/dist/ffmpeg/hap-enums.js.map +1 -0
  60. package/dist/ffmpeg/index.d.ts +16 -9
  61. package/dist/ffmpeg/index.js +6 -0
  62. package/dist/ffmpeg/index.js.map +1 -1
  63. package/dist/ffmpeg/mp4-assembler.d.ts +120 -0
  64. package/dist/ffmpeg/mp4-assembler.js +387 -0
  65. package/dist/ffmpeg/mp4-assembler.js.map +1 -0
  66. package/dist/ffmpeg/mp4-parser.d.ts +94 -0
  67. package/dist/ffmpeg/mp4-parser.js +130 -0
  68. package/dist/ffmpeg/mp4-parser.js.map +1 -0
  69. package/dist/ffmpeg/options.d.ts +62 -149
  70. package/dist/ffmpeg/options.js +608 -499
  71. package/dist/ffmpeg/options.js.map +1 -1
  72. package/dist/ffmpeg/process.d.ts +142 -96
  73. package/dist/ffmpeg/process.js +406 -278
  74. package/dist/ffmpeg/process.js.map +1 -1
  75. package/dist/ffmpeg/record.d.ts +302 -188
  76. package/dist/ffmpeg/record.js +408 -567
  77. package/dist/ffmpeg/record.js.map +1 -1
  78. package/dist/ffmpeg/recording-process-double.d.ts +144 -0
  79. package/dist/ffmpeg/recording-process-double.js +168 -0
  80. package/dist/ffmpeg/recording-process-double.js.map +1 -0
  81. package/dist/ffmpeg/rtp-parser.d.ts +70 -0
  82. package/dist/ffmpeg/rtp-parser.js +77 -0
  83. package/dist/ffmpeg/rtp-parser.js.map +1 -0
  84. package/dist/ffmpeg/rtp.d.ts +198 -141
  85. package/dist/ffmpeg/rtp.js +473 -251
  86. package/dist/ffmpeg/rtp.js.map +1 -1
  87. package/dist/ffmpeg/settings.d.ts +5 -2
  88. package/dist/ffmpeg/settings.js +20 -5
  89. package/dist/ffmpeg/settings.js.map +1 -1
  90. package/dist/ffmpeg/stream.d.ts +57 -107
  91. package/dist/ffmpeg/stream.js +121 -150
  92. package/dist/ffmpeg/stream.js.map +1 -1
  93. package/dist/formatters.d.ts +106 -0
  94. package/dist/formatters.js +174 -0
  95. package/dist/formatters.js.map +1 -0
  96. package/dist/homebridge-enums.d.ts +30 -0
  97. package/dist/homebridge-enums.js +17 -0
  98. package/dist/homebridge-enums.js.map +1 -0
  99. package/dist/index.d.ts +12 -6
  100. package/dist/index.js +7 -2
  101. package/dist/index.js.map +1 -1
  102. package/dist/logclient/auth.d.ts +114 -0
  103. package/dist/logclient/auth.js +199 -0
  104. package/dist/logclient/auth.js.map +1 -0
  105. package/dist/logclient/cli-run.d.ts +76 -0
  106. package/dist/logclient/cli-run.js +639 -0
  107. package/dist/logclient/cli-run.js.map +1 -0
  108. package/dist/logclient/cli.d.ts +3 -0
  109. package/dist/logclient/cli.js +97 -0
  110. package/dist/logclient/cli.js.map +1 -0
  111. package/dist/logclient/client.d.ts +145 -0
  112. package/dist/logclient/client.js +600 -0
  113. package/dist/logclient/client.js.map +1 -0
  114. package/dist/logclient/config.d.ts +173 -0
  115. package/dist/logclient/config.js +199 -0
  116. package/dist/logclient/config.js.map +1 -0
  117. package/dist/logclient/endpoints.d.ts +54 -0
  118. package/dist/logclient/endpoints.js +73 -0
  119. package/dist/logclient/endpoints.js.map +1 -0
  120. package/dist/logclient/filter.d.ts +45 -0
  121. package/dist/logclient/filter.js +51 -0
  122. package/dist/logclient/filter.js.map +1 -0
  123. package/dist/logclient/frame.d.ts +93 -0
  124. package/dist/logclient/frame.js +203 -0
  125. package/dist/logclient/frame.js.map +1 -0
  126. package/dist/logclient/index.d.ts +31 -0
  127. package/dist/logclient/index.js +12 -0
  128. package/dist/logclient/index.js.map +1 -0
  129. package/dist/logclient/parser.d.ts +211 -0
  130. package/dist/logclient/parser.js +393 -0
  131. package/dist/logclient/parser.js.map +1 -0
  132. package/dist/logclient/rest.d.ts +41 -0
  133. package/dist/logclient/rest.js +111 -0
  134. package/dist/logclient/rest.js.map +1 -0
  135. package/dist/logclient/settings.d.ts +15 -0
  136. package/dist/logclient/settings.js +64 -0
  137. package/dist/logclient/settings.js.map +1 -0
  138. package/dist/logclient/socket-double.d.ts +201 -0
  139. package/dist/logclient/socket-double.js +384 -0
  140. package/dist/logclient/socket-double.js.map +1 -0
  141. package/dist/logclient/socket.d.ts +257 -0
  142. package/dist/logclient/socket.js +620 -0
  143. package/dist/logclient/socket.js.map +1 -0
  144. package/dist/logclient/stitch.d.ts +83 -0
  145. package/dist/logclient/stitch.js +146 -0
  146. package/dist/logclient/stitch.js.map +1 -0
  147. package/dist/logclient/time-expression.d.ts +42 -0
  148. package/dist/logclient/time-expression.js +181 -0
  149. package/dist/logclient/time-expression.js.map +1 -0
  150. package/dist/logclient/time-window.d.ts +38 -0
  151. package/dist/logclient/time-window.js +53 -0
  152. package/dist/logclient/time-window.js.map +1 -0
  153. package/dist/logclient/types.d.ts +107 -0
  154. package/dist/logclient/types.js +6 -0
  155. package/dist/logclient/types.js.map +1 -0
  156. package/dist/mqttClient.d.ts +287 -0
  157. package/dist/mqttClient.js +433 -0
  158. package/dist/mqttClient.js.map +1 -0
  159. package/dist/service.d.ts +64 -15
  160. package/dist/service.js +93 -66
  161. package/dist/service.js.map +1 -1
  162. package/dist/ui/featureOptions.js +870 -0
  163. package/dist/ui/featureOptions.js.map +1 -0
  164. package/dist/ui/formatters.js +174 -0
  165. package/dist/ui/formatters.js.map +1 -0
  166. package/dist/ui/pluginConfigSession.mjs +141 -0
  167. package/dist/ui/webUi-featureOptions/categoryState.mjs +135 -0
  168. package/dist/ui/webUi-featureOptions/effects/keyboard.mjs +61 -0
  169. package/dist/ui/webUi-featureOptions/effects/persist.mjs +226 -0
  170. package/dist/ui/webUi-featureOptions/effects/theme.mjs +398 -0
  171. package/dist/ui/webUi-featureOptions/effects/tokens.mjs +152 -0
  172. package/dist/ui/webUi-featureOptions/rendering.mjs +431 -0
  173. package/dist/ui/webUi-featureOptions/selectors.mjs +360 -0
  174. package/dist/ui/webUi-featureOptions/state.mjs +319 -0
  175. package/dist/ui/webUi-featureOptions/store.mjs +181 -0
  176. package/dist/ui/webUi-featureOptions/utils.mjs +200 -0
  177. package/dist/ui/webUi-featureOptions/views/connectionError.mjs +152 -0
  178. package/dist/ui/webUi-featureOptions/views/deviceInfo.mjs +80 -0
  179. package/dist/ui/webUi-featureOptions/views/header.mjs +77 -0
  180. package/dist/ui/webUi-featureOptions/views/nav.mjs +327 -0
  181. package/dist/ui/webUi-featureOptions/views/options.mjs +521 -0
  182. package/dist/ui/webUi-featureOptions/views/search.mjs +395 -0
  183. package/dist/ui/webUi-featureOptions.mjs +677 -0
  184. package/dist/ui/webUi.mjs +192 -87
  185. package/dist/util.d.ts +602 -45
  186. package/dist/util.js +783 -78
  187. package/dist/util.js.map +1 -1
  188. package/package.json +33 -15
  189. package/build/eslint-rules.mjs +0 -511
  190. package/dist/featureoptions.d.ts +0 -264
  191. package/dist/featureoptions.js +0 -480
  192. package/dist/featureoptions.js.map +0 -1
  193. package/dist/mqttclient.d.ts +0 -178
  194. package/dist/mqttclient.js +0 -310
  195. package/dist/mqttclient.js.map +0 -1
  196. package/dist/ui/featureoptions.js +0 -480
  197. package/dist/ui/featureoptions.js.map +0 -1
  198. package/dist/ui/webUi-featureoptions.mjs +0 -3765
@@ -0,0 +1,64 @@
1
+ /* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * logclient/settings.ts: Scalar constants for the Homebridge UI log client.
4
+ */
5
+ /* Scalar constants only. This module is the single home for the log client's plain numbers and strings; it deliberately holds no parser data tables (the ANSI regex and
6
+ * the SGR-code-to-level map live in `parser.ts`, beside the code that owns them) and no derived policy. The reconnect-backoff curve the socket uses is deliberately the
7
+ * log client's OWN exponential shape - a 500 ms base climbing to a 5-second ceiling - rather than `defaultRetryBackoff`'s 1-second base and 30-second ceiling: a plugin
8
+ * developer restarts Homebridge frequently while iterating, so the tool should resume the tail snappily rather than lag up to half a minute behind. Both the base and
9
+ * the cap therefore live here, beside the jitter fraction, and the socket layers them into its own backoff policy (see `socket.ts`).
10
+ */
11
+ // Default host to connect to when the caller supplies none. The web UI typically runs on the same machine as Homebridge, so loopback is the sensible default.
12
+ export const DEFAULT_HOST = "localhost";
13
+ // Default homebridge-config-ui-x listen port. The web UI ships with 8581 as its out-of-the-box port; callers override via configuration when a custom port is in use.
14
+ export const DEFAULT_PORT = 8581;
15
+ // The Socket.IO namespace the live-log stream is served on. The server registers a `log` namespace; clients join it with a `40/log,` connect frame and address it on
16
+ // every subsequent message frame.
17
+ export const LOG_NAMESPACE = "log";
18
+ // The Engine.IO/Socket.IO HTTP path the WebSocket handshake is mounted at. This is the default mount point for homebridge-config-ui-x and is combined with the
19
+ // `?EIO=4&transport=websocket&token=<rawjwt>` query string to form the connect URL.
20
+ export const SOCKET_PATH = "/socket.io/";
21
+ // The pseudo-terminal column width advertised to the server in the `tail-log` request. The server sizes its `tail`/`journalctl` output to this width; 80 columns is the
22
+ // conventional terminal width and avoids the server wrapping lines at an unexpectedly narrow boundary.
23
+ export const PTY_COLUMNS = 80;
24
+ // The pseudo-terminal row count advertised to the server in the `tail-log` request. The server seeds roughly the last 500 lines regardless of this value, so the row
25
+ // count is advisory; a conventional 24-row terminal height is advertised for realism.
26
+ export const PTY_ROWS = 24;
27
+ // The base reconnect delay, in milliseconds, that anchors the log client's jittered exponential backoff curve. Kept small so a healthy session that drops momentarily
28
+ // reconnects briskly - a developer who just restarted Homebridge wants the tail back almost immediately, not after a one-second-and-climbing wait. The socket doubles
29
+ // this on each successive failed connect attempt (500, 1000, 2000, ...) until the curve reaches the ceiling at `RECONNECT_CAP_MS`.
30
+ export const RECONNECT_BASE_MS = 500;
31
+ // The maximum reconnect delay, in milliseconds, that caps the log client's exponential backoff curve. This 5-second ceiling is deliberately far lower than
32
+ // `defaultRetryBackoff`'s 30-second ceiling: a log-tailing dev tool wants to keep retrying briskly through a Homebridge restart rather than back off to a half-minute
33
+ // lag, so the curve plateaus here. Paired with `RECONNECT_BASE_MS`, the socket's backoff climbs 500, 1000, 2000, 4000, then holds at 5000 for every later attempt.
34
+ export const RECONNECT_CAP_MS = 5000;
35
+ // The fraction of a computed backoff delay used as the maximum random jitter added on top of it. Jitter spreads reconnect attempts so a fleet of clients does not
36
+ // stampede the server in lockstep after a shared outage; 0.5 means up to a 50% upward perturbation of each delay.
37
+ export const JITTER_FRACTION = 0.5;
38
+ // The additional headroom, in milliseconds, added on top of the server's advertised ping interval plus its ping timeout when sizing the liveness watchdog window. The
39
+ // server pings on a fixed cadence; the watchdog must allow for one ping interval plus the server's ping timeout plus scheduling slack before concluding the connection
40
+ // has gone silent, so this margin is added to that window to avoid a premature fire on a momentarily late ping.
41
+ export const MARGIN_MS = 5000;
42
+ // The idle gap, in milliseconds, after which a seed-served one-shot window is considered quiescent. When a `--since` (one-shot) window is served from the socket seed the
43
+ // source is the never-ending live socket, so the channel needs an explicit terminator: a re-armable quiescence watchdog re-armed on every source line. Once this much
44
+ // time elapses with no further source line (and the settle floor below has passed), the seed burst is judged complete and the one-shot ends. Kept small so a quiet log's
45
+ // one-shot exits promptly after its seed has drained.
46
+ export const SEED_QUIESCENCE_MS = 250;
47
+ // The settle floor, in milliseconds, before a seed-served one-shot window may terminate, measured from the snapshot horizon captured at channel entry. The ~500-line seed
48
+ // arrives as a fast burst, but a momentary intra-burst stall shorter than this floor must not be mistaken for the end of the burst and truncate the window. Holding the
49
+ // quiescence terminator off until the floor has passed guarantees the whole seed burst is given time to arrive before the one-shot can end, so a quiet log terminates at
50
+ // roughly this floor rather than at the much later hard cap.
51
+ export const SEED_SETTLE_MS = 1000;
52
+ // The hard cap, in milliseconds, on a seed-served one-shot window, measured from the snapshot horizon. A perpetually-chatty log keeps producing source lines, which keeps
53
+ // re-arming the quiescence terminator indefinitely, so this cap guarantees the one-shot terminates regardless. Because the seed burst is fast (the recent tail, not the
54
+ // multi-second whole-file download), the cap is comfortably longer than any legitimate seed burst and so never truncates one - it bounds only the busy-log case where
55
+ // post-horizon live lines (filtered out of the window) would otherwise keep the channel open forever.
56
+ export const SEED_WINDOW_MAX_MS = 5000;
57
+ // The maximum number of leading, non-entry lines the seed gate will drop before it gives up and admits the stream unconditionally. The server seeds a live tail from a
58
+ // BYTE offset in the log file, so the first line of content is a truncated fragment preceded by the server's `Loading logs...` / `File: ...` preamble; the gate drops
59
+ // those until the first genuine `[timestamp]` entry (see `SeedGate` in `parser.ts`). This bound is the safety valve for the pathological case of a stream whose
60
+ // timestamps are not the recognized en-US rendering (a non-default server locale): with no line ever recognized as an entry start, the gate would otherwise suppress the
61
+ // stream forever, so after this many drops it concludes the format is unrecognized and opens. It is sized well above any plausible leading-noise run yet well below the
62
+ // ~500-line seed, so it never fires for a normal log and bounds the worst-case loss to a fraction of the seed when it does.
63
+ export const SEED_GATE_MAX_SKIP = 100;
64
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/logclient/settings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;GAKG;AAEH,8JAA8J;AAC9J,MAAM,CAAC,MAAM,YAAY,GAAG,WAAW,CAAC;AAExC,sKAAsK;AACtK,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAC;AAEjC,qKAAqK;AACrK,kCAAkC;AAClC,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;AAEnC,+JAA+J;AAC/J,oFAAoF;AACpF,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AAEzC,wKAAwK;AACxK,uGAAuG;AACvG,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAE9B,qKAAqK;AACrK,sFAAsF;AACtF,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAC;AAE3B,sKAAsK;AACtK,sKAAsK;AACtK,mIAAmI;AACnI,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC,2JAA2J;AAC3J,sKAAsK;AACtK,mKAAmK;AACnK,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,kKAAkK;AAClK,kHAAkH;AAClH,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAEnC,sKAAsK;AACtK,uKAAuK;AACvK,gHAAgH;AAChH,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,CAAC;AAE9B,0KAA0K;AAC1K,sKAAsK;AACtK,yKAAyK;AACzK,sDAAsD;AACtD,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAEtC,0KAA0K;AAC1K,wKAAwK;AACxK,yKAAyK;AACzK,6DAA6D;AAC7D,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,0KAA0K;AAC1K,wKAAwK;AACxK,sKAAsK;AACtK,sGAAsG;AACtG,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEvC,uKAAuK;AACvK,sKAAsK;AACtK,gKAAgK;AAChK,yKAAyK;AACzK,wKAAwK;AACxK,4HAA4H;AAC5H,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC"}
@@ -0,0 +1,201 @@
1
+ import type { LogSocketFactory, LogSocketInit, LogSocketLike, WebSocketFactory, WebSocketLike } from "./socket.ts";
2
+ /**
3
+ * A controllable {@link WebSocketLike} test double. It captures every frame the socket under test sends and every close code it issues, and it exposes explicit emit
4
+ * methods so a test can drive the connection through its handshake, ping/pong, streaming, and teardown by hand - no real network, fully deterministic.
5
+ *
6
+ * Fidelity to the seam contract: `readyState` starts OPEN and flips to CLOSED on the first `close()` (or an inbound {@link TestWebSocket.emitClose}), so the socket's
7
+ * teardown gate (send the namespace DISCONNECT only while OPEN) behaves exactly as it would against a real connection; `send` records the frame regardless of state so a
8
+ * test can assert the exact wire sequence including any post-close send attempt.
9
+ *
10
+ * @category Testing
11
+ */
12
+ export declare class TestWebSocket implements WebSocketLike {
13
+ #private;
14
+ readonly sent: string[];
15
+ readonly closeCodes: number[];
16
+ readonly url: string;
17
+ /**
18
+ * Construct a controllable WebSocket double.
19
+ *
20
+ * @param url - The connect URL the factory was asked to build. Defaults to an empty string for tests that do not assert on the URL.
21
+ */
22
+ constructor(url?: string);
23
+ /**
24
+ * Register a listener. Mirrors the DOM `addEventListener` overloads the {@link WebSocketLike} seam declares; the matching `emit*` method dispatches to every
25
+ * registered listener of that type.
26
+ *
27
+ * @param type - The event type to listen for.
28
+ * @param listener - The listener to register.
29
+ */
30
+ addEventListener(type: "close", listener: (event: {
31
+ readonly code: number;
32
+ }) => void): void;
33
+ addEventListener(type: "error", listener: (event: unknown) => void): void;
34
+ addEventListener(type: "message", listener: (event: {
35
+ readonly data: unknown;
36
+ }) => void): void;
37
+ addEventListener(type: "open", listener: () => void): void;
38
+ /**
39
+ * Close the socket. Records the close code and flips `readyState` to CLOSED. Idempotent in the sense the seam needs: a second close simply records a second code.
40
+ *
41
+ * @param code - The close code. Defaults to 1000 (normal closure), matching the platform default.
42
+ */
43
+ close(code?: number): void;
44
+ /**
45
+ * The current readyState: {@link WEBSOCKET_OPEN} until the first close, `WEBSOCKET_CLOSED` after.
46
+ */
47
+ get readyState(): number;
48
+ /**
49
+ * Record an outbound frame. The frame is captured regardless of `readyState` so a test can assert the full wire sequence, including any send attempted after close.
50
+ *
51
+ * @param data - The frame text.
52
+ */
53
+ send(data: string): void;
54
+ /**
55
+ * Dispatch a `close` event to every registered close listener and flip `readyState` to CLOSED, simulating the peer closing the connection.
56
+ *
57
+ * @param code - The close code to deliver. Defaults to 1000.
58
+ */
59
+ emitClose(code?: number): void;
60
+ /**
61
+ * Dispatch an `error` event to every registered error listener, simulating a transport-level error.
62
+ *
63
+ * @param event - The error event payload. Defaults to an object carrying an `error` field, the shape the socket's error describer reads.
64
+ */
65
+ emitError(event?: unknown): void;
66
+ /**
67
+ * Dispatch a `message` event carrying `data` to every registered message listener, simulating an inbound frame.
68
+ *
69
+ * @param data - The frame text (or any value, to exercise the socket's non-string guard).
70
+ */
71
+ emitMessage(data: unknown): void;
72
+ /**
73
+ * Dispatch an `open` event to every registered open listener, simulating the transport-layer connection opening.
74
+ */
75
+ emitOpen(): void;
76
+ }
77
+ /**
78
+ * A {@link WebSocketFactory} double that records every `create` call (the connect URL) and returns a {@link TestWebSocket}. By default it returns a fresh socket per
79
+ * call, which is exactly what the reconnect path needs - each connect attempt gets a distinct controllable socket - and the recorded sockets are retained in order so a
80
+ * test can drive the second attempt's socket after failing the first.
81
+ *
82
+ * @category Testing
83
+ */
84
+ export declare class TestWebSocketFactory {
85
+ #private;
86
+ readonly sockets: TestWebSocket[];
87
+ readonly urls: string[];
88
+ /**
89
+ * Construct a WebSocket-factory double.
90
+ *
91
+ * @param socket - Optional fixed {@link TestWebSocket} to return from every `create`. When omitted, each `create` returns a fresh socket built with the call's URL.
92
+ */
93
+ constructor(socket?: TestWebSocket);
94
+ /**
95
+ * The {@link WebSocketFactory} function this double exposes. Bound as an arrow property so it can be passed directly as the `webSocketFactory` seam without losing
96
+ * `this`.
97
+ *
98
+ * @param url - The connect URL.
99
+ *
100
+ * @returns The recorded {@link TestWebSocket}.
101
+ */
102
+ readonly create: WebSocketFactory;
103
+ /**
104
+ * Resolve when the socket at `index` (0-based, in creation order) has been constructed, returning it - immediately when it already exists. Lets a reconnect test
105
+ * await the next connect attempt's socket deterministically instead of racing the reconnect loop's backoff against a fixed wall-clock delay.
106
+ *
107
+ * @param index - The 0-based creation-order index to await.
108
+ * @returns A promise resolving to the {@link TestWebSocket} at that index.
109
+ */
110
+ socketCreated(index: number): Promise<TestWebSocket>;
111
+ }
112
+ /**
113
+ * Construction-time configuration for a {@link TestLogSocket}. Every field defaults, so a bare `new TestLogSocket()` is usable.
114
+ *
115
+ * @property droppedLines - The value the `droppedLines` getter reports. Defaults to `0`.
116
+ * @property lines - The raw log lines `stdout()` yields, in order, before it parks awaiting abort. Defaults to an empty array.
117
+ *
118
+ * @category Testing
119
+ */
120
+ export interface TestLogSocketInit {
121
+ droppedLines?: number;
122
+ lines?: readonly string[];
123
+ }
124
+ /**
125
+ * A {@link LogSocketLike} test double. It yields caller-supplied lines from `stdout()` and then parks until aborted, mirroring how the real socket keeps a live stream
126
+ * open after the seed; `abort()` aborts a genuine {@link AbortSignal} with a real {@link HbpuAbortError} reason so a consumer's abort-reason derivations stay meaningful.
127
+ * A consumer can drive a client end to end against this double without a WebSocket.
128
+ *
129
+ * @category Testing
130
+ */
131
+ export declare class TestLogSocket implements LogSocketLike {
132
+ #private;
133
+ readonly abortCalls: unknown[];
134
+ /**
135
+ * Construct a log-socket double.
136
+ *
137
+ * @param init - Optional configuration. See {@link TestLogSocketInit}. Every field defaults, so a bare `new TestLogSocket()` is valid.
138
+ */
139
+ constructor(init?: TestLogSocketInit);
140
+ /**
141
+ * Abort the socket. Aborts the internal signal with the supplied reason, defaulting to a real `HbpuAbortError("shutdown")`, and records the (defaulted) reason. Safe to
142
+ * call more than once: the underlying signal aborts only once.
143
+ *
144
+ * @param reason - Optional abort reason. Typically an {@link HbpuAbortError}.
145
+ */
146
+ abort(reason?: unknown): void;
147
+ /**
148
+ * `AsyncDisposable` implementation. Aborts the socket (defaulting to `"shutdown"`).
149
+ *
150
+ * @returns A resolved promise once the abort has been issued.
151
+ */
152
+ [Symbol.asyncDispose](): Promise<void>;
153
+ /**
154
+ * `true` once the socket's signal has aborted.
155
+ */
156
+ get aborted(): boolean;
157
+ /**
158
+ * The configured dropped-line count.
159
+ */
160
+ get droppedLines(): number;
161
+ /**
162
+ * The composed abort signal representing this socket's lifetime. Aborts exactly once, when `abort()` is called.
163
+ */
164
+ get signal(): AbortSignal;
165
+ /**
166
+ * Yield the configured lines in order, then park until the socket aborts (mirroring a live stream that stays open after its seed). Terminates (returns) when the
167
+ * signal aborts, so a consumer iterating this generator unwinds cleanly on `abort()` or disposal.
168
+ *
169
+ * @returns An async generator yielding the configured raw log lines, then parking until abort.
170
+ */
171
+ stdout(): AsyncGenerator<string>;
172
+ }
173
+ /**
174
+ * A {@link LogSocketFactory} double that records every `create` call (the init it was passed) and returns a {@link TestLogSocket}, mirroring the create-call-recording
175
+ * discipline `TestRecordingProcessFactory` uses. By default it returns a fresh, default-configured socket per call; supply a socket to the constructor to return a single
176
+ * pre-configured instance from every `create`.
177
+ *
178
+ * @category Testing
179
+ */
180
+ export declare class TestLogSocketFactory implements LogSocketFactory {
181
+ #private;
182
+ readonly createCalls: {
183
+ init: LogSocketInit;
184
+ socket: TestLogSocket;
185
+ }[];
186
+ /**
187
+ * Construct a log-socket-factory double.
188
+ *
189
+ * @param socket - Optional pre-configured {@link TestLogSocket} to return from every `create`. When omitted, each `create` returns a fresh, default-configured socket.
190
+ */
191
+ constructor(socket?: TestLogSocket);
192
+ /**
193
+ * Record the create call and return a {@link TestLogSocket} - the constructor-supplied instance when one was given, otherwise a fresh default-configured one.
194
+ *
195
+ * @param init - The {@link LogSocketInit} the consumer passed.
196
+ *
197
+ * @returns The log-socket double.
198
+ */
199
+ create(init: LogSocketInit): LogSocketLike;
200
+ }
201
+ //# sourceMappingURL=socket-double.d.ts.map
@@ -0,0 +1,384 @@
1
+ /* Copyright(C) 2017-2026, HJD (https://github.com/hjdhjd). All rights reserved.
2
+ *
3
+ * logclient/socket-double.ts: Reusable test doubles for the WebSocket and LogSocket dependency-inversion seams.
4
+ */
5
+ var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
6
+ if (value !== null && value !== void 0) {
7
+ if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
8
+ var dispose, inner;
9
+ if (async) {
10
+ if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
11
+ dispose = value[Symbol.asyncDispose];
12
+ }
13
+ if (dispose === void 0) {
14
+ if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
15
+ dispose = value[Symbol.dispose];
16
+ if (async) inner = dispose;
17
+ }
18
+ if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
19
+ if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
20
+ env.stack.push({ value: value, dispose: dispose, async: async });
21
+ }
22
+ else if (async) {
23
+ env.stack.push({ async: true });
24
+ }
25
+ return value;
26
+ };
27
+ var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
28
+ return function (env) {
29
+ function fail(e) {
30
+ env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
31
+ env.hasError = true;
32
+ }
33
+ var r, s = 0;
34
+ function next() {
35
+ while (r = env.stack.pop()) {
36
+ try {
37
+ if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
38
+ if (r.dispose) {
39
+ var result = r.dispose.call(r.value);
40
+ if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
41
+ }
42
+ else s |= 1;
43
+ }
44
+ catch (e) {
45
+ fail(e);
46
+ }
47
+ }
48
+ if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
49
+ if (env.hasError) throw env.error;
50
+ }
51
+ return next();
52
+ };
53
+ })(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
54
+ var e = new Error(message);
55
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
56
+ });
57
+ /**
58
+ * Reusable test doubles for the log client's two socket seams.
59
+ *
60
+ * Two seams drive the live-log transport: the low-level {@link WebSocketFactory} ({@link logclient/socket!LogSocket | LogSocket} builds a {@link WebSocketLike} from
61
+ * it) and the high-level {@link LogSocketFactory} (a client builds a {@link LogSocketLike} from it). This module ships the fakes that cash both in, mirroring the
62
+ * shape of `recording-process-double.ts`:
63
+ *
64
+ * - {@link TestWebSocket} - a controllable {@link WebSocketLike} that captures every frame sent and every close code, and exposes `emitOpen` / `emitMessage` /
65
+ * `emitError` / `emitClose` so a test drives the socket state machine frame by frame without a real network. {@link TestWebSocketFactory} records every `create` call
66
+ * (the connect URL) and returns the socket.
67
+ * - {@link TestLogSocket} - a {@link LogSocketLike} that yields caller-supplied lines from `stdout()` and aborts a genuine {@link AbortSignal} on `abort()`.
68
+ * {@link TestLogSocketFactory} records every `create` call (the init it was passed) and returns the socket, so a client can be exercised without a WebSocket at all.
69
+ *
70
+ * @module
71
+ */
72
+ import { HbpuAbortError, onAbort } from "../util.js";
73
+ import { WEBSOCKET_OPEN } from "./socket.js";
74
+ // The DOM `WebSocket.CLOSED` readyState value. Named locally so the double can report a post-close readyState without depending on the platform constant, matching the
75
+ // way `socket.ts` names {@link WEBSOCKET_OPEN}.
76
+ const WEBSOCKET_CLOSED = 3;
77
+ /**
78
+ * A controllable {@link WebSocketLike} test double. It captures every frame the socket under test sends and every close code it issues, and it exposes explicit emit
79
+ * methods so a test can drive the connection through its handshake, ping/pong, streaming, and teardown by hand - no real network, fully deterministic.
80
+ *
81
+ * Fidelity to the seam contract: `readyState` starts OPEN and flips to CLOSED on the first `close()` (or an inbound {@link TestWebSocket.emitClose}), so the socket's
82
+ * teardown gate (send the namespace DISCONNECT only while OPEN) behaves exactly as it would against a real connection; `send` records the frame regardless of state so a
83
+ * test can assert the exact wire sequence including any post-close send attempt.
84
+ *
85
+ * @category Testing
86
+ */
87
+ export class TestWebSocket {
88
+ // Every frame passed to `send`, in order, so a test can assert the exact wire sequence the socket produced (the namespace connect, the `tail-log` event, each pong, the
89
+ // namespace DISCONNECT).
90
+ sent = [];
91
+ // Every close code passed to `close`, in order. A test asserts `close(1000)` is always issued on teardown.
92
+ closeCodes = [];
93
+ // The connect URL this socket was constructed with, retained for assertions on the query string (EIO version, transport, token).
94
+ url;
95
+ // The registered listeners, keyed by event type. Each emit method dispatches to every listener registered for its type, mirroring the DOM `addEventListener` fan-out.
96
+ #closeListeners = [];
97
+ #errorListeners = [];
98
+ #messageListeners = [];
99
+ #openListeners = [];
100
+ // The current readyState. Starts OPEN (the double models a connection that is already established by the time a test drives it) and flips to CLOSED on the first close.
101
+ #readyState = WEBSOCKET_OPEN;
102
+ /**
103
+ * Construct a controllable WebSocket double.
104
+ *
105
+ * @param url - The connect URL the factory was asked to build. Defaults to an empty string for tests that do not assert on the URL.
106
+ */
107
+ constructor(url = "") {
108
+ this.url = url;
109
+ }
110
+ addEventListener(type, listener) {
111
+ switch (type) {
112
+ case "close": {
113
+ this.#closeListeners.push(listener);
114
+ return;
115
+ }
116
+ case "error": {
117
+ this.#errorListeners.push(listener);
118
+ return;
119
+ }
120
+ case "message": {
121
+ this.#messageListeners.push(listener);
122
+ return;
123
+ }
124
+ case "open": {
125
+ this.#openListeners.push(listener);
126
+ return;
127
+ }
128
+ default: {
129
+ return;
130
+ }
131
+ }
132
+ }
133
+ /**
134
+ * Close the socket. Records the close code and flips `readyState` to CLOSED. Idempotent in the sense the seam needs: a second close simply records a second code.
135
+ *
136
+ * @param code - The close code. Defaults to 1000 (normal closure), matching the platform default.
137
+ */
138
+ close(code = 1000) {
139
+ this.closeCodes.push(code);
140
+ this.#readyState = WEBSOCKET_CLOSED;
141
+ }
142
+ /**
143
+ * The current readyState: {@link WEBSOCKET_OPEN} until the first close, `WEBSOCKET_CLOSED` after.
144
+ */
145
+ get readyState() {
146
+ return this.#readyState;
147
+ }
148
+ /**
149
+ * Record an outbound frame. The frame is captured regardless of `readyState` so a test can assert the full wire sequence, including any send attempted after close.
150
+ *
151
+ * @param data - The frame text.
152
+ */
153
+ send(data) {
154
+ this.sent.push(data);
155
+ }
156
+ /**
157
+ * Dispatch a `close` event to every registered close listener and flip `readyState` to CLOSED, simulating the peer closing the connection.
158
+ *
159
+ * @param code - The close code to deliver. Defaults to 1000.
160
+ */
161
+ emitClose(code = 1000) {
162
+ this.#readyState = WEBSOCKET_CLOSED;
163
+ for (const listener of [...this.#closeListeners]) {
164
+ listener({ code });
165
+ }
166
+ }
167
+ /**
168
+ * Dispatch an `error` event to every registered error listener, simulating a transport-level error.
169
+ *
170
+ * @param event - The error event payload. Defaults to an object carrying an `error` field, the shape the socket's error describer reads.
171
+ */
172
+ emitError(event = { error: new Error("Test WebSocket error.") }) {
173
+ for (const listener of [...this.#errorListeners]) {
174
+ listener(event);
175
+ }
176
+ }
177
+ /**
178
+ * Dispatch a `message` event carrying `data` to every registered message listener, simulating an inbound frame.
179
+ *
180
+ * @param data - The frame text (or any value, to exercise the socket's non-string guard).
181
+ */
182
+ emitMessage(data) {
183
+ for (const listener of [...this.#messageListeners]) {
184
+ listener({ data });
185
+ }
186
+ }
187
+ /**
188
+ * Dispatch an `open` event to every registered open listener, simulating the transport-layer connection opening.
189
+ */
190
+ emitOpen() {
191
+ for (const listener of [...this.#openListeners]) {
192
+ listener();
193
+ }
194
+ }
195
+ }
196
+ /**
197
+ * A {@link WebSocketFactory} double that records every `create` call (the connect URL) and returns a {@link TestWebSocket}. By default it returns a fresh socket per
198
+ * call, which is exactly what the reconnect path needs - each connect attempt gets a distinct controllable socket - and the recorded sockets are retained in order so a
199
+ * test can drive the second attempt's socket after failing the first.
200
+ *
201
+ * @category Testing
202
+ */
203
+ export class TestWebSocketFactory {
204
+ // Every socket handed out, in creation order, so a test can drive a specific connect attempt's socket (e.g., fail attempt 0's socket, then drive attempt 1's).
205
+ sockets = [];
206
+ // The URLs every `create` was called with, in order, for assertions on the connect URL across reconnects.
207
+ urls = [];
208
+ // An optional fixed socket to return from every `create`; when omitted, each `create` mints a fresh one.
209
+ #socket;
210
+ // Pending `socketCreated(index)` waiters, keyed by the 0-based creation-order index they await. Resolved synchronously inside `create` the moment that index is
211
+ // minted, so a reconnect test can await the ACTUAL construction of the next attempt's socket rather than racing a fixed wall-clock delay against the loop's backoff.
212
+ #createWaiters = new Map();
213
+ /**
214
+ * Construct a WebSocket-factory double.
215
+ *
216
+ * @param socket - Optional fixed {@link TestWebSocket} to return from every `create`. When omitted, each `create` returns a fresh socket built with the call's URL.
217
+ */
218
+ constructor(socket) {
219
+ this.#socket = socket;
220
+ }
221
+ /**
222
+ * The {@link WebSocketFactory} function this double exposes. Bound as an arrow property so it can be passed directly as the `webSocketFactory` seam without losing
223
+ * `this`.
224
+ *
225
+ * @param url - The connect URL.
226
+ *
227
+ * @returns The recorded {@link TestWebSocket}.
228
+ */
229
+ create = (url) => {
230
+ const socket = this.#socket ?? new TestWebSocket(url);
231
+ this.urls.push(url);
232
+ this.sockets.push(socket);
233
+ // Settle any waiter parked on the index just filled (the new length minus one), so a pending `socketCreated()` resolves the moment its socket is constructed.
234
+ const waiter = this.#createWaiters.get(this.sockets.length - 1);
235
+ if (waiter) {
236
+ this.#createWaiters.delete(this.sockets.length - 1);
237
+ waiter.resolve(socket);
238
+ }
239
+ return socket;
240
+ };
241
+ /**
242
+ * Resolve when the socket at `index` (0-based, in creation order) has been constructed, returning it - immediately when it already exists. Lets a reconnect test
243
+ * await the next connect attempt's socket deterministically instead of racing the reconnect loop's backoff against a fixed wall-clock delay.
244
+ *
245
+ * @param index - The 0-based creation-order index to await.
246
+ * @returns A promise resolving to the {@link TestWebSocket} at that index.
247
+ */
248
+ socketCreated(index) {
249
+ const existing = this.sockets[index];
250
+ if (existing) {
251
+ return Promise.resolve(existing);
252
+ }
253
+ const waiter = this.#createWaiters.get(index) ?? Promise.withResolvers();
254
+ this.#createWaiters.set(index, waiter);
255
+ return waiter.promise;
256
+ }
257
+ }
258
+ /**
259
+ * A {@link LogSocketLike} test double. It yields caller-supplied lines from `stdout()` and then parks until aborted, mirroring how the real socket keeps a live stream
260
+ * open after the seed; `abort()` aborts a genuine {@link AbortSignal} with a real {@link HbpuAbortError} reason so a consumer's abort-reason derivations stay meaningful.
261
+ * A consumer can drive a client end to end against this double without a WebSocket.
262
+ *
263
+ * @category Testing
264
+ */
265
+ export class TestLogSocket {
266
+ // The reasons every `abort(reason?)` call was invoked with, in order, for assertions. A no-argument call records the defaulted `HbpuAbortError("shutdown")`.
267
+ abortCalls = [];
268
+ // The internal controller whose signal is exposed as the socket lifetime. Owned privately so the only abort verb is `abort()`, which records the call.
269
+ #controller = new AbortController();
270
+ #droppedLines;
271
+ #lines;
272
+ /**
273
+ * Construct a log-socket double.
274
+ *
275
+ * @param init - Optional configuration. See {@link TestLogSocketInit}. Every field defaults, so a bare `new TestLogSocket()` is valid.
276
+ */
277
+ constructor(init = {}) {
278
+ this.#droppedLines = init.droppedLines ?? 0;
279
+ this.#lines = init.lines ?? [];
280
+ }
281
+ /**
282
+ * Abort the socket. Aborts the internal signal with the supplied reason, defaulting to a real `HbpuAbortError("shutdown")`, and records the (defaulted) reason. Safe to
283
+ * call more than once: the underlying signal aborts only once.
284
+ *
285
+ * @param reason - Optional abort reason. Typically an {@link HbpuAbortError}.
286
+ */
287
+ abort(reason) {
288
+ const resolvedReason = reason ?? new HbpuAbortError("shutdown");
289
+ this.abortCalls.push(resolvedReason);
290
+ this.#controller.abort(resolvedReason);
291
+ }
292
+ /**
293
+ * `AsyncDisposable` implementation. Aborts the socket (defaulting to `"shutdown"`).
294
+ *
295
+ * @returns A resolved promise once the abort has been issued.
296
+ */
297
+ async [Symbol.asyncDispose]() {
298
+ this.abort();
299
+ }
300
+ /**
301
+ * `true` once the socket's signal has aborted.
302
+ */
303
+ get aborted() {
304
+ return this.#controller.signal.aborted;
305
+ }
306
+ /**
307
+ * The configured dropped-line count.
308
+ */
309
+ get droppedLines() {
310
+ return this.#droppedLines;
311
+ }
312
+ /**
313
+ * The composed abort signal representing this socket's lifetime. Aborts exactly once, when `abort()` is called.
314
+ */
315
+ get signal() {
316
+ return this.#controller.signal;
317
+ }
318
+ /**
319
+ * Yield the configured lines in order, then park until the socket aborts (mirroring a live stream that stays open after its seed). Terminates (returns) when the
320
+ * signal aborts, so a consumer iterating this generator unwinds cleanly on `abort()` or disposal.
321
+ *
322
+ * @returns An async generator yielding the configured raw log lines, then parking until abort.
323
+ */
324
+ async *stdout() {
325
+ const env_1 = { stack: [], error: void 0, hasError: false };
326
+ try {
327
+ for (const line of this.#lines) {
328
+ if (this.#controller.signal.aborted) {
329
+ return;
330
+ }
331
+ yield line;
332
+ }
333
+ // Park until aborted. A single resolver settled by `onAbort` lets the generator suspend without a busy loop; the explicit aborted guard below returns immediately
334
+ // for an already-aborted socket, while `onAbort` settles the parked waiter when an abort arrives after parking has begun.
335
+ if (this.#controller.signal.aborted) {
336
+ return;
337
+ }
338
+ const { promise, resolve } = Promise.withResolvers();
339
+ const _registration = __addDisposableResource(env_1, onAbort(this.#controller.signal, () => resolve()), false);
340
+ await promise;
341
+ }
342
+ catch (e_1) {
343
+ env_1.error = e_1;
344
+ env_1.hasError = true;
345
+ }
346
+ finally {
347
+ __disposeResources(env_1);
348
+ }
349
+ }
350
+ }
351
+ /**
352
+ * A {@link LogSocketFactory} double that records every `create` call (the init it was passed) and returns a {@link TestLogSocket}, mirroring the create-call-recording
353
+ * discipline `TestRecordingProcessFactory` uses. By default it returns a fresh, default-configured socket per call; supply a socket to the constructor to return a single
354
+ * pre-configured instance from every `create`.
355
+ *
356
+ * @category Testing
357
+ */
358
+ export class TestLogSocketFactory {
359
+ // Every create call's init and the socket returned, in order, so a test can assert the seam was exercised with the expected init and can drive the returned socket.
360
+ createCalls = [];
361
+ // The pre-configured socket to return from every create, when supplied; otherwise each create returns a fresh default-configured socket.
362
+ #socket;
363
+ /**
364
+ * Construct a log-socket-factory double.
365
+ *
366
+ * @param socket - Optional pre-configured {@link TestLogSocket} to return from every `create`. When omitted, each `create` returns a fresh, default-configured socket.
367
+ */
368
+ constructor(socket) {
369
+ this.#socket = socket;
370
+ }
371
+ /**
372
+ * Record the create call and return a {@link TestLogSocket} - the constructor-supplied instance when one was given, otherwise a fresh default-configured one.
373
+ *
374
+ * @param init - The {@link LogSocketInit} the consumer passed.
375
+ *
376
+ * @returns The log-socket double.
377
+ */
378
+ create(init) {
379
+ const socket = this.#socket ?? new TestLogSocket();
380
+ this.createCalls.push({ init, socket });
381
+ return socket;
382
+ }
383
+ }
384
+ //# sourceMappingURL=socket-double.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket-double.js","sourceRoot":"","sources":["../../src/logclient/socket-double.ts"],"names":[],"mappings":"AAAA;;;GAGG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEH;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAErD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,uKAAuK;AACvK,gDAAgD;AAChD,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAE3B;;;;;;;;;GASG;AACH,MAAM,OAAO,aAAa;IAExB,wKAAwK;IACxK,yBAAyB;IACT,IAAI,GAAa,EAAE,CAAC;IAEpC,2GAA2G;IAC3F,UAAU,GAAa,EAAE,CAAC;IAE1C,iIAAiI;IACjH,GAAG,CAAS;IAE5B,sKAAsK;IAC7J,eAAe,GAAmD,EAAE,CAAC;IACrE,eAAe,GAAiC,EAAE,CAAC;IACnD,iBAAiB,GAAoD,EAAE,CAAC;IACxE,cAAc,GAAmB,EAAE,CAAC;IAE7C,wKAAwK;IACxK,WAAW,GAAW,cAAc,CAAC;IAErC;;;;OAIG;IACH,YAAmB,GAAG,GAAG,EAAE;QAEzB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAaM,gBAAgB,CAAC,IAA4C,EAAE,QAAiB;QAErF,QAAO,IAAI,EAAE,CAAC;YAEZ,KAAK,OAAO,CAAC,CAAC,CAAC;gBAEb,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAsD,CAAC,CAAC;gBAElF,OAAO;YACT,CAAC;YAED,KAAK,OAAO,CAAC,CAAC,CAAC;gBAEb,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,QAAoC,CAAC,CAAC;gBAEhE,OAAO;YACT,CAAC;YAED,KAAK,SAAS,CAAC,CAAC,CAAC;gBAEf,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAuD,CAAC,CAAC;gBAErF,OAAO;YACT,CAAC;YAED,KAAK,MAAM,CAAC,CAAC,CAAC;gBAEZ,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,QAAsB,CAAC,CAAC;gBAEjD,OAAO;YACT,CAAC;YAED,OAAO,CAAC,CAAC,CAAC;gBAER,OAAO;YACT,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,IAAI,GAAG,IAAI;QAEtB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,WAAW,GAAG,gBAAgB,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,IAAW,UAAU;QAEnB,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED;;;;OAIG;IACI,IAAI,CAAC,IAAY;QAEtB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,IAAI,GAAG,IAAI;QAE1B,IAAI,CAAC,WAAW,GAAG,gBAAgB,CAAC;QAEpC,KAAI,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YAEhD,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,SAAS,CAAC,QAAiB,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,uBAAuB,CAAC,EAAE;QAE7E,KAAI,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC;YAEhD,QAAQ,CAAC,KAAK,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,WAAW,CAAC,IAAa;QAE9B,KAAI,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAElD,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED;;OAEG;IACI,QAAQ;QAEb,KAAI,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;YAE/C,QAAQ,EAAE,CAAC;QACb,CAAC;IACH,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,oBAAoB;IAE/B,+JAA+J;IAC/I,OAAO,GAAoB,EAAE,CAAC;IAE9C,0GAA0G;IAC1F,IAAI,GAAa,EAAE,CAAC;IAEpC,yGAAyG;IAChG,OAAO,CAA4B;IAE5C,gKAAgK;IAChK,qKAAqK;IAC5J,cAAc,GAAG,IAAI,GAAG,EAA+C,CAAC;IAEjF;;;;OAIG;IACH,YAAmB,MAAsB;QAEvC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED;;;;;;;OAOG;IACa,MAAM,GAAqB,CAAC,GAAW,EAAiB,EAAE;QAExE,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,aAAa,CAAC,GAAG,CAAC,CAAC;QAEtD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAE1B,8JAA8J;QAC9J,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEhE,IAAG,MAAM,EAAE,CAAC;YAEV,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACpD,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;IAEF;;;;;;OAMG;IACI,aAAa,CAAC,KAAa;QAEhC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAErC,IAAG,QAAQ,EAAE,CAAC;YAEZ,OAAO,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,OAAO,CAAC,aAAa,EAAiB,CAAC;QAExF,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAEvC,OAAO,MAAM,CAAC,OAAO,CAAC;IACxB,CAAC;CACF;AAgBD;;;;;;GAMG;AACH,MAAM,OAAO,aAAa;IAExB,6JAA6J;IAC7I,UAAU,GAAc,EAAE,CAAC;IAE3C,uJAAuJ;IAC9I,WAAW,GAAG,IAAI,eAAe,EAAE,CAAC;IAEpC,aAAa,CAAS;IACtB,MAAM,CAAoB;IAEnC;;;;OAIG;IACH,YAAmB,OAA0B,EAAE;QAE7C,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,YAAY,IAAI,CAAC,CAAC;QAC5C,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,MAAgB;QAE3B,MAAM,cAAc,GAAG,MAAM,IAAI,IAAI,cAAc,CAAC,UAAU,CAAC,CAAC;QAEhE,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QACrC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IACzC,CAAC;IAED;;;;OAIG;IACI,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;QAEhC,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED;;OAEG;IACH,IAAW,OAAO;QAEhB,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,IAAW,YAAY;QAErB,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,IAAW,MAAM;QAEf,OAAO,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;IACjC,CAAC;IAED;;;;;OAKG;IACI,KAAK,CAAC,CAAC,MAAM;;;YAElB,KAAI,MAAM,IAAI,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAE9B,IAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;oBAEnC,OAAO;gBACT,CAAC;gBAED,MAAM,IAAI,CAAC;YACb,CAAC;YAED,kKAAkK;YAClK,0HAA0H;YAC1H,IAAG,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAEnC,OAAO;YACT,CAAC;YAED,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAA+B,OAAO,CAAC,aAAa,EAAE,CAAC;YAEjF,MAAM,aAAa,kCAAG,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,QAAA,CAAC;YAExE,MAAM,OAAO,CAAC;;;;;;;;;KACf;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,oBAAoB;IAE/B,oKAAoK;IACpJ,WAAW,GAAqD,EAAE,CAAC;IAEnF,yIAAyI;IAChI,OAAO,CAA4B;IAE5C;;;;OAIG;IACH,YAAmB,MAAsB;QAEvC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;IACxB,CAAC;IAED;;;;;;OAMG;IACI,MAAM,CAAC,IAAmB;QAE/B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,aAAa,EAAE,CAAC;QAEnD,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAExC,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}