@camera.ui/transport 0.0.21 → 0.0.23
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/closeCodes-ClWJVOpq.js +14 -0
- package/dist/{contract-d-0gfY8v.js → contract-dt1m8U-o.js} +1 -6
- package/dist/core/kernel.d.ts +1 -0
- package/dist/core/types.d.ts +5 -24
- package/dist/effects/backgroundProbe.d.ts +20 -0
- package/dist/effects/degradedRecovery.d.ts +16 -0
- package/dist/effects/networkChange.d.ts +1 -0
- package/dist/effects/probeLoop.d.ts +2 -0
- package/dist/effects/transportSync.d.ts +2 -0
- package/dist/index.d.ts +15 -10
- package/dist/index.js +542 -353
- package/dist/journal.d.ts +19 -0
- package/dist/race.d.ts +2 -0
- package/dist/signal.d.ts +30 -0
- package/dist/testing/fakeTransport.d.ts +3 -0
- package/dist/testing.js +15 -1
- package/dist/transports/closeCodes.d.ts +8 -0
- package/dist/transports/contract.d.ts +1 -1
- package/dist/transports/http.js +1 -1
- package/dist/transports/nats.d.ts +2 -1
- package/dist/transports/nats.js +46 -6
- package/dist/transports/socketio.d.ts +3 -1
- package/dist/transports/socketio.js +36 -26
- package/dist/transports/ws.js +6 -5
- package/dist/worker.js +0 -1
- package/package.json +3 -3
- package/dist/core/resolver.d.ts +0 -5
- package/dist/effects/reconnectWatchdog.d.ts +0 -10
- package/dist/effects/transportWatchdog.d.ts +0 -16
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface JournalEntry {
|
|
2
|
+
readonly seq: number;
|
|
3
|
+
readonly t: number;
|
|
4
|
+
readonly scope: string;
|
|
5
|
+
readonly msg: string;
|
|
6
|
+
readonly detail?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface JournalOptions {
|
|
9
|
+
readonly capacity?: number;
|
|
10
|
+
readonly now?: () => number;
|
|
11
|
+
}
|
|
12
|
+
export interface ConnectionJournal {
|
|
13
|
+
record(scope: string, msg: string, detail?: unknown): void;
|
|
14
|
+
list(): JournalEntry[];
|
|
15
|
+
clear(): void;
|
|
16
|
+
subscribe(listener: (entry: JournalEntry) => void): () => void;
|
|
17
|
+
exportText(): string;
|
|
18
|
+
}
|
|
19
|
+
export declare function createConnectionJournal(options?: JournalOptions): ConnectionJournal;
|
package/dist/race.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export interface RaceFirstOptions {
|
|
|
9
9
|
readonly timeoutByMode?: TimeoutByModeFn;
|
|
10
10
|
readonly shortCircuit?: (err: unknown) => boolean;
|
|
11
11
|
readonly parentSignal?: AbortSignal;
|
|
12
|
+
readonly prefer?: (endpoint: Endpoint) => boolean;
|
|
13
|
+
readonly preferGraceMs?: number;
|
|
12
14
|
}
|
|
13
15
|
export interface RaceFirstResult<T> {
|
|
14
16
|
readonly endpoint: Endpoint;
|
package/dist/signal.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Kernel } from './core/kernel.js';
|
|
2
|
+
import { TransportId } from './core/types.js';
|
|
3
|
+
import { Transport } from './transports/contract.js';
|
|
4
|
+
export type ConnectionSignal = {
|
|
5
|
+
readonly kind: 'connecting';
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: 'online';
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'degraded';
|
|
10
|
+
readonly channels: readonly TransportId[];
|
|
11
|
+
} | {
|
|
12
|
+
readonly kind: 'offline';
|
|
13
|
+
readonly retryAt?: number;
|
|
14
|
+
} | {
|
|
15
|
+
readonly kind: 'needs-auth';
|
|
16
|
+
};
|
|
17
|
+
export interface ConnectionSignalOptions {
|
|
18
|
+
readonly kernel: Kernel;
|
|
19
|
+
readonly transports: readonly Transport[];
|
|
20
|
+
readonly debounceMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface ConnectionSignalHandle {
|
|
23
|
+
readonly current: ConnectionSignal;
|
|
24
|
+
raw(): ConnectionSignal;
|
|
25
|
+
recompute(): void;
|
|
26
|
+
subscribe(listener: (signal: ConnectionSignal) => void): () => void;
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function signalEquals(a: ConnectionSignal, b: ConnectionSignal): boolean;
|
|
30
|
+
export declare function createConnectionSignal(options: ConnectionSignalOptions): ConnectionSignalHandle;
|
|
@@ -7,6 +7,8 @@ export interface FakeTransportOptions {
|
|
|
7
7
|
export declare class FakeTransport implements Transport {
|
|
8
8
|
readonly spec: TransportSpec;
|
|
9
9
|
readonly applyCalls: (ConnectionTarget | null)[];
|
|
10
|
+
failNextApplies: number;
|
|
11
|
+
ensureAliveCalls: number;
|
|
10
12
|
private readonly emitter;
|
|
11
13
|
private readonly applyDelayMs;
|
|
12
14
|
private currentTarget;
|
|
@@ -15,6 +17,7 @@ export declare class FakeTransport implements Transport {
|
|
|
15
17
|
constructor(options: FakeTransportOptions);
|
|
16
18
|
apply(target: ConnectionTarget | null): Promise<void>;
|
|
17
19
|
health(): TransportStatus;
|
|
20
|
+
ensureAlive(): Promise<TransportStatus>;
|
|
18
21
|
on<E extends TransportEvent>(event: E, handler: TransportEventHandler<E>): Unsubscribe;
|
|
19
22
|
dispose(): Promise<void>;
|
|
20
23
|
emitDown(reason: string): void;
|
package/dist/testing.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { t as TransportEmitter } from "./contract-
|
|
1
|
+
import { t as TransportEmitter } from "./contract-dt1m8U-o.js";
|
|
2
2
|
//#region src/testing/fakeTransport.ts
|
|
3
3
|
var FakeTransport = class {
|
|
4
4
|
spec;
|
|
5
5
|
applyCalls = [];
|
|
6
|
+
failNextApplies = 0;
|
|
7
|
+
ensureAliveCalls = 0;
|
|
6
8
|
emitter = new TransportEmitter();
|
|
7
9
|
applyDelayMs;
|
|
8
10
|
currentTarget = null;
|
|
@@ -16,6 +18,14 @@ var FakeTransport = class {
|
|
|
16
18
|
if (this.disposed) throw new Error("disposed");
|
|
17
19
|
this.applyCalls.push(target);
|
|
18
20
|
if (this.applyDelayMs > 0) await new Promise((resolve) => setTimeout(resolve, this.applyDelayMs));
|
|
21
|
+
if (this.failNextApplies > 0) {
|
|
22
|
+
this.failNextApplies--;
|
|
23
|
+
this.currentStatus = {
|
|
24
|
+
up: false,
|
|
25
|
+
lastError: "apply failed"
|
|
26
|
+
};
|
|
27
|
+
throw new Error("apply failed");
|
|
28
|
+
}
|
|
19
29
|
this.currentTarget = target;
|
|
20
30
|
this.currentStatus = target ? { up: true } : { up: false };
|
|
21
31
|
if (target) this.emitter.emit("up", void 0);
|
|
@@ -24,6 +34,10 @@ var FakeTransport = class {
|
|
|
24
34
|
health() {
|
|
25
35
|
return this.currentStatus;
|
|
26
36
|
}
|
|
37
|
+
async ensureAlive() {
|
|
38
|
+
this.ensureAliveCalls++;
|
|
39
|
+
return this.currentStatus;
|
|
40
|
+
}
|
|
27
41
|
on(event, handler) {
|
|
28
42
|
return this.emitter.on(event, handler);
|
|
29
43
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type CloseClass = 'auth-expired' | 'forbidden' | 'other';
|
|
2
|
+
export interface CloseLike {
|
|
3
|
+
readonly code?: number;
|
|
4
|
+
readonly wsCloseCode?: number;
|
|
5
|
+
readonly message?: string;
|
|
6
|
+
readonly reason?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function classifyClose(input: CloseLike | string): CloseClass;
|
|
@@ -12,6 +12,7 @@ export interface Transport {
|
|
|
12
12
|
readonly spec: TransportSpec;
|
|
13
13
|
apply(target: ConnectionTarget | null): Promise<void>;
|
|
14
14
|
health(): TransportStatus;
|
|
15
|
+
ensureAlive?(): Promise<TransportStatus>;
|
|
15
16
|
on<E extends TransportEvent>(event: E, handler: TransportEventHandler<E>): Unsubscribe;
|
|
16
17
|
dispose(): Promise<void>;
|
|
17
18
|
}
|
|
@@ -20,7 +21,6 @@ export interface PerResourceTransport<R = unknown, S = unknown> extends Transpor
|
|
|
20
21
|
}
|
|
21
22
|
export declare function isSameTarget(a: ConnectionTarget | null, b: ConnectionTarget | null): boolean;
|
|
22
23
|
export declare function isEndpointChange(a: ConnectionTarget | null, b: ConnectionTarget | null): boolean;
|
|
23
|
-
export declare function isTokenOnlyChange(a: ConnectionTarget | null, b: ConnectionTarget | null): boolean;
|
|
24
24
|
export declare class TransportEmitter {
|
|
25
25
|
private readonly listeners;
|
|
26
26
|
on<E extends TransportEvent>(event: E, handler: TransportEventHandler<E>): Unsubscribe;
|
package/dist/transports/http.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-
|
|
1
|
+
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-dt1m8U-o.js";
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
//#region src/transports/http.ts
|
|
4
4
|
var HTTP_SPEC = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Logger } from '@camera.ui/logger';
|
|
2
2
|
import { RPCClient } from '@camera.ui/rpc';
|
|
3
|
-
import { TransportSpec } from '../core/types.js';
|
|
3
|
+
import { TransportSpec, TransportStatus } from '../core/types.js';
|
|
4
4
|
import { Transport, Unsubscribe } from './contract.js';
|
|
5
5
|
export interface NatsTransportOptions {
|
|
6
6
|
readonly spec?: Partial<TransportSpec>;
|
|
@@ -24,5 +24,6 @@ export interface NatsTransport extends Transport {
|
|
|
24
24
|
subscribeClient(listener: NatsClientListener): Unsubscribe;
|
|
25
25
|
probeAlive(timeoutMs?: number): Promise<void>;
|
|
26
26
|
forceReconnect(): Promise<void>;
|
|
27
|
+
ensureAlive(): Promise<TransportStatus>;
|
|
27
28
|
}
|
|
28
29
|
export declare function createNatsTransport(options?: NatsTransportOptions): NatsTransport;
|
package/dist/transports/nats.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-
|
|
1
|
+
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-dt1m8U-o.js";
|
|
2
|
+
import { t as classifyClose } from "../closeCodes-ClWJVOpq.js";
|
|
2
3
|
import { createRPCClient } from "@camera.ui/rpc";
|
|
3
4
|
//#region src/transports/nats.ts
|
|
4
5
|
var NATS_SPEC = {
|
|
5
6
|
id: "nats",
|
|
6
7
|
kind: "persistent",
|
|
7
|
-
phaseGating: true
|
|
8
|
-
graceMs: 4e3
|
|
8
|
+
phaseGating: true
|
|
9
9
|
};
|
|
10
10
|
function newConnId() {
|
|
11
11
|
const c = globalThis.crypto;
|
|
@@ -112,11 +112,23 @@ function createNatsTransport(options = {}) {
|
|
|
112
112
|
}
|
|
113
113
|
function handleErrorEvent(event) {
|
|
114
114
|
const message = stringifyError(event.data);
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
if (message.toLowerCase().includes("permissions violation")) {
|
|
116
|
+
logger?.warn(`permission violation (connection stays up): ${message}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const cls = classifyClose(typeof event.data === "object" && event.data !== null ? {
|
|
120
|
+
...event.data,
|
|
121
|
+
message
|
|
122
|
+
} : message);
|
|
123
|
+
if (cls === "auth-expired") {
|
|
117
124
|
emitter.emit("auth-error", { message });
|
|
118
125
|
return;
|
|
119
126
|
}
|
|
127
|
+
if (cls === "forbidden") {
|
|
128
|
+
logger?.warn(`forbidden by server: ${message}`);
|
|
129
|
+
markDown(`forbidden: ${message}`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
120
132
|
markDown(message);
|
|
121
133
|
}
|
|
122
134
|
async function rebuildClient(target, epoch) {
|
|
@@ -163,6 +175,7 @@ function createNatsTransport(options = {}) {
|
|
|
163
175
|
if (disposed || epoch !== applyEpoch) return;
|
|
164
176
|
const msg = err instanceof Error ? err.message : String(err);
|
|
165
177
|
logger?.debug(`rebuildClient connect FAILED after ${Date.now() - t0}ms: ${msg}`);
|
|
178
|
+
if (classifyClose(err instanceof Error ? err : msg) === "auth-expired") emitter.emit("auth-error", { message: msg });
|
|
166
179
|
markDown(msg);
|
|
167
180
|
throw err;
|
|
168
181
|
} finally {
|
|
@@ -268,6 +281,32 @@ function createNatsTransport(options = {}) {
|
|
|
268
281
|
await proxy.forceReconnect();
|
|
269
282
|
logger?.debug(`forceReconnect: returned after ${Date.now() - t0}ms (up=${status.up})`);
|
|
270
283
|
}
|
|
284
|
+
let ensureInflight = null;
|
|
285
|
+
function ensureAlive() {
|
|
286
|
+
if (ensureInflight) return ensureInflight;
|
|
287
|
+
const run = async () => {
|
|
288
|
+
if (disposed || !currentTarget) return health();
|
|
289
|
+
if (!proxy) {
|
|
290
|
+
const epoch = ++applyEpoch;
|
|
291
|
+
try {
|
|
292
|
+
await rebuildClient(currentTarget, epoch);
|
|
293
|
+
} catch {}
|
|
294
|
+
return health();
|
|
295
|
+
}
|
|
296
|
+
if (health().up) try {
|
|
297
|
+
await probeAlive(3e3);
|
|
298
|
+
return health();
|
|
299
|
+
} catch {}
|
|
300
|
+
try {
|
|
301
|
+
await forceReconnect();
|
|
302
|
+
} catch {}
|
|
303
|
+
return health();
|
|
304
|
+
};
|
|
305
|
+
ensureInflight = run().finally(() => {
|
|
306
|
+
ensureInflight = null;
|
|
307
|
+
});
|
|
308
|
+
return ensureInflight;
|
|
309
|
+
}
|
|
271
310
|
return {
|
|
272
311
|
spec,
|
|
273
312
|
apply,
|
|
@@ -277,7 +316,8 @@ function createNatsTransport(options = {}) {
|
|
|
277
316
|
getClient,
|
|
278
317
|
subscribeClient,
|
|
279
318
|
probeAlive,
|
|
280
|
-
forceReconnect
|
|
319
|
+
forceReconnect,
|
|
320
|
+
ensureAlive
|
|
281
321
|
};
|
|
282
322
|
}
|
|
283
323
|
function stringifyError(value) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Manager, Socket } from 'socket.io-client';
|
|
2
2
|
import { Logger } from '@camera.ui/logger';
|
|
3
|
-
import { TransportSpec } from '../core/types.js';
|
|
3
|
+
import { TransportSpec, TransportStatus } from '../core/types.js';
|
|
4
4
|
import { Transport } from './contract.js';
|
|
5
5
|
export type { Socket };
|
|
6
6
|
export interface SocketioTransportOptions {
|
|
@@ -11,6 +11,7 @@ export interface SocketioTransportOptions {
|
|
|
11
11
|
readonly reconnectionDelay?: number;
|
|
12
12
|
readonly reconnectionDelayMax?: number;
|
|
13
13
|
readonly timeout?: number;
|
|
14
|
+
readonly staleAfterMs?: number;
|
|
14
15
|
readonly logger?: Logger;
|
|
15
16
|
}
|
|
16
17
|
export interface SocketioTransport extends Transport {
|
|
@@ -18,5 +19,6 @@ export interface SocketioTransport extends Transport {
|
|
|
18
19
|
socket(namespace?: string): Socket | null;
|
|
19
20
|
ensureSocket(namespace: string): Socket | null;
|
|
20
21
|
reviveDeadSockets(): void;
|
|
22
|
+
ensureAlive(): Promise<TransportStatus>;
|
|
21
23
|
}
|
|
22
24
|
export declare function createSocketioTransport(options?: SocketioTransportOptions): SocketioTransport;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-
|
|
1
|
+
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-dt1m8U-o.js";
|
|
2
|
+
import { t as classifyClose } from "../closeCodes-ClWJVOpq.js";
|
|
2
3
|
import { Manager, io } from "socket.io-client";
|
|
3
4
|
//#region src/transports/socketio.ts
|
|
4
5
|
var SOCKETIO_SPEC = {
|
|
5
6
|
id: "socketio",
|
|
6
7
|
kind: "persistent",
|
|
7
|
-
phaseGating: true
|
|
8
|
-
graceMs: 4e3
|
|
8
|
+
phaseGating: true
|
|
9
9
|
};
|
|
10
10
|
function createSocketioTransport(options = {}) {
|
|
11
11
|
const spec = {
|
|
@@ -25,6 +25,14 @@ function createSocketioTransport(options = {}) {
|
|
|
25
25
|
let currentTarget = null;
|
|
26
26
|
let status = { up: false };
|
|
27
27
|
let disposed = false;
|
|
28
|
+
const staleAfterMs = options.staleAfterMs ?? 6e4;
|
|
29
|
+
let lastActivityAt = 0;
|
|
30
|
+
function touchActivity() {
|
|
31
|
+
lastActivityAt = Date.now();
|
|
32
|
+
}
|
|
33
|
+
function isStaleNow() {
|
|
34
|
+
return lastActivityAt > 0 && Date.now() - lastActivityAt > staleAfterMs;
|
|
35
|
+
}
|
|
28
36
|
function buildAuth(target) {
|
|
29
37
|
const auth = { token: `Bearer ${target.tokens.access}` };
|
|
30
38
|
if (target.tokens.proxySession) auth.session = target.tokens.proxySession;
|
|
@@ -51,25 +59,7 @@ function createSocketioTransport(options = {}) {
|
|
|
51
59
|
emitter.emit("down", { reason });
|
|
52
60
|
}
|
|
53
61
|
function isAuthError(msg) {
|
|
54
|
-
|
|
55
|
-
return m.includes("auth") || m.includes("unauthorized");
|
|
56
|
-
}
|
|
57
|
-
function bindCommonAuthEvents(socket) {
|
|
58
|
-
let rotatedOut = false;
|
|
59
|
-
socket.on("unauthenticated", () => {
|
|
60
|
-
const fresh = currentTarget ? buildAuth(currentTarget) : null;
|
|
61
|
-
const current = socket.auth?.token;
|
|
62
|
-
if (fresh && current !== fresh.token) {
|
|
63
|
-
socket.auth = fresh;
|
|
64
|
-
rotatedOut = true;
|
|
65
|
-
} else emitter.emit("auth-error", { message: "unauthenticated" });
|
|
66
|
-
});
|
|
67
|
-
socket.on("disconnect", () => {
|
|
68
|
-
if (rotatedOut) {
|
|
69
|
-
rotatedOut = false;
|
|
70
|
-
socket.connect();
|
|
71
|
-
}
|
|
72
|
-
});
|
|
62
|
+
return classifyClose(msg) === "auth-expired";
|
|
73
63
|
}
|
|
74
64
|
function bindMainSocketEvents(socket) {
|
|
75
65
|
socket.on("connect", () => markUp());
|
|
@@ -83,14 +73,12 @@ function createSocketioTransport(options = {}) {
|
|
|
83
73
|
}
|
|
84
74
|
markDown(msg);
|
|
85
75
|
});
|
|
86
|
-
bindCommonAuthEvents(socket);
|
|
87
76
|
}
|
|
88
77
|
function bindSecondarySocketEvents(socket) {
|
|
89
78
|
socket.on("connect_error", (err) => {
|
|
90
79
|
const msg = err?.message ?? "connect_error";
|
|
91
80
|
if (isAuthError(msg)) emitter.emit("auth-error", { message: msg });
|
|
92
81
|
});
|
|
93
|
-
bindCommonAuthEvents(socket);
|
|
94
82
|
}
|
|
95
83
|
function socketOrigin(target) {
|
|
96
84
|
return new URL(target.endpoint.url).origin;
|
|
@@ -125,7 +113,12 @@ function createSocketioTransport(options = {}) {
|
|
|
125
113
|
rejectUnauthorized: false,
|
|
126
114
|
transports: ["websocket"]
|
|
127
115
|
});
|
|
128
|
-
|
|
116
|
+
const main = openSocket(mainNs, target);
|
|
117
|
+
bindMainSocketEvents(main);
|
|
118
|
+
lastActivityAt = Date.now();
|
|
119
|
+
main.io.on("open", touchActivity);
|
|
120
|
+
main.io.on("ping", touchActivity);
|
|
121
|
+
main.io.on("packet", touchActivity);
|
|
129
122
|
}
|
|
130
123
|
function closeAllSockets() {
|
|
131
124
|
for (const sock of sockets.values()) {
|
|
@@ -152,6 +145,7 @@ function createSocketioTransport(options = {}) {
|
|
|
152
145
|
currentTarget = target;
|
|
153
146
|
if (!target) {
|
|
154
147
|
closeAllSockets();
|
|
148
|
+
lastActivityAt = 0;
|
|
155
149
|
markDown("detached");
|
|
156
150
|
return;
|
|
157
151
|
}
|
|
@@ -162,6 +156,10 @@ function createSocketioTransport(options = {}) {
|
|
|
162
156
|
rebindAuth(target);
|
|
163
157
|
}
|
|
164
158
|
function health() {
|
|
159
|
+
if (status.up && isStaleNow()) return {
|
|
160
|
+
up: false,
|
|
161
|
+
lastError: "staleConnection"
|
|
162
|
+
};
|
|
165
163
|
return status;
|
|
166
164
|
}
|
|
167
165
|
function on(event, handler) {
|
|
@@ -188,6 +186,12 @@ function createSocketioTransport(options = {}) {
|
|
|
188
186
|
}
|
|
189
187
|
function reviveDeadSockets() {
|
|
190
188
|
if (!currentTarget) return;
|
|
189
|
+
if (status.up && isStaleNow()) {
|
|
190
|
+
logger?.debug("reviveDeadSockets: engine stale — forcing reconnect");
|
|
191
|
+
lastActivityAt = Date.now();
|
|
192
|
+
sockets.get(mainNs)?.io.engine?.close();
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
191
195
|
const auth = buildAuth(currentTarget);
|
|
192
196
|
let revived = 0;
|
|
193
197
|
for (const sock of sockets.values()) if (!sock.connected) {
|
|
@@ -197,6 +201,11 @@ function createSocketioTransport(options = {}) {
|
|
|
197
201
|
}
|
|
198
202
|
logger?.debug(`reviveDeadSockets: ${revived}/${sockets.size} reconnecting`);
|
|
199
203
|
}
|
|
204
|
+
async function ensureAlive() {
|
|
205
|
+
if (disposed || !currentTarget) return health();
|
|
206
|
+
reviveDeadSockets();
|
|
207
|
+
return health();
|
|
208
|
+
}
|
|
200
209
|
return {
|
|
201
210
|
get spec() {
|
|
202
211
|
return spec;
|
|
@@ -210,7 +219,8 @@ function createSocketioTransport(options = {}) {
|
|
|
210
219
|
dispose,
|
|
211
220
|
socket,
|
|
212
221
|
ensureSocket,
|
|
213
|
-
reviveDeadSockets
|
|
222
|
+
reviveDeadSockets,
|
|
223
|
+
ensureAlive
|
|
214
224
|
};
|
|
215
225
|
}
|
|
216
226
|
//#endregion
|
package/dist/transports/ws.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-
|
|
1
|
+
import { n as isEndpointChange, r as isSameTarget, t as TransportEmitter } from "../contract-dt1m8U-o.js";
|
|
2
|
+
import { t as classifyClose } from "../closeCodes-ClWJVOpq.js";
|
|
2
3
|
//#region src/transports/ws.ts
|
|
3
4
|
var WS_SPEC = {
|
|
4
5
|
id: "ws",
|
|
@@ -39,10 +40,10 @@ function createWsTransport(options = {}) {
|
|
|
39
40
|
return `${wsProtocol}//${base.hostname}:${port}${prefix}${handleSpec.path}?${params.toString()}`;
|
|
40
41
|
}
|
|
41
42
|
function isAuthCloseEvent(event) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
return classifyClose({
|
|
44
|
+
code: event.code,
|
|
45
|
+
reason: event.reason
|
|
46
|
+
}) === "auth-expired";
|
|
46
47
|
}
|
|
47
48
|
function bindWs(handle, ws) {
|
|
48
49
|
ws.onopen = () => {
|
package/dist/worker.js
CHANGED
|
@@ -28,7 +28,6 @@ function createWorkerKernelMirror(options) {
|
|
|
28
28
|
options.source.addEventListener("message", handler);
|
|
29
29
|
function targetOf(p) {
|
|
30
30
|
if (p.kind === "online") return p.target;
|
|
31
|
-
if (p.kind === "reconnecting") return p.lastTarget;
|
|
32
31
|
return null;
|
|
33
32
|
}
|
|
34
33
|
function phaseEquals(a, b) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camera.ui/transport",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
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.10",
|
|
60
60
|
"axios": ">=1.18.1",
|
|
61
61
|
"socket.io-client": ">=4.8.3"
|
|
62
62
|
},
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"@eslint/js": "9.39.4",
|
|
65
65
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
66
66
|
"@typescript-eslint/parser": "^8.65.0",
|
|
67
|
-
"@vue/language-core": "^3.3.
|
|
67
|
+
"@vue/language-core": "^3.3.8",
|
|
68
68
|
"eslint": "9.39.2",
|
|
69
69
|
"globals": "^17.7.0",
|
|
70
70
|
"jiti": "^2.7.0",
|
package/dist/core/resolver.d.ts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { Endpoint, EndpointMode } from './types.js';
|
|
2
|
-
export declare function sortByPriority(endpoints: readonly Endpoint[]): Endpoint[];
|
|
3
|
-
export declare function isSameEndpoint(a: Endpoint, b: Endpoint): boolean;
|
|
4
|
-
export declare function endpointKey(ep: Endpoint): string;
|
|
5
|
-
export type { EndpointMode };
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { Kernel } from '../core/kernel.js';
|
|
2
|
-
export type Detach = () => void;
|
|
3
|
-
export interface ReconnectWatchdogOptions {
|
|
4
|
-
readonly kernel: Kernel;
|
|
5
|
-
readonly escalateAfterMs?: number;
|
|
6
|
-
readonly setTimer?: (cb: () => void, ms: number) => unknown;
|
|
7
|
-
readonly clearTimer?: (handle: unknown) => void;
|
|
8
|
-
readonly onEscalate?: (attempt: number) => void;
|
|
9
|
-
}
|
|
10
|
-
export declare function attachReconnectWatchdog(options: ReconnectWatchdogOptions): Detach;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { Kernel } from '../core/kernel.js';
|
|
2
|
-
import { TransportId } from '../core/types.js';
|
|
3
|
-
import { Transport } from '../transports/contract.js';
|
|
4
|
-
export type Detach = () => void;
|
|
5
|
-
export type WatchdogClearReason = 'up' | 'detach' | 'phase-change';
|
|
6
|
-
export interface TransportWatchdogOptions {
|
|
7
|
-
readonly kernel: Kernel;
|
|
8
|
-
readonly transports: readonly Transport[];
|
|
9
|
-
readonly defaultGraceMs?: number;
|
|
10
|
-
readonly setTimer?: (cb: () => void, ms: number) => unknown;
|
|
11
|
-
readonly clearTimer?: (handle: unknown) => void;
|
|
12
|
-
readonly onGraceStarted?: (id: TransportId, graceMs: number) => void;
|
|
13
|
-
readonly onGraceCleared?: (id: TransportId, reason: WatchdogClearReason) => void;
|
|
14
|
-
readonly onConfirmed?: (id: TransportId) => void;
|
|
15
|
-
}
|
|
16
|
-
export declare function attachTransportWatchdog(options: TransportWatchdogOptions): Detach;
|