@camera.ui/transport 0.0.24 → 0.0.26
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/effects/backgroundProbe.d.ts +2 -1
- package/dist/index.js +17 -10
- package/dist/race.d.ts +3 -2
- package/dist/transports/nats.js +3 -4
- package/dist/transports/socketio.js +2 -1
- package/package.json +9 -9
|
@@ -2,7 +2,7 @@ import { Kernel } from '../core/kernel.js';
|
|
|
2
2
|
import { ConnectionTarget, Endpoint, Tokens } from '../core/types.js';
|
|
3
3
|
import { TimeoutByModeFn } from '../race.js';
|
|
4
4
|
import { ProbeContext } from './probeLoop.js';
|
|
5
|
-
export type BackgroundProbeOutcome = 'swap' | 'same' | 'failed' | 'skipped';
|
|
5
|
+
export type BackgroundProbeOutcome = 'swap' | 'same' | 'kept' | 'failed' | 'skipped';
|
|
6
6
|
export interface BackgroundProbeOptions {
|
|
7
7
|
readonly kernel: Kernel;
|
|
8
8
|
readonly discover: (signal: AbortSignal) => Promise<readonly Endpoint[]>;
|
|
@@ -11,6 +11,7 @@ export interface BackgroundProbeOptions {
|
|
|
11
11
|
readonly timeoutByMode?: TimeoutByModeFn;
|
|
12
12
|
readonly prefer?: (endpoint: Endpoint) => boolean;
|
|
13
13
|
readonly preferGraceMs?: number;
|
|
14
|
+
readonly isCurrentHealthy?: () => boolean;
|
|
14
15
|
readonly onResult?: (outcome: BackgroundProbeOutcome, detail?: string) => void;
|
|
15
16
|
}
|
|
16
17
|
export interface BackgroundProbe {
|
package/dist/index.js
CHANGED
|
@@ -192,7 +192,7 @@ var RaceFirstError = class extends Error {
|
|
|
192
192
|
}
|
|
193
193
|
};
|
|
194
194
|
function raceFirst(candidates, options = {}) {
|
|
195
|
-
const { timeoutByMode = (mode) => DEFAULT_RACE_TIMEOUT_BY_MODE[mode] ?? 5e3, shortCircuit, parentSignal, prefer } = options;
|
|
195
|
+
const { timeoutByMode = (mode) => DEFAULT_RACE_TIMEOUT_BY_MODE[mode] ?? 5e3, shortCircuit, informative, parentSignal, prefer } = options;
|
|
196
196
|
const preferGraceMs = options.preferGraceMs ?? DEFAULT_PREFER_GRACE_MS;
|
|
197
197
|
return new Promise((resolve, reject) => {
|
|
198
198
|
if (candidates.length === 0) {
|
|
@@ -215,7 +215,7 @@ function raceFirst(candidates, options = {}) {
|
|
|
215
215
|
const timers = [];
|
|
216
216
|
function cleanupAllExcept(except) {
|
|
217
217
|
for (const t of timers) clearTimeout(t);
|
|
218
|
-
for (const a of abortControllers) if (a !== except) a.abort();
|
|
218
|
+
for (const a of abortControllers) if (a !== except) a.abort("race-settled");
|
|
219
219
|
}
|
|
220
220
|
function finishSuccess(endpoint, value, except) {
|
|
221
221
|
if (settled) return;
|
|
@@ -259,7 +259,7 @@ function raceFirst(candidates, options = {}) {
|
|
|
259
259
|
const ctrl = new AbortController();
|
|
260
260
|
abortControllers.push(ctrl);
|
|
261
261
|
const delay = timeoutByMode(cand.endpoint.mode);
|
|
262
|
-
const timer = setTimeout(() => ctrl.abort(), delay);
|
|
262
|
+
const timer = setTimeout(() => ctrl.abort("race-timeout"), delay);
|
|
263
263
|
timers.push(timer);
|
|
264
264
|
cand.run(ctrl.signal).then((value) => handleSuccess(cand.endpoint, value, ctrl), (err) => {
|
|
265
265
|
if (settled) {
|
|
@@ -282,10 +282,11 @@ function raceFirst(candidates, options = {}) {
|
|
|
282
282
|
}
|
|
283
283
|
remaining--;
|
|
284
284
|
if (remaining <= 0) {
|
|
285
|
-
const
|
|
285
|
+
const usable = [...lastErrors.entries()].filter(([, e]) => {
|
|
286
286
|
if (e instanceof RaceFirstError) return e.kind !== "all-failed" || !(e.cause instanceof Error && e.cause.message === "aborted");
|
|
287
287
|
return true;
|
|
288
|
-
})
|
|
288
|
+
});
|
|
289
|
+
const [endpoint, cause] = (informative ? usable.find(([, e]) => informative(e instanceof RaceFirstError ? e.cause : e)) : void 0) ?? usable[0] ?? [cand.endpoint, finalErr];
|
|
289
290
|
finishFail(new RaceFirstError("raceFirst: all candidates failed", endpoint, cause, "all-failed"));
|
|
290
291
|
}
|
|
291
292
|
});
|
|
@@ -330,7 +331,12 @@ function createBackgroundProbe(options) {
|
|
|
330
331
|
options.onResult?.("skipped", `phase=${phase.kind}`);
|
|
331
332
|
return "skipped";
|
|
332
333
|
}
|
|
333
|
-
const
|
|
334
|
+
const current = phase.target.endpoint;
|
|
335
|
+
const same = current.url === endpoint.url && current.mode === endpoint.mode;
|
|
336
|
+
if (!same && options.prefer && options.prefer(current) && !options.prefer(endpoint) && options.isCurrentHealthy?.() === true) {
|
|
337
|
+
options.onResult?.("kept", current.url);
|
|
338
|
+
return "kept";
|
|
339
|
+
}
|
|
334
340
|
options.kernel.dispatch({
|
|
335
341
|
type: "ENDPOINT_SWAP",
|
|
336
342
|
endpoint,
|
|
@@ -778,9 +784,9 @@ function attachProbeLoop(options) {
|
|
|
778
784
|
options.onProbeSuccess?.(endpoint, tokens);
|
|
779
785
|
return tokens;
|
|
780
786
|
} catch (err) {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
787
|
+
let finalErr = err;
|
|
788
|
+
if (signal.aborted && !ctrl.signal.aborted && !isProbeFailure(err)) if (signal.reason === "race-settled") finalErr = makeProbeFailure("transient", "superseded");
|
|
789
|
+
else finalErr = makeProbeFailure("transient", `timeout (${(options.timeoutByMode ?? ((m) => DEFAULT_RACE_TIMEOUT_BY_MODE[m] ?? 5e3))(endpoint.mode)}ms)`);
|
|
784
790
|
options.onProbeError?.(endpoint, finalErr);
|
|
785
791
|
throw finalErr;
|
|
786
792
|
}
|
|
@@ -792,7 +798,8 @@ function attachProbeLoop(options) {
|
|
|
792
798
|
parentSignal: ctrl.signal,
|
|
793
799
|
prefer: options.prefer,
|
|
794
800
|
preferGraceMs: options.preferGraceMs,
|
|
795
|
-
shortCircuit: (err) => isProbeFailure(err) && (err.kind === "needs-auth" || err.kind === "fatal"
|
|
801
|
+
shortCircuit: (err) => isProbeFailure(err) && (err.kind === "needs-auth" || err.kind === "fatal"),
|
|
802
|
+
informative: (cause) => !(isProbeFailure(cause) && cause.kind === "aborted")
|
|
796
803
|
});
|
|
797
804
|
if (ctrl.signal.aborted) return;
|
|
798
805
|
options.kernel.dispatch({
|
package/dist/race.d.ts
CHANGED
|
@@ -6,11 +6,12 @@ export interface RaceCandidate<T> {
|
|
|
6
6
|
readonly run: (signal: AbortSignal) => Promise<T>;
|
|
7
7
|
}
|
|
8
8
|
export interface RaceFirstOptions {
|
|
9
|
+
readonly parentSignal?: AbortSignal;
|
|
10
|
+
readonly preferGraceMs?: number;
|
|
9
11
|
readonly timeoutByMode?: TimeoutByModeFn;
|
|
10
12
|
readonly shortCircuit?: (err: unknown) => boolean;
|
|
11
|
-
readonly
|
|
13
|
+
readonly informative?: (cause: unknown) => boolean;
|
|
12
14
|
readonly prefer?: (endpoint: Endpoint) => boolean;
|
|
13
|
-
readonly preferGraceMs?: number;
|
|
14
15
|
}
|
|
15
16
|
export interface RaceFirstResult<T> {
|
|
16
17
|
readonly endpoint: Endpoint;
|
package/dist/transports/nats.js
CHANGED
|
@@ -103,9 +103,7 @@ function createNatsTransport(options = {}) {
|
|
|
103
103
|
markDown("staleConnection");
|
|
104
104
|
notifyClient();
|
|
105
105
|
break;
|
|
106
|
-
case "error":
|
|
107
|
-
handleErrorEvent(event);
|
|
108
|
-
break;
|
|
106
|
+
case "error": handleErrorEvent(event);
|
|
109
107
|
}
|
|
110
108
|
}
|
|
111
109
|
})().catch(() => {});
|
|
@@ -144,8 +142,9 @@ function createNatsTransport(options = {}) {
|
|
|
144
142
|
notifyClient();
|
|
145
143
|
}
|
|
146
144
|
connId = newConnId();
|
|
145
|
+
const servers = buildServers(target);
|
|
147
146
|
const next = createRPCClient({
|
|
148
|
-
servers
|
|
147
|
+
servers,
|
|
149
148
|
name: clientName,
|
|
150
149
|
connId,
|
|
151
150
|
auth: {
|
|
@@ -87,7 +87,8 @@ function createSocketioTransport(options = {}) {
|
|
|
87
87
|
return `${new URL(target.endpoint.url).pathname.replace(/\/$/, "")}${path}`;
|
|
88
88
|
}
|
|
89
89
|
function openSocket(namespace, target) {
|
|
90
|
-
const
|
|
90
|
+
const url = `${socketOrigin(target)}${namespace}`;
|
|
91
|
+
const sock = io(url, {
|
|
91
92
|
path: socketPath(target),
|
|
92
93
|
auth: buildAuth(target),
|
|
93
94
|
query: buildQuery(target),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camera.ui/transport",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.26",
|
|
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,25 +56,25 @@
|
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
58
|
"@camera.ui/logger": ">=0.0.3",
|
|
59
|
-
"@camera.ui/rpc": ">=1.0.
|
|
60
|
-
"axios": ">=1.
|
|
59
|
+
"@camera.ui/rpc": ">=1.0.11",
|
|
60
|
+
"axios": ">=1.19.0",
|
|
61
61
|
"socket.io-client": ">=4.8.3"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@eslint/js": "9.39.4",
|
|
65
65
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
66
|
-
"@typescript-eslint/parser": "^8.
|
|
67
|
-
"@vue/language-core": "^3.3.
|
|
66
|
+
"@typescript-eslint/parser": "^8.67.0",
|
|
67
|
+
"@vue/language-core": "^3.3.10",
|
|
68
68
|
"eslint": "9.39.2",
|
|
69
|
-
"globals": "^17.
|
|
69
|
+
"globals": "^17.11.0",
|
|
70
70
|
"jiti": "^2.7.0",
|
|
71
71
|
"prettier": "^3.9.6",
|
|
72
72
|
"rimraf": "^6.1.3",
|
|
73
73
|
"typescript": "5.9.3",
|
|
74
|
-
"typescript-eslint": "^8.
|
|
74
|
+
"typescript-eslint": "^8.67.0",
|
|
75
75
|
"unplugin-dts": "^1.0.3",
|
|
76
|
-
"updates": "^
|
|
77
|
-
"vite": "^8.1
|
|
76
|
+
"updates": "^18.0.1",
|
|
77
|
+
"vite": "^8.2.1",
|
|
78
78
|
"vitest": "^4.1.10"
|
|
79
79
|
},
|
|
80
80
|
"overrides": {
|