@heybox/hb-sdk 0.2.0-alpha.1 → 0.3.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.
@@ -146,26 +146,96 @@ function createMacAppProtocol(appUrl) {
146
146
  return `heybox://${encodeURIComponent(JSON.stringify(protocolPayload))}`;
147
147
  }
148
148
 
149
+ async function getMiniProgramRuntimeUserInfo(platformAdapter) {
150
+ const userId = await resolveCurrentUserId(platformAdapter);
151
+ if (!userId) {
152
+ return {
153
+ isLogin: false,
154
+ userInfo: null,
155
+ };
156
+ }
157
+ const result = await platformAdapter.user.getUserBasicInfo(userId);
158
+ return {
159
+ isLogin: true,
160
+ userInfo: {
161
+ heybox_id: userId,
162
+ nickname: result?.nickname || '',
163
+ avatar: result?.avatar || '',
164
+ },
165
+ };
166
+ }
167
+ function resolveCurrentUserId(platformAdapter) {
168
+ return platformAdapter.auth.getCurrentUserId()
169
+ .then(userId => (userId ? String(userId) : ''))
170
+ .catch(() => '');
171
+ }
172
+
173
+ async function loginAndGetMiniProgramRuntimeUserInfo(platformAdapter) {
174
+ await platformAdapter.auth.login();
175
+ return getMiniProgramRuntimeUserInfo(platformAdapter);
176
+ }
177
+
178
+ function getMiniProgramRuntimeWindowInfo(platformAdapter) {
179
+ const metrics = platformAdapter.viewport.getViewportMetrics();
180
+ const windowWidth = getViewportWidth(metrics);
181
+ const windowHeight = getViewportHeight(metrics);
182
+ const screenWidth = getPositiveNumber(metrics.screenWidth, windowWidth);
183
+ const screenHeight = getPositiveNumber(metrics.screenHeight, windowHeight);
184
+ const navigationBarHeight = getNavigationBarHeight(platformAdapter);
185
+ const safeAreaHeight = Math.max(windowHeight - navigationBarHeight, 0);
186
+ return {
187
+ pixelRatio: getPositiveNumber(metrics.pixelRatio, 1),
188
+ screenWidth,
189
+ screenHeight,
190
+ windowWidth,
191
+ windowHeight,
192
+ statusBarHeight: navigationBarHeight,
193
+ safeArea: {
194
+ left: 0,
195
+ right: windowWidth,
196
+ top: navigationBarHeight,
197
+ bottom: windowHeight,
198
+ width: windowWidth,
199
+ height: safeAreaHeight,
200
+ },
201
+ screenTop: navigationBarHeight,
202
+ };
203
+ }
204
+ function getMiniProgramRuntimeViewportRect(platformAdapter) {
205
+ const metrics = platformAdapter.viewport.getViewportMetrics();
206
+ const width = getPositiveNumber(metrics.innerWidth, getPositiveNumber(metrics.documentElementClientWidth, 0));
207
+ const height = getPositiveNumber(metrics.innerHeight, getPositiveNumber(metrics.documentElementClientHeight, 0));
208
+ return {
209
+ left: 0,
210
+ top: 0,
211
+ width,
212
+ height,
213
+ };
214
+ }
215
+ function getNavigationBarHeight(platformAdapter) {
216
+ try {
217
+ return platformAdapter.viewport.getNavigationBarHeight();
218
+ }
219
+ catch {
220
+ return 0;
221
+ }
222
+ }
223
+ function getViewportWidth(metrics) {
224
+ return getPositiveNumber(metrics.innerWidth, getPositiveNumber(metrics.documentElementClientWidth, 0));
225
+ }
226
+ function getViewportHeight(metrics) {
227
+ return getPositiveNumber(metrics.innerHeight, getPositiveNumber(metrics.documentElementClientHeight, 0));
228
+ }
229
+ function getPositiveNumber(value, fallback) {
230
+ const numberValue = Number(value);
231
+ if (!Number.isFinite(numberValue) || numberValue <= 0) {
232
+ return fallback;
233
+ }
234
+ return numberValue;
235
+ }
236
+
149
237
  const DEFAULT_STORAGE_KEY_MAX_LENGTH = 128;
150
238
  const STORAGE_KEY_CHAR_RE = /^[A-Za-z0-9_-]+$/;
