@heybox/hb-sdk 0.1.3 → 0.2.0-alpha.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/README.md +167 -37
- package/bin/hb-sdk.cjs +3 -0
- package/dist/cli.cjs +9639 -0
- package/dist/devtools/mock-host/index.html +626 -0
- package/dist/index.cjs.js +477 -75
- package/dist/index.esm.js +463 -70
- package/dist/protocol.cjs.js +104 -0
- package/dist/protocol.esm.js +90 -0
- package/dist/templates/vue3-vite-ts/.gitignore.ejs +5 -0
- package/dist/templates/vue3-vite-ts/README.md.ejs +46 -0
- package/dist/templates/vue3-vite-ts/index.html.ejs +12 -0
- package/dist/templates/vue3-vite-ts/package.json.ejs +29 -0
- package/dist/templates/vue3-vite-ts/src/App.vue +63 -0
- package/dist/templates/vue3-vite-ts/src/__tests__/App.spec.ts +67 -0
- package/dist/templates/vue3-vite-ts/src/main.ts +5 -0
- package/dist/templates/vue3-vite-ts/src/styles.css +60 -0
- package/dist/templates/vue3-vite-ts/src/vite-env.d.ts +1 -0
- package/dist/templates/vue3-vite-ts/tsconfig.app.json +17 -0
- package/dist/templates/vue3-vite-ts/tsconfig.json +11 -0
- package/dist/templates/vue3-vite-ts/tsconfig.node.json +11 -0
- package/dist/templates/vue3-vite-ts/vite.config.ts +6 -0
- package/dist/templates/vue3-vite-ts/vitest.config.ts +10 -0
- package/package.json +28 -5
- package/types/core/client.d.ts +23 -3
- package/types/core/errors.d.ts +45 -2
- package/types/core/sdk.d.ts +78 -10
- package/types/core/singleton.d.ts +33 -7
- package/types/core/utils.d.ts +2 -0
- package/types/index.d.ts +10 -4
- package/types/modules/auth/index.d.ts +42 -0
- package/types/modules/network/index.d.ts +125 -0
- package/types/modules/share/index.d.ts +12 -2
- package/types/modules/share/screenshot.d.ts +14 -2
- package/types/modules/share/show-share-menu.d.ts +14 -2
- package/types/modules/share/types.d.ts +24 -4
- package/types/modules/storage/index.d.ts +70 -0
- package/types/modules/user/get-info.d.ts +11 -1
- package/types/modules/user/index.d.ts +13 -9
- package/types/modules/user/types.d.ts +1 -0
- package/types/modules/viewport/index.d.ts +78 -0
- package/types/protocol/guards.d.ts +6 -1
- package/types/protocol/types.d.ts +19 -4
- package/types/protocol.d.ts +18 -0
- package/types/modules/system/get-storage.d.ts +0 -15
- package/types/modules/system/get-window-info.d.ts +0 -16
- package/types/modules/system/index.d.ts +0 -23
- package/types/modules/system/set-storage.d.ts +0 -12
- package/types/modules/system/types.d.ts +0 -34
- package/types/modules/user/login.d.ts +0 -18
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="zh-CN">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>HB SDK Mock Runtime</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
box-sizing: border-box;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
body {
|
|
13
|
+
margin: 0;
|
|
14
|
+
min-height: 100vh;
|
|
15
|
+
color: #1f2933;
|
|
16
|
+
background: #eef2f6;
|
|
17
|
+
font-family:
|
|
18
|
+
Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
button,
|
|
22
|
+
input {
|
|
23
|
+
font: inherit;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.layout {
|
|
27
|
+
display: grid;
|
|
28
|
+
grid-template-columns: minmax(320px, 400px) minmax(360px, 1fr);
|
|
29
|
+
min-height: 100vh;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.panel {
|
|
33
|
+
display: flex;
|
|
34
|
+
flex-direction: column;
|
|
35
|
+
gap: 16px;
|
|
36
|
+
padding: 18px;
|
|
37
|
+
border-right: 1px solid #d7dee8;
|
|
38
|
+
background: #ffffff;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.panel h1 {
|
|
42
|
+
margin: 0;
|
|
43
|
+
font-size: 22px;
|
|
44
|
+
line-height: 1.25;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.panel p {
|
|
48
|
+
margin: 0;
|
|
49
|
+
color: #64748b;
|
|
50
|
+
line-height: 1.5;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.status {
|
|
54
|
+
display: grid;
|
|
55
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
56
|
+
gap: 8px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.status div,
|
|
60
|
+
.block {
|
|
61
|
+
min-width: 0;
|
|
62
|
+
border: 1px solid #d7dee8;
|
|
63
|
+
border-radius: 8px;
|
|
64
|
+
padding: 12px;
|
|
65
|
+
background: #f8fafc;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.status span,
|
|
69
|
+
.block span {
|
|
70
|
+
display: block;
|
|
71
|
+
color: #64748b;
|
|
72
|
+
font-size: 12px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.status strong {
|
|
76
|
+
display: block;
|
|
77
|
+
margin-top: 6px;
|
|
78
|
+
overflow-wrap: anywhere;
|
|
79
|
+
font-size: 16px;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.block strong {
|
|
83
|
+
display: block;
|
|
84
|
+
margin-top: 6px;
|
|
85
|
+
overflow-wrap: anywhere;
|
|
86
|
+
word-break: break-word;
|
|
87
|
+
font-size: 13px;
|
|
88
|
+
line-height: 1.45;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.actions {
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-wrap: wrap;
|
|
94
|
+
gap: 8px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.actions button {
|
|
98
|
+
min-height: 36px;
|
|
99
|
+
border: 1px solid #cad5e2;
|
|
100
|
+
border-radius: 6px;
|
|
101
|
+
padding: 0 12px;
|
|
102
|
+
color: #1f2933;
|
|
103
|
+
background: #fff;
|
|
104
|
+
cursor: pointer;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.actions button.primary {
|
|
108
|
+
border-color: #2563eb;
|
|
109
|
+
color: #fff;
|
|
110
|
+
background: #2563eb;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.field {
|
|
114
|
+
display: grid;
|
|
115
|
+
gap: 6px;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.field input {
|
|
119
|
+
min-height: 36px;
|
|
120
|
+
width: 100%;
|
|
121
|
+
border: 1px solid #cad5e2;
|
|
122
|
+
border-radius: 6px;
|
|
123
|
+
padding: 0 10px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.logs {
|
|
127
|
+
display: grid;
|
|
128
|
+
gap: 8px;
|
|
129
|
+
max-height: 38vh;
|
|
130
|
+
overflow: auto;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.log {
|
|
134
|
+
border: 1px solid #d7dee8;
|
|
135
|
+
border-radius: 8px;
|
|
136
|
+
padding: 10px;
|
|
137
|
+
background: #fff;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.log strong {
|
|
141
|
+
display: block;
|
|
142
|
+
overflow-wrap: anywhere;
|
|
143
|
+
font-size: 13px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.log pre {
|
|
147
|
+
margin: 8px 0 0;
|
|
148
|
+
overflow: auto;
|
|
149
|
+
color: #475569;
|
|
150
|
+
font-size: 12px;
|
|
151
|
+
line-height: 1.45;
|
|
152
|
+
white-space: pre-wrap;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.stage {
|
|
156
|
+
display: grid;
|
|
157
|
+
place-items: center;
|
|
158
|
+
min-width: 0;
|
|
159
|
+
padding: 22px;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.device {
|
|
163
|
+
width: min(430px, 100%);
|
|
164
|
+
height: min(860px, calc(100vh - 44px));
|
|
165
|
+
overflow: hidden;
|
|
166
|
+
border: 1px solid #cad5e2;
|
|
167
|
+
border-radius: 22px;
|
|
168
|
+
background: #fff;
|
|
169
|
+
box-shadow: 0 20px 45px rgba(15, 23, 42, 0.15);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
iframe {
|
|
173
|
+
width: 100%;
|
|
174
|
+
height: 100%;
|
|
175
|
+
border: 0;
|
|
176
|
+
background: #fff;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.error {
|
|
180
|
+
padding: 24px;
|
|
181
|
+
color: #991b1b;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
@media (max-width: 860px) {
|
|
185
|
+
.layout {
|
|
186
|
+
grid-template-columns: 1fr;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.panel {
|
|
190
|
+
border-right: 0;
|
|
191
|
+
border-bottom: 1px solid #d7dee8;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
195
|
+
</head>
|
|
196
|
+
<body>
|
|
197
|
+
<div class="layout">
|
|
198
|
+
<aside class="panel">
|
|
199
|
+
<div>
|
|
200
|
+
<h1>HB SDK Mock Runtime</h1>
|
|
201
|
+
<p>本地浏览器宿主通过 iframe 加载小程序,使用真实 @heybox/hb-sdk bridge。</p>
|
|
202
|
+
</div>
|
|
203
|
+
|
|
204
|
+
<section class="status">
|
|
205
|
+
<div>
|
|
206
|
+
<span>Bridge</span>
|
|
207
|
+
<strong id="bridge-status">waiting</strong>
|
|
208
|
+
</div>
|
|
209
|
+
<div>
|
|
210
|
+
<span>User</span>
|
|
211
|
+
<strong id="user-status">PC Debug User</strong>
|
|
212
|
+
</div>
|
|
213
|
+
</section>
|
|
214
|
+
|
|
215
|
+
<section class="block">
|
|
216
|
+
<span>Mini URL</span>
|
|
217
|
+
<strong id="mini-url"></strong>
|
|
218
|
+
</section>
|
|
219
|
+
|
|
220
|
+
<section class="block">
|
|
221
|
+
<label class="field">
|
|
222
|
+
<span>Nickname</span>
|
|
223
|
+
<input id="nickname-input" value="PC Debug User" />
|
|
224
|
+
</label>
|
|
225
|
+
<div class="actions" style="margin-top: 10px">
|
|
226
|
+
<button class="primary" id="login-button" type="button">登录</button>
|
|
227
|
+
<button id="logout-button" type="button">登出</button>
|
|
228
|
+
<button id="show-button" type="button">show</button>
|
|
229
|
+
<button id="hide-button" type="button">hide</button>
|
|
230
|
+
</div>
|
|
231
|
+
</section>
|
|
232
|
+
|
|
233
|
+
<section class="block">
|
|
234
|
+
<span>Storage Snapshot</span>
|
|
235
|
+
<pre id="storage-snapshot">{}</pre>
|
|
236
|
+
</section>
|
|
237
|
+
|
|
238
|
+
<section class="block">
|
|
239
|
+
<span>Call Logs</span>
|
|
240
|
+
<div class="logs" id="logs"></div>
|
|
241
|
+
</section>
|
|
242
|
+
</aside>
|
|
243
|
+
|
|
244
|
+
<main class="stage">
|
|
245
|
+
<div class="device" id="device"></div>
|
|
246
|
+
</main>
|
|
247
|
+
</div>
|
|
248
|
+
|
|
249
|
+
<script>
|
|
250
|
+
const NAMESPACE = 'heybox:miniprogram';
|
|
251
|
+
const VERSION = 1;
|
|
252
|
+
const NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
253
|
+
const NETWORK_PROXY_PATH = '/__hb_sdk_mock_network__';
|
|
254
|
+
const params = new URL(location.href).searchParams;
|
|
255
|
+
const miniUrl = params.get('mini_url');
|
|
256
|
+
const nonce = createNonce();
|
|
257
|
+
const storage = new Map();
|
|
258
|
+
const logs = [];
|
|
259
|
+
let handshaken = false;
|
|
260
|
+
let currentUser = {
|
|
261
|
+
heybox_id: 'debug_user',
|
|
262
|
+
nickname: 'PC Debug User',
|
|
263
|
+
avatar: '',
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
const elements = {
|
|
267
|
+
bridgeStatus: document.querySelector('#bridge-status'),
|
|
268
|
+
device: document.querySelector('#device'),
|
|
269
|
+
logs: document.querySelector('#logs'),
|
|
270
|
+
miniUrl: document.querySelector('#mini-url'),
|
|
271
|
+
nicknameInput: document.querySelector('#nickname-input'),
|
|
272
|
+
storageSnapshot: document.querySelector('#storage-snapshot'),
|
|
273
|
+
userStatus: document.querySelector('#user-status'),
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
if (!miniUrl) {
|
|
277
|
+
elements.device.innerHTML = '<div class="error">Missing mini_url query.</div>';
|
|
278
|
+
throw new Error('Missing mini_url query.');
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const iframe = document.createElement('iframe');
|
|
282
|
+
iframe.src = appendNonce(miniUrl, nonce);
|
|
283
|
+
iframe.allow = 'clipboard-read; clipboard-write';
|
|
284
|
+
elements.device.appendChild(iframe);
|
|
285
|
+
elements.miniUrl.textContent = miniUrl;
|
|
286
|
+
updateUserStatus();
|
|
287
|
+
updateStorageSnapshot();
|
|
288
|
+
|
|
289
|
+
window.addEventListener('message', handleMessage);
|
|
290
|
+
window.addEventListener('beforeunload', () => {
|
|
291
|
+
postEvent('unload', { timestamp: Date.now() });
|
|
292
|
+
});
|
|
293
|
+
document.querySelector('#login-button').addEventListener('click', () => {
|
|
294
|
+
currentUser = {
|
|
295
|
+
heybox_id: 'debug_user',
|
|
296
|
+
nickname: elements.nicknameInput.value.trim() || 'PC Debug User',
|
|
297
|
+
avatar: '',
|
|
298
|
+
};
|
|
299
|
+
updateUserStatus();
|
|
300
|
+
postEvent('authChange', getUserInfoResult());
|
|
301
|
+
});
|
|
302
|
+
document.querySelector('#logout-button').addEventListener('click', () => {
|
|
303
|
+
currentUser = null;
|
|
304
|
+
updateUserStatus();
|
|
305
|
+
postEvent('authChange', getUserInfoResult());
|
|
306
|
+
});
|
|
307
|
+
document.querySelector('#show-button').addEventListener('click', () => {
|
|
308
|
+
postEvent('show', { timestamp: Date.now(), source: 'mock-host' });
|
|
309
|
+
});
|
|
310
|
+
document.querySelector('#hide-button').addEventListener('click', () => {
|
|
311
|
+
postEvent('hide', { timestamp: Date.now(), source: 'mock-host' });
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
function handleMessage(event) {
|
|
315
|
+
if (event.source !== iframe.contentWindow || !isBridgeMessage(event.data)) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const message = event.data;
|
|
320
|
+
if (message.type === 'handshake') {
|
|
321
|
+
handleHandshake();
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (message.type === 'request') {
|
|
326
|
+
void handleRequest(message);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function handleHandshake() {
|
|
331
|
+
const firstHandshake = !handshaken;
|
|
332
|
+
handshaken = true;
|
|
333
|
+
elements.bridgeStatus.textContent = 'ready';
|
|
334
|
+
|
|
335
|
+
if (firstHandshake) {
|
|
336
|
+
postEvent('launch', {
|
|
337
|
+
timestamp: Date.now(),
|
|
338
|
+
url: iframe.src,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
postEvent('ready', {
|
|
343
|
+
timestamp: Date.now(),
|
|
344
|
+
});
|
|
345
|
+
pushLog('sdk.handshake', { firstHandshake });
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
async function handleRequest(message) {
|
|
349
|
+
if (!message.id || !message.method) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const payload = await runMethod(message.method, message.payload);
|
|
355
|
+
postResponse(message, payload);
|
|
356
|
+
pushLog(message.method, { payload: message.payload, result: payload });
|
|
357
|
+
} catch (error) {
|
|
358
|
+
const bridgeError = toBridgeError(error);
|
|
359
|
+
postResponse(message, undefined, bridgeError);
|
|
360
|
+
postEvent('error', bridgeError);
|
|
361
|
+
pushLog(message.method, { payload: message.payload, error: bridgeError });
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async function runMethod(method, payload) {
|
|
366
|
+
if (method === 'auth.login') {
|
|
367
|
+
currentUser = {
|
|
368
|
+
heybox_id: 'debug_user',
|
|
369
|
+
nickname: elements.nicknameInput.value.trim() || 'PC Debug User',
|
|
370
|
+
avatar: '',
|
|
371
|
+
};
|
|
372
|
+
updateUserStatus();
|
|
373
|
+
const result = getUserInfoResult();
|
|
374
|
+
postEvent('authChange', result);
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (method === 'user.getInfo') {
|
|
379
|
+
return getUserInfoResult();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (method === 'share.showShareMenu') {
|
|
383
|
+
return {
|
|
384
|
+
ok: true,
|
|
385
|
+
platform: 'hb-sdk-mock-host',
|
|
386
|
+
type: 'showShareMenu',
|
|
387
|
+
params: payload,
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (method === 'share.screenshot') {
|
|
392
|
+
return {
|
|
393
|
+
ok: true,
|
|
394
|
+
platform: 'hb-sdk-mock-host',
|
|
395
|
+
type: 'screenshot',
|
|
396
|
+
params: payload,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (method === 'viewport.getWindowInfo') {
|
|
401
|
+
return getWindowInfo();
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (method === 'storage.getStorage') {
|
|
405
|
+
return {
|
|
406
|
+
data: storage.get(payload && payload.key),
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (method === 'storage.setStorage') {
|
|
411
|
+
storage.set(payload && payload.key, payload && payload.data);
|
|
412
|
+
updateStorageSnapshot();
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (method === 'network.request') {
|
|
417
|
+
return requestNetwork(payload || {});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
throw {
|
|
421
|
+
code: 'METHOD_NOT_FOUND',
|
|
422
|
+
message: `未开放小程序能力: ${method}`,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async function requestNetwork(config) {
|
|
427
|
+
const proxyPayload = createNetworkProxyPayload(config);
|
|
428
|
+
|
|
429
|
+
const response = await fetch(NETWORK_PROXY_PATH, {
|
|
430
|
+
method: 'POST',
|
|
431
|
+
headers: {
|
|
432
|
+
'content-type': 'application/json',
|
|
433
|
+
},
|
|
434
|
+
body: JSON.stringify(proxyPayload),
|
|
435
|
+
});
|
|
436
|
+
const payload = await readNetworkProxyResponse(response);
|
|
437
|
+
|
|
438
|
+
if (!response.ok) {
|
|
439
|
+
throw payload;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return payload;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async function readNetworkProxyResponse(response) {
|
|
446
|
+
const text = await response.text();
|
|
447
|
+
|
|
448
|
+
if (!text.trim()) {
|
|
449
|
+
return undefined;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
try {
|
|
453
|
+
return JSON.parse(text);
|
|
454
|
+
} catch {
|
|
455
|
+
return {
|
|
456
|
+
code: response.status === 404 ? 'MOCK_NETWORK_PROXY_NOT_FOUND' : 'MOCK_NETWORK_PROXY_ERROR',
|
|
457
|
+
message:
|
|
458
|
+
response.status === 404
|
|
459
|
+
? 'mock network proxy 不可用,请重启 hb-sdk dev --mock'
|
|
460
|
+
: text,
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
function createNetworkProxyPayload(config) {
|
|
466
|
+
const url = new URL(config.url, iframe.src);
|
|
467
|
+
|
|
468
|
+
if (config.params && typeof config.params === 'object') {
|
|
469
|
+
Object.entries(config.params).forEach(([key, value]) => {
|
|
470
|
+
if (value !== undefined && value !== null) {
|
|
471
|
+
url.searchParams.set(key, String(value));
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
return {
|
|
477
|
+
url: url.toString(),
|
|
478
|
+
method: config.method,
|
|
479
|
+
data: config.data,
|
|
480
|
+
headers: config.headers,
|
|
481
|
+
timeout: config.timeout,
|
|
482
|
+
withCredentials: config.withCredentials,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function postEvent(method, payload) {
|
|
487
|
+
postMessageToMiniProgram({
|
|
488
|
+
namespace: NAMESPACE,
|
|
489
|
+
version: VERSION,
|
|
490
|
+
nonce,
|
|
491
|
+
type: 'event',
|
|
492
|
+
method,
|
|
493
|
+
payload,
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
function postResponse(request, payload, error) {
|
|
498
|
+
postMessageToMiniProgram({
|
|
499
|
+
namespace: NAMESPACE,
|
|
500
|
+
version: VERSION,
|
|
501
|
+
nonce,
|
|
502
|
+
type: 'response',
|
|
503
|
+
id: request.id,
|
|
504
|
+
method: request.method,
|
|
505
|
+
payload,
|
|
506
|
+
error,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function postMessageToMiniProgram(message) {
|
|
511
|
+
iframe.contentWindow && iframe.contentWindow.postMessage(message, readTargetOrigin());
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
function isBridgeMessage(message) {
|
|
515
|
+
return Boolean(
|
|
516
|
+
message &&
|
|
517
|
+
typeof message === 'object' &&
|
|
518
|
+
message.namespace === NAMESPACE &&
|
|
519
|
+
message.version === VERSION &&
|
|
520
|
+
message.nonce === nonce,
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function getUserInfoResult() {
|
|
525
|
+
return {
|
|
526
|
+
isLogin: Boolean(currentUser),
|
|
527
|
+
userInfo: currentUser ? { ...currentUser } : null,
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function getWindowInfo() {
|
|
532
|
+
const rect = iframe.getBoundingClientRect();
|
|
533
|
+
const width = Math.max(1, Math.round(rect.width));
|
|
534
|
+
const height = Math.max(1, Math.round(rect.height));
|
|
535
|
+
|
|
536
|
+
return {
|
|
537
|
+
pixelRatio: window.devicePixelRatio || 1,
|
|
538
|
+
screenWidth: window.screen.width || width,
|
|
539
|
+
screenHeight: window.screen.height || height,
|
|
540
|
+
windowWidth: width,
|
|
541
|
+
windowHeight: height,
|
|
542
|
+
statusBarHeight: 0,
|
|
543
|
+
safeArea: {
|
|
544
|
+
left: 0,
|
|
545
|
+
top: 0,
|
|
546
|
+
right: width,
|
|
547
|
+
bottom: height,
|
|
548
|
+
width,
|
|
549
|
+
height,
|
|
550
|
+
},
|
|
551
|
+
screenTop: 0,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function updateUserStatus() {
|
|
556
|
+
elements.userStatus.textContent = currentUser ? currentUser.nickname : '未登录';
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function updateStorageSnapshot() {
|
|
560
|
+
elements.storageSnapshot.textContent = JSON.stringify(
|
|
561
|
+
Object.fromEntries(storage.entries()),
|
|
562
|
+
null,
|
|
563
|
+
2,
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function pushLog(method, detail) {
|
|
568
|
+
logs.unshift({
|
|
569
|
+
method,
|
|
570
|
+
detail,
|
|
571
|
+
timestamp: new Date().toLocaleTimeString(),
|
|
572
|
+
});
|
|
573
|
+
logs.splice(30);
|
|
574
|
+
elements.logs.innerHTML = logs
|
|
575
|
+
.map(
|
|
576
|
+
log => `<article class="log"><strong>${escapeHtml(log.method)} · ${escapeHtml(
|
|
577
|
+
log.timestamp,
|
|
578
|
+
)}</strong><pre>${escapeHtml(JSON.stringify(log.detail, null, 2))}</pre></article>`,
|
|
579
|
+
)
|
|
580
|
+
.join('');
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
function appendNonce(input, value) {
|
|
584
|
+
const url = new URL(input, location.href);
|
|
585
|
+
url.searchParams.set(NONCE_PARAM, value);
|
|
586
|
+
return url.toString();
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
function readTargetOrigin() {
|
|
590
|
+
try {
|
|
591
|
+
return new URL(iframe.src).origin;
|
|
592
|
+
} catch {
|
|
593
|
+
return '*';
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function createNonce() {
|
|
598
|
+
if (crypto && crypto.randomUUID) {
|
|
599
|
+
return crypto.randomUUID();
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
return `mock_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
function toBridgeError(error) {
|
|
606
|
+
if (error && typeof error === 'object' && error.code && error.message) {
|
|
607
|
+
return error;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return {
|
|
611
|
+
code: 'MOCK_RUNTIME_ERROR',
|
|
612
|
+
message: error && error.message ? error.message : String(error),
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function escapeHtml(value) {
|
|
617
|
+
return String(value)
|
|
618
|
+
.replace(/&/g, '&')
|
|
619
|
+
.replace(/</g, '<')
|
|
620
|
+
.replace(/>/g, '>')
|
|
621
|
+
.replace(/"/g, '"')
|
|
622
|
+
.replace(/'/g, ''');
|
|
623
|
+
}
|
|
624
|
+
</script>
|
|
625
|
+
</body>
|
|
626
|
+
</html>
|