@camera.ui/transport 0.0.16 → 0.0.17

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.
@@ -1,3 +1,4 @@
1
+ import { Logger } from '@camera.ui/logger';
1
2
  import { AxiosInstance, AxiosRequestConfig } from 'axios';
2
3
  import { TransportSpec } from '../core/types.js';
3
4
  import { Transport } from './contract.js';
@@ -6,6 +7,7 @@ export interface HttpTransportOptions {
6
7
  readonly timeoutMs?: number;
7
8
  readonly targetWaitMs?: number;
8
9
  readonly spec?: Partial<TransportSpec>;
10
+ readonly logger?: Logger;
9
11
  }
10
12
  export interface HttpTransport extends Transport {
11
13
  readonly client: AxiosInstance;
@@ -13,6 +13,7 @@ function createHttpTransport(options = {}) {
13
13
  };
14
14
  const apiPrefix = options.apiPrefix ?? "/api";
15
15
  const targetWaitMs = options.targetWaitMs ?? 15e3;
16
+ const logger = options.logger;
16
17
  const emitter = new TransportEmitter();
17
18
  let currentTarget = null;
18
19
  let status = { up: false };
@@ -63,6 +64,7 @@ function createHttpTransport(options = {}) {
63
64
  client.interceptors.response.use((response) => {
64
65
  if (!status.up) {
65
66
  status = { up: true };
67
+ logger?.debug("markUp");
66
68
  emitter.emit("up", void 0);
67
69
  }
68
70
  return response;
@@ -72,10 +74,13 @@ function createHttpTransport(options = {}) {
72
74
  markDown(error.message ?? "network");
73
75
  return Promise.reject(error);
74
76
  }
75
- if (error.response.status === 401) emitter.emit("auth-error", {
76
- status: 401,
77
- message: extractMessage(error)
78
- });
77
+ if (error.response.status === 401) {
78
+ logger?.debug(`auth-error 401 (${error.config?.url ?? "unknown-url"})`);
79
+ emitter.emit("auth-error", {
80
+ status: 401,
81
+ message: extractMessage(error)
82
+ });
83
+ }
79
84
  return Promise.reject(error);
80
85
  });
81
86
  function markDown(reason) {
@@ -84,6 +89,7 @@ function createHttpTransport(options = {}) {
84
89
  up: false,
85
90
  lastError: reason
86
91
  };
92
+ logger?.debug(`markDown (${reason})`);
87
93
  emitter.emit("down", { reason });
88
94
  }
89
95
  }
@@ -1,3 +1,4 @@
1
+ import { Logger } from '@camera.ui/logger';
1
2
  import { RPCClient } from '@camera.ui/rpc';
2
3
  import { TransportSpec } from '../core/types.js';
3
4
  import { Transport, Unsubscribe } from './contract.js';
@@ -14,6 +15,7 @@ export interface NatsTransportOptions {
14
15
  readonly pingInterval?: number;
15
16
  readonly pingTimeout?: number;
16
17
  readonly maxPingOut?: number;
18
+ readonly logger?: Logger;
17
19
  }
18
20
  export type NatsClientListener = (client: RPCClient | null) => void;
19
21
  export interface NatsTransport extends Transport {
@@ -33,6 +33,7 @@ function createNatsTransport(options = {}) {
33
33
  const pingInterval = options.pingInterval ?? 25e3;
34
34
  const pingTimeout = options.pingTimeout ?? 2e4;
35
35
  const maxPingOut = options.maxPingOut ?? 1;
36
+ const logger = options.logger;
36
37
  const emitter = new TransportEmitter();
37
38
  const clientListeners = /* @__PURE__ */ new Set();
38
39
  let proxy = null;
@@ -61,6 +62,7 @@ function createNatsTransport(options = {}) {
61
62
  function markUp() {
62
63
  if (status.up) return;
63
64
  status = { up: true };
65
+ logger?.debug("markUp");
64
66
  emitter.emit("up", void 0);
65
67
  }
66
68
  function markDown(reason) {
@@ -69,6 +71,7 @@ function createNatsTransport(options = {}) {
69
71
  up: false,
70
72
  lastError: reason
71
73
  };
74
+ logger?.debug(`markDown (${reason})`);
72
75
  emitter.emit("down", { reason });
73
76
  }
74
77
  function stopStatusMonitor() {
@@ -84,6 +87,7 @@ function createNatsTransport(options = {}) {
84
87
  if (!iter) return;
85
88
  for await (const event of iter) {
86
89
  if (abort.signal.aborted) break;
90
+ logger?.debug(`status: ${event.type}${"server" in event && event.server ? ` server=${String(event.server)}` : ""}`);
87
91
  switch (event.type) {
88
92
  case "reconnect":
89
93
  markUp();
@@ -114,6 +118,8 @@ function createNatsTransport(options = {}) {
114
118
  markDown(message);
115
119
  }
116
120
  async function rebuildClient(target, epoch) {
121
+ logger?.debug(`rebuildClient start (epoch=${epoch})`);
122
+ const t0 = Date.now();
117
123
  stopStatusMonitor();
118
124
  pendingConnectAbort?.abort();
119
125
  if (proxy) {
@@ -152,7 +158,9 @@ function createNatsTransport(options = {}) {
152
158
  next.abortClose();
153
159
  } catch {}
154
160
  if (disposed || epoch !== applyEpoch) return;
155
- markDown(err instanceof Error ? err.message : String(err));
161
+ const msg = err instanceof Error ? err.message : String(err);
162
+ logger?.debug(`rebuildClient connect FAILED after ${Date.now() - t0}ms: ${msg}`);
163
+ markDown(msg);
156
164
  throw err;
157
165
  } finally {
158
166
  if (pendingConnectAbort === connectAbort) pendingConnectAbort = null;
@@ -164,6 +172,7 @@ function createNatsTransport(options = {}) {
164
172
  return;
165
173
  }
166
174
  proxy = next;
175
+ logger?.debug(`rebuildClient connected in ${Date.now() - t0}ms`);
167
176
  markUp();
168
177
  notifyClient();
169
178
  startStatusMonitor(next);
@@ -237,11 +246,24 @@ function createNatsTransport(options = {}) {
237
246
  }
238
247
  async function probeAlive(timeoutMs = 5e3) {
239
248
  if (!proxy) throw new Error("nats-transport: no client");
240
- await proxy.flush(timeoutMs);
249
+ const t0 = Date.now();
250
+ try {
251
+ await proxy.flush(timeoutMs);
252
+ logger?.debug(`probeAlive OK in ${Date.now() - t0}ms`);
253
+ } catch (err) {
254
+ logger?.debug(`probeAlive FAILED after ${Date.now() - t0}ms: ${err instanceof Error ? err.message : String(err)}`);
255
+ throw err;
256
+ }
241
257
  }
242
258
  async function forceReconnect() {
243
- if (!proxy) return;
259
+ if (!proxy) {
260
+ logger?.debug("forceReconnect: no client");
261
+ return;
262
+ }
263
+ logger?.debug(`forceReconnect: issuing (up=${status.up} stale=${proxy.isStale})`);
264
+ const t0 = Date.now();
244
265
  await proxy.forceReconnect();
266
+ logger?.debug(`forceReconnect: returned after ${Date.now() - t0}ms (up=${status.up})`);
245
267
  }
246
268
  return {
247
269
  spec,
@@ -1,4 +1,5 @@
1
1
  import { Manager, Socket } from 'socket.io-client';
2
+ import { Logger } from '@camera.ui/logger';
2
3
  import { TransportSpec } from '../core/types.js';
3
4
  import { Transport } from './contract.js';
4
5
  export type { Socket };
@@ -10,6 +11,7 @@ export interface SocketioTransportOptions {
10
11
  readonly reconnectionDelay?: number;
11
12
  readonly reconnectionDelayMax?: number;
12
13
  readonly timeout?: number;
14
+ readonly logger?: Logger;
13
15
  }
14
16
  export interface SocketioTransport extends Transport {
15
17
  readonly manager: Manager | null;
@@ -18,6 +18,7 @@ function createSocketioTransport(options = {}) {
18
18
  const reconnectionDelay = options.reconnectionDelay ?? 1e3;
19
19
  const reconnectionDelayMax = options.reconnectionDelayMax ?? 5e3;
20
20
  const timeout = options.timeout ?? 2e4;
21
+ const logger = options.logger;
21
22
  const emitter = new TransportEmitter();
22
23
  const sockets = /* @__PURE__ */ new Map();
23
24
  let manager = null;
@@ -37,6 +38,7 @@ function createSocketioTransport(options = {}) {
37
38
  function markUp() {
38
39
  if (status.up) return;
39
40
  status = { up: true };
41
+ logger?.debug("markUp");
40
42
  emitter.emit("up", void 0);
41
43
  }
42
44
  function markDown(reason) {
@@ -45,6 +47,7 @@ function createSocketioTransport(options = {}) {
45
47
  up: false,
46
48
  lastError: reason
47
49
  };
50
+ logger?.debug(`markDown (${reason})`);
48
51
  emitter.emit("down", { reason });
49
52
  }
50
53
  function isAuthError(msg) {
@@ -73,6 +76,7 @@ function createSocketioTransport(options = {}) {
73
76
  socket.on("disconnect", (reason) => markDown(reason));
74
77
  socket.on("connect_error", (err) => {
75
78
  const msg = err?.message ?? "connect_error";
79
+ logger?.debug(`connect_error (main): ${msg}`);
76
80
  if (isAuthError(msg)) {
77
81
  emitter.emit("auth-error", { message: msg });
78
82
  return;
@@ -179,10 +183,13 @@ function createSocketioTransport(options = {}) {
179
183
  function reviveDeadSockets() {
180
184
  if (!currentTarget) return;
181
185
  const auth = buildAuth(currentTarget);
186
+ let revived = 0;
182
187
  for (const sock of sockets.values()) if (!sock.connected) {
183
188
  sock.auth = auth;
184
189
  sock.connect();
190
+ revived++;
185
191
  }
192
+ logger?.debug(`reviveDeadSockets: ${revived}/${sockets.size} reconnecting`);
186
193
  }
187
194
  return {
188
195
  get spec() {
@@ -1,3 +1,4 @@
1
+ import { Logger } from '@camera.ui/logger';
1
2
  import { TransportSpec } from '../core/types.js';
2
3
  import { PerResourceTransport, Unsubscribe } from './contract.js';
3
4
  export interface WsHandleSpec {
@@ -27,6 +28,7 @@ export interface WsTransportOptions {
27
28
  readonly webSocketCtor?: typeof WebSocket;
28
29
  readonly tokenParam?: string;
29
30
  readonly sessionParam?: string;
31
+ readonly logger?: Logger;
30
32
  }
31
33
  export interface WsTransport extends PerResourceTransport<WsHandle, WsHandleSpec> {
32
34
  readonly handleCount: number;
@@ -16,6 +16,7 @@ function createWsTransport(options = {}) {
16
16
  const tokenParam = options.tokenParam ?? "token";
17
17
  const sessionParam = options.sessionParam ?? "session";
18
18
  const WsCtor = options.webSocketCtor ?? (typeof WebSocket !== "undefined" ? WebSocket : void 0);
19
+ const logger = options.logger;
19
20
  const emitter = new TransportEmitter();
20
21
  const handles = /* @__PURE__ */ new Set();
21
22
  const closeDelivered = /* @__PURE__ */ new WeakSet();
@@ -45,6 +46,7 @@ function createWsTransport(options = {}) {
45
46
  function bindWs(handle, ws) {
46
47
  ws.onopen = () => {
47
48
  if (handle.disposed || handle.ws !== ws) return;
49
+ logger?.debug(`open ${handle.spec.path}`);
48
50
  if (!status.up) {
49
51
  status = { up: true };
50
52
  emitter.emit("up", void 0);
@@ -60,6 +62,7 @@ function createWsTransport(options = {}) {
60
62
  if (closeDelivered.has(ws)) return;
61
63
  closeDelivered.add(ws);
62
64
  if (!isCurrent) return;
65
+ logger?.debug(`close ${handle.spec.path} code=${event.code} clean=${event.wasClean}${event.reason ? ` reason=${event.reason}` : ""}`);
63
66
  if (isAuthCloseEvent(event)) emitter.emit("auth-error", { message: event.reason || `ws close code ${event.code}` });
64
67
  const info = {
65
68
  code: event.code,
@@ -112,6 +115,7 @@ function createWsTransport(options = {}) {
112
115
  }
113
116
  function recycleAll(code, reason) {
114
117
  const snapshot = [...handles];
118
+ logger?.debug(`recycleAll (${reason}) handles=${snapshot.length}`);
115
119
  for (const handle of snapshot) closeWs(handle, code, reason);
116
120
  if (status.up) {
117
121
  status = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camera.ui/transport",
3
- "version": "0.0.16",
3
+ "version": "0.0.17",
4
4
  "description": "camera.ui transport layer — framework-agnostic connection kernel, reducer state, pluggable transports (HTTP/WS/Socket.IO/NATS), lifecycle effects and worker bridge",
5
5
  "author": "seydx (https://github.com/cameraui/clients)",
6
6
  "type": "module",