151
- const NETWORK_REQUEST_ALLOWED_KEYS = new Set([
152
- 'url',
153
- 'method',
154
- 'params',
155
- 'data',
156
- 'headers',
157
- 'timeout',
158
- 'withCredentials',
159
- ]);
160
- const NETWORK_HTTP_METHODS = new Set([
161
- 'GET',
162
- 'POST',
163
- 'PUT',
164
- 'PATCH',
165
- 'DELETE',
166
- 'HEAD',
167
- 'OPTIONS',
168
- ]);
169
239
  function createMiniProgramRuntimeBridgeError(code, message, data) {
170
240
  return {
171
241
  code,
@@ -248,68 +318,83 @@ function stringifyMiniProgramRuntimeJsonData(data, options) {
248
318
  }
249
319
  return value;
250
320
  }
251
- async function getMiniProgramRuntimeUserInfo(platformAdapter) {
252
- const userId = await resolveCurrentUserId(platformAdapter);
253
- if (!userId) {
254
- return {
255
- isLogin: false,
256
- userInfo: null,
257
- };
258
- }
259
- const result = await platformAdapter.user.getUserBasicInfo(userId);
260
- return {
261
- isLogin: true,
262
- userInfo: {
263
- heybox_id: userId,
264
- nickname: result?.nickname || '',
265
- avatar: result?.avatar || '',
266
- },
267
- };
268
- }
269
- async function loginAndGetMiniProgramRuntimeUserInfo(platformAdapter) {
270
- await platformAdapter.auth.login();
271
- return getMiniProgramRuntimeUserInfo(platformAdapter);
321
+
322
+ /** 展示基础分享面板。 */
323
+ function showMiniProgramRuntimeShareMenu(payload, platformAdapter) {
324
+ return platformAdapter.share.showShareMenu(readMiniProgramShareMenuOptions(payload));
272
325
  }
273
- function getMiniProgramRuntimeWindowInfo(platformAdapter) {
274
- const metrics = platformAdapter.viewport.getViewportMetrics();
275
- const windowWidth = getViewportWidth(metrics);
276
- const windowHeight = getViewportHeight(metrics);
277
- const screenWidth = getPositiveNumber(metrics.screenWidth, windowWidth);
278
- const screenHeight = getPositiveNumber(metrics.screenHeight, windowHeight);
279
- const navigationBarHeight = getNavigationBarHeight(platformAdapter);
280
- const safeAreaHeight = Math.max(windowHeight - navigationBarHeight, 0);
326
+ function readMiniProgramShareMenuOptions(payload) {
327
+ assertMiniProgramRuntimeRecord(payload, 'share.showShareMenu 参数必须是对象');
328
+ const title = readMiniProgramRuntimeRequiredString(payload.title, 'share.showShareMenu title 必须是非空字符串');
329
+ const desc = readMiniProgramRuntimeRequiredString(payload.desc, 'share.showShareMenu desc 必须是非空字符串');
330
+ const url = readMiniProgramRuntimeHttpUrl(payload.url, 'share.showShareMenu url 必须是 HTTP(S) URL');
331
+ const imageUrl = payload.imageUrl === undefined
332
+ ? undefined
333
+ : readMiniProgramRuntimeHttpUrl(payload.imageUrl, 'share.showShareMenu imageUrl 必须是 HTTP(S) URL');
334
+ const channel = payload.channel;
335
+ if (channel !== undefined && !isShareChannel(channel)) {
336
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'share.showShareMenu channel 不合法');
337
+ }
281
338
  return {
282
- pixelRatio: getPositiveNumber(metrics.pixelRatio, 1),
283
- screenWidth,
284
- screenHeight,
285
- windowWidth,
286
- windowHeight,
287
- statusBarHeight: navigationBarHeight,
288
- safeArea: {
289
- left: 0,
290
- right: windowWidth,
291
- top: navigationBarHeight,
292
- bottom: windowHeight,
293
- width: windowWidth,
294
- height: safeAreaHeight,
295
- },
296
- screenTop: navigationBarHeight,
339
+ title,
340
+ desc,
341
+ url,
342
+ imageUrl,
343
+ channel,
297
344
  };
298
345
  }
299
- function showMiniProgramRuntimeShareMenu(payload, platformAdapter) {
300
- return platformAdapter.share.showShareMenu(readMiniProgramRuntimeShareMenuOptions(payload));
346
+ function isShareChannel(value) {
347
+ return (value === 'wechatSession' ||
348
+ value === 'wechatTimeline' ||
349
+ value === 'qqFriend' ||
350
+ value === 'qzone' ||
351
+ value === 'weibo');
301
352
  }
353
+
354
+ /** 截图并唤起分享。 */
302
355
  function shareMiniProgramRuntimeScreenshot(payload, platformAdapter) {
303
- const options = readMiniProgramRuntimeScreenshotOptions(payload);
304
- const rect = options.rect || getViewportRect(platformAdapter);
356
+ const options = readMiniProgramScreenshotOptions(payload);
357
+ const rect = options.rect || getMiniProgramRuntimeViewportRect(platformAdapter);
305
358
  return platformAdapter.share.shareScreenshot({
306
359
  rect,
307
360
  delay: options.delay,
308
361
  saveToAlbum: options.saveToAlbum,
309
362
  });
310
363
  }
364
+ function readMiniProgramScreenshotOptions(payload) {
365
+ if (payload === undefined || payload === null) {
366
+ return {};
367
+ }
368
+ assertMiniProgramRuntimeRecord(payload, 'share.screenshot 参数必须是对象');
369
+ const options = {};
370
+ if (payload.rect !== undefined) {
371
+ options.rect = readScreenshotRect(payload.rect);
372
+ }
373
+ if (payload.delay !== undefined) {
374
+ options.delay = readMiniProgramRuntimeNonNegativeNumber(payload.delay, 'share.screenshot delay 必须是非负数字');
375
+ }
376
+ if (payload.saveToAlbum !== undefined) {
377
+ options.saveToAlbum = readMiniProgramRuntimeBoolean(payload.saveToAlbum, 'share.screenshot saveToAlbum 必须是布尔值');
378
+ }
379
+ return options;
380
+ }
381
+ function readScreenshotRect(value) {
382
+ assertMiniProgramRuntimeRecord(value, 'share.screenshot rect 必须是对象');
383
+ const left = readMiniProgramRuntimeNonNegativeNumber(value.left, 'share.screenshot rect.left 必须是非负数字');
384
+ const top = readMiniProgramRuntimeNonNegativeNumber(value.top, 'share.screenshot rect.top 必须是非负数字');
385
+ const width = readMiniProgramRuntimePositiveNumber(value.width, 'share.screenshot rect.width 必须是正数');
386
+ const height = readMiniProgramRuntimePositiveNumber(value.height, 'share.screenshot rect.height 必须是正数');
387
+ return {
388
+ left,
389
+ top,
390
+ width,
391
+ height,
392
+ };
393
+ }
394
+
395
+ /** 获取小程序隔离 storage。 */
311
396
  function getMiniProgramRuntimeStorage(payload, options, platformAdapter) {
312
- const { key } = readMiniProgramRuntimeGetStoragePayload(payload, options.maxKeyLength);
397
+ const { key } = readMiniProgramGetStoragePayload(payload, options.maxKeyLength);
313
398
  const storageValue = platformAdapter.storage.getStorage({
314
399
  key: createMiniProgramRuntimeStorageKey(key, options.scope || getMiniProgramRuntimeStorageScope(undefined, platformAdapter), options.maxKeyLength),
315
400
  });
@@ -317,8 +402,9 @@ function getMiniProgramRuntimeStorage(payload, options, platformAdapter) {
317
402
  data: parseStorageData(storageValue),
318
403
  };
319
404
  }
405
+ /** 写入小程序隔离 storage。 */
320
406
  async function setMiniProgramRuntimeStorage(payload, options, platformAdapter) {
321
- const { key, data } = readMiniProgramRuntimeSetStoragePayload(payload, options.maxKeyLength);
407
+ const { key, data } = readMiniProgramSetStoragePayload(payload, options.maxKeyLength);
322
408
  await platformAdapter.storage.setStorage({
323
409
  key: createMiniProgramRuntimeStorageKey(key, options.scope || getMiniProgramRuntimeStorageScope(undefined, platformAdapter), options.maxKeyLength),
324
410
  value: stringifyMiniProgramRuntimeJsonData(data, {
@@ -327,8 +413,9 @@ async function setMiniProgramRuntimeStorage(payload, options, platformAdapter) {
327
413
  }),
328
414
  });
329
415
  }
416
+ /** 根据容器入口生成小程序 storage 隔离 scope。 */
330
417
  function getMiniProgramRuntimeStorageScope(href, platformAdapter) {
331
- const currentHref = platformAdapter.app.getCurrentHref();
418
+ const currentHref = platformAdapter?.app.getCurrentHref() ?? '';
332
419
  try {
333
420
  const url = new URL(currentHref);
334
421
  const miniProgramId = url.searchParams.get('mini_program_id')?.trim();
@@ -345,10 +432,90 @@ function getMiniProgramRuntimeStorageScope(href, platformAdapter) {
345
432
  return `url_${hashString(currentHref)}`;
346
433
  }
347
434
  }
435
+ /** 生成真实写入黑盒客户端的 storage key。 */
348
436
  function createMiniProgramRuntimeStorageKey(key, storageScope, maxKeyLength) {
349
437
  readMiniProgramRuntimeStorageKey(key, 'storage.setStorage', maxKeyLength);
350
438
  return `hb_miniprogram_${storageScope}_${key}`;
351
439
  }
440
+ function readMiniProgramGetStoragePayload(payload, maxKeyLength) {
441
+ if (!isMiniProgramRuntimeRecord(payload)) {
442
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'storage.getStorage 参数必须是对象');
443
+ }
444
+ const key = readMiniProgramRuntimeStorageKey(payload.key, 'storage.getStorage', maxKeyLength);
445
+ return {
446
+ key,
447
+ };
448
+ }
449
+ function readMiniProgramSetStoragePayload(payload, maxKeyLength) {
450
+ if (!isMiniProgramRuntimeRecord(payload)) {
451
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'storage.setStorage 参数必须是对象');
452
+ }
453
+ const key = readMiniProgramRuntimeStorageKey(payload.key, 'storage.setStorage', maxKeyLength);
454
+ return {
455
+ key,
456
+ data: payload.data,
457
+ };
458
+ }
459
+ function parseStorageData(value) {
460
+ if (value === undefined || value === null || value === '') {
461
+ return undefined;
462
+ }
463
+ try {
464
+ return JSON.parse(value);
465
+ }
466
+ catch {
467
+ return value;
468
+ }
469
+ }
470
+ function toStorageScopeToken(value) {
471
+ if (/^[A-Za-z0-9_-]{1,64}$/.test(value)) {
472
+ return value;
473
+ }
474
+ return hashString(value);
475
+ }
476
+ function hashString(value) {
477
+ let hash = 2166136261;
478
+ for (let index = 0; index < value.length; index += 1) {
479
+ hash ^= value.charCodeAt(index);
480
+ hash = Math.imul(hash, 16777619);
481
+ }
482
+ return (hash >>> 0).toString(36);
483
+ }
484
+
485
+ const NETWORK_REQUEST_ALLOWED_KEYS = new Set([
486
+ 'url',
487
+ 'method',
488
+ 'params',
489
+ 'data',
490
+ 'headers',
491
+ 'timeout',
492
+ 'withCredentials',
493
+ ]);
494
+ const NETWORK_REQUEST_MAX_TIMEOUT = 30000;
495
+ const NETWORK_REQUEST_MAX_BODY_BYTES = 2 * 1024 * 1024;
496
+ const NETWORK_REQUEST_DENIED_HEADERS = new Set([
497
+ 'authorization',
498
+ 'connection',
499
+ 'content-length',
500
+ 'cookie',
501
+ 'host',
502
+ 'origin',
503
+ 'proxy-authorization',
504
+ 'referer',
505
+ 'set-cookie',
506
+ 'transfer-encoding',
507
+ 'user-agent',
508
+ ]);
509
+ const NETWORK_HTTP_METHODS = new Set([
510
+ 'GET',
511
+ 'POST',
512
+ 'PUT',
513
+ 'PATCH',
514
+ 'DELETE',
515
+ 'HEAD',
516
+ 'OPTIONS',
517
+ ]);
518
+ /** network.request:校验桥接参数并委托平台适配层完成真实 HTTP 交换。 */
352
519
  async function requestMiniProgramRuntimeNetwork(payload, platformAdapter) {
353
520
  return platformAdapter.network.request(readMiniProgramRuntimeNetworkRequest(payload));
354
521
  }
@@ -356,7 +523,7 @@ function readMiniProgramRuntimeNetworkRequest(payload) {
356
523
  assertMiniProgramRuntimeRecord(payload, 'network.request 参数必须是对象');
357
524
  assertNetworkAllowedKeys(payload);
358
525
  const request = {
359
- url: readMiniProgramRuntimeHttpUrl(payload.url, 'network.request url 必须是 HTTP(S) URL'),
526
+ url: readNetworkRequestUrl(payload.url),
360
527
  };
361
528
  if (payload.method !== undefined) {
362
529
  request.method = readNetworkMethod(payload.method);
@@ -365,92 +532,129 @@ function readMiniProgramRuntimeNetworkRequest(payload) {
365
532
  request.params = readUnknownRecord(payload.params, 'network.request params 必须是对象');
366
533
  }
367
534
  if (payload.data !== undefined) {
535
+ assertNetworkRequestBody(payload.data);
368
536
  request.data = payload.data;
369
537
  }
370
538
  if (payload.headers !== undefined) {
371
- request.headers = readStringRecord(payload.headers, 'network.request headers 必须是 Record<string, string>');
539
+ request.headers = readNetworkRequestHeaders(payload.headers);
372
540
  }
373
541
  if (payload.timeout !== undefined) {
374
542
  request.timeout = readMiniProgramRuntimeNonNegativeNumber(payload.timeout, 'network.request timeout 必须是非负数字');
543
+ if (request.timeout > NETWORK_REQUEST_MAX_TIMEOUT) {
544
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `network.request timeout 不能超过 ${NETWORK_REQUEST_MAX_TIMEOUT}ms`);
545
+ }
375
546
  }
376
547
  if (payload.withCredentials !== undefined) {
377
548
  request.withCredentials = readMiniProgramRuntimeBoolean(payload.withCredentials, 'network.request withCredentials 必须是布尔值');
378
549
  }
379
550
  return request;
380
551
  }
381
- function readMiniProgramRuntimeShareMenuOptions(payload) {
382
- assertMiniProgramRuntimeRecord(payload, 'share.showShareMenu 参数必须是对象');
383
- const title = readMiniProgramRuntimeRequiredString(payload.title, 'share.showShareMenu title 必须是非空字符串');
384
- const desc = readMiniProgramRuntimeRequiredString(payload.desc, 'share.showShareMenu desc 必须是非空字符串');
385
- const url = readMiniProgramRuntimeHttpUrl(payload.url, 'share.showShareMenu url 必须是 HTTP(S) URL');
386
- const imageUrl = payload.imageUrl === undefined
387
- ? undefined
388
- : readMiniProgramRuntimeHttpUrl(payload.imageUrl, 'share.showShareMenu imageUrl 必须是 HTTP(S) URL');
389
- const channel = payload.channel;
390
- if (channel !== undefined && !isShareChannel(channel)) {
391
- throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'share.showShareMenu channel 不合法');
552
+ function assertNetworkAllowedKeys(payload) {
553
+ const invalidKeys = Object.keys(payload).filter(key => !NETWORK_REQUEST_ALLOWED_KEYS.has(key));
554
+ if (invalidKeys.length > 0) {
555
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `network.request 不支持字段: ${invalidKeys.join(', ')}`);
392
556
  }
393
- return {
394
- title,
395
- desc,
396
- url,
397
- imageUrl,
398
- channel,
399
- };
400
557
  }
401
- function readMiniProgramRuntimeScreenshotOptions(payload) {
402
- if (payload === undefined || payload === null) {
403
- return {};
558
+ function readNetworkRequestUrl(value) {
559
+ const requestUrl = readMiniProgramRuntimeHttpUrl(value, 'network.request url 必须是 HTTP(S) URL');
560
+ assertPublicNetworkRequestUrl(requestUrl);
561
+ return requestUrl;
562
+ }
563
+ function assertPublicNetworkRequestUrl(requestUrl) {
564
+ const url = new URL(requestUrl);
565
+ if (url.username || url.password) {
566
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'network.request url 不允许包含用户信息');
404
567
  }
405
- assertMiniProgramRuntimeRecord(payload, 'share.screenshot 参数必须是对象');
406
- const options = {};
407
- if (payload.rect !== undefined) {
408
- options.rect = readScreenshotRect(payload.rect);
568
+ if (isUnsafeNetworkHost(url.hostname)) {
569
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'network.request url 不允许指向本机、内网或保留地址');
409
570
  }
410
- if (payload.delay !== undefined) {
411
- options.delay = readMiniProgramRuntimeNonNegativeNumber(payload.delay, 'share.screenshot delay 必须是非负数字');
571
+ }
572
+ function isUnsafeNetworkHost(hostname) {
573
+ const normalizedHost = hostname.toLowerCase();
574
+ const ipv6Host = normalizedHost.startsWith('[') && normalizedHost.endsWith(']')
575
+ ? normalizedHost.slice(1, -1)
576
+ : normalizedHost;
577
+ if (normalizedHost === 'localhost' ||
578
+ normalizedHost.endsWith('.localhost') ||
579
+ normalizedHost.endsWith('.local') ||
580
+ normalizedHost === 'metadata.google.internal') {
581
+ return true;
412
582
  }
413
- if (payload.saveToAlbum !== undefined) {
414
- options.saveToAlbum = readMiniProgramRuntimeBoolean(payload.saveToAlbum, 'share.screenshot saveToAlbum 必须是布尔值');
583
+ if (isUnsafeIPv4Host(normalizedHost)) {
584
+ return true;
415
585
  }
416
- return options;
586
+ return isUnsafeIPv6Host(ipv6Host);
417
587
  }
418
- function readScreenshotRect(value) {
419
- assertMiniProgramRuntimeRecord(value, 'share.screenshot rect 必须是对象');
420
- const left = readMiniProgramRuntimeNonNegativeNumber(value.left, 'share.screenshot rect.left 必须是非负数字');
421
- const top = readMiniProgramRuntimeNonNegativeNumber(value.top, 'share.screenshot rect.top 必须是非负数字');
422
- const width = readMiniProgramRuntimePositiveNumber(value.width, 'share.screenshot rect.width 必须是正数');
423
- const height = readMiniProgramRuntimePositiveNumber(value.height, 'share.screenshot rect.height 必须是正数');
424
- return {
425
- left,
426
- top,
427
- width,
428
- height,
429
- };
588
+ function isUnsafeIPv4Host(hostname) {
589
+ if (!/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname)) {
590
+ return false;
591
+ }
592
+ const parts = hostname.split('.').map(part => Number(part));
593
+ if (parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) {
594
+ return true;
595
+ }
596
+ const [a, b] = parts;
597
+ return (a === 0 ||
598
+ a === 10 ||
599
+ a === 127 ||
600
+ (a === 100 && b >= 64 && b <= 127) ||
601
+ (a === 169 && b === 254) ||
602
+ (a === 172 && b >= 16 && b <= 31) ||
603
+ (a === 192 && b === 168));
430
604
  }
431
- function readMiniProgramRuntimeGetStoragePayload(payload, maxKeyLength) {
432
- if (!isMiniProgramRuntimeRecord(payload)) {
433
- throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'storage.getStorage 参数必须是对象');
605
+ function isUnsafeIPv6Host(hostname) {
606
+ return (hostname === '::' ||
607
+ hostname === '::1' ||
608
+ hostname.startsWith('fc') ||
609
+ hostname.startsWith('fd') ||
610
+ hostname.startsWith('fe80:'));
611
+ }
612
+ function readNetworkRequestHeaders(value) {
613
+ const headers = readStringRecord(value, 'network.request headers 必须是 Record<string, string>');
614
+ const deniedHeader = Object.keys(headers).find(isDeniedNetworkRequestHeader);
615
+ if (deniedHeader) {
616
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `network.request 不允许设置敏感请求头: ${deniedHeader}`);
434
617
  }
435
- const key = readMiniProgramRuntimeStorageKey(payload.key, 'storage.getStorage', maxKeyLength);
436
- return {
437
- key,
438
- };
618
+ return headers;
439
619
  }
440
- function readMiniProgramRuntimeSetStoragePayload(payload, maxKeyLength) {
441
- if (!isMiniProgramRuntimeRecord(payload)) {
442
- throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'storage.setStorage 参数必须是对象');
620
+ function isDeniedNetworkRequestHeader(headerName) {
621
+ const normalizedHeaderName = headerName.trim().toLowerCase();
622
+ return (NETWORK_REQUEST_DENIED_HEADERS.has(normalizedHeaderName) ||
623
+ normalizedHeaderName.startsWith('x-heybox-') ||
624
+ normalizedHeaderName.startsWith('x-xhh-'));
625
+ }
626
+ function assertNetworkRequestBody(data) {
627
+ if (isUnsupportedNetworkRequestBody(data)) {
628
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'network.request data 不支持 File、Blob 或 FormData,请使用专用上传能力');
629
+ }
630
+ const bodyBytes = estimateNetworkRequestBodyBytes(data);
631
+ if (bodyBytes > NETWORK_REQUEST_MAX_BODY_BYTES) {
632
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `network.request data 不能超过 ${NETWORK_REQUEST_MAX_BODY_BYTES} 字节`);
443
633
  }
444
- const key = readMiniProgramRuntimeStorageKey(payload.key, 'storage.setStorage', maxKeyLength);
445
- return {
446
- key,
447
- data: payload.data,
448
- };
449
634
  }
450
- function assertNetworkAllowedKeys(payload) {
451
- const invalidKeys = Object.keys(payload).filter(key => !NETWORK_REQUEST_ALLOWED_KEYS.has(key));
452
- if (invalidKeys.length > 0) {
453
- throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', `network.request 不支持字段: ${invalidKeys.join(', ')}`);
635
+ function isUnsupportedNetworkRequestBody(data) {
636
+ return ((typeof Blob !== 'undefined' && data instanceof Blob) ||
637
+ (typeof FormData !== 'undefined' && data instanceof FormData));
638
+ }
639
+ function estimateNetworkRequestBodyBytes(data) {
640
+ if (typeof data === 'string') {
641
+ return getMiniProgramRuntimeUtf8ByteLength(data);
642
+ }
643
+ if (data instanceof ArrayBuffer) {
644
+ return data.byteLength;
645
+ }
646
+ if (ArrayBuffer.isView(data)) {
647
+ return data.byteLength;
648
+ }
649
+ if (typeof URLSearchParams !== 'undefined' && data instanceof URLSearchParams) {
650
+ return getMiniProgramRuntimeUtf8ByteLength(data.toString());
651
+ }
652
+ try {
653
+ const serialized = JSON.stringify(data);
654
+ return serialized ? getMiniProgramRuntimeUtf8ByteLength(serialized) : 0;
655
+ }
656
+ catch {
657
+ throw createMiniProgramRuntimeBridgeError('INVALID_PARAMS', 'network.request data 必须可序列化');
454
658
  }
455
659
  }
456
660
  function readNetworkMethod(value) {
@@ -474,75 +678,6 @@ function readStringRecord(value, message) {
474
678
  return headers;
475
679
  }, {});
476
680
  }
477
- function resolveCurrentUserId(platformAdapter) {
478
- return platformAdapter.auth.getCurrentUserId()
479
- .then(userId => (userId ? String(userId) : ''))
480
- .catch(() => '');
481
- }
482
- function getNavigationBarHeight(platformAdapter) {
483
- try {
484
- return platformAdapter.viewport.getNavigationBarHeight();
485
- }
486
- catch {
487
- return 0;
488
- }
489
- }
490
- function getViewportRect(platformAdapter) {
491
- const metrics = platformAdapter.viewport.getViewportMetrics();
492
- const width = getPositiveNumber(metrics.innerWidth, getPositiveNumber(metrics.documentElementClientWidth, 0));
493
- const height = getPositiveNumber(metrics.innerHeight, getPositiveNumber(metrics.documentElementClientHeight, 0));
494
- return {
495
- left: 0,
496
- top: 0,
497
- width,
498
- height,
499
- };
500
- }
501
- function getViewportWidth(metrics) {
502
- return getPositiveNumber(metrics.innerWidth, getPositiveNumber(metrics.documentElementClientWidth, 0));
503
- }
504
- function getViewportHeight(metrics) {
505
- return getPositiveNumber(metrics.innerHeight, getPositiveNumber(metrics.documentElementClientHeight, 0));
506
- }
507
- function getPositiveNumber(value, fallback) {
508
- const numberValue = Number(value);
509
- if (!Number.isFinite(numberValue) || numberValue <= 0) {
510
- return fallback;
511
- }
512
- return numberValue;
513
- }
514
- function parseStorageData(value) {
515
- if (value === undefined || value === null || value === '') {
516
- return undefined;
517
- }
518
- try {
519
- return JSON.parse(value);
520
- }
521
- catch {
522
- return value;
523
- }
524
- }
525
- function toStorageScopeToken(value) {
526
- if (/^[A-Za-z0-9_-]{1,64}$/.test(value)) {
527
- return value;
528
- }
529
- return hashString(value);
530
- }
531
- function hashString(value) {
532
- let hash = 2166136261;
533
- for (let index = 0; index < value.length; index += 1) {
534
- hash ^= value.charCodeAt(index);
535
- hash = Math.imul(hash, 16777619);
536
- }
537
- return (hash >>> 0).toString(36);
538
- }
539
- function isShareChannel(value) {
540
- return (value === 'wechatSession' ||
541
- value === 'wechatTimeline' ||
542
- value === 'qqFriend' ||
543
- value === 'qzone' ||
544
- value === 'weibo');
545
- }
546
681
 
547
682
  const MINI_PROGRAM_MOCK_RUNTIME_METHODS = MINI_PROGRAM_PROTOCOL_CAPABILITIES.map(capability => capability.method);
548
683
  const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS = {
@@ -556,8 +691,6 @@ const MINI_PROGRAM_MOCK_RUNTIME_METHOD_HANDLERS = {
556
691
  [NETWORK_REQUEST_METHOD]: (runtime, payload) => runtime.requestNetwork(payload),
557
692
  };
558
693
  class MiniProgramMockRuntime {
559
- adapter;
560
- options;
561
694
  constructor(adapter, options = {}) {
562
695
  this.adapter = adapter;
563
696
  this.options = options;
package/dist/index.cjs.js CHANGED
@@ -240,8 +240,9 @@ class MiniProgramBridgeClient {
240
240
  this.eventBus.off(eventName, handler);
241
241
  }
242
242
  /** 调用父容器开放能力。 */
243
- async request(method, payload) {
243
+ async request(method, ...args) {
244
244
  await this.ready();
245
+ const payload = args[0];
245
246
  const id = createMessageId();
246
247
  const message = {
247
248
  namespace: MINI_PROGRAM_MESSAGE_NAMESPACE,
@@ -888,25 +889,12 @@ const hbSDK = {
888
889
  network,
889
890
  };
890
891
 
891
- exports.AUTH_LOGIN_METHOD = AUTH_LOGIN_METHOD;
892
892
  exports.HbMiniProgramNetworkError = HbMiniProgramNetworkError;
893
893
  exports.HbMiniProgramSDKError = HbMiniProgramSDKError;
894
- exports.MINI_PROGRAM_BRIDGE_NONCE_PARAM = MINI_PROGRAM_BRIDGE_NONCE_PARAM;
895
- exports.MINI_PROGRAM_MESSAGE_NAMESPACE = MINI_PROGRAM_MESSAGE_NAMESPACE;
896
- exports.MINI_PROGRAM_MESSAGE_VERSION = MINI_PROGRAM_MESSAGE_VERSION;
897
894
  exports.MiniProgramSDK = MiniProgramSDK;
898
- exports.NETWORK_REQUEST_METHOD = NETWORK_REQUEST_METHOD;
899
- exports.SDK_HANDSHAKE_METHOD = SDK_HANDSHAKE_METHOD;
900
- exports.SHARE_SCREENSHOT_METHOD = SHARE_SCREENSHOT_METHOD;
901
- exports.SHARE_SHOW_SHARE_MENU_METHOD = SHARE_SHOW_SHARE_MENU_METHOD;
902
- exports.STORAGE_GET_STORAGE_METHOD = STORAGE_GET_STORAGE_METHOD;
903
- exports.STORAGE_SET_STORAGE_METHOD = STORAGE_SET_STORAGE_METHOD;
904
- exports.USER_GET_INFO_METHOD = USER_GET_INFO_METHOD;
905
- exports.VIEWPORT_GET_WINDOW_INFO_METHOD = VIEWPORT_GET_WINDOW_INFO_METHOD;
906
895
  exports.auth = auth;
907
896
  exports.createMiniProgramSDK = createMiniProgramSDK;
908
897
  exports.default = hbSDK;
909
- exports.isMiniProgramBridgeMessage = isMiniProgramBridgeMessage;
910
898
  exports.network = network;
911
899
  exports.off = off;
912
900
  exports.on = on;