@lingxia/bridge 0.10.0 → 0.11.1
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/bridge-runtime.es2020.js +1 -1
- package/dist/bridge-runtime.es5.js +1 -1
- package/dist/es2020/bridge.js +200 -20
- package/dist/es2020/index.js +1 -0
- package/dist/es2020/runtime-env.js +25 -0
- package/dist/es2020/ui/action-sheet.js +2 -2
- package/dist/es5/bridge.js +205 -25
- package/dist/es5/index.js +1 -0
- package/dist/es5/runtime-env.js +26 -0
- package/dist/es5/ui/action-sheet.js +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/runtime-env.d.ts +4 -0
- package/dist/types.d.ts +6 -0
- package/package.json +1 -1
package/dist/es5/bridge.js
CHANGED
|
@@ -84,7 +84,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
|
|
|
84
84
|
import { BRIDGE_ERROR } from "./types";
|
|
85
85
|
import { toBridgeError, toNativeError } from "./invocation";
|
|
86
86
|
import { installNativeComponentCoverageMonitor } from "./nativecomponents/coverage-monitor";
|
|
87
|
-
import { BRIDGE_CONFIG, getCommunicationMethod, getPlatformOS, isAndroid, isHarmony, isIOS, isMacOS, isWindows, isDesktop, } from "./runtime-env";
|
|
87
|
+
import { BRIDGE_CONFIG, getCommunicationMethod, getPlatformOS, isAndroid, isHarmony, isIOS, isMacOS, isWindows, isDesktop, isApple, isDevSession, isRunner, } from "./runtime-env";
|
|
88
88
|
var NATIVE_HANDLER_NAME = "LingXia";
|
|
89
89
|
var GLOBAL_RECEIVER_NAME = "__LingXiaRecvMessage";
|
|
90
90
|
var DEFAULT_TIMEOUT_MS = 5000;
|
|
@@ -159,11 +159,22 @@ function activateReceiver(receiver) {
|
|
|
159
159
|
function isDebugEnabled(flag) {
|
|
160
160
|
return debugFlags.all || debugFlags[flag];
|
|
161
161
|
}
|
|
162
|
+
// `log` is the bridge's own protocol/lifecycle trace. Native log capture
|
|
163
|
+
// forwards whatever the page emits to `console`, so the bridge itself decides
|
|
164
|
+
// whether to surface this framework chatter: only in a `lingxia dev` session
|
|
165
|
+
// (or when a debug flag is set). Shipped apps stay quiet, leaving the captured
|
|
166
|
+
// stream to the page's own output plus bridge warnings/errors.
|
|
162
167
|
function log() {
|
|
163
168
|
var args = [];
|
|
164
169
|
for (var _i = 0; _i < arguments.length; _i++) {
|
|
165
170
|
args[_i] = arguments[_i];
|
|
166
171
|
}
|
|
172
|
+
if (!isDevSession() &&
|
|
173
|
+
!debugFlags.all &&
|
|
174
|
+
!debugFlags.proto &&
|
|
175
|
+
!debugFlags.data) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
167
178
|
console.log.apply(console, __spreadArray([LOG_PREFIX], __read(args), false));
|
|
168
179
|
}
|
|
169
180
|
function warn() {
|
|
@@ -218,6 +229,17 @@ var appleDownstreamTask = null;
|
|
|
218
229
|
var appleDownstreamAbortController = null;
|
|
219
230
|
var appleReconnectTimer = null;
|
|
220
231
|
var appleReconnectDelayMs = APPLE_RECONNECT_BASE_MS;
|
|
232
|
+
var appleDownstreamDisposed = false;
|
|
233
|
+
// Highest transport frame seq processed. Sent as `?from=` on reconnect so the
|
|
234
|
+
// host replays the gap; survives reconnects so a WebKit-replaced stream resumes
|
|
235
|
+
// without losing frames or tearing down the bridge session.
|
|
236
|
+
var appleLastFrameSeq = 0;
|
|
237
|
+
// Liveness tracking: the host heartbeats an idle connection, so a gap longer
|
|
238
|
+
// than a couple of heartbeats means the connection is silently dead and we
|
|
239
|
+
// reconnect proactively instead of waiting for a read error that may never come.
|
|
240
|
+
var APPLE_DOWNSTREAM_STALE_MS = 35000;
|
|
241
|
+
var appleLastFrameAt = 0;
|
|
242
|
+
var appleWatchdogTimer = null;
|
|
221
243
|
var portInitState = {
|
|
222
244
|
listenerInstalled: false,
|
|
223
245
|
promise: null,
|
|
@@ -262,6 +284,23 @@ function clearAppleReconnectTimer() {
|
|
|
262
284
|
appleReconnectTimer = null;
|
|
263
285
|
}
|
|
264
286
|
}
|
|
287
|
+
function disposeAppleDownstream(reason) {
|
|
288
|
+
if (!useAppleDownstreamTransport() || appleDownstreamDisposed)
|
|
289
|
+
return;
|
|
290
|
+
appleDownstreamDisposed = true;
|
|
291
|
+
clearAppleReconnectTimer();
|
|
292
|
+
if (appleWatchdogTimer !== null) {
|
|
293
|
+
clearInterval(appleWatchdogTimer);
|
|
294
|
+
appleWatchdogTimer = null;
|
|
295
|
+
}
|
|
296
|
+
if (typeof document !== "undefined") {
|
|
297
|
+
document.removeEventListener("visibilitychange", handleAppleVisibilityChange);
|
|
298
|
+
}
|
|
299
|
+
appleForcedReconnectPending = false;
|
|
300
|
+
appleDownstreamAbortController === null || appleDownstreamAbortController === void 0 ? void 0 : appleDownstreamAbortController.abort();
|
|
301
|
+
appleDownstreamConnected = false;
|
|
302
|
+
resetHandshakeState(reason, true);
|
|
303
|
+
}
|
|
265
304
|
function closeActiveChannelsFromTransport(reason) {
|
|
266
305
|
var e_2, _a;
|
|
267
306
|
try {
|
|
@@ -335,7 +374,7 @@ function processAppleDownstreamBuffer(buffer) {
|
|
|
335
374
|
if (!line)
|
|
336
375
|
continue;
|
|
337
376
|
try {
|
|
338
|
-
|
|
377
|
+
handleAppleDownstreamFrame(JSON.parse(line));
|
|
339
378
|
}
|
|
340
379
|
catch (e) {
|
|
341
380
|
warn("Apple downstream parse error:", e, line);
|
|
@@ -343,9 +382,42 @@ function processAppleDownstreamBuffer(buffer) {
|
|
|
343
382
|
}
|
|
344
383
|
return remaining;
|
|
345
384
|
}
|
|
385
|
+
// Unwrap the transport envelope: {"lxff":seq,"m":<message>} carries a business
|
|
386
|
+
// message with its frame seq; {"lxreset":true} means the host cannot replay our
|
|
387
|
+
// resume point and we must re-handshake.
|
|
388
|
+
function handleAppleDownstreamFrame(frame) {
|
|
389
|
+
if (!frame || typeof frame !== "object")
|
|
390
|
+
return;
|
|
391
|
+
// Any frame — data, heartbeat, or reset — proves the connection is alive.
|
|
392
|
+
appleLastFrameAt = Date.now();
|
|
393
|
+
var record = frame;
|
|
394
|
+
if (record.lxshutdown === true) {
|
|
395
|
+
disposeAppleDownstream("Apple downstream closed");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
if (record.lxhb !== undefined)
|
|
399
|
+
return; // heartbeat: liveness only, nothing to dispatch
|
|
400
|
+
if (record.lxreset === true) {
|
|
401
|
+
appleLastFrameSeq = 0;
|
|
402
|
+
resetHandshakeState("Apple downstream reset", true);
|
|
403
|
+
startHandshake();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
if (typeof record.lxff === "number") {
|
|
407
|
+
// Replayed frames after a reconnect can repeat the last-seen seq; ignore
|
|
408
|
+
// anything we have already processed so the session is not double-fed.
|
|
409
|
+
if (record.lxff <= appleLastFrameSeq)
|
|
410
|
+
return;
|
|
411
|
+
appleLastFrameSeq = record.lxff;
|
|
412
|
+
handleIncomingMessage(record.m);
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
// Legacy/un-enveloped line (e.g. a bare keepalive); pass through.
|
|
416
|
+
handleIncomingMessage(frame);
|
|
417
|
+
}
|
|
346
418
|
function runAppleDownstream() {
|
|
347
419
|
return __awaiter(this, void 0, void 0, function () {
|
|
348
|
-
var controller, response, reader, decoder, buffered, _a, value, done, tail, e_5;
|
|
420
|
+
var controller, separator, url, response, reader, decoder, buffered, _a, value, done, tail, e_5;
|
|
349
421
|
return __generator(this, function (_b) {
|
|
350
422
|
switch (_b.label) {
|
|
351
423
|
case 0:
|
|
@@ -354,7 +426,9 @@ function runAppleDownstream() {
|
|
|
354
426
|
}
|
|
355
427
|
controller = new AbortController();
|
|
356
428
|
appleDownstreamAbortController = controller;
|
|
357
|
-
|
|
429
|
+
separator = APPLE_DOWNSTREAM_URL.includes("?") ? "&" : "?";
|
|
430
|
+
url = "".concat(APPLE_DOWNSTREAM_URL).concat(separator, "from=").concat(appleLastFrameSeq);
|
|
431
|
+
return [4 /*yield*/, fetch(url, {
|
|
358
432
|
method: "GET",
|
|
359
433
|
cache: "no-store",
|
|
360
434
|
headers: { Accept: "application/x-ndjson" },
|
|
@@ -365,11 +439,16 @@ function runAppleDownstream() {
|
|
|
365
439
|
if (!response.ok) {
|
|
366
440
|
throw new Error("Apple downstream HTTP ".concat(response.status));
|
|
367
441
|
}
|
|
442
|
+
if (response.headers.get("X-LingXia-Bridge-Shutdown") === "1") {
|
|
443
|
+
disposeAppleDownstream("Apple downstream closed");
|
|
444
|
+
return [2 /*return*/];
|
|
445
|
+
}
|
|
368
446
|
if (!response.body) {
|
|
369
447
|
throw new Error("Apple downstream response body unavailable");
|
|
370
448
|
}
|
|
371
449
|
appleDownstreamConnected = true;
|
|
372
450
|
appleReconnectDelayMs = APPLE_RECONNECT_BASE_MS;
|
|
451
|
+
appleLastFrameAt = Date.now();
|
|
373
452
|
if (isDebugEnabled("proto"))
|
|
374
453
|
log("Apple downstream connected");
|
|
375
454
|
startHandshake();
|
|
@@ -395,7 +474,7 @@ function runAppleDownstream() {
|
|
|
395
474
|
tail = buffered.trim();
|
|
396
475
|
if (tail) {
|
|
397
476
|
try {
|
|
398
|
-
|
|
477
|
+
handleAppleDownstreamFrame(JSON.parse(tail));
|
|
399
478
|
}
|
|
400
479
|
catch (e) {
|
|
401
480
|
warn("Apple downstream trailing parse error:", e, tail);
|
|
@@ -418,13 +497,18 @@ function runAppleDownstream() {
|
|
|
418
497
|
});
|
|
419
498
|
});
|
|
420
499
|
}
|
|
421
|
-
function scheduleAppleDownstreamReconnect(reason) {
|
|
422
|
-
if (
|
|
500
|
+
function scheduleAppleDownstreamReconnect(reason, immediate) {
|
|
501
|
+
if (immediate === void 0) { immediate = false; }
|
|
502
|
+
if (!useAppleDownstreamTransport() || appleDownstreamDisposed)
|
|
423
503
|
return;
|
|
424
504
|
clearAppleReconnectTimer();
|
|
425
|
-
var delay = appleReconnectDelayMs;
|
|
426
|
-
|
|
427
|
-
|
|
505
|
+
var delay = immediate ? 0 : appleReconnectDelayMs;
|
|
506
|
+
if (!immediate) {
|
|
507
|
+
appleReconnectDelayMs = Math.min(appleReconnectDelayMs * 2, APPLE_RECONNECT_MAX_MS);
|
|
508
|
+
}
|
|
509
|
+
var message = immediate
|
|
510
|
+
? "Apple downstream completed, reconnecting immediately: ".concat(reason)
|
|
511
|
+
: "Apple downstream disconnected, retrying in ".concat(delay, "ms: ").concat(reason);
|
|
428
512
|
if (delay <= APPLE_RECONNECT_BASE_MS) {
|
|
429
513
|
log(message);
|
|
430
514
|
}
|
|
@@ -437,12 +521,16 @@ function scheduleAppleDownstreamReconnect(reason) {
|
|
|
437
521
|
}, delay);
|
|
438
522
|
}
|
|
439
523
|
function ensureAppleDownstream() {
|
|
440
|
-
if (!useAppleDownstreamTransport())
|
|
524
|
+
if (!useAppleDownstreamTransport() || appleDownstreamDisposed)
|
|
441
525
|
return;
|
|
442
526
|
if (appleDownstreamTask)
|
|
443
527
|
return;
|
|
444
528
|
clearAppleReconnectTimer();
|
|
529
|
+
var completedCleanly = false;
|
|
445
530
|
appleDownstreamTask = runAppleDownstream()
|
|
531
|
+
.then(function () {
|
|
532
|
+
completedCleanly = true;
|
|
533
|
+
})
|
|
446
534
|
.catch(function (e) {
|
|
447
535
|
if (appleDownstreamAbortController === null || appleDownstreamAbortController === void 0 ? void 0 : appleDownstreamAbortController.signal.aborted)
|
|
448
536
|
return;
|
|
@@ -453,13 +541,72 @@ function ensureAppleDownstream() {
|
|
|
453
541
|
var aborted = (_a = appleDownstreamAbortController === null || appleDownstreamAbortController === void 0 ? void 0 : appleDownstreamAbortController.signal.aborted) !== null && _a !== void 0 ? _a : false;
|
|
454
542
|
appleDownstreamTask = null;
|
|
455
543
|
appleDownstreamAbortController = null;
|
|
456
|
-
var wasConnected = appleDownstreamConnected;
|
|
457
544
|
appleDownstreamConnected = false;
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
545
|
+
// A dropped transport is not a dead session: reconnect resumes from the
|
|
546
|
+
// last seq and the host replays the gap, so the handshake and in-flight
|
|
547
|
+
// streams stay intact. Only a host-sent reset (handled on the frame path)
|
|
548
|
+
// or a real abort tears things down.
|
|
549
|
+
if (!aborted && !appleDownstreamDisposed) {
|
|
550
|
+
// Native intentionally completes bootstrap/replay responses to flush
|
|
551
|
+
// WebKit. Reconnect without the failure backoff so a live stream can
|
|
552
|
+
// catch up and settle onto a long-lived downstream.
|
|
553
|
+
scheduleAppleDownstreamReconnect("stream closed", completedCleanly);
|
|
554
|
+
}
|
|
461
555
|
});
|
|
462
556
|
}
|
|
557
|
+
// Drop the current downstream and reconnect, exactly as WebKit does when it
|
|
558
|
+
// replaces the streaming fetch. The reconnect carries the real `from=<lastSeq>`,
|
|
559
|
+
// so nothing is lost. Driven by the staleness watchdog and the foreground
|
|
560
|
+
// handler, and exposed as a dev hook for repro harnesses. A no-op off Apple.
|
|
561
|
+
var appleForcedReconnectPending = false;
|
|
562
|
+
function forceDownstreamReconnect() {
|
|
563
|
+
if (!useAppleDownstreamTransport() || appleDownstreamDisposed)
|
|
564
|
+
return;
|
|
565
|
+
// Coalesce a burst of triggers (rapid foreground toggles, repeated calls)
|
|
566
|
+
// into a single reconnect so they cannot stack concurrent fetches.
|
|
567
|
+
if (appleForcedReconnectPending)
|
|
568
|
+
return;
|
|
569
|
+
appleForcedReconnectPending = true;
|
|
570
|
+
var task = appleDownstreamTask;
|
|
571
|
+
appleDownstreamAbortController === null || appleDownstreamAbortController === void 0 ? void 0 : appleDownstreamAbortController.abort();
|
|
572
|
+
var reconnect = function () {
|
|
573
|
+
appleForcedReconnectPending = false;
|
|
574
|
+
ensureAppleDownstream();
|
|
575
|
+
};
|
|
576
|
+
if (task)
|
|
577
|
+
task.finally(reconnect);
|
|
578
|
+
else
|
|
579
|
+
reconnect();
|
|
580
|
+
}
|
|
581
|
+
// A silently dead connection (half-open socket) never surfaces a read error, so
|
|
582
|
+
// poll for heartbeat staleness and reconnect when the host has gone quiet.
|
|
583
|
+
function startAppleWatchdog() {
|
|
584
|
+
if (!useAppleDownstreamTransport() ||
|
|
585
|
+
appleDownstreamDisposed ||
|
|
586
|
+
appleWatchdogTimer !== null)
|
|
587
|
+
return;
|
|
588
|
+
appleWatchdogTimer = setInterval(function () {
|
|
589
|
+
// No `connected` guard: a reconnect that hung before delivering a frame
|
|
590
|
+
// also goes stale, and forcing a reconnect aborts and retries it.
|
|
591
|
+
if (appleLastFrameAt === 0)
|
|
592
|
+
return;
|
|
593
|
+
if (Date.now() - appleLastFrameAt > APPLE_DOWNSTREAM_STALE_MS) {
|
|
594
|
+
warn("Apple downstream stale (no heartbeat), forcing reconnect");
|
|
595
|
+
forceDownstreamReconnect();
|
|
596
|
+
}
|
|
597
|
+
}, 5000);
|
|
598
|
+
}
|
|
599
|
+
// WebKit suspends/throttles a backgrounded webview and may tear the stream
|
|
600
|
+
// down; reconnect the moment it returns so the UI is never left stale.
|
|
601
|
+
function handleAppleVisibilityChange() {
|
|
602
|
+
if (document.visibilityState === "visible")
|
|
603
|
+
forceDownstreamReconnect();
|
|
604
|
+
}
|
|
605
|
+
function installAppleForegroundReconnect() {
|
|
606
|
+
if (!useAppleDownstreamTransport() || typeof document === "undefined")
|
|
607
|
+
return;
|
|
608
|
+
document.addEventListener("visibilitychange", handleAppleVisibilityChange);
|
|
609
|
+
}
|
|
463
610
|
function getMessagePort() {
|
|
464
611
|
var _a, _b;
|
|
465
612
|
if (messagePort)
|
|
@@ -542,6 +689,9 @@ function createStreamHandle(id, cancelFn) {
|
|
|
542
689
|
var _a;
|
|
543
690
|
var listeners = createListenerBuckets();
|
|
544
691
|
var done = false;
|
|
692
|
+
// Only buffer for the async iterator; long-lived streams consumed via
|
|
693
|
+
// .on("data") would otherwise accumulate payloads forever.
|
|
694
|
+
var iteratorRequested = false;
|
|
545
695
|
var resolveResult = function () { };
|
|
546
696
|
var rejectResult = function () { };
|
|
547
697
|
var pendingData = [];
|
|
@@ -564,6 +714,7 @@ function createStreamHandle(id, cancelFn) {
|
|
|
564
714
|
}
|
|
565
715
|
},
|
|
566
716
|
_a[Symbol.asyncIterator] = function () {
|
|
717
|
+
iteratorRequested = true;
|
|
567
718
|
return {
|
|
568
719
|
next: function () {
|
|
569
720
|
if (pendingData.length > 0) {
|
|
@@ -593,7 +744,7 @@ function createStreamHandle(id, cancelFn) {
|
|
|
593
744
|
if (pendingReads.length > 0) {
|
|
594
745
|
pendingReads.shift().resolve({ done: false, value: payload });
|
|
595
746
|
}
|
|
596
|
-
else {
|
|
747
|
+
else if (iteratorRequested) {
|
|
597
748
|
pendingData.push(payload);
|
|
598
749
|
}
|
|
599
750
|
try {
|
|
@@ -680,6 +831,9 @@ function createChannel(id, sendFn, closeFn) {
|
|
|
680
831
|
var open = false;
|
|
681
832
|
var outboundSeq = 0;
|
|
682
833
|
var closed = false;
|
|
834
|
+
// Only buffer for the async iterator or pre-first-listener replay;
|
|
835
|
+
// otherwise a listener-driven channel would accumulate payloads forever.
|
|
836
|
+
var iteratorRequested = false;
|
|
683
837
|
var pendingData = [];
|
|
684
838
|
var pendingReads = [];
|
|
685
839
|
var channel = (_a = {
|
|
@@ -732,6 +886,7 @@ function createChannel(id, sendFn, closeFn) {
|
|
|
732
886
|
}
|
|
733
887
|
},
|
|
734
888
|
_a[Symbol.asyncIterator] = function () {
|
|
889
|
+
iteratorRequested = true;
|
|
735
890
|
return {
|
|
736
891
|
next: function () {
|
|
737
892
|
if (pendingData.length > 0) {
|
|
@@ -764,7 +919,7 @@ function createChannel(id, sendFn, closeFn) {
|
|
|
764
919
|
if (pendingReads.length > 0) {
|
|
765
920
|
pendingReads.shift().resolve({ done: false, value: payload });
|
|
766
921
|
}
|
|
767
|
-
else {
|
|
922
|
+
else if (iteratorRequested || listeners.data.size === 0) {
|
|
768
923
|
pendingData.push(payload);
|
|
769
924
|
}
|
|
770
925
|
try {
|
|
@@ -884,7 +1039,15 @@ function isTransportReady() {
|
|
|
884
1039
|
communicationMethod === WEB_MESSAGE_TYPE);
|
|
885
1040
|
}
|
|
886
1041
|
function canSendAppMessages() {
|
|
887
|
-
|
|
1042
|
+
if (!handshakeDone)
|
|
1043
|
+
return false;
|
|
1044
|
+
// Apple upstream posts through WKScriptMessageHandler independently of the
|
|
1045
|
+
// downstream fetch. During a replay reconnect, native retains responses by
|
|
1046
|
+
// sequence, so treating that short gap as an unsendable session can strand
|
|
1047
|
+
// requests in the outbox with no second `ready` event to flush them.
|
|
1048
|
+
if (useAppleDownstreamTransport())
|
|
1049
|
+
return true;
|
|
1050
|
+
return isTransportReady();
|
|
888
1051
|
}
|
|
889
1052
|
function rejectPendingRequest(reqId, err) {
|
|
890
1053
|
var _a;
|
|
@@ -1230,7 +1393,7 @@ function applySnapshotFromResult(result) {
|
|
|
1230
1393
|
}
|
|
1231
1394
|
function handleIncomingMessage(msg) {
|
|
1232
1395
|
var e_16, _a;
|
|
1233
|
-
var _b, _c;
|
|
1396
|
+
var _b, _c, _d, _e;
|
|
1234
1397
|
// Handle native component events (from Android NativeBridge.sendEventToView)
|
|
1235
1398
|
if (msg && typeof msg === "object") {
|
|
1236
1399
|
var obj = msg;
|
|
@@ -1271,15 +1434,15 @@ function handleIncomingMessage(msg) {
|
|
|
1271
1434
|
handshakeRetryCount = 0;
|
|
1272
1435
|
if (message.hostMethods) {
|
|
1273
1436
|
try {
|
|
1274
|
-
for (var
|
|
1275
|
-
var
|
|
1437
|
+
for (var _f = __values(Object.entries(message.hostMethods)), _g = _f.next(); !_g.done; _g = _f.next()) {
|
|
1438
|
+
var _h = __read(_g.value, 2), k = _h[0], v = _h[1];
|
|
1276
1439
|
hostMethodKinds[k] = v;
|
|
1277
1440
|
}
|
|
1278
1441
|
}
|
|
1279
1442
|
catch (e_16_1) { e_16 = { error: e_16_1 }; }
|
|
1280
1443
|
finally {
|
|
1281
1444
|
try {
|
|
1282
|
-
if (
|
|
1445
|
+
if (_g && !_g.done && (_a = _f.return)) _a.call(_f);
|
|
1283
1446
|
}
|
|
1284
1447
|
finally { if (e_16) throw e_16.error; }
|
|
1285
1448
|
}
|
|
@@ -1321,7 +1484,15 @@ function handleIncomingMessage(msg) {
|
|
|
1321
1484
|
if (pendingReq.has(message.id)) {
|
|
1322
1485
|
var req = pendingReq.get(message.id);
|
|
1323
1486
|
if ((req === null || req === void 0 ? void 0 : req.mode) === "stream" && req.stream) {
|
|
1324
|
-
|
|
1487
|
+
// The first frame proves the handler is live. Long-lived streams
|
|
1488
|
+
// (bookmarks.watch, proxy.watch) are idle between pushes, so the
|
|
1489
|
+
// request timeout must not treat silence as death — disarm it for
|
|
1490
|
+
// good instead of re-arming per frame.
|
|
1491
|
+
if (req.timerId !== null) {
|
|
1492
|
+
clearTimeout(req.timerId);
|
|
1493
|
+
req.timerId = null;
|
|
1494
|
+
}
|
|
1495
|
+
req.timeoutMs = 0;
|
|
1325
1496
|
req.stream._emitData(message.payload);
|
|
1326
1497
|
}
|
|
1327
1498
|
else {
|
|
@@ -1399,8 +1570,10 @@ function handleIncomingMessage(msg) {
|
|
|
1399
1570
|
case "ch.close": {
|
|
1400
1571
|
var pendingChannel = pendingChannels.get(message.id);
|
|
1401
1572
|
if (pendingChannel) {
|
|
1402
|
-
|
|
1403
|
-
|
|
1573
|
+
rejectPendingChannel(message.id, {
|
|
1574
|
+
code: (_d = message.code) !== null && _d !== void 0 ? _d : BRIDGE_ERROR.STREAM_CLOSED,
|
|
1575
|
+
message: (_e = message.reason) !== null && _e !== void 0 ? _e : "Channel closed before opening",
|
|
1576
|
+
});
|
|
1404
1577
|
return;
|
|
1405
1578
|
}
|
|
1406
1579
|
var channel_3 = activeChannels.get(message.id);
|
|
@@ -1752,6 +1925,8 @@ export var LingXiaBridge = {
|
|
|
1752
1925
|
isMacOS: isMacOS,
|
|
1753
1926
|
isWindows: isWindows,
|
|
1754
1927
|
isDesktop: isDesktop,
|
|
1928
|
+
isApple: isApple,
|
|
1929
|
+
isRunner: isRunner,
|
|
1755
1930
|
getOS: getPlatformOS,
|
|
1756
1931
|
},
|
|
1757
1932
|
dom: {
|
|
@@ -1940,6 +2115,8 @@ export function initBridge() {
|
|
|
1940
2115
|
activateReceiver(LingXiaBridge._receiveEvaluateMessage);
|
|
1941
2116
|
if (useAppleDownstreamTransport()) {
|
|
1942
2117
|
ensureAppleDownstream();
|
|
2118
|
+
startAppleWatchdog();
|
|
2119
|
+
installAppleForegroundReconnect();
|
|
1943
2120
|
}
|
|
1944
2121
|
else if (communicationMethod === MESSAGE_PORT_TYPE) {
|
|
1945
2122
|
installMessagePortInitListener();
|
|
@@ -1954,6 +2131,9 @@ export function initBridge() {
|
|
|
1954
2131
|
warn("Unknown method");
|
|
1955
2132
|
}
|
|
1956
2133
|
window.LingXiaBridge = LingXiaBridge;
|
|
2134
|
+
// Dev hook so repro/test pages can simulate a WebKit stream replacement.
|
|
2135
|
+
window.__lxForceDownstreamReconnect =
|
|
2136
|
+
forceDownstreamReconnect;
|
|
1957
2137
|
installNativeComponentCoverageMonitor({
|
|
1958
2138
|
os: getPlatformOS(),
|
|
1959
2139
|
send: sendNativeComponentMessage,
|
package/dist/es5/index.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { LingXiaBridge, channel, initBridge, invoke, notify, stream, } from './bridge';
|
|
9
9
|
export { isNativeError } from './invocation';
|
|
10
|
+
export { getDisplayLanguage } from './runtime-env';
|
|
10
11
|
export { renderErrorUI, hasError, getErrorInfo } from './error';
|
|
11
12
|
export { boot, bootWhenReady } from './boot';
|
|
12
13
|
import { bootWhenReady } from './boot';
|
package/dist/es5/runtime-env.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
var _a;
|
|
1
2
|
export var BRIDGE_CONFIG = (typeof window !== 'undefined' && window.__LX_BRIDGE_CFG) || {};
|
|
3
|
+
var displayLanguage = ((_a = BRIDGE_CONFIG.displayLanguage) === null || _a === void 0 ? void 0 : _a.trim()) || 'en-US';
|
|
4
|
+
if (typeof document !== 'undefined' && document.documentElement) {
|
|
5
|
+
document.documentElement.lang = displayLanguage;
|
|
6
|
+
}
|
|
7
|
+
export function getDisplayLanguage() {
|
|
8
|
+
return displayLanguage;
|
|
9
|
+
}
|
|
2
10
|
export function getPlatformOS() {
|
|
3
11
|
return BRIDGE_CONFIG.os || 'unknown';
|
|
4
12
|
}
|
|
@@ -20,6 +28,24 @@ export function isWindows() {
|
|
|
20
28
|
export function isDesktop() {
|
|
21
29
|
return isMacOS() || isWindows();
|
|
22
30
|
}
|
|
31
|
+
// iOS and macOS share the WKWebView transport, so features scoped to it (e.g.
|
|
32
|
+
// the streaming downstream) key off this rather than the two OS checks.
|
|
33
|
+
export function isApple() {
|
|
34
|
+
return isIOS() || isMacOS();
|
|
35
|
+
}
|
|
36
|
+
// True when attached to a `lingxia dev` session (the host sets `dev` in
|
|
37
|
+
// `__LX_BRIDGE_CFG`). Used to surface the bridge's own protocol/lifecycle trace
|
|
38
|
+
// only during development.
|
|
39
|
+
export function isDevSession() {
|
|
40
|
+
return BRIDGE_CONFIG.dev === true;
|
|
41
|
+
}
|
|
42
|
+
// True when running inside the LingXia Runner (the `lingxia dev` device
|
|
43
|
+
// simulator), which the host marks in `__LX_BRIDGE_CFG`. Unlike a real host
|
|
44
|
+
// app in dev mode, the Runner lacks host-declared surfaces such as the
|
|
45
|
+
// terminal — apps read this to hide those affordances.
|
|
46
|
+
export function isRunner() {
|
|
47
|
+
return BRIDGE_CONFIG.runner === true;
|
|
48
|
+
}
|
|
23
49
|
export function getCommunicationMethod() {
|
|
24
50
|
var _a, _b;
|
|
25
51
|
if (BRIDGE_CONFIG.os === 'iOS' || BRIDGE_CONFIG.os === 'macOS')
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDesktop } from '../runtime-env';
|
|
2
2
|
var ANIMATION_MS = 300;
|
|
3
3
|
var STYLE_ID = 'lx-action-sheet-style';
|
|
4
4
|
var activeSheet = null;
|
|
5
5
|
function isDesktopMode() {
|
|
6
|
-
return
|
|
6
|
+
return isDesktop();
|
|
7
7
|
}
|
|
8
8
|
function ensureActionSheetStyle() {
|
|
9
9
|
if (document.getElementById(STYLE_ID))
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,6 @@
|
|
|
8
8
|
export type { BridgeConfig, CallOptions, ChannelCloseEvent, ChannelOptions, ChannelOpenOptions, BridgeErrorCode, ChannelEventName, DataSubscriber, ErrorInfo, LingXiaBridgeInterface, LxChannel, LxBridgeError, LxMethod, LxMethodParams, LxMethodResult, LxMethodStreamData, LxStream, NativeComponentMessage, NativeChannel, NativeError, NativeStream, NotifyOptions, InvokeOptions, StateInfo, StreamCallOptions, StreamOptions, StreamEventName, } from './types';
|
|
9
9
|
export { LingXiaBridge, channel, initBridge, invoke, notify, stream, } from './bridge';
|
|
10
10
|
export { isNativeError } from './invocation';
|
|
11
|
+
export { getDisplayLanguage } from './runtime-env';
|
|
11
12
|
export { renderErrorUI, hasError, getErrorInfo } from './error';
|
|
12
13
|
export { boot, bootWhenReady } from './boot';
|
package/dist/runtime-env.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { BridgeConfig } from './types';
|
|
|
2
2
|
export type CommunicationMethod = 'messageport' | 'jsinterface' | 'webmessage' | 'webkit' | 'unknown';
|
|
3
3
|
export type PlatformOS = NonNullable<BridgeConfig['os']> | 'unknown';
|
|
4
4
|
export declare const BRIDGE_CONFIG: BridgeConfig;
|
|
5
|
+
export declare function getDisplayLanguage(): string;
|
|
5
6
|
export declare function getPlatformOS(): PlatformOS;
|
|
6
7
|
export declare function isHarmony(): boolean;
|
|
7
8
|
export declare function isIOS(): boolean;
|
|
@@ -9,4 +10,7 @@ export declare function isAndroid(): boolean;
|
|
|
9
10
|
export declare function isMacOS(): boolean;
|
|
10
11
|
export declare function isWindows(): boolean;
|
|
11
12
|
export declare function isDesktop(): boolean;
|
|
13
|
+
export declare function isApple(): boolean;
|
|
14
|
+
export declare function isDevSession(): boolean;
|
|
15
|
+
export declare function isRunner(): boolean;
|
|
12
16
|
export declare function getCommunicationMethod(): CommunicationMethod;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
export interface BridgeConfig {
|
|
2
2
|
os?: 'Harmony' | 'iOS' | 'Android' | 'macOS' | 'Windows';
|
|
3
|
+
/** Effective host display language; distinct from the raw system locale. */
|
|
4
|
+
displayLanguage?: string;
|
|
3
5
|
nonce?: string;
|
|
4
6
|
appleDownstreamURL?: string;
|
|
7
|
+
dev?: boolean;
|
|
8
|
+
runner?: boolean;
|
|
5
9
|
}
|
|
6
10
|
export interface RuntimeConfig {
|
|
7
11
|
error?: ErrorInfo;
|
|
@@ -161,6 +165,8 @@ export interface LingXiaBridgeInterface {
|
|
|
161
165
|
isMacOS(): boolean;
|
|
162
166
|
isWindows(): boolean;
|
|
163
167
|
isDesktop(): boolean;
|
|
168
|
+
isApple(): boolean;
|
|
169
|
+
isRunner(): boolean;
|
|
164
170
|
getOS(): string;
|
|
165
171
|
};
|
|
166
172
|
dom: {
|