@camera.ui/transport 0.0.15 → 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.
- package/dist/transports/http.d.ts +2 -0
- package/dist/transports/http.js +10 -4
- package/dist/transports/nats.d.ts +2 -0
- package/dist/transports/nats.js +25 -3
- package/dist/transports/socketio.d.ts +2 -0
- package/dist/transports/socketio.js +7 -0
- package/dist/transports/ws.d.ts +2 -0
- package/dist/transports/ws.js +4 -0
- package/package.json +2 -2
|
@@ -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;
|
package/dist/transports/http.js
CHANGED
|
@@ -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)
|
|
76
|
-
|
|
77
|
-
|
|
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 {
|
package/dist/transports/nats.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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)
|
|
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() {
|
package/dist/transports/ws.d.ts
CHANGED
|
@@ -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;
|
package/dist/transports/ws.js
CHANGED
|
@@ -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.
|
|
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",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@camera.ui/logger": ">=0.0.3",
|
|
59
|
-
"@camera.ui/rpc": ">=1.0.
|
|
59
|
+
"@camera.ui/rpc": ">=1.0.8",
|
|
60
60
|
"axios": ">=1.18.1",
|
|
61
61
|
"socket.io-client": ">=4.8.3"
|
|
62
62
|
},
|