@camera.ui/transport 0.0.16 → 0.0.18
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 +3 -0
- package/dist/transports/nats.js +27 -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';
|
|
@@ -11,9 +12,11 @@ export interface NatsTransportOptions {
|
|
|
11
12
|
readonly reconnectionDelayMax?: number;
|
|
12
13
|
readonly reconnectionRandomizationFactor?: number;
|
|
13
14
|
readonly timeout?: number;
|
|
15
|
+
readonly connectTimeout?: number;
|
|
14
16
|
readonly pingInterval?: number;
|
|
15
17
|
readonly pingTimeout?: number;
|
|
16
18
|
readonly maxPingOut?: number;
|
|
19
|
+
readonly logger?: Logger;
|
|
17
20
|
}
|
|
18
21
|
export type NatsClientListener = (client: RPCClient | null) => void;
|
|
19
22
|
export interface NatsTransport extends Transport {
|
package/dist/transports/nats.js
CHANGED
|
@@ -30,9 +30,11 @@ function createNatsTransport(options = {}) {
|
|
|
30
30
|
const reconnectionDelayMax = options.reconnectionDelayMax ?? 5e3;
|
|
31
31
|
const reconnectionRandomizationFactor = options.reconnectionRandomizationFactor ?? .5;
|
|
32
32
|
const timeout = options.timeout ?? 1e4;
|
|
33
|
+
const connectTimeout = options.connectTimeout ?? 5e3;
|
|
33
34
|
const pingInterval = options.pingInterval ?? 25e3;
|
|
34
35
|
const pingTimeout = options.pingTimeout ?? 2e4;
|
|
35
36
|
const maxPingOut = options.maxPingOut ?? 1;
|
|
37
|
+
const logger = options.logger;
|
|
36
38
|
const emitter = new TransportEmitter();
|
|
37
39
|
const clientListeners = /* @__PURE__ */ new Set();
|
|
38
40
|
let proxy = null;
|
|
@@ -61,6 +63,7 @@ function createNatsTransport(options = {}) {
|
|
|
61
63
|
function markUp() {
|
|
62
64
|
if (status.up) return;
|
|
63
65
|
status = { up: true };
|
|
66
|
+
logger?.debug("markUp");
|
|
64
67
|
emitter.emit("up", void 0);
|
|
65
68
|
}
|
|
66
69
|
function markDown(reason) {
|
|
@@ -69,6 +72,7 @@ function createNatsTransport(options = {}) {
|
|
|
69
72
|
up: false,
|
|
70
73
|
lastError: reason
|
|
71
74
|
};
|
|
75
|
+
logger?.debug(`markDown (${reason})`);
|
|
72
76
|
emitter.emit("down", { reason });
|
|
73
77
|
}
|
|
74
78
|
function stopStatusMonitor() {
|
|
@@ -84,6 +88,7 @@ function createNatsTransport(options = {}) {
|
|
|
84
88
|
if (!iter) return;
|
|
85
89
|
for await (const event of iter) {
|
|
86
90
|
if (abort.signal.aborted) break;
|
|
91
|
+
logger?.debug(`status: ${event.type}${"server" in event && event.server ? ` server=${String(event.server)}` : ""}`);
|
|
87
92
|
switch (event.type) {
|
|
88
93
|
case "reconnect":
|
|
89
94
|
markUp();
|
|
@@ -114,6 +119,8 @@ function createNatsTransport(options = {}) {
|
|
|
114
119
|
markDown(message);
|
|
115
120
|
}
|
|
116
121
|
async function rebuildClient(target, epoch) {
|
|
122
|
+
logger?.debug(`rebuildClient start (epoch=${epoch})`);
|
|
123
|
+
const t0 = Date.now();
|
|
117
124
|
stopStatusMonitor();
|
|
118
125
|
pendingConnectAbort?.abort();
|
|
119
126
|
if (proxy) {
|
|
@@ -138,6 +145,7 @@ function createNatsTransport(options = {}) {
|
|
|
138
145
|
reconnectionDelayMax,
|
|
139
146
|
reconnectionRandomizationFactor,
|
|
140
147
|
timeout,
|
|
148
|
+
connectTimeout,
|
|
141
149
|
pingInterval,
|
|
142
150
|
pingTimeout,
|
|
143
151
|
maxPingOut,
|
|
@@ -152,7 +160,9 @@ function createNatsTransport(options = {}) {
|
|
|
152
160
|
next.abortClose();
|
|
153
161
|
} catch {}
|
|
154
162
|
if (disposed || epoch !== applyEpoch) return;
|
|
155
|
-
|
|
163
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
164
|
+
logger?.debug(`rebuildClient connect FAILED after ${Date.now() - t0}ms: ${msg}`);
|
|
165
|
+
markDown(msg);
|
|
156
166
|
throw err;
|
|
157
167
|
} finally {
|
|
158
168
|
if (pendingConnectAbort === connectAbort) pendingConnectAbort = null;
|
|
@@ -164,6 +174,7 @@ function createNatsTransport(options = {}) {
|
|
|
164
174
|
return;
|
|
165
175
|
}
|
|
166
176
|
proxy = next;
|
|
177
|
+
logger?.debug(`rebuildClient connected in ${Date.now() - t0}ms`);
|
|
167
178
|
markUp();
|
|
168
179
|
notifyClient();
|
|
169
180
|
startStatusMonitor(next);
|
|
@@ -237,11 +248,24 @@ function createNatsTransport(options = {}) {
|
|
|
237
248
|
}
|
|
238
249
|
async function probeAlive(timeoutMs = 5e3) {
|
|
239
250
|
if (!proxy) throw new Error("nats-transport: no client");
|
|
240
|
-
|
|
251
|
+
const t0 = Date.now();
|
|
252
|
+
try {
|
|
253
|
+
await proxy.flush(timeoutMs);
|
|
254
|
+
logger?.debug(`probeAlive OK in ${Date.now() - t0}ms`);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
logger?.debug(`probeAlive FAILED after ${Date.now() - t0}ms: ${err instanceof Error ? err.message : String(err)}`);
|
|
257
|
+
throw err;
|
|
258
|
+
}
|
|
241
259
|
}
|
|
242
260
|
async function forceReconnect() {
|
|
243
|
-
if (!proxy)
|
|
261
|
+
if (!proxy) {
|
|
262
|
+
logger?.debug("forceReconnect: no client");
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
logger?.debug(`forceReconnect: issuing (up=${status.up} stale=${proxy.isStale})`);
|
|
266
|
+
const t0 = Date.now();
|
|
244
267
|
await proxy.forceReconnect();
|
|
268
|
+
logger?.debug(`forceReconnect: returned after ${Date.now() - t0}ms (up=${status.up})`);
|
|
245
269
|
}
|
|
246
270
|
return {
|
|
247
271
|
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.18",
|
|
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.9",
|
|
60
60
|
"axios": ">=1.18.1",
|
|
61
61
|
"socket.io-client": ">=4.8.3"
|
|
62
62
|
},
|