@camera.ui/browser 0.1.13 → 0.1.15
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/index.d.ts +9 -1
- package/dist/index.js +286 -40
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -209,6 +209,8 @@ export declare interface CameraStream {
|
|
|
209
209
|
readonly status: Ref<StreamStatus>;
|
|
210
210
|
readonly isPlaying: Ref<boolean>;
|
|
211
211
|
readonly stalled: Ref<boolean>;
|
|
212
|
+
readonly connectionPath: Ref<StreamConnectionPath | undefined>;
|
|
213
|
+
readonly webrtcUnreachable: Ref<boolean>;
|
|
212
214
|
readonly activeMode: Ref<ActiveStreamingMode>;
|
|
213
215
|
readonly activeResolution: Ref<StreamingRole>;
|
|
214
216
|
readonly degradedFrom: Ref<StreamingRole | null>;
|
|
@@ -331,6 +333,7 @@ export declare interface CoreManagerInterface {
|
|
|
331
333
|
getCloudServerId(): Promise<string>;
|
|
332
334
|
getRestoreGeneration(): Promise<string>;
|
|
333
335
|
getInstanceId(): Promise<string>;
|
|
336
|
+
getLanguage(): Promise<string>;
|
|
334
337
|
getPlugin(pluginName: string): Promise<HostPluginInfo | undefined>;
|
|
335
338
|
getPluginsByInterface(interfaceName: PluginInterface): Promise<HostPluginInfo[]>;
|
|
336
339
|
getFloorPlan(): Promise<FloorPlan>;
|
|
@@ -1128,6 +1131,8 @@ export declare interface ReactiveStream {
|
|
|
1128
1131
|
readonly hasBackchannel: Ref<boolean>;
|
|
1129
1132
|
readonly isPlaying: Ref<boolean>;
|
|
1130
1133
|
readonly stalled?: Ref<boolean>;
|
|
1134
|
+
readonly connectionPath?: Readonly<Ref<StreamConnectionPath | undefined>>;
|
|
1135
|
+
readonly webrtcUnreachable?: Readonly<Ref<boolean>>;
|
|
1131
1136
|
readonly error: Ref<Error | undefined>;
|
|
1132
1137
|
readonly probeInfo: Ref<ProbeStream | undefined>;
|
|
1133
1138
|
readonly muted: Ref<boolean>;
|
|
@@ -1344,8 +1349,9 @@ export declare const STREAM_CONFIG: {
|
|
|
1344
1349
|
readonly RECONNECT_DELAY: 1000;
|
|
1345
1350
|
readonly CONNECT_TIMEOUT: 10000;
|
|
1346
1351
|
readonly WS_CONNECT_TIMEOUT: 5000;
|
|
1352
|
+
readonly DISCONNECT_GRACE: 5000;
|
|
1347
1353
|
readonly ICE_SERVERS: readonly [{
|
|
1348
|
-
readonly urls: "stun:stun.
|
|
1354
|
+
readonly urls: "stun:stun.cloudflare.com:3478";
|
|
1349
1355
|
}];
|
|
1350
1356
|
};
|
|
1351
1357
|
readonly MSE: {
|
|
@@ -1360,6 +1366,8 @@ export declare const STREAM_CONFIG: {
|
|
|
1360
1366
|
};
|
|
1361
1367
|
};
|
|
1362
1368
|
|
|
1369
|
+
export declare type StreamConnectionPath = 'direct' | 'relay' | 'tunnel';
|
|
1370
|
+
|
|
1363
1371
|
declare type StreamDegradeReason = 'codec';
|
|
1364
1372
|
|
|
1365
1373
|
export declare interface StreamOptions {
|
package/dist/index.js
CHANGED
|
@@ -484,7 +484,7 @@ function makeAbortError() {
|
|
|
484
484
|
return err;
|
|
485
485
|
}
|
|
486
486
|
// src/composables/bulkLane.ts
|
|
487
|
-
var PROBE_TIMEOUT_MS = 2e3;
|
|
487
|
+
var PROBE_TIMEOUT_MS$1 = 2e3;
|
|
488
488
|
var RECHECK_AFTER_MS = 6e4;
|
|
489
489
|
var deadMarks = /* @__PURE__ */ new WeakMap();
|
|
490
490
|
var runningProbes = /* @__PURE__ */ new WeakMap();
|
|
@@ -521,7 +521,7 @@ function probeBulkClient(client) {
|
|
|
521
521
|
}
|
|
522
522
|
async function answersPing(client) {
|
|
523
523
|
try {
|
|
524
|
-
await client.flush(PROBE_TIMEOUT_MS);
|
|
524
|
+
await client.flush(PROBE_TIMEOUT_MS$1);
|
|
525
525
|
return true;
|
|
526
526
|
} catch {
|
|
527
527
|
return false;
|
|
@@ -3668,7 +3668,8 @@ var STREAM_CONFIG = {
|
|
|
3668
3668
|
RECONNECT_DELAY: 1e3,
|
|
3669
3669
|
CONNECT_TIMEOUT: 1e4,
|
|
3670
3670
|
WS_CONNECT_TIMEOUT: 5e3,
|
|
3671
|
-
|
|
3671
|
+
DISCONNECT_GRACE: 5e3,
|
|
3672
|
+
ICE_SERVERS: [{ urls: "stun:stun.cloudflare.com:3478" }]
|
|
3672
3673
|
},
|
|
3673
3674
|
MSE: {
|
|
3674
3675
|
BUFFER_SIZE: 2097152,
|
|
@@ -3999,6 +4000,9 @@ function checkWebRTCCompatibility(probe) {
|
|
|
3999
4000
|
function hasManagedMediaSource() {
|
|
4000
4001
|
return "ManagedMediaSource" in window;
|
|
4001
4002
|
}
|
|
4003
|
+
function hasMediaSource() {
|
|
4004
|
+
return "MediaSource" in window || hasManagedMediaSource();
|
|
4005
|
+
}
|
|
4002
4006
|
function getMediaSourceConstructor() {
|
|
4003
4007
|
if (hasManagedMediaSource()) return window.ManagedMediaSource;
|
|
4004
4008
|
if ("MediaSource" in window) return window.MediaSource;
|
|
@@ -4281,6 +4285,101 @@ async function playVideo(videoElement) {
|
|
|
4281
4285
|
}
|
|
4282
4286
|
}
|
|
4283
4287
|
}
|
|
4288
|
+
// src/streaming/relayFirst.ts
|
|
4289
|
+
var PROBE_STUN_SERVERS = [{ urls: "stun:stun.cloudflare.com:3478" }, { urls: "stun:stun.l.google.com:19302" }];
|
|
4290
|
+
var PROBE_SETTLE_MS = 1500;
|
|
4291
|
+
var PROBE_TIMEOUT_MS = 4e3;
|
|
4292
|
+
var PROBE_REUSE_MS = 6e4;
|
|
4293
|
+
var RELAY_WAIT_MS = 3e3;
|
|
4294
|
+
var RELAY_HEAD_START_MS = 500;
|
|
4295
|
+
var IPV4 = /^\d{1,3}(\.\d{1,3}){3}$/;
|
|
4296
|
+
var lastProbe;
|
|
4297
|
+
function relayFirstAddress(iceServers) {
|
|
4298
|
+
if (!usesWebKitNetworking() || !iceServers?.some((server) => urlsOf(server).some(isIPv6OnlyStun))) return Promise.resolve(void 0);
|
|
4299
|
+
if (!lastProbe || Date.now() - lastProbe.startedAt > PROBE_REUSE_MS) lastProbe = {
|
|
4300
|
+
startedAt: Date.now(),
|
|
4301
|
+
address: probeSingleIPv4Target()
|
|
4302
|
+
};
|
|
4303
|
+
return lastProbe.address;
|
|
4304
|
+
}
|
|
4305
|
+
function relayFirstIceServers(iceServers) {
|
|
4306
|
+
return (iceServers ?? []).map((server) => ({
|
|
4307
|
+
...server,
|
|
4308
|
+
urls: urlsOf(server).filter((url) => !/^stuns?:/i.test(url) || isIPv6OnlyStun(url))
|
|
4309
|
+
})).filter((server) => server.urls.length > 0);
|
|
4310
|
+
}
|
|
4311
|
+
function relayPermitCandidate(address) {
|
|
4312
|
+
return `candidate:1 1 udp 1 ${address} 9 typ srflx raddr 0.0.0.0 rport 0`;
|
|
4313
|
+
}
|
|
4314
|
+
function orderRelayFirst(add) {
|
|
4315
|
+
const held = [];
|
|
4316
|
+
let relaySeen = false;
|
|
4317
|
+
let open = false;
|
|
4318
|
+
function release() {
|
|
4319
|
+
if (open) return;
|
|
4320
|
+
open = true;
|
|
4321
|
+
clearTimeout(wait);
|
|
4322
|
+
for (const candidate of held.splice(0)) add(candidate).catch(() => {});
|
|
4323
|
+
}
|
|
4324
|
+
const wait = setTimeout(release, RELAY_WAIT_MS);
|
|
4325
|
+
return async (candidate) => {
|
|
4326
|
+
if (open || !isUdpIPv4(candidate)) return add(candidate);
|
|
4327
|
+
if (relaySeen || !/ typ relay\b/.test(candidate)) {
|
|
4328
|
+
held.push(candidate);
|
|
4329
|
+
return;
|
|
4330
|
+
}
|
|
4331
|
+
relaySeen = true;
|
|
4332
|
+
setTimeout(release, RELAY_HEAD_START_MS);
|
|
4333
|
+
return add(candidate);
|
|
4334
|
+
};
|
|
4335
|
+
}
|
|
4336
|
+
function probeSingleIPv4Target() {
|
|
4337
|
+
return new Promise((resolve) => {
|
|
4338
|
+
let pc;
|
|
4339
|
+
try {
|
|
4340
|
+
pc = new RTCPeerConnection({ iceServers: PROBE_STUN_SERVERS });
|
|
4341
|
+
} catch {
|
|
4342
|
+
resolve(void 0);
|
|
4343
|
+
return;
|
|
4344
|
+
}
|
|
4345
|
+
let address;
|
|
4346
|
+
let settle;
|
|
4347
|
+
const timeout = setTimeout(() => finish(void 0), PROBE_TIMEOUT_MS);
|
|
4348
|
+
function finish(result) {
|
|
4349
|
+
clearTimeout(timeout);
|
|
4350
|
+
clearTimeout(settle);
|
|
4351
|
+
pc.onicecandidate = null;
|
|
4352
|
+
pc.close();
|
|
4353
|
+
resolve(result);
|
|
4354
|
+
}
|
|
4355
|
+
pc.onicecandidate = (event) => {
|
|
4356
|
+
if (!event.candidate) {
|
|
4357
|
+
finish(void 0);
|
|
4358
|
+
return;
|
|
4359
|
+
}
|
|
4360
|
+
const found = event.candidate.address ?? event.candidate.candidate.split(" ")[4];
|
|
4361
|
+
if (address || event.candidate.type !== "srflx" || !found || !IPV4.test(found)) return;
|
|
4362
|
+
address = found;
|
|
4363
|
+
settle = setTimeout(() => finish(address), PROBE_SETTLE_MS);
|
|
4364
|
+
};
|
|
4365
|
+
pc.createDataChannel("probe");
|
|
4366
|
+
pc.createOffer().then((offer) => pc.setLocalDescription(offer)).catch(() => finish(void 0));
|
|
4367
|
+
});
|
|
4368
|
+
}
|
|
4369
|
+
function usesWebKitNetworking() {
|
|
4370
|
+
if (typeof navigator === "undefined") return false;
|
|
4371
|
+
return /AppleWebKit/i.test(navigator.userAgent) && !/Chrome|Chromium|Android|Edg/i.test(navigator.userAgent);
|
|
4372
|
+
}
|
|
4373
|
+
function urlsOf(server) {
|
|
4374
|
+
return Array.isArray(server.urls) ? server.urls : [server.urls];
|
|
4375
|
+
}
|
|
4376
|
+
function isIPv6OnlyStun(url) {
|
|
4377
|
+
return /^stuns?:\[/i.test(url);
|
|
4378
|
+
}
|
|
4379
|
+
function isUdpIPv4(candidate) {
|
|
4380
|
+
const fields = candidate.split(" ");
|
|
4381
|
+
return fields[2]?.toLowerCase() === "udp" && IPV4.test(fields[4] ?? "");
|
|
4382
|
+
}
|
|
4284
4383
|
// src/streaming/webrtc.ts
|
|
4285
4384
|
function newPeerConnection(iceServers = [...STREAM_CONFIG.WEBRTC.ICE_SERVERS]) {
|
|
4286
4385
|
try {
|
|
@@ -4320,12 +4419,40 @@ async function handleIceCandidate(pc, signal, candidate, filterUdp) {
|
|
|
4320
4419
|
sdpMid: "0"
|
|
4321
4420
|
});
|
|
4322
4421
|
}
|
|
4422
|
+
function createRelayFirst(state, signal, filterUdp, probe) {
|
|
4423
|
+
const add = (candidate) => handleIceCandidate(state.pc, signal, candidate, filterUdp);
|
|
4424
|
+
let address;
|
|
4425
|
+
let handle = add;
|
|
4426
|
+
return {
|
|
4427
|
+
async settle() {
|
|
4428
|
+
address = probe && !filterUdp ? await probe : void 0;
|
|
4429
|
+
if (address) handle = orderRelayFirst(add);
|
|
4430
|
+
},
|
|
4431
|
+
iceServers: (servers) => address ? relayFirstIceServers(servers) : servers,
|
|
4432
|
+
permit(send) {
|
|
4433
|
+
if (address && !signal.aborted) send(relayPermitCandidate(address));
|
|
4434
|
+
},
|
|
4435
|
+
handleCandidate: (candidate) => handle(candidate)
|
|
4436
|
+
};
|
|
4437
|
+
}
|
|
4323
4438
|
async function setMicTrack(micTransceiver, signal, track) {
|
|
4324
4439
|
if (signal.aborted || !micTransceiver) return;
|
|
4325
4440
|
try {
|
|
4326
4441
|
await micTransceiver.sender.replaceTrack(track);
|
|
4327
4442
|
} catch {}
|
|
4328
4443
|
}
|
|
4444
|
+
async function readPeerPath(pc) {
|
|
4445
|
+
const reports = /* @__PURE__ */ new Map();
|
|
4446
|
+
let pairId;
|
|
4447
|
+
(await pc.getStats()).forEach((report, id) => {
|
|
4448
|
+
reports.set(id, report);
|
|
4449
|
+
if (report.type === "transport" && report.selectedCandidatePairId) pairId = report.selectedCandidatePairId;
|
|
4450
|
+
else if (report.type === "candidate-pair" && report.selected) pairId ??= id;
|
|
4451
|
+
});
|
|
4452
|
+
const pair = pairId ? reports.get(pairId) : void 0;
|
|
4453
|
+
if (!pair) return void 0;
|
|
4454
|
+
return [reports.get(pair.localCandidateId)?.candidateType, reports.get(pair.remoteCandidateId)?.candidateType].includes("relay") ? "relay" : "direct";
|
|
4455
|
+
}
|
|
4329
4456
|
function closePeerConnection(state) {
|
|
4330
4457
|
if (state.micTransceiver) {
|
|
4331
4458
|
state.micTransceiver.sender.replaceTrack(null).catch(() => {});
|
|
@@ -4339,21 +4466,38 @@ function closePeerConnection(state) {
|
|
|
4339
4466
|
state.isConnected = false;
|
|
4340
4467
|
}
|
|
4341
4468
|
function createWebRTCHandler(options) {
|
|
4342
|
-
const { mode, iceServers, onConnected, onDisconnected, onFailed, onCandidate, signal } = options;
|
|
4469
|
+
const { mode, iceServers, relayFirst, onConnected, onReconnected, onDisconnected, onFailed, onCandidate, onPath, signal } = options;
|
|
4343
4470
|
const state = {
|
|
4344
4471
|
pc: null,
|
|
4345
4472
|
micTransceiver: null,
|
|
4346
4473
|
isConnected: false
|
|
4347
4474
|
};
|
|
4348
4475
|
const filterUdp = mode === "webrtc/tcp";
|
|
4476
|
+
const relay = createRelayFirst(state, signal, filterUdp, relayFirst);
|
|
4477
|
+
async function handleAnswer(sdp) {
|
|
4478
|
+
await handleSdpAnswer(state.pc, signal, sdp);
|
|
4479
|
+
relay.permit(onCandidate);
|
|
4480
|
+
}
|
|
4349
4481
|
function createPeerConnection() {
|
|
4350
|
-
const peerConnection = createBasePeerConnection(signal, onCandidate, filterUdp, iceServers);
|
|
4482
|
+
const peerConnection = createBasePeerConnection(signal, onCandidate, filterUdp, relay.iceServers(iceServers));
|
|
4483
|
+
let stream;
|
|
4484
|
+
function reportPath() {
|
|
4485
|
+
readPeerPath(peerConnection).then((path) => {
|
|
4486
|
+
if (path && !signal.aborted && state.pc === peerConnection) onPath(path);
|
|
4487
|
+
}, () => {});
|
|
4488
|
+
}
|
|
4351
4489
|
peerConnection.onconnectionstatechange = () => {
|
|
4352
4490
|
if (signal.aborted) return;
|
|
4353
4491
|
const connectionState = peerConnection.connectionState;
|
|
4354
4492
|
if (connectionState === "connected") {
|
|
4355
4493
|
state.isConnected = true;
|
|
4356
|
-
|
|
4494
|
+
reportPath();
|
|
4495
|
+
if (stream) {
|
|
4496
|
+
onReconnected();
|
|
4497
|
+
return;
|
|
4498
|
+
}
|
|
4499
|
+
stream = new MediaStream(peerConnection.getTransceivers().filter((tr) => tr.currentDirection === "recvonly").map((tr) => tr.receiver.track));
|
|
4500
|
+
peerConnection.getReceivers()[0]?.transport?.iceTransport?.addEventListener("selectedcandidatepairchange", reportPath);
|
|
4357
4501
|
onConnected(stream);
|
|
4358
4502
|
} else if (connectionState === "failed") {
|
|
4359
4503
|
state.isConnected = false;
|
|
@@ -4363,11 +4507,16 @@ function createWebRTCHandler(options) {
|
|
|
4363
4507
|
onDisconnected();
|
|
4364
4508
|
}
|
|
4365
4509
|
};
|
|
4510
|
+
peerConnection.oniceconnectionstatechange = () => {
|
|
4511
|
+
if (!signal.aborted && state.isConnected) reportPath();
|
|
4512
|
+
};
|
|
4366
4513
|
peerConnection.addTransceiver("video", { direction: "recvonly" });
|
|
4367
4514
|
peerConnection.addTransceiver("audio", { direction: "recvonly" });
|
|
4368
4515
|
return peerConnection;
|
|
4369
4516
|
}
|
|
4370
4517
|
async function createOffer() {
|
|
4518
|
+
if (signal.aborted) return void 0;
|
|
4519
|
+
await relay.settle();
|
|
4371
4520
|
if (signal.aborted) return void 0;
|
|
4372
4521
|
state.pc = createPeerConnection();
|
|
4373
4522
|
const offer = await state.pc.createOffer();
|
|
@@ -4386,13 +4535,13 @@ function createWebRTCHandler(options) {
|
|
|
4386
4535
|
return state.isConnected;
|
|
4387
4536
|
},
|
|
4388
4537
|
createOffer,
|
|
4389
|
-
handleAnswer
|
|
4390
|
-
handleCandidate:
|
|
4538
|
+
handleAnswer,
|
|
4539
|
+
handleCandidate: relay.handleCandidate,
|
|
4391
4540
|
close
|
|
4392
4541
|
};
|
|
4393
4542
|
}
|
|
4394
4543
|
function createBackchannelHandler(options) {
|
|
4395
|
-
const { filterUdp, iceServers, onConnected, onFailed, onCandidate, signal } = options;
|
|
4544
|
+
const { filterUdp, iceServers, relayFirst, onConnected, onFailed, onCandidate, signal } = options;
|
|
4396
4545
|
const state = {
|
|
4397
4546
|
pc: null,
|
|
4398
4547
|
micTransceiver: null,
|
|
@@ -4400,6 +4549,8 @@ function createBackchannelHandler(options) {
|
|
|
4400
4549
|
};
|
|
4401
4550
|
let answered = false;
|
|
4402
4551
|
const heldCandidates = [];
|
|
4552
|
+
let disconnectGrace;
|
|
4553
|
+
const relay = createRelayFirst(state, signal, filterUdp, relayFirst);
|
|
4403
4554
|
function sendCandidate(candidate) {
|
|
4404
4555
|
if (answered) onCandidate(candidate);
|
|
4405
4556
|
else heldCandidates.push(candidate);
|
|
@@ -4408,21 +4559,26 @@ function createBackchannelHandler(options) {
|
|
|
4408
4559
|
if (signal.aborted || !state.pc) return;
|
|
4409
4560
|
answered = true;
|
|
4410
4561
|
for (const candidate of heldCandidates.splice(0)) onCandidate(candidate);
|
|
4562
|
+
relay.permit(onCandidate);
|
|
4411
4563
|
await state.pc.setRemoteDescription({
|
|
4412
4564
|
type: "answer",
|
|
4413
4565
|
sdp
|
|
4414
4566
|
});
|
|
4415
4567
|
}
|
|
4416
4568
|
function createPeerConnection() {
|
|
4417
|
-
const peerConnection = createBasePeerConnection(signal, sendCandidate, filterUdp, iceServers);
|
|
4569
|
+
const peerConnection = createBasePeerConnection(signal, sendCandidate, filterUdp, relay.iceServers(iceServers));
|
|
4418
4570
|
peerConnection.onconnectionstatechange = () => {
|
|
4419
4571
|
if (signal.aborted) return;
|
|
4420
4572
|
const connectionState = peerConnection.connectionState;
|
|
4421
4573
|
if (connectionState === "connected") {
|
|
4574
|
+
clearTimeout(disconnectGrace);
|
|
4422
4575
|
state.isConnected = true;
|
|
4423
4576
|
onConnected();
|
|
4424
|
-
} else if (connectionState === "disconnected")
|
|
4425
|
-
|
|
4577
|
+
} else if (connectionState === "disconnected") {
|
|
4578
|
+
state.isConnected = false;
|
|
4579
|
+
disconnectGrace = setTimeout(onFailed, STREAM_CONFIG.WEBRTC.DISCONNECT_GRACE);
|
|
4580
|
+
} else if (connectionState === "failed") {
|
|
4581
|
+
clearTimeout(disconnectGrace);
|
|
4426
4582
|
state.isConnected = false;
|
|
4427
4583
|
onFailed();
|
|
4428
4584
|
}
|
|
@@ -4431,6 +4587,8 @@ function createBackchannelHandler(options) {
|
|
|
4431
4587
|
return peerConnection;
|
|
4432
4588
|
}
|
|
4433
4589
|
async function createOffer() {
|
|
4590
|
+
if (signal.aborted) return void 0;
|
|
4591
|
+
await relay.settle();
|
|
4434
4592
|
if (signal.aborted) return void 0;
|
|
4435
4593
|
state.pc = createPeerConnection();
|
|
4436
4594
|
const offer = await state.pc.createOffer();
|
|
@@ -4438,6 +4596,7 @@ function createBackchannelHandler(options) {
|
|
|
4438
4596
|
return offer.sdp;
|
|
4439
4597
|
}
|
|
4440
4598
|
function close() {
|
|
4599
|
+
clearTimeout(disconnectGrace);
|
|
4441
4600
|
closePeerConnection(state);
|
|
4442
4601
|
}
|
|
4443
4602
|
signal.addEventListener("abort", close, { once: true });
|
|
@@ -4447,7 +4606,7 @@ function createBackchannelHandler(options) {
|
|
|
4447
4606
|
},
|
|
4448
4607
|
createOffer,
|
|
4449
4608
|
handleAnswer,
|
|
4450
|
-
handleCandidate:
|
|
4609
|
+
handleCandidate: relay.handleCandidate,
|
|
4451
4610
|
setMicrophoneTrack: (track) => setMicTrack(state.micTransceiver, signal, track),
|
|
4452
4611
|
close
|
|
4453
4612
|
};
|
|
@@ -4472,6 +4631,11 @@ var STALL_AFTER = 3e3;
|
|
|
4472
4631
|
var STALL_RECONNECT_AFTER = 15e3;
|
|
4473
4632
|
var HAVE_CURRENT_DATA$1 = 2;
|
|
4474
4633
|
var PROBE_WAIT_MS = 2e3;
|
|
4634
|
+
var WEBRTC_RETRY_DELAYS = [
|
|
4635
|
+
3e4,
|
|
4636
|
+
12e4,
|
|
4637
|
+
6e5
|
|
4638
|
+
];
|
|
4475
4639
|
function isPageHidden() {
|
|
4476
4640
|
return typeof document !== "undefined" && document.visibilityState === "hidden";
|
|
4477
4641
|
}
|
|
@@ -4495,6 +4659,8 @@ var StreamConnection = class {
|
|
|
4495
4659
|
hasBackchannel;
|
|
4496
4660
|
isPlaying;
|
|
4497
4661
|
stalled;
|
|
4662
|
+
connectionPath;
|
|
4663
|
+
webrtcUnreachable;
|
|
4498
4664
|
error;
|
|
4499
4665
|
probeInfo;
|
|
4500
4666
|
muted;
|
|
@@ -4512,6 +4678,7 @@ var StreamConnection = class {
|
|
|
4512
4678
|
iceServers;
|
|
4513
4679
|
scope = effectScope(true);
|
|
4514
4680
|
stopsWhenHidden = suspendsWhenHidden();
|
|
4681
|
+
webrtcPath = ref();
|
|
4515
4682
|
connectionGeneration = 0;
|
|
4516
4683
|
abortController = new AbortController();
|
|
4517
4684
|
probedSourceId;
|
|
@@ -4548,6 +4715,9 @@ var StreamConnection = class {
|
|
|
4548
4715
|
lastMediaTime = 0;
|
|
4549
4716
|
lastFrameCount;
|
|
4550
4717
|
frameCounterLive = false;
|
|
4718
|
+
webrtcRetries = 0;
|
|
4719
|
+
webrtcRetryDue = false;
|
|
4720
|
+
webrtcPlayingSince;
|
|
4551
4721
|
offTabVisible;
|
|
4552
4722
|
offTabPaused;
|
|
4553
4723
|
onVideoPauseBound;
|
|
@@ -4559,6 +4729,10 @@ var StreamConnection = class {
|
|
|
4559
4729
|
stopConnectTimeout;
|
|
4560
4730
|
startMseConnectTimeout;
|
|
4561
4731
|
stopMseConnectTimeout;
|
|
4732
|
+
startDisconnectGrace;
|
|
4733
|
+
stopDisconnectGrace;
|
|
4734
|
+
startWebRTCRetry;
|
|
4735
|
+
stopWebRTCRetry;
|
|
4562
4736
|
constructor(options) {
|
|
4563
4737
|
this.options = options;
|
|
4564
4738
|
const ctx = useCameraUi();
|
|
@@ -4578,6 +4752,7 @@ var StreamConnection = class {
|
|
|
4578
4752
|
this.hasBackchannel = ref(false);
|
|
4579
4753
|
this.isPlaying = ref(false);
|
|
4580
4754
|
this.stalled = ref(false);
|
|
4755
|
+
this.webrtcUnreachable = ref(false);
|
|
4581
4756
|
this.error = ref();
|
|
4582
4757
|
this.probeInfo = shallowRef();
|
|
4583
4758
|
this.muted = ref(true);
|
|
@@ -4590,6 +4765,10 @@ var StreamConnection = class {
|
|
|
4590
4765
|
this.videoElement = computed(() => toValue(options.videoElement));
|
|
4591
4766
|
this.containerElement = computed(() => toValue(options.containerElement));
|
|
4592
4767
|
this.isReady = computed(() => !!this.camera.value && !!this.videoElement.value && !!this.target.value);
|
|
4768
|
+
this.connectionPath = computed(() => {
|
|
4769
|
+
if (this.status.value !== "connected") return void 0;
|
|
4770
|
+
return this.activeMode.value === "mse" || this.activeMode.value === "mjpeg" ? "tunnel" : this.webrtcPath.value;
|
|
4771
|
+
});
|
|
4593
4772
|
this.scope.run(() => {
|
|
4594
4773
|
const { onTabPaused, onTabVisible } = useTabVisibility();
|
|
4595
4774
|
const hiddenStopDelay = this.stopsWhenHidden ? 0 : DEFAULT_TAB_PAUSE_MS;
|
|
@@ -4605,10 +4784,8 @@ var StreamConnection = class {
|
|
|
4605
4784
|
}, STREAM_CONFIG.WEBRTC.WS_CONNECT_TIMEOUT, { immediate: false });
|
|
4606
4785
|
const connectTimeout = useTimeoutFn(() => {
|
|
4607
4786
|
if (this.abortController.signal.aborted) return;
|
|
4608
|
-
if (this.webrtcHandler && !this.webrtcHandler.isConnected)
|
|
4609
|
-
|
|
4610
|
-
else this.retryAfterNoMedia();
|
|
4611
|
-
} else if (this.webrtcHandler && this.awaitingWebRTCFrame || this.mjpegHandler && !this.isPlaying.value || this.status.value === "connected" && !this.isPlaying.value) {
|
|
4787
|
+
if (this.webrtcHandler && !this.webrtcHandler.isConnected) this.handleWebRTCFailed(true);
|
|
4788
|
+
else if (this.webrtcHandler && this.awaitingWebRTCFrame || this.mjpegHandler && !this.isPlaying.value || this.status.value === "connected" && !this.isPlaying.value) {
|
|
4612
4789
|
if (isPageHidden()) {
|
|
4613
4790
|
this.startConnectTimeout();
|
|
4614
4791
|
return;
|
|
@@ -4620,12 +4797,18 @@ var StreamConnection = class {
|
|
|
4620
4797
|
if (this.abortController.signal.aborted || this.status.value === "connected" || this.status.value === "closed") return;
|
|
4621
4798
|
if (this.mseHandler && !this.mseHandler.isReady) this.retryAfterNoMedia();
|
|
4622
4799
|
}, STREAM_CONFIG.WEBRTC.CONNECT_TIMEOUT, { immediate: false });
|
|
4800
|
+
const disconnectGrace = useTimeoutFn(() => this.handleWebRTCLost(), STREAM_CONFIG.WEBRTC.DISCONNECT_GRACE, { immediate: false });
|
|
4801
|
+
const webrtcRetry = useTimeoutFn(() => this.retryWebRTC(), () => WEBRTC_RETRY_DELAYS[Math.min(this.webrtcRetries, WEBRTC_RETRY_DELAYS.length - 1)], { immediate: false });
|
|
4623
4802
|
this.startWsConnectTimeout = wsConnectTimeout.start;
|
|
4624
4803
|
this.stopWsConnectTimeout = wsConnectTimeout.stop;
|
|
4625
4804
|
this.startConnectTimeout = connectTimeout.start;
|
|
4626
4805
|
this.stopConnectTimeout = connectTimeout.stop;
|
|
4627
4806
|
this.startMseConnectTimeout = mseConnectTimeout.start;
|
|
4628
4807
|
this.stopMseConnectTimeout = mseConnectTimeout.stop;
|
|
4808
|
+
this.startDisconnectGrace = disconnectGrace.start;
|
|
4809
|
+
this.stopDisconnectGrace = disconnectGrace.stop;
|
|
4810
|
+
this.startWebRTCRetry = webrtcRetry.start;
|
|
4811
|
+
this.stopWebRTCRetry = webrtcRetry.stop;
|
|
4629
4812
|
this.setupWatchers();
|
|
4630
4813
|
this.offTabPaused = onTabPaused(() => {
|
|
4631
4814
|
log.debug(`onTabPaused fired — status=${this.status.value}, isReady=${this.isReady.value}, target=${!!this.target.value}`);
|
|
@@ -4658,6 +4841,11 @@ var StreamConnection = class {
|
|
|
4658
4841
|
this.restart();
|
|
4659
4842
|
return;
|
|
4660
4843
|
}
|
|
4844
|
+
if (this.webrtcRetryDue) {
|
|
4845
|
+
log.debug("onTabVisible: trying WebRTC again");
|
|
4846
|
+
this.retryWebRTC();
|
|
4847
|
+
return;
|
|
4848
|
+
}
|
|
4661
4849
|
log.debug("onTabVisible — not paused by visibility, no-op");
|
|
4662
4850
|
});
|
|
4663
4851
|
if (autoStart) whenever(this.isReady, () => {
|
|
@@ -4710,6 +4898,8 @@ var StreamConnection = class {
|
|
|
4710
4898
|
this.status.value = "connecting";
|
|
4711
4899
|
this.error.value = void 0;
|
|
4712
4900
|
this.isPlaying.value = false;
|
|
4901
|
+
this.webrtcUnreachable.value = false;
|
|
4902
|
+
if (this.requestedMode.value !== "mse") relayFirstAddress(this.iceServers?.value);
|
|
4713
4903
|
try {
|
|
4714
4904
|
const init = this.initializeSource();
|
|
4715
4905
|
if (init === "unsupported") {
|
|
@@ -4993,6 +5183,10 @@ var StreamConnection = class {
|
|
|
4993
5183
|
if (active) this.startHealthCheck();
|
|
4994
5184
|
}, { immediate: true });
|
|
4995
5185
|
this.stopWatchers.push(stopHealthWatch);
|
|
5186
|
+
const stopPathWatch = watch(this.connectionPath, (path) => {
|
|
5187
|
+
if (path) log.debug(`connection path: ${path}`);
|
|
5188
|
+
});
|
|
5189
|
+
this.stopWatchers.push(stopPathWatch);
|
|
4996
5190
|
const stopCameraWatch = watch([
|
|
4997
5191
|
this.camera,
|
|
4998
5192
|
this.videoElement,
|
|
@@ -5129,8 +5323,7 @@ var StreamConnection = class {
|
|
|
5129
5323
|
handleNoFramesWhileConnected() {
|
|
5130
5324
|
if (this.sourceLikelyH265() && !this.webrtcTookOver()) reportH265DecodeFailure(this.activeMode.value === "mse" ? "mse" : "webrtc");
|
|
5131
5325
|
if (this.requestedMode.value === "auto" && this.activeMode.value !== "mse" && !this.mjpegHandler && !this.mseRejected) {
|
|
5132
|
-
this.
|
|
5133
|
-
this.webrtcHandler = void 0;
|
|
5326
|
+
this.closeWebRTC();
|
|
5134
5327
|
this.awaitingWebRTCFrame = false;
|
|
5135
5328
|
this.activeMode.value = "mse";
|
|
5136
5329
|
this.isPlaying.value = false;
|
|
@@ -5333,7 +5526,7 @@ var StreamConnection = class {
|
|
|
5333
5526
|
if (peer && peer === this.webrtcHandler && !peer.isConnected) {
|
|
5334
5527
|
this.lastError = err;
|
|
5335
5528
|
this.stopConnectTimeout();
|
|
5336
|
-
if (this.requestedMode.value === "auto") this.fallBackToMSE();
|
|
5529
|
+
if (this.requestedMode.value === "auto") this.fallBackToMSE(false);
|
|
5337
5530
|
else this.retryLater(err);
|
|
5338
5531
|
return;
|
|
5339
5532
|
}
|
|
@@ -5370,14 +5563,19 @@ var StreamConnection = class {
|
|
|
5370
5563
|
const handler = createWebRTCHandler({
|
|
5371
5564
|
mode,
|
|
5372
5565
|
iceServers: this.iceServers?.value,
|
|
5566
|
+
relayFirst: relayFirstAddress(this.iceServers?.value),
|
|
5373
5567
|
signal: this.abortController.signal,
|
|
5374
5568
|
onConnected: (stream) => this.handleWebRTCConnected(stream),
|
|
5569
|
+
onReconnected: () => this.handleWebRTCReconnected(),
|
|
5375
5570
|
onDisconnected: () => this.handleWebRTCDisconnected(),
|
|
5376
|
-
onFailed: () => this.handleWebRTCFailed(),
|
|
5571
|
+
onFailed: () => this.handleWebRTCFailed(true),
|
|
5377
5572
|
onCandidate: (candidate) => this.sendWsMessage({
|
|
5378
5573
|
type: "webrtc/candidate",
|
|
5379
5574
|
value: candidate
|
|
5380
|
-
})
|
|
5575
|
+
}),
|
|
5576
|
+
onPath: (path) => {
|
|
5577
|
+
this.webrtcPath.value = path;
|
|
5578
|
+
}
|
|
5381
5579
|
});
|
|
5382
5580
|
this.webrtcHandler = handler;
|
|
5383
5581
|
const offer = await handler.createOffer();
|
|
@@ -5385,12 +5583,14 @@ var StreamConnection = class {
|
|
|
5385
5583
|
}
|
|
5386
5584
|
handleWebRTCConnected(stream) {
|
|
5387
5585
|
if (this.abortController.signal.aborted) return;
|
|
5586
|
+
this.stopDisconnectGrace();
|
|
5388
5587
|
const video = this.videoElement.value;
|
|
5389
5588
|
if (!video) return;
|
|
5390
5589
|
if (this.hasVideo.value && stream.getVideoTracks().length === 0) {
|
|
5391
|
-
this.handleWebRTCFailed();
|
|
5590
|
+
this.handleWebRTCFailed(false);
|
|
5392
5591
|
return;
|
|
5393
5592
|
}
|
|
5593
|
+
this.webrtcUnreachable.value = false;
|
|
5394
5594
|
this.noteDeliveredAudio(stream.getAudioTracks().length > 0);
|
|
5395
5595
|
this.activeMode.value = this.requestedMode.value === "auto" ? "webrtc" : this.requestedMode.value;
|
|
5396
5596
|
this.lastMediaStream = stream;
|
|
@@ -5407,26 +5607,35 @@ var StreamConnection = class {
|
|
|
5407
5607
|
if (this.requestedMode.value === "auto" && this.mseHandler) this.dropMSE();
|
|
5408
5608
|
}
|
|
5409
5609
|
handleWebRTCDisconnected() {
|
|
5410
|
-
if (this.status.value
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5610
|
+
if (this.status.value === "closed" || this.abortController.signal.aborted) return;
|
|
5611
|
+
log.debug("WebRTC disconnected, waiting for it to come back");
|
|
5612
|
+
if (this.activeMode.value !== "mse") this.status.value = "reconnecting";
|
|
5613
|
+
this.startDisconnectGrace();
|
|
5614
|
+
}
|
|
5615
|
+
handleWebRTCReconnected() {
|
|
5616
|
+
if (this.abortController.signal.aborted) return;
|
|
5617
|
+
log.debug("WebRTC connected again");
|
|
5618
|
+
this.stopDisconnectGrace();
|
|
5619
|
+
this.status.value = "connected";
|
|
5620
|
+
}
|
|
5621
|
+
handleWebRTCLost() {
|
|
5622
|
+
log.debug("WebRTC did not come back");
|
|
5623
|
+
if (this.requestedMode.value !== "auto" || this.mseRejected || !hasMediaSource()) {
|
|
5624
|
+
this.retryLater();
|
|
5625
|
+
return;
|
|
5419
5626
|
}
|
|
5627
|
+
if (this.activeMode.value !== "mse") this.isPlaying.value = false;
|
|
5628
|
+
this.fallBackToMSE(true);
|
|
5420
5629
|
}
|
|
5421
|
-
handleWebRTCFailed() {
|
|
5630
|
+
handleWebRTCFailed(retryWebRTC) {
|
|
5422
5631
|
if (this.abortController.signal.aborted) return;
|
|
5423
5632
|
this.stopConnectTimeout();
|
|
5424
|
-
if (this.requestedMode.value === "auto") this.fallBackToMSE();
|
|
5633
|
+
if (this.requestedMode.value === "auto") this.fallBackToMSE(retryWebRTC);
|
|
5425
5634
|
else this.retryLater(this.lastError ?? /* @__PURE__ */ new Error("WebRTC connection failed"));
|
|
5426
5635
|
}
|
|
5427
|
-
fallBackToMSE() {
|
|
5428
|
-
this.
|
|
5429
|
-
this.
|
|
5636
|
+
fallBackToMSE(retryWebRTC) {
|
|
5637
|
+
this.closeWebRTC();
|
|
5638
|
+
this.webrtcUnreachable.value = retryWebRTC;
|
|
5430
5639
|
this.awaitingWebRTCFrame = false;
|
|
5431
5640
|
if (this.mseRejected) {
|
|
5432
5641
|
this.retryLater(this.lastError);
|
|
@@ -5436,6 +5645,33 @@ var StreamConnection = class {
|
|
|
5436
5645
|
if (this.mseHandler?.isReady) this.handleMSEReady();
|
|
5437
5646
|
else this.startMSE();
|
|
5438
5647
|
this.resumeBackchannel();
|
|
5648
|
+
if (retryWebRTC) this.scheduleWebRTCRetry();
|
|
5649
|
+
}
|
|
5650
|
+
scheduleWebRTCRetry() {
|
|
5651
|
+
if (this.webrtcPlayingSince !== void 0 && performance.now() - this.webrtcPlayingSince >= RETRY_RESET_AFTER) this.webrtcRetries = 0;
|
|
5652
|
+
this.webrtcPlayingSince = void 0;
|
|
5653
|
+
this.startWebRTCRetry();
|
|
5654
|
+
}
|
|
5655
|
+
retryWebRTC() {
|
|
5656
|
+
if (isPageHidden()) {
|
|
5657
|
+
this.webrtcRetryDue = true;
|
|
5658
|
+
return;
|
|
5659
|
+
}
|
|
5660
|
+
this.webrtcRetryDue = false;
|
|
5661
|
+
this.webrtcRetries++;
|
|
5662
|
+
const signalingBusy = !!this.offerPeer || !!this.backchannelHandler;
|
|
5663
|
+
if (this.requestedMode.value !== "auto" || this.activeMode.value !== "mse" || this.status.value !== "connected" || this.webrtcHandler || signalingBusy) {
|
|
5664
|
+
this.startWebRTCRetry();
|
|
5665
|
+
return;
|
|
5666
|
+
}
|
|
5667
|
+
log.debug("trying WebRTC again while MSE plays");
|
|
5668
|
+
this.startWebRTC("webrtc");
|
|
5669
|
+
}
|
|
5670
|
+
closeWebRTC() {
|
|
5671
|
+
this.stopDisconnectGrace();
|
|
5672
|
+
this.webrtcHandler?.close();
|
|
5673
|
+
this.webrtcHandler = void 0;
|
|
5674
|
+
this.webrtcPath.value = void 0;
|
|
5439
5675
|
}
|
|
5440
5676
|
startMSE() {
|
|
5441
5677
|
if (this.mseHandler) return;
|
|
@@ -5599,6 +5835,7 @@ var StreamConnection = class {
|
|
|
5599
5835
|
if (!this.webrtcHandler || this.webrtcHandler.isConnected) {
|
|
5600
5836
|
this.stopConnectTimeout();
|
|
5601
5837
|
this.awaitingWebRTCFrame = false;
|
|
5838
|
+
if (this.webrtcHandler) this.webrtcPlayingSince = performance.now();
|
|
5602
5839
|
this.resumeBackchannel();
|
|
5603
5840
|
}
|
|
5604
5841
|
this.error.value = void 0;
|
|
@@ -5680,12 +5917,15 @@ var StreamConnection = class {
|
|
|
5680
5917
|
this.stopConnectTimeout();
|
|
5681
5918
|
this.stopWsConnectTimeout();
|
|
5682
5919
|
this.stopMseConnectTimeout();
|
|
5920
|
+
this.stopWebRTCRetry();
|
|
5921
|
+
this.webrtcRetryDue = false;
|
|
5922
|
+
this.webrtcRetries = 0;
|
|
5923
|
+
this.webrtcPlayingSince = void 0;
|
|
5683
5924
|
this.stopHealthCheck();
|
|
5684
5925
|
this.cancelFirstFrameWatch?.();
|
|
5685
5926
|
this.awaitingWebRTCFrame = false;
|
|
5686
5927
|
this.lastMediaStream = null;
|
|
5687
|
-
this.
|
|
5688
|
-
this.webrtcHandler = void 0;
|
|
5928
|
+
this.closeWebRTC();
|
|
5689
5929
|
this.mseHandler?.close();
|
|
5690
5930
|
this.mseHandler = void 0;
|
|
5691
5931
|
this.mjpegHandler?.close();
|
|
@@ -5709,6 +5949,7 @@ var StreamConnection = class {
|
|
|
5709
5949
|
const handler = createBackchannelHandler({
|
|
5710
5950
|
filterUdp: this.requestedMode.value === "webrtc/tcp",
|
|
5711
5951
|
iceServers: this.iceServers?.value,
|
|
5952
|
+
relayFirst: relayFirstAddress(this.iceServers?.value),
|
|
5712
5953
|
signal: this.abortController.signal,
|
|
5713
5954
|
onCandidate: (candidate) => this.sendWsMessage({
|
|
5714
5955
|
type: "webrtc/candidate",
|
|
@@ -6114,6 +6355,8 @@ function useCameraStream(options) {
|
|
|
6114
6355
|
const status = computed(() => currentStream.value?.status.value ?? "idle");
|
|
6115
6356
|
const isPlaying = computed(() => currentStream.value?.isPlaying.value ?? false);
|
|
6116
6357
|
const stalled = computed(() => currentStream.value?.stalled?.value ?? false);
|
|
6358
|
+
const connectionPath = computed(() => currentStream.value?.connectionPath?.value);
|
|
6359
|
+
const webrtcUnreachable = computed(() => currentStream.value?.webrtcUnreachable?.value ?? false);
|
|
6117
6360
|
const activeMode = computed(() => currentStream.value?.activeMode.value ?? "webrtc");
|
|
6118
6361
|
const activeResolution = computed(() => currentStream.value?.activeResolution.value ?? "low-resolution");
|
|
6119
6362
|
const degradedFrom = computed(() => currentStream.value?.degradedFrom.value ?? null);
|
|
@@ -6665,6 +6908,8 @@ function useCameraStream(options) {
|
|
|
6665
6908
|
status,
|
|
6666
6909
|
isPlaying,
|
|
6667
6910
|
stalled,
|
|
6911
|
+
connectionPath,
|
|
6912
|
+
webrtcUnreachable,
|
|
6668
6913
|
activeMode,
|
|
6669
6914
|
activeResolution,
|
|
6670
6915
|
degradedFrom,
|
|
@@ -6722,7 +6967,8 @@ function useCoreManager() {
|
|
|
6722
6967
|
getPluginsByInterface: (interfaceName) => rpcCall(ctx.rpc, (rpc) => proxy(rpc).getPluginsByInterface(interfaceName)),
|
|
6723
6968
|
getCloudServerId: () => rpcCall(ctx.rpc, (rpc) => proxy(rpc).getCloudServerId()),
|
|
6724
6969
|
getRestoreGeneration: () => rpcCall(ctx.rpc, (rpc) => proxy(rpc).getRestoreGeneration()),
|
|
6725
|
-
getInstanceId: () => rpcCall(ctx.rpc, (rpc) => proxy(rpc).getInstanceId())
|
|
6970
|
+
getInstanceId: () => rpcCall(ctx.rpc, (rpc) => proxy(rpc).getInstanceId()),
|
|
6971
|
+
getLanguage: () => rpcCall(ctx.rpc, (rpc) => proxy(rpc).getLanguage())
|
|
6726
6972
|
};
|
|
6727
6973
|
}
|
|
6728
6974
|
// src/composables/useTerminal.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camera.ui/browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"description": "camera.ui browser client",
|
|
5
5
|
"author": "seydx (https://github.com/cameraui/clients)",
|
|
6
6
|
"type": "module",
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@camera.ui/logger": "file:../../packages/logger",
|
|
41
41
|
"@camera.ui/rpc": "^1.1.2",
|
|
42
|
-
"@camera.ui/sdk": "~1.4.
|
|
42
|
+
"@camera.ui/sdk": "~1.4.8",
|
|
43
43
|
"@camera.ui/transport": "file:../../packages/transport",
|
|
44
44
|
"@eneris/push-receiver": "^4.4.0",
|
|
45
45
|
"@microsoft/api-extractor": "^7.60.1",
|
|
46
46
|
"@stylistic/eslint-plugin": "^5.10.0",
|
|
47
47
|
"@types/webrtc": "^0.0.47",
|
|
48
|
-
"@typescript-eslint/parser": "^8.72.
|
|
48
|
+
"@typescript-eslint/parser": "^8.72.2",
|
|
49
49
|
"@vitejs/plugin-vue": "^6.0.10",
|
|
50
50
|
"@vue/eslint-config-prettier": "^10.2.0",
|
|
51
51
|
"@vue/eslint-config-typescript": "^14.9.0",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"@vueuse/core": "^15.0.0",
|
|
55
55
|
"@webgpu/types": "^0.1.74",
|
|
56
56
|
"eslint": "9.39.2",
|
|
57
|
-
"eslint-plugin-vue": "^10.12.
|
|
57
|
+
"eslint-plugin-vue": "^10.12.1",
|
|
58
58
|
"globals": "^17.14.0",
|
|
59
59
|
"prettier": "^3.9.10",
|
|
60
60
|
"rimraf": "^6.1.4",
|
|
61
61
|
"typescript": "5.9.3",
|
|
62
|
-
"typescript-eslint": "^8.72.
|
|
62
|
+
"typescript-eslint": "^8.72.2",
|
|
63
63
|
"unplugin-dts": "^1.1.2",
|
|
64
64
|
"updates": "^20.0.1",
|
|
65
|
-
"vite": "^8.3.
|
|
65
|
+
"vite": "^8.3.7",
|
|
66
66
|
"vitest": "^5.1.0",
|
|
67
67
|
"vue": "^3.5.45",
|
|
68
68
|
"vue-tsc": "3.3.11"
|