@jhh96/light-app-bridge 1.0.2 → 1.0.3
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/README.md +5 -3
- package/dist/lightAppBridge.d.ts +4 -3
- package/dist/lightAppBridge.js +104 -71
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,10 +19,12 @@
|
|
|
19
19
|
|
|
20
20
|
| 监听后端 | 优先级 | 适用环境 | 说明 |
|
|
21
21
|
|---------|--------|---------|------|
|
|
22
|
-
| `window.waltz.event.on('native-call-js')` | 高(优先) | 有 waltz 注入的环境 |
|
|
23
|
-
| `window.handleMessageFromNative` | 低(回退) | 无 waltz 的环境 | hook 此全局方法(waltzBridge
|
|
22
|
+
| `window.waltz.event.on('native-call-js')` | 高(优先) | 有 waltz 注入的环境 | 推荐路径,立即可用 |
|
|
23
|
+
| `window.handleMessageFromNative` | 低(回退) | 无 waltz 的环境 | hook 此全局方法(waltzBridge 投递消息的统一入口)。**可能晚于构造注入**,SDK 会轮询等待(上限 10s) |
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
- 两者都始终不可用时,`connect()`/`ready()` reject(构造不抛,因监听后端允许稍后出现)
|
|
26
|
+
- SDK 内部按 waltz → handleMessageFromNative 顺序探测,无需手动指定
|
|
27
|
+
- 握手会等监听就绪后才开始,避免早到的 ack/hello 丢失
|
|
26
28
|
|
|
27
29
|
## 安装(两个项目)
|
|
28
30
|
|
package/dist/lightAppBridge.d.ts
CHANGED
|
@@ -68,16 +68,17 @@ export default class LightAppBridge {
|
|
|
68
68
|
private _connectDeferred;
|
|
69
69
|
private _connectPromise;
|
|
70
70
|
private _unlisten;
|
|
71
|
+
private _listenerReady;
|
|
71
72
|
/**
|
|
72
73
|
* @param role 角色,省略时按 window.worklink.plugin 是否存在自动探测
|
|
73
74
|
* @param options 超时配置
|
|
74
|
-
* @throws 依赖未就绪时抛出(window.worklink
|
|
75
|
+
* @throws 依赖未就绪时抛出(window.worklink;监听后端可稍后出现,不算构造失败)
|
|
75
76
|
*/
|
|
76
77
|
constructor(role?: BridgeRole, options?: BridgeOptions);
|
|
77
78
|
/** 校验依赖是否就绪,缺失抛出明确异常 */
|
|
78
79
|
private _checkDeps;
|
|
79
|
-
/**
|
|
80
|
-
private
|
|
80
|
+
/** 异步绑定消息监听:优先 waltz,回退 hook handleMessageFromNative(可能需等待其注入) */
|
|
81
|
+
private _startListener;
|
|
81
82
|
/** 构造消息信封 */
|
|
82
83
|
private _envelope;
|
|
83
84
|
/** 处理一条归一化后的消息 */
|
package/dist/lightAppBridge.js
CHANGED
|
@@ -52,59 +52,77 @@ function getInvoke() {
|
|
|
52
52
|
}
|
|
53
53
|
return null;
|
|
54
54
|
}
|
|
55
|
+
/** 等待 handleMessageFromNative 出现的轮询间隔(ms) */
|
|
56
|
+
const BACKEND_POLL_INTERVAL = 100;
|
|
57
|
+
/** 等待监听后端就绪的超时(ms) */
|
|
58
|
+
const BACKEND_POLL_TIMEOUT = 10000;
|
|
55
59
|
/** waltz 路径是否可用 */
|
|
56
60
|
function hasWaltz() {
|
|
57
61
|
const w = window.waltz;
|
|
58
62
|
return !!(w && w.event && typeof w.event.on === 'function');
|
|
59
63
|
}
|
|
64
|
+
/** handleMessageFromNative 是否可用 */
|
|
65
|
+
function hasHandleMessageFromNative() {
|
|
66
|
+
return typeof window.handleMessageFromNative === 'function';
|
|
67
|
+
}
|
|
68
|
+
/** 轮询等待条件成立 */
|
|
69
|
+
function waitFor(check, timeout) {
|
|
70
|
+
return new Promise((resolve, reject) => {
|
|
71
|
+
const start = Date.now();
|
|
72
|
+
const tick = () => {
|
|
73
|
+
if (check())
|
|
74
|
+
return resolve();
|
|
75
|
+
if (Date.now() - start > timeout) {
|
|
76
|
+
return reject(new Error(`等待超时 (${timeout}ms)`));
|
|
77
|
+
}
|
|
78
|
+
setTimeout(tick, BACKEND_POLL_INTERVAL);
|
|
79
|
+
};
|
|
80
|
+
tick();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
60
83
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
84
|
+
* 异步解析可用的监听后端并绑定:优先 waltz,回退 hook window.handleMessageFromNative。
|
|
85
|
+
* 无 waltz 且 handleMessageFromNative 尚未注入时,轮询等待其出现(上限 BACKEND_POLL_TIMEOUT)。
|
|
86
|
+
* 两者始终不可用则 reject。
|
|
63
87
|
*/
|
|
64
|
-
function
|
|
65
|
-
|
|
88
|
+
function bindMessageBackend(cb) {
|
|
89
|
+
const w = window;
|
|
90
|
+
// 1. waltz 路径:有 waltz 的环境(立即可用)
|
|
66
91
|
if (hasWaltz()) {
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
cb(msg);
|
|
72
|
-
};
|
|
73
|
-
window.waltz.event.on('native-call-js', handler);
|
|
74
|
-
return () => {
|
|
75
|
-
var _a;
|
|
76
|
-
const ev = (_a = window.waltz) === null || _a === void 0 ? void 0 : _a.event;
|
|
77
|
-
if (ev && typeof ev.off === 'function')
|
|
78
|
-
ev.off('native-call-js', handler);
|
|
79
|
-
else if (ev && typeof ev.removeListener === 'function')
|
|
80
|
-
ev.removeListener('native-call-js', handler);
|
|
81
|
-
};
|
|
92
|
+
const handler = (raw) => {
|
|
93
|
+
const msg = parseHandleMessageFromNative(raw);
|
|
94
|
+
if (msg)
|
|
95
|
+
cb(msg);
|
|
82
96
|
};
|
|
97
|
+
w.waltz.event.on('native-call-js', handler);
|
|
98
|
+
return Promise.resolve(() => {
|
|
99
|
+
var _a;
|
|
100
|
+
const ev = (_a = w.waltz) === null || _a === void 0 ? void 0 : _a.event;
|
|
101
|
+
if (ev && typeof ev.off === 'function')
|
|
102
|
+
ev.off('native-call-js', handler);
|
|
103
|
+
else if (ev && typeof ev.removeListener === 'function')
|
|
104
|
+
ev.removeListener('native-call-js', handler);
|
|
105
|
+
});
|
|
83
106
|
}
|
|
84
|
-
// 2. hook handleMessageFromNative 路径:无 waltz
|
|
85
|
-
// waltzBridge
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// 透传给原 handler(若存在),保持 waltz/业务自身逻辑不被破坏
|
|
96
|
-
if (typeof prev === 'function')
|
|
97
|
-
return prev.apply(this, arguments);
|
|
98
|
-
};
|
|
99
|
-
w.handleMessageFromNative = wrapped;
|
|
100
|
-
return () => {
|
|
101
|
-
// 还原:仅当仍为自身 wrapped 时才还原,避免覆盖后续可能的重新赋值
|
|
102
|
-
if (w.handleMessageFromNative === wrapped)
|
|
103
|
-
w.handleMessageFromNative = prev;
|
|
104
|
-
};
|
|
107
|
+
// 2. hook handleMessageFromNative 路径:无 waltz 时。
|
|
108
|
+
// 它可能晚于本 SDK 构造才被 preload/waltzBridge 注册,故轮询等待。
|
|
109
|
+
return waitFor(hasHandleMessageFromNative, BACKEND_POLL_TIMEOUT).then(() => {
|
|
110
|
+
const prev = w.handleMessageFromNative;
|
|
111
|
+
const wrapped = function (raw) {
|
|
112
|
+
const msg = parseHandleMessageFromNative(raw);
|
|
113
|
+
if (msg)
|
|
114
|
+
cb(msg);
|
|
115
|
+
// 透传给原 handler(若存在),保持 waltz/业务自身逻辑不被破坏
|
|
116
|
+
if (typeof prev === 'function')
|
|
117
|
+
return prev.apply(this, arguments);
|
|
105
118
|
};
|
|
106
|
-
|
|
107
|
-
|
|
119
|
+
w.handleMessageFromNative = wrapped;
|
|
120
|
+
return () => {
|
|
121
|
+
// 还原:仅当仍为自身 wrapped 时才还原,避免覆盖后续可能的重新赋值
|
|
122
|
+
if (w.handleMessageFromNative === wrapped)
|
|
123
|
+
w.handleMessageFromNative = prev;
|
|
124
|
+
};
|
|
125
|
+
});
|
|
108
126
|
}
|
|
109
127
|
/**
|
|
110
128
|
* 解析 handleMessageFromNative / waltz native-call-js 的统一数据格式。
|
|
@@ -137,7 +155,7 @@ export default class LightAppBridge {
|
|
|
137
155
|
/**
|
|
138
156
|
* @param role 角色,省略时按 window.worklink.plugin 是否存在自动探测
|
|
139
157
|
* @param options 超时配置
|
|
140
|
-
* @throws 依赖未就绪时抛出(window.worklink
|
|
158
|
+
* @throws 依赖未就绪时抛出(window.worklink;监听后端可稍后出现,不算构造失败)
|
|
141
159
|
*/
|
|
142
160
|
constructor(role, options = {}) {
|
|
143
161
|
this._pending = new Map();
|
|
@@ -151,6 +169,7 @@ export default class LightAppBridge {
|
|
|
151
169
|
this._handshakeTimer = null;
|
|
152
170
|
this._started = false;
|
|
153
171
|
this._unlisten = null;
|
|
172
|
+
this._listenerReady = null;
|
|
154
173
|
let r = role === 'plugin' || role === 'lightapp' ? role : 'lightapp';
|
|
155
174
|
if (role !== 'plugin' && role !== 'lightapp') {
|
|
156
175
|
const wl = window.worklink;
|
|
@@ -166,8 +185,8 @@ export default class LightAppBridge {
|
|
|
166
185
|
return peerId;
|
|
167
186
|
});
|
|
168
187
|
this._checkDeps(); // 依赖校验,缺失直接抛异常
|
|
169
|
-
this.
|
|
170
|
-
this.connect(); //
|
|
188
|
+
this._startListener(); // 异步绑定消息监听(waltz 或 handleMessageFromNative)
|
|
189
|
+
this.connect(); // 构造即自动发起握手(内部会等监听就绪)
|
|
171
190
|
}
|
|
172
191
|
/** 校验依赖是否就绪,缺失抛出明确异常 */
|
|
173
192
|
_checkDeps() {
|
|
@@ -178,17 +197,23 @@ export default class LightAppBridge {
|
|
|
178
197
|
if (this.role === 'plugin' && typeof (w.worklink.plugin && w.worklink.plugin.getContentContext) !== 'function') {
|
|
179
198
|
throw new Error('LightAppBridge(plugin) 依赖 window.worklink.plugin.getContentContext,当前环境不可用');
|
|
180
199
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
200
|
+
// 监听后端(waltz / handleMessageFromNative)允许稍后出现,由 _startListener 异步等待,
|
|
201
|
+
// 不在构造时强校验;两者始终未出现时 connect()/ready() 会 reject。
|
|
184
202
|
}
|
|
185
|
-
/**
|
|
186
|
-
|
|
203
|
+
/** 异步绑定消息监听:优先 waltz,回退 hook handleMessageFromNative(可能需等待其注入) */
|
|
204
|
+
_startListener() {
|
|
187
205
|
if (this._started)
|
|
188
206
|
return;
|
|
189
207
|
this._started = true;
|
|
190
|
-
|
|
191
|
-
|
|
208
|
+
this._listenerReady = bindMessageBackend((msg) => this._onMessage(msg))
|
|
209
|
+
.then((unlisten) => {
|
|
210
|
+
this._unlisten = unlisten;
|
|
211
|
+
})
|
|
212
|
+
.catch((e) => {
|
|
213
|
+
// 监听后端不可用:让连接失败并给出明确原因
|
|
214
|
+
this._connectDeferred.reject(new Error('LightAppBridge 无可用监听后端:window.waltz.event 与 window.handleMessageFromNative 均不可用 (' +
|
|
215
|
+
(e && e.message ? e.message : String(e)) + ')'));
|
|
216
|
+
});
|
|
192
217
|
}
|
|
193
218
|
/** 构造消息信封 */
|
|
194
219
|
_envelope(type, data, id = genId()) {
|
|
@@ -225,34 +250,42 @@ export default class LightAppBridge {
|
|
|
225
250
|
* @returns 对端 webContentsId
|
|
226
251
|
*/
|
|
227
252
|
connect() {
|
|
253
|
+
var _a;
|
|
228
254
|
if (this._connectStarted)
|
|
229
255
|
return this._connectPromise;
|
|
230
256
|
this._connectStarted = true;
|
|
231
|
-
//
|
|
257
|
+
// 等监听后端就绪(handleMessageFromNative 可能需等待注入)后再握手,
|
|
258
|
+
// 避免对端的 ack/hello 在监听绑定前到达而丢失
|
|
259
|
+
const listenerReady = (_a = this._listenerReady) !== null && _a !== void 0 ? _a : Promise.resolve();
|
|
260
|
+
// 握手超时保护(含监听等待时间)
|
|
232
261
|
this._handshakeTimer = setTimeout(() => {
|
|
233
262
|
if (!this._connected) {
|
|
234
263
|
this._connectDeferred.reject(new Error(`握手超时 (${this.handshakeTimeout}ms),对端未响应`));
|
|
235
264
|
this._clearHelloTimer();
|
|
236
265
|
}
|
|
237
266
|
}, this.handshakeTimeout);
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
267
|
+
listenerReady
|
|
268
|
+
.then(() => {
|
|
269
|
+
if (this._connected)
|
|
270
|
+
return;
|
|
271
|
+
if (this.role === 'plugin') {
|
|
272
|
+
// 插件:解析目标后发 hello,并重试直到收到 ack
|
|
273
|
+
return window.worklink.plugin.getContentContext()
|
|
274
|
+
.then((ctx) => {
|
|
275
|
+
if (!ctx || ctx.webContentsId == null) {
|
|
276
|
+
throw new Error('getContentContext 未返回 webContentsId');
|
|
277
|
+
}
|
|
278
|
+
this._target = ctx.webContentsId;
|
|
279
|
+
this._sendHello();
|
|
280
|
+
this._helloTimer = setInterval(() => {
|
|
281
|
+
if (!this._connected)
|
|
282
|
+
this._sendHello();
|
|
283
|
+
}, HELLO_RETRY_INTERVAL);
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
// lightapp 被动等待 hello(listener 已绑定),收到后在 _onHello 中回 ack 并 resolve
|
|
287
|
+
})
|
|
288
|
+
.catch((e) => this._connectDeferred.reject(e));
|
|
256
289
|
return this._connectPromise;
|
|
257
290
|
}
|
|
258
291
|
/** 发送 hello(携带自身 webContentsId) */
|