@lingxia/bridge 0.14.0 → 0.16.0
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 +317 -66
- package/dist/es2020/protocol-v3.js +191 -0
- package/dist/es2020/runtime-env.js +32 -3
- package/dist/es2020/types.js +1 -0
- package/dist/es5/bridge.js +430 -143
- package/dist/es5/protocol-v3.js +215 -0
- package/dist/es5/runtime-env.js +32 -3
- package/dist/es5/types.js +1 -0
- package/dist/protocol-v3.d.ts +43 -0
- package/dist/runtime-env.d.ts +10 -2
- package/dist/types.d.ts +34 -0
- package/package.json +3 -1
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var __assign = (this && this.__assign) || function () {
|
|
2
|
+
__assign = Object.assign || function(t) {
|
|
3
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
4
|
+
s = arguments[i];
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
6
|
+
t[p] = s[p];
|
|
7
|
+
}
|
|
8
|
+
return t;
|
|
9
|
+
};
|
|
10
|
+
return __assign.apply(this, arguments);
|
|
11
|
+
};
|
|
12
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
13
|
+
var t = {};
|
|
14
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
15
|
+
t[p] = s[p];
|
|
16
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
17
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
18
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
19
|
+
t[p[i]] = s[p[i]];
|
|
20
|
+
}
|
|
21
|
+
return t;
|
|
22
|
+
};
|
|
23
|
+
/** Document-side framing for host-attested RequiredV3 documents. */
|
|
24
|
+
export var V3_PROTOCOL = 3;
|
|
25
|
+
export var DEFAULT_MAX_V3_FRAME_BYTES = 64 * 1024;
|
|
26
|
+
var documentToNativeKinds = new Set([
|
|
27
|
+
"hello",
|
|
28
|
+
"req",
|
|
29
|
+
"res",
|
|
30
|
+
"notify",
|
|
31
|
+
"cancel",
|
|
32
|
+
"ch.open",
|
|
33
|
+
"ch.data",
|
|
34
|
+
"ch.close",
|
|
35
|
+
"state.ack",
|
|
36
|
+
"console",
|
|
37
|
+
]);
|
|
38
|
+
var nativeToDocumentKinds = new Set([
|
|
39
|
+
"helloAck",
|
|
40
|
+
"ready",
|
|
41
|
+
"req",
|
|
42
|
+
"res",
|
|
43
|
+
"event",
|
|
44
|
+
"state.snapshot",
|
|
45
|
+
"state.patch",
|
|
46
|
+
"ch.ack",
|
|
47
|
+
"ch.data",
|
|
48
|
+
"ch.close",
|
|
49
|
+
]);
|
|
50
|
+
function isRecord(value) {
|
|
51
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
function hasDuplicateTopLevelSecurityKey(frame) {
|
|
54
|
+
var _a;
|
|
55
|
+
var securityKeys = new Set(["v", "kind", "sessionId", "secret"]);
|
|
56
|
+
var seen = new Set();
|
|
57
|
+
var depth = 0;
|
|
58
|
+
for (var index = 0; index < frame.length; index++) {
|
|
59
|
+
var char = frame[index];
|
|
60
|
+
if (char === "{" || char === "[") {
|
|
61
|
+
depth++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (char === "}" || char === "]") {
|
|
65
|
+
depth--;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (char !== '"')
|
|
69
|
+
continue;
|
|
70
|
+
var start = index;
|
|
71
|
+
index++;
|
|
72
|
+
while (index < frame.length) {
|
|
73
|
+
if (frame[index] === "\\") {
|
|
74
|
+
index += 2;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (frame[index] === '"')
|
|
78
|
+
break;
|
|
79
|
+
index++;
|
|
80
|
+
}
|
|
81
|
+
if (index >= frame.length)
|
|
82
|
+
return false;
|
|
83
|
+
if (depth !== 1)
|
|
84
|
+
continue;
|
|
85
|
+
var next = index + 1;
|
|
86
|
+
while (/\s/.test((_a = frame[next]) !== null && _a !== void 0 ? _a : ""))
|
|
87
|
+
next++;
|
|
88
|
+
if (frame[next] !== ":")
|
|
89
|
+
continue;
|
|
90
|
+
var key = void 0;
|
|
91
|
+
try {
|
|
92
|
+
key = JSON.parse(frame.slice(start, index + 1));
|
|
93
|
+
}
|
|
94
|
+
catch (_b) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (typeof key === "string" && securityKeys.has(key)) {
|
|
98
|
+
if (seen.has(key))
|
|
99
|
+
return true;
|
|
100
|
+
seen.add(key);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Captures the document secret in a closure. Neither the returned codec nor
|
|
107
|
+
* parsed native messages expose it.
|
|
108
|
+
*/
|
|
109
|
+
export function createV3DocumentCodec(binding) {
|
|
110
|
+
if (!isRecord(binding) ||
|
|
111
|
+
typeof binding.sessionId !== "string" ||
|
|
112
|
+
binding.sessionId.length === 0 ||
|
|
113
|
+
typeof binding.secret !== "string" ||
|
|
114
|
+
binding.secret.length === 0) {
|
|
115
|
+
return { ok: false, error: "INVALID_DOCUMENT_BINDING" };
|
|
116
|
+
}
|
|
117
|
+
var sessionId = binding.sessionId, secret = binding.secret;
|
|
118
|
+
return {
|
|
119
|
+
ok: true,
|
|
120
|
+
value: {
|
|
121
|
+
encode: function (kind, payload) {
|
|
122
|
+
if (!documentToNativeKinds.has(kind) || !isRecord(payload)) {
|
|
123
|
+
return { ok: false, error: "INVALID_DOCUMENT_PAYLOAD" };
|
|
124
|
+
}
|
|
125
|
+
if (["v", "kind", "sessionId", "secret"].some(function (field) { return field in payload; })) {
|
|
126
|
+
return { ok: false, error: "SECURITY_FIELD_IN_PAYLOAD" };
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
ok: true,
|
|
130
|
+
value: __assign(__assign({}, payload), { v: V3_PROTOCOL, kind: kind, sessionId: sessionId, secret: secret }),
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
parse: function (frame, maxFrameBytes) {
|
|
134
|
+
if (maxFrameBytes === void 0) { maxFrameBytes = DEFAULT_MAX_V3_FRAME_BYTES; }
|
|
135
|
+
// Envelope checks deliberately precede typed payload handling.
|
|
136
|
+
if (new TextEncoder().encode(frame).byteLength > maxFrameBytes) {
|
|
137
|
+
return { ok: false, error: "FRAME_TOO_LARGE" };
|
|
138
|
+
}
|
|
139
|
+
if (hasDuplicateTopLevelSecurityKey(frame)) {
|
|
140
|
+
return { ok: false, error: "MALFORMED_ENVELOPE" };
|
|
141
|
+
}
|
|
142
|
+
var candidate;
|
|
143
|
+
try {
|
|
144
|
+
candidate = JSON.parse(frame);
|
|
145
|
+
}
|
|
146
|
+
catch (_a) {
|
|
147
|
+
return { ok: false, error: "MALFORMED_ENVELOPE" };
|
|
148
|
+
}
|
|
149
|
+
if (!isRecord(candidate))
|
|
150
|
+
return { ok: false, error: "MALFORMED_ENVELOPE" };
|
|
151
|
+
if (candidate.v !== V3_PROTOCOL) {
|
|
152
|
+
return { ok: false, error: "UNSUPPORTED_VERSION" };
|
|
153
|
+
}
|
|
154
|
+
if (typeof candidate.kind !== "string" ||
|
|
155
|
+
!nativeToDocumentKinds.has(candidate.kind)) {
|
|
156
|
+
return { ok: false, error: "UNSUPPORTED_NATIVE_KIND" };
|
|
157
|
+
}
|
|
158
|
+
if (typeof candidate.sessionId !== "string" || candidate.sessionId.length === 0) {
|
|
159
|
+
return { ok: false, error: "MALFORMED_ENVELOPE" };
|
|
160
|
+
}
|
|
161
|
+
if (candidate.sessionId !== sessionId)
|
|
162
|
+
return { ok: false, error: "SESSION_MISMATCH" };
|
|
163
|
+
if (Object.prototype.hasOwnProperty.call(candidate, "secret")) {
|
|
164
|
+
return { ok: false, error: "UNEXPECTED_SECRET" };
|
|
165
|
+
}
|
|
166
|
+
var _v = candidate.v, _kind = candidate.kind, _sessionId = candidate.sessionId, payload = __rest(candidate, ["v", "kind", "sessionId"]);
|
|
167
|
+
return {
|
|
168
|
+
ok: true,
|
|
169
|
+
value: { kind: candidate.kind, payload: payload },
|
|
170
|
+
};
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Atomically takes the host-installed one-shot handoff. The secret remains in
|
|
177
|
+
* the returned codec closure and is never copied into bridge globals/config.
|
|
178
|
+
*/
|
|
179
|
+
export function consumeV3Bootstrap() {
|
|
180
|
+
if (typeof window === "undefined")
|
|
181
|
+
return { kind: "absent" };
|
|
182
|
+
var take = window.__LingXiaTakeControlBootstrap;
|
|
183
|
+
if (typeof take !== "function")
|
|
184
|
+
return { kind: "absent" };
|
|
185
|
+
try {
|
|
186
|
+
var bootstrap = take();
|
|
187
|
+
if (!bootstrap ||
|
|
188
|
+
bootstrap.requiredProtocol !== V3_PROTOCOL ||
|
|
189
|
+
typeof bootstrap.publicSessionId !== "string" ||
|
|
190
|
+
bootstrap.publicSessionId.length === 0 ||
|
|
191
|
+
typeof bootstrap.secret !== "string" ||
|
|
192
|
+
bootstrap.secret.length === 0) {
|
|
193
|
+
return { kind: "blocked" };
|
|
194
|
+
}
|
|
195
|
+
var codec = createV3DocumentCodec({
|
|
196
|
+
sessionId: bootstrap.publicSessionId,
|
|
197
|
+
secret: bootstrap.secret,
|
|
198
|
+
});
|
|
199
|
+
return codec.ok
|
|
200
|
+
? { kind: "required", codec: codec.value }
|
|
201
|
+
: { kind: "blocked" };
|
|
202
|
+
}
|
|
203
|
+
catch (_a) {
|
|
204
|
+
return { kind: "blocked" };
|
|
205
|
+
}
|
|
206
|
+
finally {
|
|
207
|
+
// A malformed replacement must not leave a callable handoff for later.
|
|
208
|
+
try {
|
|
209
|
+
delete window.__LingXiaTakeControlBootstrap;
|
|
210
|
+
}
|
|
211
|
+
catch (_b) {
|
|
212
|
+
// A hostile non-configurable property has no attested secret to retain.
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
package/dist/es5/runtime-env.js
CHANGED
|
@@ -83,7 +83,7 @@ function applyDisplayLanguage(next) {
|
|
|
83
83
|
try {
|
|
84
84
|
for (var _b = __values(__spreadArray([], __read(current.listeners), false)), _c = _b.next(); !_c.done; _c = _b.next()) {
|
|
85
85
|
var listener = _c.value;
|
|
86
|
-
listener();
|
|
86
|
+
listener(normalized);
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
@@ -104,7 +104,14 @@ if (typeof window !== 'undefined' && !window.__lingxiaApplyDisplayLanguage) {
|
|
|
104
104
|
export function getDisplayLanguage() {
|
|
105
105
|
return store().value;
|
|
106
106
|
}
|
|
107
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Subscribe to host display-language changes. Returns an unsubscribe.
|
|
109
|
+
*
|
|
110
|
+
* Change-only, so that this composes with `useSyncExternalStore`: the listener
|
|
111
|
+
* runs when the language changes, never on subscribe. Read the current value
|
|
112
|
+
* with `getDisplayLanguage()`. Logic's `lx.app.displayLanguage.watch` differs
|
|
113
|
+
* deliberately — it has no render loop to feed, so it delivers immediately.
|
|
114
|
+
*/
|
|
108
115
|
export function subscribeDisplayLanguage(listener) {
|
|
109
116
|
var listeners = store().listeners;
|
|
110
117
|
listeners.add(listener);
|
|
@@ -115,6 +122,23 @@ export function subscribeDisplayLanguage(listener) {
|
|
|
115
122
|
export function getPlatformOS() {
|
|
116
123
|
return BRIDGE_CONFIG.os || 'unknown';
|
|
117
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Which kind of machine this is, mobile or desktop. Read through `isMobile()`
|
|
127
|
+
* and `isDesktop()`; the class itself is host vocabulary, not lxapp API.
|
|
128
|
+
*
|
|
129
|
+
* Fixed for the life of the document. A shipped host is one machine; the
|
|
130
|
+
* Runner re-serves the page when its simulated device changes class, so this
|
|
131
|
+
* never has to change under a page that is already rendering.
|
|
132
|
+
*
|
|
133
|
+
* Hosts from before this config key shipped send nothing, so fall back to the
|
|
134
|
+
* OS: every one of them is the machine it names.
|
|
135
|
+
*/
|
|
136
|
+
function hostClass() {
|
|
137
|
+
if (BRIDGE_CONFIG.hostClass === 'mobile' || BRIDGE_CONFIG.hostClass === 'desktop') {
|
|
138
|
+
return BRIDGE_CONFIG.hostClass;
|
|
139
|
+
}
|
|
140
|
+
return BRIDGE_CONFIG.os === 'macOS' || BRIDGE_CONFIG.os === 'Windows' ? 'desktop' : 'mobile';
|
|
141
|
+
}
|
|
118
142
|
export function isHarmony() {
|
|
119
143
|
return BRIDGE_CONFIG.os === 'Harmony';
|
|
120
144
|
}
|
|
@@ -130,8 +154,13 @@ export function isMacOS() {
|
|
|
130
154
|
export function isWindows() {
|
|
131
155
|
return BRIDGE_CONFIG.os === 'Windows';
|
|
132
156
|
}
|
|
157
|
+
// Form factor, not OS: the Runner shows a real macOS/Windows build inside a
|
|
158
|
+
// phone frame, and a page that keyed off the OS would keep its desktop layout.
|
|
133
159
|
export function isDesktop() {
|
|
134
|
-
return
|
|
160
|
+
return hostClass() === 'desktop';
|
|
161
|
+
}
|
|
162
|
+
export function isMobile() {
|
|
163
|
+
return hostClass() === 'mobile';
|
|
135
164
|
}
|
|
136
165
|
// iOS and macOS share the WKWebView transport, so features scoped to it (e.g.
|
|
137
166
|
// the streaming downstream) key off this rather than the two OS checks.
|
package/dist/es5/types.js
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/** Document-side framing for host-attested RequiredV3 documents. */
|
|
2
|
+
export declare const V3_PROTOCOL: 3;
|
|
3
|
+
export declare const DEFAULT_MAX_V3_FRAME_BYTES: number;
|
|
4
|
+
export type V3DocumentToNativeKind = "hello" | "req" | "res" | "notify" | "cancel" | "ch.open" | "ch.data" | "ch.close" | "state.ack" | "console";
|
|
5
|
+
export type V3NativeToDocumentKind = "helloAck" | "ready" | "req" | "res" | "event" | "state.snapshot" | "state.patch" | "ch.ack" | "ch.data" | "ch.close";
|
|
6
|
+
export type V3CodecError = "FRAME_TOO_LARGE" | "MALFORMED_ENVELOPE" | "UNSUPPORTED_VERSION" | "UNSUPPORTED_NATIVE_KIND" | "INVALID_DOCUMENT_BINDING" | "INVALID_DOCUMENT_PAYLOAD" | "SECURITY_FIELD_IN_PAYLOAD" | "SESSION_MISMATCH" | "UNEXPECTED_SECRET";
|
|
7
|
+
export type V3CodecResult<T> = {
|
|
8
|
+
readonly ok: true;
|
|
9
|
+
readonly value: T;
|
|
10
|
+
} | {
|
|
11
|
+
readonly ok: false;
|
|
12
|
+
readonly error: V3CodecError;
|
|
13
|
+
};
|
|
14
|
+
export type V3DocumentBinding = {
|
|
15
|
+
readonly sessionId: string;
|
|
16
|
+
readonly secret: string;
|
|
17
|
+
};
|
|
18
|
+
export type V3NativeEnvelope = {
|
|
19
|
+
readonly kind: V3NativeToDocumentKind;
|
|
20
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
21
|
+
};
|
|
22
|
+
export type V3DocumentCodec = {
|
|
23
|
+
encode(kind: V3DocumentToNativeKind, payload: Record<string, unknown>): V3CodecResult<Record<string, unknown>>;
|
|
24
|
+
parse(frame: string, maxFrameBytes?: number): V3CodecResult<V3NativeEnvelope>;
|
|
25
|
+
};
|
|
26
|
+
export type V3BootstrapActivation = {
|
|
27
|
+
readonly kind: "absent";
|
|
28
|
+
} | {
|
|
29
|
+
readonly kind: "required";
|
|
30
|
+
readonly codec: V3DocumentCodec;
|
|
31
|
+
} | {
|
|
32
|
+
readonly kind: "blocked";
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Captures the document secret in a closure. Neither the returned codec nor
|
|
36
|
+
* parsed native messages expose it.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createV3DocumentCodec(binding: V3DocumentBinding): V3CodecResult<V3DocumentCodec>;
|
|
39
|
+
/**
|
|
40
|
+
* Atomically takes the host-installed one-shot handoff. The secret remains in
|
|
41
|
+
* the returned codec closure and is never copied into bridge globals/config.
|
|
42
|
+
*/
|
|
43
|
+
export declare function consumeV3Bootstrap(): V3BootstrapActivation;
|
package/dist/runtime-env.d.ts
CHANGED
|
@@ -3,8 +3,15 @@ export type CommunicationMethod = 'messageport' | 'jsinterface' | 'webmessage' |
|
|
|
3
3
|
export type PlatformOS = NonNullable<BridgeConfig['os']> | 'unknown';
|
|
4
4
|
export declare const BRIDGE_CONFIG: BridgeConfig;
|
|
5
5
|
export declare function getDisplayLanguage(): string;
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Subscribe to host display-language changes. Returns an unsubscribe.
|
|
8
|
+
*
|
|
9
|
+
* Change-only, so that this composes with `useSyncExternalStore`: the listener
|
|
10
|
+
* runs when the language changes, never on subscribe. Read the current value
|
|
11
|
+
* with `getDisplayLanguage()`. Logic's `lx.app.displayLanguage.watch` differs
|
|
12
|
+
* deliberately — it has no render loop to feed, so it delivers immediately.
|
|
13
|
+
*/
|
|
14
|
+
export declare function subscribeDisplayLanguage(listener: (language: string) => void): () => void;
|
|
8
15
|
export declare function getPlatformOS(): PlatformOS;
|
|
9
16
|
export declare function isHarmony(): boolean;
|
|
10
17
|
export declare function isIOS(): boolean;
|
|
@@ -12,6 +19,7 @@ export declare function isAndroid(): boolean;
|
|
|
12
19
|
export declare function isMacOS(): boolean;
|
|
13
20
|
export declare function isWindows(): boolean;
|
|
14
21
|
export declare function isDesktop(): boolean;
|
|
22
|
+
export declare function isMobile(): boolean;
|
|
15
23
|
export declare function isApple(): boolean;
|
|
16
24
|
export declare function isDevSession(): boolean;
|
|
17
25
|
export declare function isRunner(): boolean;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
export interface BridgeConfig {
|
|
2
2
|
os?: 'Harmony' | 'iOS' | 'Android' | 'macOS' | 'Windows';
|
|
3
|
+
/**
|
|
4
|
+
* Transport for the host's form factor, read through `isMobile()` /
|
|
5
|
+
* `isDesktop()` rather than directly. Distinct from `os`, which names the
|
|
6
|
+
* actual system: the Runner simulating a phone is still macOS or Windows.
|
|
7
|
+
*/
|
|
8
|
+
hostClass?: 'mobile' | 'desktop';
|
|
3
9
|
/** Effective host display language; distinct from the raw system locale. */
|
|
4
10
|
displayLanguage?: string;
|
|
5
11
|
nonce?: string;
|
|
@@ -15,6 +21,18 @@ export interface ErrorInfo {
|
|
|
15
21
|
reason?: string;
|
|
16
22
|
code?: number;
|
|
17
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Private, one-shot handoff installed only in host-attested internal HTML.
|
|
26
|
+
*
|
|
27
|
+
* This is intentionally not part of the public bridge API and V2 never reads
|
|
28
|
+
* it. A future V3 bootstrap consumes the handoff before protocol activation.
|
|
29
|
+
*/
|
|
30
|
+
interface ControlBootstrap {
|
|
31
|
+
readonly requiredProtocol: 3;
|
|
32
|
+
readonly publicSessionId: string;
|
|
33
|
+
readonly secret: string;
|
|
34
|
+
}
|
|
35
|
+
type TakeControlBootstrap = () => ControlBootstrap | undefined;
|
|
18
36
|
export declare const BRIDGE_ERROR: {
|
|
19
37
|
readonly NOT_READY: "BRIDGE_NOT_READY";
|
|
20
38
|
readonly TIMEOUT: "BRIDGE_TIMEOUT";
|
|
@@ -29,6 +47,7 @@ export declare const BRIDGE_ERROR: {
|
|
|
29
47
|
readonly OUTBOX_FULL: "BRIDGE_OUTBOX_FULL";
|
|
30
48
|
readonly STREAM_OVERFLOW: "BRIDGE_STREAM_OVERFLOW";
|
|
31
49
|
readonly STREAM_CLOSED: "BRIDGE_STREAM_CLOSED";
|
|
50
|
+
readonly MESSAGE_TOO_LARGE: "BRIDGE_MESSAGE_TOO_LARGE";
|
|
32
51
|
};
|
|
33
52
|
export type BridgeErrorCode = (typeof BRIDGE_ERROR)[keyof typeof BRIDGE_ERROR];
|
|
34
53
|
export interface LxBridgeError {
|
|
@@ -126,16 +145,24 @@ declare global {
|
|
|
126
145
|
};
|
|
127
146
|
__LX_BRIDGE_INIT_STATE?: 'initializing' | 'initialized';
|
|
128
147
|
__LX_RUNTIME_CONFIG?: RuntimeConfig;
|
|
148
|
+
/**
|
|
149
|
+
* Host-attested, one-shot V3 bootstrap handoff. It is absent for legacy
|
|
150
|
+
* scheme loads, assets, and all untrusted documents.
|
|
151
|
+
*/
|
|
152
|
+
__LingXiaTakeControlBootstrap?: TakeControlBootstrap;
|
|
129
153
|
__pageBridge?: {
|
|
130
154
|
__names: string[];
|
|
131
155
|
[key: string]: unknown;
|
|
132
156
|
};
|
|
133
157
|
__LingXiaRecvMessage?: (message: string) => void;
|
|
158
|
+
/** WebView2 inject script queues inbound payloads here until the bridge binds `__LingXiaRecvMessage`. */
|
|
159
|
+
__LingXiaEarlyNativeMessages?: string[];
|
|
134
160
|
LingXiaBridge?: LingXiaBridgeInterface;
|
|
135
161
|
LingXiaProxy?: {
|
|
136
162
|
supportsMessagePort: () => boolean;
|
|
137
163
|
getPort: (name: string) => string;
|
|
138
164
|
postMessage: (message: string) => void;
|
|
165
|
+
nativeComponentUpdate?: ((payload: string) => void) | ((componentId: string, payload: string) => void);
|
|
139
166
|
};
|
|
140
167
|
NativeComponentBridge?: {
|
|
141
168
|
postMessage: (message: string) => void;
|
|
@@ -165,6 +192,11 @@ export interface LingXiaBridgeInterface {
|
|
|
165
192
|
proto: boolean;
|
|
166
193
|
all: boolean;
|
|
167
194
|
};
|
|
195
|
+
displayLanguage: {
|
|
196
|
+
get(): string;
|
|
197
|
+
/** Change-only; read the current value with `get()`. */
|
|
198
|
+
subscribe(listener: (language: string) => void): () => void;
|
|
199
|
+
};
|
|
168
200
|
platform: {
|
|
169
201
|
isHarmony(): boolean;
|
|
170
202
|
isIOS(): boolean;
|
|
@@ -172,6 +204,7 @@ export interface LingXiaBridgeInterface {
|
|
|
172
204
|
isMacOS(): boolean;
|
|
173
205
|
isWindows(): boolean;
|
|
174
206
|
isDesktop(): boolean;
|
|
207
|
+
isMobile(): boolean;
|
|
175
208
|
isApple(): boolean;
|
|
176
209
|
isRunner(): boolean;
|
|
177
210
|
getOS(): string;
|
|
@@ -204,3 +237,4 @@ export interface StreamOptions extends StreamCallOptions {
|
|
|
204
237
|
}
|
|
205
238
|
export interface ChannelOptions extends ChannelOpenOptions {
|
|
206
239
|
}
|
|
240
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingxia/bridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "LingXia Web Runtime - Bridge, boot, and error handling for WebView",
|
|
6
6
|
"repository": {
|
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
"build:modules:es2020": "tsc -p tsconfig.modules.json",
|
|
40
40
|
"build:modules:es5": "tsc -p tsconfig.modules.legacy.json",
|
|
41
41
|
"build:types": "tsc -p tsconfig.json --emitDeclarationOnly",
|
|
42
|
+
"build:v3-test": "tsc -p tsconfig.v3-test.json",
|
|
43
|
+
"test": "npm run build:es2020 && npm run build:es5 && npm run build:v3-test && node ./test-support/test-protocol-v3.mjs && node ./test-support/test-bridge-required-v3.mjs && node ./test-support/test-control-bootstrap.mjs",
|
|
42
44
|
"dev": "rolldown -c -w",
|
|
43
45
|
"clean": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\"",
|
|
44
46
|
"prepublishOnly": "npm run build"
|