@heybox/hb-sdk 0.7.5-alpha.0 → 0.8.0-alpha.2
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/CHANGELOG.md +86 -0
- package/README.md +69 -16
- package/THIRD_PARTY_NOTICES.md +1755 -0
- package/dist/cli-chunks/{build-V1YBdpzY.cjs → build-CCLWgXyA.cjs} +6 -5
- package/dist/cli-chunks/{context-Ba1Je-wT.cjs → context-Brh56h56.cjs} +2 -27
- package/dist/cli-chunks/{create-CKGjqo9E.cjs → create-BZ-hi2s1.cjs} +1 -1
- package/dist/cli-chunks/{dev-XopYKLYZ.cjs → dev-CoSXtEYa.cjs} +336 -517
- package/dist/cli-chunks/{doctor-BHbrYQqT.cjs → doctor-EsCYJGhV.cjs} +1 -1
- package/dist/cli-chunks/{index-C9mpajVG.cjs → index-BL5eEPap.cjs} +14 -14
- package/dist/cli-chunks/{index-DKWfX6Ut.cjs → index-CS6Q0o5C.cjs} +2 -2
- package/dist/cli-chunks/{index.esm-SXAQ3WVR.cjs → index.esm-BoBHPfpq.cjs} +6 -6
- package/dist/cli-chunks/{login-BVWicWqe.cjs → login-CILLxGuf.cjs} +2 -2
- package/dist/cli-chunks/{project-vite-CBb38HJ3.cjs → project-vite-Qu6Xe9Dn.cjs} +1 -1
- package/dist/cli-chunks/{remote-BG1u0NKT.cjs → remote-DRuTQ51u.cjs} +10 -8
- package/dist/cli-chunks/{runtime-gate-CtV7rWyX.cjs → runtime-gate-DFjw66kF.cjs} +20 -1
- package/dist/cli-chunks/{runtime-permission-env-D-8_jPG3.cjs → runtime-permission-env-CjsCe5bp.cjs} +13 -13
- package/dist/cli-chunks/{session-CJS_SEp9.cjs → session-D-_N3WVL.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-uq-Wac6k.js +97 -0
- package/dist/devtools/browser-dev-host/assets/heybox-logo-CogNENsk.svg +6 -0
- package/dist/devtools/browser-dev-host/assets/index-CeP6SLB3.js +567 -0
- package/dist/devtools/browser-dev-host/assets/index-KD2f3Jdz.css +1 -0
- package/dist/devtools/browser-dev-host/assets/workbench-state-D-1U0JRq.js +5 -0
- package/dist/devtools/browser-dev-host/index.html +6 -435
- package/dist/index.cjs.js +1036 -90
- package/dist/index.esm.js +1036 -91
- package/dist/miniapp-publish.cjs.js +13 -13
- package/dist/miniapp-publish.esm.js +13 -13
- package/dist/protocol.cjs.js +208 -6
- package/dist/protocol.esm.js +182 -7
- package/dist/templates/vue3-vite-ts/README.md.ejs +2 -0
- package/dist/templates/vue3-vite-ts/vite.config.ts +1 -1
- package/dist/vite.cjs.js +31 -4
- package/dist/vite.esm.js +31 -5
- package/package.json +28 -11
- package/skill/SKILL.md +9 -5
- package/skill/references/api-protocol.md +111 -3
- package/skill/references/api-root.md +170 -22
- package/skill/references/cli.md +21 -17
- package/skill/references/examples.md +30 -1
- package/skill/references/recipes.md +105 -1
- package/skill/references/safety-boundaries.md +12 -1
- package/skill/scripts/sync-references.mjs +37 -3
- package/skill/skill.json +4 -4
- package/types/core/client.d.ts +17 -1
- package/types/core/errors.d.ts +5 -5
- package/types/core/sdk.d.ts +8 -5
- package/types/core/singleton.d.ts +59 -6
- package/types/index.d.ts +4 -2
- package/types/miniapp-manifest/schema.d.ts +5 -0
- package/types/miniapp-publish/index.d.ts +8 -8
- package/types/modules/auth/index.d.ts +15 -1
- package/types/modules/cloud/index.d.ts +75 -5
- package/types/modules/device/index.d.ts +31 -3
- package/types/modules/files/index.d.ts +5 -0
- package/types/modules/files/registry.d.ts +35 -0
- package/types/modules/files/types.d.ts +159 -0
- package/types/modules/navigation/index.d.ts +46 -4
- package/types/modules/network/index.d.ts +72 -14
- package/types/modules/share/copy-link.d.ts +3 -3
- package/types/modules/share/index.d.ts +61 -5
- package/types/modules/share/screenshot.d.ts +3 -3
- package/types/modules/share/show-share-menu.d.ts +3 -3
- package/types/modules/storage/index.d.ts +38 -10
- package/types/modules/ui/index.d.ts +46 -4
- package/types/modules/user/get-info.d.ts +1 -1
- package/types/modules/user/index.d.ts +46 -4
- package/types/modules/user/steam-game-list.d.ts +2 -2
- package/types/modules/viewport/index.d.ts +36 -8
- package/types/protocol/capabilities.d.ts +2 -2
- package/types/protocol/constants.d.ts +1 -1
- package/types/protocol/guards.d.ts +1 -1
- package/types/protocol/types.d.ts +1 -1
- package/types/protocol.d.ts +4 -3
- package/types/vite/index.d.ts +15 -7
- package/dist/devtools/browser-dev-host/main.js +0 -12158
package/dist/index.cjs.js
CHANGED
|
@@ -296,9 +296,9 @@ class HbMiniProgramSDKError extends Error {
|
|
|
296
296
|
/**
|
|
297
297
|
* 创建 SDK 标准错误。
|
|
298
298
|
*
|
|
299
|
-
* @param code 稳定错误码。
|
|
300
|
-
* @param message 面向开发者的错误说明。
|
|
301
|
-
* @param data 可选调试数据。
|
|
299
|
+
* @param code - 稳定错误码。
|
|
300
|
+
* @param message - 面向开发者的错误说明。
|
|
301
|
+
* @param data - 可选调试数据。
|
|
302
302
|
* @returns 标准化 `HbMiniProgramSDKError` 实例。
|
|
303
303
|
*
|
|
304
304
|
* @remarks
|
|
@@ -318,7 +318,7 @@ function createSDKError(code, message, data) {
|
|
|
318
318
|
* 仅在请求已经成功走完 bridge / runtime / 宿主调用链、但最终 HTTP 状态不满足
|
|
319
319
|
* `validateStatus` 时抛出。它不表示握手失败或 bridge 级错误。
|
|
320
320
|
*
|
|
321
|
-
* @typeParam T 标准化响应数据的类型。
|
|
321
|
+
* @typeParam T - 标准化响应数据的类型。
|
|
322
322
|
*/
|
|
323
323
|
class HbMiniProgramNetworkError extends Error {
|
|
324
324
|
/** HTTP 状态码。 */
|
|
@@ -332,7 +332,7 @@ class HbMiniProgramNetworkError extends Error {
|
|
|
332
332
|
/** 完整标准化响应。 */
|
|
333
333
|
response;
|
|
334
334
|
/**
|
|
335
|
-
* @param response 已完成请求的标准化网络响应。
|
|
335
|
+
* @param response - 已完成请求的标准化网络响应。
|
|
336
336
|
*/
|
|
337
337
|
constructor(response) {
|
|
338
338
|
const method = (response.config.method || 'GET').toUpperCase();
|
|
@@ -576,6 +576,22 @@ const VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD = 'viewport.setNavigationBarStyle
|
|
|
576
576
|
const STORAGE_GET_STORAGE_METHOD = 'storage.getStorage';
|
|
577
577
|
const STORAGE_SET_STORAGE_METHOD = 'storage.setStorage';
|
|
578
578
|
const NETWORK_REQUEST_METHOD = 'network.request';
|
|
579
|
+
const NETWORK_DOWNLOAD_METHOD = 'network.download';
|
|
580
|
+
const FILES_PICK_FILES_METHOD = 'files.pickFiles';
|
|
581
|
+
const FILES_PICK_DIRECTORY_METHOD = 'files.pickDirectory';
|
|
582
|
+
const FILES_SAVE_FILE_METHOD = 'files.saveFile';
|
|
583
|
+
const FILE_CREATE_METHOD = 'file.create';
|
|
584
|
+
const FILE_REMOVE_METHOD = 'file.remove';
|
|
585
|
+
const FILE_EXISTS_METHOD = 'file.exists';
|
|
586
|
+
const FILE_READ_TEXT_METHOD = 'file.readText';
|
|
587
|
+
const FILE_WRITE_TEXT_METHOD = 'file.writeText';
|
|
588
|
+
const FILE_READ_BYTES_METHOD = 'file.readBytes';
|
|
589
|
+
const FILE_WRITE_BYTES_METHOD = 'file.writeBytes';
|
|
590
|
+
const FILE_STAT_METHOD = 'file.stat';
|
|
591
|
+
const DIRECTORY_CREATE_METHOD = 'directory.create';
|
|
592
|
+
const DIRECTORY_REMOVE_METHOD = 'directory.remove';
|
|
593
|
+
const DIRECTORY_EXISTS_METHOD = 'directory.exists';
|
|
594
|
+
const DIRECTORY_LIST_METHOD = 'directory.list';
|
|
579
595
|
const UI_SHOW_TOAST_METHOD = 'ui.showToast';
|
|
580
596
|
const UI_SHOW_LOADING_METHOD = 'ui.showLoading';
|
|
581
597
|
const UI_HIDE_LOADING_METHOD = 'ui.hideLoading';
|
|
@@ -597,15 +613,112 @@ const SDK_HANDSHAKE_METHOD = 'sdk.handshake';
|
|
|
597
613
|
const RUNTIME_LOCATION_PROBE_METHOD = 'runtime.location.probe';
|
|
598
614
|
const SDK_LOCATION_REPORT_METHOD = 'sdk.location.report';
|
|
599
615
|
const SDK_CSP_VIOLATION_METHOD = 'sdk.csp.violation';
|
|
616
|
+
const MINI_PROGRAM_OPERATION_PROGRESS_METHOD = 'operation.progress';
|
|
600
617
|
|
|
601
618
|
function isMiniProgramBridgeMessage(value) {
|
|
602
|
-
if (!value
|
|
619
|
+
if (!isRecord$1(value))
|
|
603
620
|
return false;
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
621
|
+
if (value.namespace !== MINI_PROGRAM_MESSAGE_NAMESPACE || (value.version !== 1 && value.version !== 2) || !isNonEmptyString(value.nonce)) {
|
|
622
|
+
return false;
|
|
623
|
+
}
|
|
624
|
+
if (value.type === 'handshake') {
|
|
625
|
+
return isNonEmptyString(value.id) && value.method === SDK_HANDSHAKE_METHOD && value.error === undefined && isSDKHandshakePayload(value.payload);
|
|
626
|
+
}
|
|
627
|
+
if (value.type === 'request') {
|
|
628
|
+
return isNonEmptyString(value.id) && isNonEmptyString(value.method) && value.error === undefined;
|
|
629
|
+
}
|
|
630
|
+
if (value.type === 'response') {
|
|
631
|
+
return (isNonEmptyString(value.id) &&
|
|
632
|
+
(value.method === undefined || isNonEmptyString(value.method)) &&
|
|
633
|
+
(value.error === undefined || isBridgeError(value.error)));
|
|
634
|
+
}
|
|
635
|
+
if (value.type === 'event') {
|
|
636
|
+
if (!isNonEmptyString(value.method) || value.error !== undefined)
|
|
637
|
+
return false;
|
|
638
|
+
if (value.method === MINI_PROGRAM_OPERATION_PROGRESS_METHOD) {
|
|
639
|
+
return value.version === 2 && isNonEmptyString(value.id) && isMiniProgramDownloadProgressPayload(value.payload);
|
|
640
|
+
}
|
|
641
|
+
return value.id === undefined || isNonEmptyString(value.id);
|
|
642
|
+
}
|
|
643
|
+
if (value.type === 'cancel') {
|
|
644
|
+
return (value.version === 2 && isNonEmptyString(value.id) && value.method === undefined && value.payload === undefined && value.error === undefined);
|
|
645
|
+
}
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
function isMiniProgramRelativePath(value) {
|
|
649
|
+
if (typeof value !== 'string' || !value || value.startsWith('/') || value.endsWith('/')) {
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
652
|
+
if (/[\\\u0000-\u001f\u007f]/.test(value))
|
|
653
|
+
return false;
|
|
654
|
+
return value.split('/').every((segment) => Boolean(segment) && segment !== '.' && segment !== '..');
|
|
655
|
+
}
|
|
656
|
+
function isMiniProgramPathRootRef(value) {
|
|
657
|
+
if (!isRecord$1(value))
|
|
658
|
+
return false;
|
|
659
|
+
if (value.kind === 'sandbox')
|
|
660
|
+
return hasOnlyKeys(value, ['kind']);
|
|
661
|
+
return value.kind === 'directory' && isNonEmptyString(value.handleId) && hasOnlyKeys(value, ['kind', 'handleId']);
|
|
662
|
+
}
|
|
663
|
+
function isMiniProgramFileRef(value) {
|
|
664
|
+
if (!isRecord$1(value) || value.kind !== 'file')
|
|
665
|
+
return false;
|
|
666
|
+
if (isNonEmptyString(value.handleId)) {
|
|
667
|
+
return hasOnlyKeys(value, ['kind', 'handleId']);
|
|
668
|
+
}
|
|
669
|
+
return (isMiniProgramPathRootRef(value.root) && isMiniProgramRelativePath(value.relativePath) && hasOnlyKeys(value, ['kind', 'root', 'relativePath']));
|
|
670
|
+
}
|
|
671
|
+
function isMiniProgramDirectoryRef(value) {
|
|
672
|
+
if (!isRecord$1(value) || value.kind !== 'directory')
|
|
673
|
+
return false;
|
|
674
|
+
if (isNonEmptyString(value.handleId)) {
|
|
675
|
+
return hasOnlyKeys(value, ['kind', 'handleId']);
|
|
676
|
+
}
|
|
677
|
+
return (isMiniProgramPathRootRef(value.root) && isMiniProgramRelativePath(value.relativePath) && hasOnlyKeys(value, ['kind', 'root', 'relativePath']));
|
|
678
|
+
}
|
|
679
|
+
function isMiniProgramByteArray(value) {
|
|
680
|
+
return value instanceof Uint8Array && value.buffer instanceof ArrayBuffer;
|
|
681
|
+
}
|
|
682
|
+
function isMiniProgramFileStat(value) {
|
|
683
|
+
return (isRecord$1(value) &&
|
|
684
|
+
value.kind === 'file' &&
|
|
685
|
+
isNonNegativeSafeInteger(value.size) &&
|
|
686
|
+
(value.modifiedAt === undefined || isNonNegativeFiniteNumber(value.modifiedAt)) &&
|
|
687
|
+
(value.createdAt === undefined || isNonNegativeFiniteNumber(value.createdAt)) &&
|
|
688
|
+
hasOnlyKeys(value, ['kind', 'size', 'modifiedAt', 'createdAt']));
|
|
689
|
+
}
|
|
690
|
+
function isMiniProgramDownloadProgressPayload(value) {
|
|
691
|
+
if (!isRecord$1(value) ||
|
|
692
|
+
!isNonNegativeSafeInteger(value.loaded) ||
|
|
693
|
+
typeof value.lengthComputable !== 'boolean' ||
|
|
694
|
+
!hasOnlyKeys(value, ['loaded', 'total', 'lengthComputable'])) {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
if (!value.lengthComputable)
|
|
698
|
+
return value.total === undefined;
|
|
699
|
+
return isNonNegativeSafeInteger(value.total) && value.loaded <= value.total;
|
|
700
|
+
}
|
|
701
|
+
function isSDKHandshakePayload(value) {
|
|
702
|
+
return isRecord$1(value) && isNonEmptyString(value.href) && typeof value.userAgent === 'string' && isNonEmptyString(value.sdkVersion);
|
|
703
|
+
}
|
|
704
|
+
function isBridgeError(value) {
|
|
705
|
+
return isRecord$1(value) && isNonEmptyString(value.code) && typeof value.message === 'string';
|
|
706
|
+
}
|
|
707
|
+
function isNonNegativeSafeInteger(value) {
|
|
708
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
709
|
+
}
|
|
710
|
+
function isNonNegativeFiniteNumber(value) {
|
|
711
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
712
|
+
}
|
|
713
|
+
function isNonEmptyString(value) {
|
|
714
|
+
return typeof value === 'string' && value.length > 0;
|
|
715
|
+
}
|
|
716
|
+
function hasOnlyKeys(value, allowedKeys) {
|
|
717
|
+
const allowed = new Set(allowedKeys);
|
|
718
|
+
return Object.keys(value).every((key) => allowed.has(key));
|
|
719
|
+
}
|
|
720
|
+
function isRecord$1(value) {
|
|
721
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
609
722
|
}
|
|
610
723
|
|
|
611
724
|
/** 当前用户信息与登录能力支持的完整 scope 目录。 */
|
|
@@ -672,7 +785,7 @@ function createMessageId() {
|
|
|
672
785
|
/** 构建时替换为当前发布包的实际版本。 */
|
|
673
786
|
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
674
787
|
? undefined
|
|
675
|
-
: '0.
|
|
788
|
+
: '0.8.0-alpha.2';
|
|
676
789
|
|
|
677
790
|
const DEFAULT_TIMEOUT = 10000;
|
|
678
791
|
const HANDSHAKE_RETRY_INTERVAL = 250;
|
|
@@ -707,8 +820,7 @@ class MiniProgramBridgeClient {
|
|
|
707
820
|
constructor(options = {}) {
|
|
708
821
|
this.timeout = options.timeout || DEFAULT_TIMEOUT;
|
|
709
822
|
this.selfWindow = options.selfWindow || getGlobalWindow();
|
|
710
|
-
this.targetWindow =
|
|
711
|
-
options.targetWindow === undefined ? getParentWindow(this.selfWindow) : options.targetWindow;
|
|
823
|
+
this.targetWindow = options.targetWindow === undefined ? getParentWindow(this.selfWindow) : options.targetWindow;
|
|
712
824
|
this.targetOrigin = options.targetOrigin || readParentOrigin(this.selfWindow) || '*';
|
|
713
825
|
this.nonce = options.nonce || readBridgeNonce(this.selfWindow);
|
|
714
826
|
this.handleMessage = this.onMessage.bind(this);
|
|
@@ -741,20 +853,42 @@ class MiniProgramBridgeClient {
|
|
|
741
853
|
}
|
|
742
854
|
/** 调用父容器开放能力。 */
|
|
743
855
|
async request(method, ...args) {
|
|
856
|
+
return this.performRequest(method, args[0], true);
|
|
857
|
+
}
|
|
858
|
+
/** 发起不使用固定总时长 timer 的长操作。 */
|
|
859
|
+
async requestOperation(method, payload, options = {}) {
|
|
860
|
+
return this.performRequest(method, payload, false, options);
|
|
861
|
+
}
|
|
862
|
+
async performRequest(method, payload, useTimeout, options = {}) {
|
|
744
863
|
if (this.destroyed) {
|
|
745
864
|
throw createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
|
|
746
865
|
}
|
|
747
866
|
if (this.runtimeUnavailable) {
|
|
748
867
|
throw this.getRuntimeUnavailableError();
|
|
749
868
|
}
|
|
750
|
-
|
|
869
|
+
if (!useTimeout && options.signal?.aborted) {
|
|
870
|
+
throw options.createAbortError?.() ?? createSDKError('REQUEST_CANCELLED', `${method} 已取消`);
|
|
871
|
+
}
|
|
872
|
+
if (!useTimeout && options.signal && !this.handshakeSettled) {
|
|
873
|
+
await waitForHandshakeOrAbort(this.waitForHandshake(), options.signal, () => options.createAbortError?.() ?? createSDKError('REQUEST_CANCELLED', `${method} 已取消`), () => {
|
|
874
|
+
const state = this.handshakeState.getState();
|
|
875
|
+
if (state.status === 'failed')
|
|
876
|
+
return state.error;
|
|
877
|
+
return this.destroyed ? createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁') : undefined;
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
await this.waitForHandshake();
|
|
882
|
+
}
|
|
751
883
|
if (this.destroyed) {
|
|
752
884
|
throw createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
|
|
753
885
|
}
|
|
754
886
|
if (this.runtimeUnavailable) {
|
|
755
887
|
throw this.getRuntimeUnavailableError();
|
|
756
888
|
}
|
|
757
|
-
|
|
889
|
+
if (options.signal?.aborted) {
|
|
890
|
+
throw options.createAbortError?.() ?? createSDKError('REQUEST_CANCELLED', `${method} 已取消`);
|
|
891
|
+
}
|
|
758
892
|
const id = createMessageId();
|
|
759
893
|
const message = {
|
|
760
894
|
namespace: MINI_PROGRAM_MESSAGE_NAMESPACE,
|
|
@@ -766,27 +900,46 @@ class MiniProgramBridgeClient {
|
|
|
766
900
|
payload,
|
|
767
901
|
};
|
|
768
902
|
return new Promise((resolve, reject) => {
|
|
769
|
-
const
|
|
770
|
-
this.pendingRequests.delete(id);
|
|
771
|
-
reject(createSDKError('REQUEST_TIMEOUT', `${method} 调用超时`));
|
|
772
|
-
}, this.timeout);
|
|
773
|
-
this.pendingRequests.set(id, {
|
|
903
|
+
const pending = {
|
|
774
904
|
resolve: resolve,
|
|
775
905
|
reject,
|
|
776
|
-
|
|
777
|
-
|
|
906
|
+
isOperation: !useTimeout,
|
|
907
|
+
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
908
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
909
|
+
};
|
|
910
|
+
if (useTimeout) {
|
|
911
|
+
pending.timer = setTimeout(() => {
|
|
912
|
+
const active = this.pendingRequests.get(id);
|
|
913
|
+
if (!active) {
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
this.cleanupPending(active);
|
|
917
|
+
this.pendingRequests.delete(id);
|
|
918
|
+
reject(createSDKError('REQUEST_TIMEOUT', `${method} 调用超时`));
|
|
919
|
+
}, this.timeout);
|
|
920
|
+
}
|
|
921
|
+
this.pendingRequests.set(id, pending);
|
|
778
922
|
try {
|
|
779
923
|
this.postMessage(message);
|
|
780
924
|
}
|
|
781
925
|
catch (error) {
|
|
782
|
-
|
|
926
|
+
this.cleanupPending(pending);
|
|
783
927
|
this.pendingRequests.delete(id);
|
|
784
928
|
reject(error);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (options.signal) {
|
|
932
|
+
pending.abortHandler = () => this.sendCancel(id);
|
|
933
|
+
options.signal.addEventListener('abort', pending.abortHandler, { once: true });
|
|
934
|
+
if (options.signal.aborted) {
|
|
935
|
+
pending.abortHandler();
|
|
936
|
+
}
|
|
785
937
|
}
|
|
786
938
|
});
|
|
787
939
|
}
|
|
788
940
|
/** 销毁 SDK 实例,并拒绝尚未完成的请求。 */
|
|
789
941
|
destroy() {
|
|
942
|
+
this.pendingRequests.forEach((_pending, id) => this.sendCancel(id));
|
|
790
943
|
this.destroyed = true;
|
|
791
944
|
this.selfWindow?.removeEventListener('message', this.handleMessage);
|
|
792
945
|
this.selfWindow?.removeEventListener('popstate', this.handlePopState);
|
|
@@ -839,16 +992,12 @@ class MiniProgramBridgeClient {
|
|
|
839
992
|
this.postHandshake();
|
|
840
993
|
}
|
|
841
994
|
catch (error) {
|
|
842
|
-
this.failHandshake(error instanceof HbMiniProgramSDKError
|
|
843
|
-
? error
|
|
844
|
-
: createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
|
|
995
|
+
this.failHandshake(error instanceof HbMiniProgramSDKError ? error : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
|
|
845
996
|
}
|
|
846
997
|
}, HANDSHAKE_RETRY_INTERVAL);
|
|
847
998
|
}
|
|
848
999
|
catch (error) {
|
|
849
|
-
this.failHandshake(error instanceof HbMiniProgramSDKError
|
|
850
|
-
? error
|
|
851
|
-
: createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
|
|
1000
|
+
this.failHandshake(error instanceof HbMiniProgramSDKError ? error : createSDKError('HANDSHAKE_FAILED', '小程序沙盒握手发送失败', error));
|
|
852
1001
|
}
|
|
853
1002
|
}
|
|
854
1003
|
postHandshake() {
|
|
@@ -877,6 +1026,10 @@ class MiniProgramBridgeClient {
|
|
|
877
1026
|
return;
|
|
878
1027
|
}
|
|
879
1028
|
if (event.data.type === 'event') {
|
|
1029
|
+
if (event.data.id && event.data.method === MINI_PROGRAM_OPERATION_PROGRESS_METHOD) {
|
|
1030
|
+
this.handleOperationProgress(event.data);
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
880
1033
|
this.handleEvent(event.data);
|
|
881
1034
|
return;
|
|
882
1035
|
}
|
|
@@ -958,7 +1111,7 @@ class MiniProgramBridgeClient {
|
|
|
958
1111
|
if (!pending) {
|
|
959
1112
|
return;
|
|
960
1113
|
}
|
|
961
|
-
|
|
1114
|
+
this.cleanupPending(pending);
|
|
962
1115
|
this.pendingRequests.delete(message.id);
|
|
963
1116
|
if (message.error) {
|
|
964
1117
|
pending.reject(new HbMiniProgramSDKError(message.error));
|
|
@@ -966,6 +1119,52 @@ class MiniProgramBridgeClient {
|
|
|
966
1119
|
}
|
|
967
1120
|
pending.resolve(message.payload);
|
|
968
1121
|
}
|
|
1122
|
+
handleOperationProgress(message) {
|
|
1123
|
+
if (!message.id) {
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
const pending = this.pendingRequests.get(message.id);
|
|
1127
|
+
if (!pending?.onProgress) {
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
try {
|
|
1131
|
+
pending.onProgress(message.payload);
|
|
1132
|
+
}
|
|
1133
|
+
catch {
|
|
1134
|
+
// Mini-program 回调失败不能改变 Host 长操作的生命周期。
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
sendCancel(id) {
|
|
1138
|
+
const pending = this.pendingRequests.get(id);
|
|
1139
|
+
if (!pending?.isOperation || pending.cancelSent) {
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
pending.cancelSent = true;
|
|
1143
|
+
try {
|
|
1144
|
+
const message = {
|
|
1145
|
+
namespace: MINI_PROGRAM_MESSAGE_NAMESPACE,
|
|
1146
|
+
version: MINI_PROGRAM_MESSAGE_VERSION,
|
|
1147
|
+
nonce: this.nonce,
|
|
1148
|
+
type: 'cancel',
|
|
1149
|
+
id,
|
|
1150
|
+
};
|
|
1151
|
+
this.postMessage(message);
|
|
1152
|
+
}
|
|
1153
|
+
catch {
|
|
1154
|
+
// cancel 只是意图,发送失败后仍等待唯一 terminal response。
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
cleanupPending(pending) {
|
|
1158
|
+
if (pending.timer) {
|
|
1159
|
+
clearTimeout(pending.timer);
|
|
1160
|
+
}
|
|
1161
|
+
if (pending.signal && pending.abortHandler) {
|
|
1162
|
+
pending.signal.removeEventListener('abort', pending.abortHandler);
|
|
1163
|
+
}
|
|
1164
|
+
pending.onProgress = undefined;
|
|
1165
|
+
pending.abortHandler = undefined;
|
|
1166
|
+
pending.signal = undefined;
|
|
1167
|
+
}
|
|
969
1168
|
postMessage(message) {
|
|
970
1169
|
if (!this.targetWindow) {
|
|
971
1170
|
throw createSDKError('NOT_IN_IFRAME', '当前页面不在小程序沙盒 iframe 中');
|
|
@@ -1017,13 +1216,34 @@ class MiniProgramBridgeClient {
|
|
|
1017
1216
|
return this.runtimeUnavailableError;
|
|
1018
1217
|
}
|
|
1019
1218
|
rejectAllPending(error) {
|
|
1020
|
-
this.pendingRequests.forEach(pending => {
|
|
1021
|
-
|
|
1219
|
+
this.pendingRequests.forEach((pending) => {
|
|
1220
|
+
this.cleanupPending(pending);
|
|
1022
1221
|
pending.reject(error);
|
|
1023
1222
|
});
|
|
1024
1223
|
this.pendingRequests.clear();
|
|
1025
1224
|
}
|
|
1026
1225
|
}
|
|
1226
|
+
function waitForHandshakeOrAbort(handshake, signal, createAbortError, getTerminalError) {
|
|
1227
|
+
if (!signal)
|
|
1228
|
+
return handshake;
|
|
1229
|
+
if (signal.aborted)
|
|
1230
|
+
return Promise.reject(createAbortError());
|
|
1231
|
+
return new Promise((resolve, reject) => {
|
|
1232
|
+
let settled = false;
|
|
1233
|
+
const settle = (callback) => {
|
|
1234
|
+
if (settled)
|
|
1235
|
+
return;
|
|
1236
|
+
settled = true;
|
|
1237
|
+
signal.removeEventListener('abort', onAbort);
|
|
1238
|
+
callback();
|
|
1239
|
+
};
|
|
1240
|
+
const onAbort = () => settle(() => reject(getTerminalError() ?? createAbortError()));
|
|
1241
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
1242
|
+
handshake.then(() => settle(() => (signal.aborted ? reject(createAbortError()) : resolve())), (error) => settle(() => reject(error)));
|
|
1243
|
+
if (signal.aborted)
|
|
1244
|
+
onAbort();
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1027
1247
|
|
|
1028
1248
|
/** 获取短期授权码。 */
|
|
1029
1249
|
function login(requester, options) {
|
|
@@ -1052,10 +1272,10 @@ function createCloudModule(requester) {
|
|
|
1052
1272
|
/**
|
|
1053
1273
|
* 复制当前小程序的通用分享链接。
|
|
1054
1274
|
*
|
|
1055
|
-
* @param requester 底层 bridge 请求能力。
|
|
1056
|
-
* @param options 分享扩展数据;不传时复制默认首页链接。
|
|
1275
|
+
* @param requester - 底层 bridge 请求能力。
|
|
1276
|
+
* @param options - 分享扩展数据;不传时复制默认首页链接。
|
|
1057
1277
|
* @returns 已写入系统剪贴板的完整分享链接。
|
|
1058
|
-
* @throws {HbMiniProgramSDKError} 当参数、bridge、父容器或剪贴板能力调用失败时抛出。
|
|
1278
|
+
* @throws {@link HbMiniProgramSDKError} 当参数、bridge、父容器或剪贴板能力调用失败时抛出。
|
|
1059
1279
|
*/
|
|
1060
1280
|
function copyLink(requester, options) {
|
|
1061
1281
|
return requester.request(SHARE_COPY_LINK_METHOD, options);
|
|
@@ -2255,10 +2475,10 @@ function decodeBase64Url(value) {
|
|
|
2255
2475
|
/**
|
|
2256
2476
|
* 截图并唤起分享。
|
|
2257
2477
|
*
|
|
2258
|
-
* @param requester 底层 bridge 请求能力。
|
|
2259
|
-
* @param options 截图区域、延迟与保存相册等配置。
|
|
2478
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2479
|
+
* @param options - 截图区域、延迟与保存相册等配置。
|
|
2260
2480
|
* @returns 由宿主客户端协议决定的结果。
|
|
2261
|
-
* @throws {HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2481
|
+
* @throws {@link HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2262
2482
|
*/
|
|
2263
2483
|
function screenshot(requester, options) {
|
|
2264
2484
|
return requester.request(SHARE_SCREENSHOT_METHOD, options);
|
|
@@ -2267,10 +2487,10 @@ function screenshot(requester, options) {
|
|
|
2267
2487
|
/**
|
|
2268
2488
|
* 展示基础分享面板。
|
|
2269
2489
|
*
|
|
2270
|
-
* @param requester 底层 bridge 请求能力。
|
|
2271
|
-
* @param options 基础分享参数。
|
|
2490
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2491
|
+
* @param options - 基础分享参数。
|
|
2272
2492
|
* @returns 由宿主客户端协议决定的结果。
|
|
2273
|
-
* @throws {HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2493
|
+
* @throws {@link HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2274
2494
|
*/
|
|
2275
2495
|
function showShareMenu(requester, options) {
|
|
2276
2496
|
return requester.request(SHARE_SHOW_SHARE_MENU_METHOD, options);
|
|
@@ -2279,25 +2499,25 @@ function showShareMenu(requester, options) {
|
|
|
2279
2499
|
/**
|
|
2280
2500
|
* 创建分享模块。
|
|
2281
2501
|
*
|
|
2282
|
-
* @param requester 底层 bridge 请求能力。
|
|
2502
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2283
2503
|
* @returns 面向业务层的分享模块对象。
|
|
2284
2504
|
*/
|
|
2285
2505
|
function createShareModule(requester, launchHref) {
|
|
2286
2506
|
return {
|
|
2287
|
-
copyLink: options => copyLink(requester, options),
|
|
2507
|
+
copyLink: (options) => copyLink(requester, options),
|
|
2288
2508
|
getExtra: () => readMiniProgramShareExtra(launchHref),
|
|
2289
|
-
showShareMenu: options => showShareMenu(requester, options),
|
|
2290
|
-
screenshot: options => screenshot(requester, options),
|
|
2509
|
+
showShareMenu: (options) => showShareMenu(requester, options),
|
|
2510
|
+
screenshot: (options) => screenshot(requester, options),
|
|
2291
2511
|
};
|
|
2292
2512
|
}
|
|
2293
2513
|
|
|
2294
2514
|
/**
|
|
2295
2515
|
* 获取小程序隔离 storage。
|
|
2296
2516
|
*
|
|
2297
|
-
* @param requester 底层 bridge 请求能力。
|
|
2298
|
-
* @param options 读取的 storage key。
|
|
2517
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2518
|
+
* @param options - 读取的 storage key。
|
|
2299
2519
|
* @returns 对应 key 的标准化读取结果。
|
|
2300
|
-
* @throws {HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2520
|
+
* @throws {@link HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2301
2521
|
*/
|
|
2302
2522
|
function getStorage(requester, options) {
|
|
2303
2523
|
return requester.request(STORAGE_GET_STORAGE_METHOD, options);
|
|
@@ -2305,10 +2525,10 @@ function getStorage(requester, options) {
|
|
|
2305
2525
|
/**
|
|
2306
2526
|
* 写入小程序隔离 storage。
|
|
2307
2527
|
*
|
|
2308
|
-
* @param requester 底层 bridge 请求能力。
|
|
2309
|
-
* @param options 要写入的 storage key 与数据。
|
|
2528
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2529
|
+
* @param options - 要写入的 storage key 与数据。
|
|
2310
2530
|
* @returns 当写入完成后 resolve。
|
|
2311
|
-
* @throws {HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2531
|
+
* @throws {@link HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2312
2532
|
*/
|
|
2313
2533
|
function setStorage(requester, options) {
|
|
2314
2534
|
return requester.request(STORAGE_SET_STORAGE_METHOD, options);
|
|
@@ -2316,15 +2536,382 @@ function setStorage(requester, options) {
|
|
|
2316
2536
|
/**
|
|
2317
2537
|
* 创建 storage 模块。
|
|
2318
2538
|
*
|
|
2319
|
-
* @param requester 底层 bridge 请求能力。
|
|
2539
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2320
2540
|
* @returns 面向业务层的 storage 模块对象。
|
|
2321
2541
|
*/
|
|
2322
2542
|
function createStorageModule(requester) {
|
|
2323
2543
|
return {
|
|
2324
|
-
getStorage: options => getStorage(requester, options),
|
|
2325
|
-
setStorage: options => setStorage(requester, options),
|
|
2544
|
+
getStorage: (options) => getStorage(requester, options),
|
|
2545
|
+
setStorage: (options) => setStorage(requester, options),
|
|
2546
|
+
};
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
const registries = new WeakMap();
|
|
2550
|
+
const brandedFileSystemValues = new WeakSet();
|
|
2551
|
+
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
2552
|
+
function invalidParams$1(message, data) {
|
|
2553
|
+
return createSDKError('INVALID_PARAMS', message, data);
|
|
2554
|
+
}
|
|
2555
|
+
function clonePathRootRef(ref) {
|
|
2556
|
+
return ref.kind === 'sandbox' ? Object.freeze({ kind: 'sandbox' }) : Object.freeze({ kind: 'directory', handleId: ref.handleId });
|
|
2557
|
+
}
|
|
2558
|
+
function cloneFileRef(ref) {
|
|
2559
|
+
return 'handleId' in ref
|
|
2560
|
+
? Object.freeze({ kind: 'file', handleId: ref.handleId })
|
|
2561
|
+
: Object.freeze({
|
|
2562
|
+
kind: 'file',
|
|
2563
|
+
root: clonePathRootRef(ref.root),
|
|
2564
|
+
relativePath: ref.relativePath,
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
function cloneDirectoryRef(ref) {
|
|
2568
|
+
return 'handleId' in ref
|
|
2569
|
+
? Object.freeze({ kind: 'directory', handleId: ref.handleId })
|
|
2570
|
+
: Object.freeze({
|
|
2571
|
+
kind: 'directory',
|
|
2572
|
+
root: clonePathRootRef(ref.root),
|
|
2573
|
+
relativePath: ref.relativePath,
|
|
2574
|
+
});
|
|
2575
|
+
}
|
|
2576
|
+
function assertMode(mode) {
|
|
2577
|
+
if (mode !== 'read' && mode !== 'readwrite') {
|
|
2578
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的文件访问模式');
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
function assertName(name) {
|
|
2582
|
+
if (typeof name !== 'string' || name.length === 0 || name.includes('/') || name.includes('\\') || CONTROL_CHARACTER_PATTERN.test(name)) {
|
|
2583
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的文件名');
|
|
2584
|
+
}
|
|
2585
|
+
}
|
|
2586
|
+
function assertFileRef(ref) {
|
|
2587
|
+
if (!isMiniProgramFileRef(ref)) {
|
|
2588
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 File handle');
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
function assertDirectoryRef(ref) {
|
|
2592
|
+
if (!isMiniProgramDirectoryRef(ref)) {
|
|
2593
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 Directory handle');
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
function defineReadonlyValue(target, key, value, enumerable = true) {
|
|
2597
|
+
Object.defineProperty(target, key, {
|
|
2598
|
+
value,
|
|
2599
|
+
enumerable,
|
|
2600
|
+
configurable: false,
|
|
2601
|
+
writable: false,
|
|
2602
|
+
});
|
|
2603
|
+
}
|
|
2604
|
+
function joinRelativePath(prefix, relativePath) {
|
|
2605
|
+
return prefix ? `${prefix}/${relativePath}` : relativePath;
|
|
2606
|
+
}
|
|
2607
|
+
function basename(relativePath) {
|
|
2608
|
+
const segments = relativePath.split('/');
|
|
2609
|
+
return segments[segments.length - 1];
|
|
2610
|
+
}
|
|
2611
|
+
function assertWritable(mode) {
|
|
2612
|
+
if (mode !== 'readwrite') {
|
|
2613
|
+
throw createSDKError('FILE_ACCESS_DENIED', '当前文件句柄不允许写入');
|
|
2614
|
+
}
|
|
2615
|
+
}
|
|
2616
|
+
function assertSandboxRef(ref) {
|
|
2617
|
+
if ('handleId' in ref || ref.root.kind !== 'sandbox') {
|
|
2618
|
+
throw createSDKError('FILE_ACCESS_DENIED', '外部文件授权不允许删除');
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
function normalizeCreateOptions(options) {
|
|
2622
|
+
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
2623
|
+
throw invalidParams$1('create options 必须是对象');
|
|
2624
|
+
}
|
|
2625
|
+
if (options?.recursive !== undefined && typeof options.recursive !== 'boolean') {
|
|
2626
|
+
throw invalidParams$1('recursive 必须是 boolean');
|
|
2627
|
+
}
|
|
2628
|
+
if (options?.exclusive !== undefined && typeof options.exclusive !== 'boolean') {
|
|
2629
|
+
throw invalidParams$1('exclusive 必须是 boolean');
|
|
2630
|
+
}
|
|
2631
|
+
return {
|
|
2632
|
+
recursive: options?.recursive ?? false,
|
|
2633
|
+
exclusive: options?.exclusive ?? false,
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2636
|
+
function normalizeListOptions(options) {
|
|
2637
|
+
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
2638
|
+
throw invalidParams$1('list options 必须是对象');
|
|
2639
|
+
}
|
|
2640
|
+
if (options?.limit !== undefined && (!Number.isSafeInteger(options.limit) || options.limit <= 0)) {
|
|
2641
|
+
throw invalidParams$1('limit 必须是正的 safe integer');
|
|
2642
|
+
}
|
|
2643
|
+
return options?.limit;
|
|
2644
|
+
}
|
|
2645
|
+
function normalizeRemoveOptions(options) {
|
|
2646
|
+
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
2647
|
+
throw invalidParams$1('remove options 必须是对象');
|
|
2648
|
+
}
|
|
2649
|
+
if (options?.recursive !== undefined && typeof options.recursive !== 'boolean') {
|
|
2650
|
+
throw invalidParams$1('recursive 必须是 boolean');
|
|
2651
|
+
}
|
|
2652
|
+
return { recursive: options?.recursive ?? false };
|
|
2653
|
+
}
|
|
2654
|
+
function cloneBytes(content) {
|
|
2655
|
+
if (!(content instanceof Uint8Array)) {
|
|
2656
|
+
throw invalidParams$1('content 必须是 Uint8Array');
|
|
2657
|
+
}
|
|
2658
|
+
if (typeof SharedArrayBuffer !== 'undefined' && content.buffer instanceof SharedArrayBuffer) {
|
|
2659
|
+
throw invalidParams$1('content 不能由 SharedArrayBuffer 承载');
|
|
2660
|
+
}
|
|
2661
|
+
return new Uint8Array(content);
|
|
2662
|
+
}
|
|
2663
|
+
function normalizeStat(stat) {
|
|
2664
|
+
if (!isMiniProgramFileStat(stat)) {
|
|
2665
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 FileStat');
|
|
2666
|
+
}
|
|
2667
|
+
return {
|
|
2668
|
+
kind: 'file',
|
|
2669
|
+
size: stat.size,
|
|
2670
|
+
...(stat.modifiedAt === undefined ? {} : { modifiedAt: stat.modifiedAt }),
|
|
2671
|
+
...(stat.createdAt === undefined ? {} : { createdAt: stat.createdAt }),
|
|
2326
2672
|
};
|
|
2327
2673
|
}
|
|
2674
|
+
/** 校验并返回平台无关的 `/` 分隔逻辑相对路径。 */
|
|
2675
|
+
function normalizeRelativePath(value) {
|
|
2676
|
+
if (!isMiniProgramRelativePath(value)) {
|
|
2677
|
+
throw invalidParams$1('relativePath 必须是规范的 `/` 分隔逻辑相对路径');
|
|
2678
|
+
}
|
|
2679
|
+
return value;
|
|
2680
|
+
}
|
|
2681
|
+
class MiniProgramFileHandleRegistry {
|
|
2682
|
+
requester;
|
|
2683
|
+
fileStates = new WeakMap();
|
|
2684
|
+
directoryStates = new WeakMap();
|
|
2685
|
+
pathRootStates = new WeakMap();
|
|
2686
|
+
sandboxRoot;
|
|
2687
|
+
constructor(requester) {
|
|
2688
|
+
this.requester = requester;
|
|
2689
|
+
}
|
|
2690
|
+
getSandbox() {
|
|
2691
|
+
if (this.sandboxRoot) {
|
|
2692
|
+
return this.sandboxRoot;
|
|
2693
|
+
}
|
|
2694
|
+
const sandbox = {};
|
|
2695
|
+
const state = {
|
|
2696
|
+
root: Object.freeze({ kind: 'sandbox' }),
|
|
2697
|
+
prefix: '',
|
|
2698
|
+
mode: 'readwrite',
|
|
2699
|
+
};
|
|
2700
|
+
this.definePathRootMethods(sandbox, state);
|
|
2701
|
+
this.pathRootStates.set(sandbox, state);
|
|
2702
|
+
brandedFileSystemValues.add(sandbox);
|
|
2703
|
+
this.sandboxRoot = Object.freeze(sandbox);
|
|
2704
|
+
return this.sandboxRoot;
|
|
2705
|
+
}
|
|
2706
|
+
createFile(descriptor) {
|
|
2707
|
+
this.assertFileDescriptor(descriptor);
|
|
2708
|
+
const ref = cloneFileRef(descriptor.ref);
|
|
2709
|
+
const state = { ref, mode: descriptor.mode };
|
|
2710
|
+
const handle = {};
|
|
2711
|
+
defineReadonlyValue(handle, 'kind', 'file');
|
|
2712
|
+
defineReadonlyValue(handle, 'name', descriptor.name);
|
|
2713
|
+
defineReadonlyValue(handle, 'mode', descriptor.mode);
|
|
2714
|
+
defineReadonlyValue(handle, 'create', async (options) => {
|
|
2715
|
+
assertWritable(state.mode);
|
|
2716
|
+
await this.requester.request(FILE_CREATE_METHOD, {
|
|
2717
|
+
target: state.ref,
|
|
2718
|
+
...normalizeCreateOptions(options),
|
|
2719
|
+
});
|
|
2720
|
+
return handle;
|
|
2721
|
+
}, false);
|
|
2722
|
+
defineReadonlyValue(handle, 'remove', async () => {
|
|
2723
|
+
assertWritable(state.mode);
|
|
2724
|
+
assertSandboxRef(state.ref);
|
|
2725
|
+
await this.requester.request(FILE_REMOVE_METHOD, { target: state.ref });
|
|
2726
|
+
}, false);
|
|
2727
|
+
defineReadonlyValue(handle, 'exists', async () => {
|
|
2728
|
+
const result = await this.requester.request(FILE_EXISTS_METHOD, { target: state.ref });
|
|
2729
|
+
if (typeof result !== 'boolean') {
|
|
2730
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 exists 结果');
|
|
2731
|
+
}
|
|
2732
|
+
return result;
|
|
2733
|
+
}, false);
|
|
2734
|
+
defineReadonlyValue(handle, 'readText', async () => {
|
|
2735
|
+
const result = await this.requester.request(FILE_READ_TEXT_METHOD, { target: state.ref });
|
|
2736
|
+
if (typeof result !== 'string') {
|
|
2737
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的文本内容');
|
|
2738
|
+
}
|
|
2739
|
+
return result;
|
|
2740
|
+
}, false);
|
|
2741
|
+
defineReadonlyValue(handle, 'writeText', async (content) => {
|
|
2742
|
+
assertWritable(state.mode);
|
|
2743
|
+
if (typeof content !== 'string') {
|
|
2744
|
+
throw invalidParams$1('content 必须是 string');
|
|
2745
|
+
}
|
|
2746
|
+
await this.requester.request(FILE_WRITE_TEXT_METHOD, { target: state.ref, content });
|
|
2747
|
+
}, false);
|
|
2748
|
+
defineReadonlyValue(handle, 'readBytes', async () => {
|
|
2749
|
+
const result = await this.requester.request(FILE_READ_BYTES_METHOD, { target: state.ref });
|
|
2750
|
+
if (!isMiniProgramByteArray(result)) {
|
|
2751
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的二进制内容');
|
|
2752
|
+
}
|
|
2753
|
+
return new Uint8Array(result);
|
|
2754
|
+
}, false);
|
|
2755
|
+
defineReadonlyValue(handle, 'writeBytes', async (content) => {
|
|
2756
|
+
assertWritable(state.mode);
|
|
2757
|
+
const snapshot = cloneBytes(content);
|
|
2758
|
+
await this.requester.request(FILE_WRITE_BYTES_METHOD, { target: state.ref, content: snapshot });
|
|
2759
|
+
}, false);
|
|
2760
|
+
defineReadonlyValue(handle, 'stat', async () => {
|
|
2761
|
+
const result = await this.requester.request(FILE_STAT_METHOD, { target: state.ref });
|
|
2762
|
+
return normalizeStat(result);
|
|
2763
|
+
}, false);
|
|
2764
|
+
this.fileStates.set(handle, state);
|
|
2765
|
+
brandedFileSystemValues.add(handle);
|
|
2766
|
+
return Object.freeze(handle);
|
|
2767
|
+
}
|
|
2768
|
+
createDirectFile(descriptor) {
|
|
2769
|
+
this.assertFileDescriptor(descriptor);
|
|
2770
|
+
if (!('handleId' in descriptor.ref)) {
|
|
2771
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的 File 不是精确文件授权');
|
|
2772
|
+
}
|
|
2773
|
+
return this.createFile(descriptor);
|
|
2774
|
+
}
|
|
2775
|
+
createDirectory(descriptor) {
|
|
2776
|
+
this.assertDirectoryDescriptor(descriptor);
|
|
2777
|
+
const ref = cloneDirectoryRef(descriptor.ref);
|
|
2778
|
+
const state = { ref, mode: descriptor.mode };
|
|
2779
|
+
const pathRootState = 'handleId' in ref ? { root: ref, prefix: '', mode: descriptor.mode } : { root: ref.root, prefix: ref.relativePath, mode: descriptor.mode };
|
|
2780
|
+
const handle = {};
|
|
2781
|
+
defineReadonlyValue(handle, 'kind', 'directory');
|
|
2782
|
+
defineReadonlyValue(handle, 'name', descriptor.name);
|
|
2783
|
+
defineReadonlyValue(handle, 'mode', descriptor.mode);
|
|
2784
|
+
this.definePathRootMethods(handle, pathRootState);
|
|
2785
|
+
defineReadonlyValue(handle, 'create', async (options) => {
|
|
2786
|
+
assertWritable(state.mode);
|
|
2787
|
+
await this.requester.request(DIRECTORY_CREATE_METHOD, {
|
|
2788
|
+
target: state.ref,
|
|
2789
|
+
...normalizeCreateOptions(options),
|
|
2790
|
+
});
|
|
2791
|
+
return handle;
|
|
2792
|
+
}, false);
|
|
2793
|
+
defineReadonlyValue(handle, 'remove', async (options) => {
|
|
2794
|
+
assertWritable(state.mode);
|
|
2795
|
+
assertSandboxRef(state.ref);
|
|
2796
|
+
await this.requester.request(DIRECTORY_REMOVE_METHOD, {
|
|
2797
|
+
target: state.ref,
|
|
2798
|
+
...normalizeRemoveOptions(options),
|
|
2799
|
+
});
|
|
2800
|
+
}, false);
|
|
2801
|
+
defineReadonlyValue(handle, 'exists', async () => {
|
|
2802
|
+
const result = await this.requester.request(DIRECTORY_EXISTS_METHOD, { target: state.ref });
|
|
2803
|
+
if (typeof result !== 'boolean') {
|
|
2804
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 exists 结果');
|
|
2805
|
+
}
|
|
2806
|
+
return result;
|
|
2807
|
+
}, false);
|
|
2808
|
+
defineReadonlyValue(handle, 'list', async (options) => {
|
|
2809
|
+
const limit = normalizeListOptions(options);
|
|
2810
|
+
const result = await this.requester.request(DIRECTORY_LIST_METHOD, {
|
|
2811
|
+
target: state.ref,
|
|
2812
|
+
...(limit === undefined ? {} : { limit }),
|
|
2813
|
+
});
|
|
2814
|
+
if (!Array.isArray(result)) {
|
|
2815
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的目录列表');
|
|
2816
|
+
}
|
|
2817
|
+
if (result.some((entry) => entry?.mode !== state.mode)) {
|
|
2818
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的目录项未继承父目录 mode');
|
|
2819
|
+
}
|
|
2820
|
+
return result.map((entry) => this.createEntity(entry));
|
|
2821
|
+
}, false);
|
|
2822
|
+
this.directoryStates.set(handle, state);
|
|
2823
|
+
this.pathRootStates.set(handle, pathRootState);
|
|
2824
|
+
brandedFileSystemValues.add(handle);
|
|
2825
|
+
return Object.freeze(handle);
|
|
2826
|
+
}
|
|
2827
|
+
createDirectDirectory(descriptor) {
|
|
2828
|
+
this.assertDirectoryDescriptor(descriptor);
|
|
2829
|
+
if (!('handleId' in descriptor.ref)) {
|
|
2830
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的 Directory 不是精确目录授权');
|
|
2831
|
+
}
|
|
2832
|
+
return this.createDirectory(descriptor);
|
|
2833
|
+
}
|
|
2834
|
+
createEntity(descriptor) {
|
|
2835
|
+
if (descriptor?.kind === 'file') {
|
|
2836
|
+
return this.createFile(descriptor);
|
|
2837
|
+
}
|
|
2838
|
+
if (descriptor?.kind === 'directory') {
|
|
2839
|
+
return this.createDirectory(descriptor);
|
|
2840
|
+
}
|
|
2841
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的文件实体');
|
|
2842
|
+
}
|
|
2843
|
+
requireFile(handle) {
|
|
2844
|
+
const state = handle && typeof handle === 'object' ? this.fileStates.get(handle) : undefined;
|
|
2845
|
+
if (!state) {
|
|
2846
|
+
throw invalidParams$1('to 必须是当前 SDK session 创建的 FileHandle');
|
|
2847
|
+
}
|
|
2848
|
+
return state;
|
|
2849
|
+
}
|
|
2850
|
+
requireDirectory(handle) {
|
|
2851
|
+
const state = handle && typeof handle === 'object' ? this.directoryStates.get(handle) : undefined;
|
|
2852
|
+
if (!state) {
|
|
2853
|
+
throw invalidParams$1('to 必须是当前 SDK session 创建的 DirectoryHandle');
|
|
2854
|
+
}
|
|
2855
|
+
return state;
|
|
2856
|
+
}
|
|
2857
|
+
definePathRootMethods(target, state) {
|
|
2858
|
+
defineReadonlyValue(target, 'file', (relativePath) => {
|
|
2859
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
2860
|
+
const fullPath = joinRelativePath(state.prefix, normalized);
|
|
2861
|
+
return this.createFile({
|
|
2862
|
+
kind: 'file',
|
|
2863
|
+
name: basename(normalized),
|
|
2864
|
+
mode: state.mode,
|
|
2865
|
+
ref: {
|
|
2866
|
+
kind: 'file',
|
|
2867
|
+
root: state.root,
|
|
2868
|
+
relativePath: fullPath,
|
|
2869
|
+
},
|
|
2870
|
+
});
|
|
2871
|
+
}, false);
|
|
2872
|
+
defineReadonlyValue(target, 'directory', (relativePath) => {
|
|
2873
|
+
const normalized = normalizeRelativePath(relativePath);
|
|
2874
|
+
const fullPath = joinRelativePath(state.prefix, normalized);
|
|
2875
|
+
return this.createDirectory({
|
|
2876
|
+
kind: 'directory',
|
|
2877
|
+
name: basename(normalized),
|
|
2878
|
+
mode: state.mode,
|
|
2879
|
+
ref: {
|
|
2880
|
+
kind: 'directory',
|
|
2881
|
+
root: state.root,
|
|
2882
|
+
relativePath: fullPath,
|
|
2883
|
+
},
|
|
2884
|
+
});
|
|
2885
|
+
}, false);
|
|
2886
|
+
}
|
|
2887
|
+
assertFileDescriptor(descriptor) {
|
|
2888
|
+
if (!descriptor || descriptor.kind !== 'file') {
|
|
2889
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 File descriptor');
|
|
2890
|
+
}
|
|
2891
|
+
assertName(descriptor.name);
|
|
2892
|
+
assertMode(descriptor.mode);
|
|
2893
|
+
assertFileRef(descriptor.ref);
|
|
2894
|
+
}
|
|
2895
|
+
assertDirectoryDescriptor(descriptor) {
|
|
2896
|
+
if (!descriptor || descriptor.kind !== 'directory') {
|
|
2897
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 Directory descriptor');
|
|
2898
|
+
}
|
|
2899
|
+
assertName(descriptor.name);
|
|
2900
|
+
assertMode(descriptor.mode);
|
|
2901
|
+
assertDirectoryRef(descriptor.ref);
|
|
2902
|
+
}
|
|
2903
|
+
}
|
|
2904
|
+
function getFileHandleRegistry(requester) {
|
|
2905
|
+
let registry = registries.get(requester);
|
|
2906
|
+
if (!registry) {
|
|
2907
|
+
registry = new MiniProgramFileHandleRegistry(requester);
|
|
2908
|
+
registries.set(requester, registry);
|
|
2909
|
+
}
|
|
2910
|
+
return registry;
|
|
2911
|
+
}
|
|
2912
|
+
function isMiniProgramFileSystemValue(value) {
|
|
2913
|
+
return !!value && typeof value === 'object' && brandedFileSystemValues.has(value);
|
|
2914
|
+
}
|
|
2328
2915
|
|
|
2329
2916
|
const LOG_PREFIX = '[hb-sdk][network.request]';
|
|
2330
2917
|
/**
|
|
@@ -2397,13 +2984,13 @@ function formatNetworkRequestFailureMessage(detail) {
|
|
|
2397
2984
|
return `${detail.kind} ${detail.method} ${detail.url}${statusPart}${codePart}`;
|
|
2398
2985
|
}
|
|
2399
2986
|
|
|
2400
|
-
const DEFAULT_VALIDATE_STATUS = status => status >= 200 && status < 300;
|
|
2987
|
+
const DEFAULT_VALIDATE_STATUS = (status) => status >= 200 && status < 300;
|
|
2401
2988
|
function isPlainObject(value) {
|
|
2402
2989
|
return Object.prototype.toString.call(value) === '[object Object]';
|
|
2403
2990
|
}
|
|
2404
2991
|
function clonePublicValue(value) {
|
|
2405
2992
|
if (Array.isArray(value)) {
|
|
2406
|
-
return value.map(item => clonePublicValue(item));
|
|
2993
|
+
return value.map((item) => clonePublicValue(item));
|
|
2407
2994
|
}
|
|
2408
2995
|
if (isPlainObject(value)) {
|
|
2409
2996
|
return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, clonePublicValue(item)]));
|
|
@@ -2457,11 +3044,11 @@ function toNetworkResponse(payload, config) {
|
|
|
2457
3044
|
/**
|
|
2458
3045
|
* 发起网络请求并返回标准化响应。
|
|
2459
3046
|
*
|
|
2460
|
-
* @param requester 底层 bridge 请求能力。
|
|
2461
|
-
* @param config 面向业务层的网络请求配置。
|
|
3047
|
+
* @param requester - 底层 bridge 请求能力。
|
|
3048
|
+
* @param config - 面向业务层的网络请求配置。
|
|
2462
3049
|
* @returns 标准化网络响应。HTTP 2xx 默认返回 resolve。
|
|
2463
|
-
* @throws {HbMiniProgramNetworkError} 当请求已完成但 HTTP 状态不满足 `validateStatus` 时抛出。
|
|
2464
|
-
* @throws {HbMiniProgramSDKError} 当 bridge、运行时或宿主能力调用失败时抛出。
|
|
3050
|
+
* @throws {@link HbMiniProgramNetworkError} 当请求已完成但 HTTP 状态不满足 `validateStatus` 时抛出。
|
|
3051
|
+
* @throws {@link HbMiniProgramSDKError} 当 bridge、运行时或宿主能力调用失败时抛出。
|
|
2465
3052
|
*
|
|
2466
3053
|
* @remarks
|
|
2467
3054
|
* `validateStatus` 仅在 SDK 侧执行,不会跨 bridge 传输函数值。
|
|
@@ -2479,7 +3066,7 @@ async function request(requester, config) {
|
|
|
2479
3066
|
const validateStatus = config.validateStatus || DEFAULT_VALIDATE_STATUS;
|
|
2480
3067
|
try {
|
|
2481
3068
|
assertSupportedPublicNetworkRequest(config);
|
|
2482
|
-
const responsePayload = await requester.request(NETWORK_REQUEST_METHOD, toRequestPayload(config));
|
|
3069
|
+
const responsePayload = (await requester.request(NETWORK_REQUEST_METHOD, toRequestPayload(config)));
|
|
2483
3070
|
const response = toNetworkResponse(responsePayload, config);
|
|
2484
3071
|
if (!validateStatus(response.status)) {
|
|
2485
3072
|
const error = new HbMiniProgramNetworkError(response);
|
|
@@ -2527,25 +3114,319 @@ function assertSupportedPublicNetworkRequest(config) {
|
|
|
2527
3114
|
if (isUnsupportedMultipartNetworkRequest(config)) {
|
|
2528
3115
|
throw createSDKError('INVALID_PARAMS', NETWORK_REQUEST_MULTIPART_UNSUPPORTED_MESSAGE);
|
|
2529
3116
|
}
|
|
3117
|
+
if (containsUnsupportedNetworkData(config.data)) {
|
|
3118
|
+
throw createSDKError('INVALID_PARAMS', 'network.request data 不支持 FileHandle、FileSystem、Blob、FormData 或二进制数据');
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
function containsUnsupportedNetworkData(value, visiting = new WeakSet(), scanned = new WeakSet()) {
|
|
3122
|
+
if (isMiniProgramFileSystemValue(value))
|
|
3123
|
+
return true;
|
|
3124
|
+
if (typeof Blob !== 'undefined' && value instanceof Blob)
|
|
3125
|
+
return true;
|
|
3126
|
+
if (typeof FormData !== 'undefined' && value instanceof FormData)
|
|
3127
|
+
return true;
|
|
3128
|
+
if (value instanceof ArrayBuffer || ArrayBuffer.isView(value))
|
|
3129
|
+
return true;
|
|
3130
|
+
if (typeof SharedArrayBuffer !== 'undefined' && value instanceof SharedArrayBuffer)
|
|
3131
|
+
return true;
|
|
3132
|
+
if (!value || typeof value !== 'object' || value instanceof URLSearchParams)
|
|
3133
|
+
return false;
|
|
3134
|
+
if (visiting.has(value))
|
|
3135
|
+
return true;
|
|
3136
|
+
if (scanned.has(value))
|
|
3137
|
+
return false;
|
|
3138
|
+
visiting.add(value);
|
|
3139
|
+
let unsupported = false;
|
|
3140
|
+
if (Array.isArray(value))
|
|
3141
|
+
unsupported = value.some((item) => containsUnsupportedNetworkData(item, visiting, scanned));
|
|
3142
|
+
if (isPlainObject(value)) {
|
|
3143
|
+
unsupported ||= Object.values(value).some((item) => containsUnsupportedNetworkData(item, visiting, scanned));
|
|
3144
|
+
}
|
|
3145
|
+
visiting.delete(value);
|
|
3146
|
+
if (!unsupported)
|
|
3147
|
+
scanned.add(value);
|
|
3148
|
+
return unsupported;
|
|
3149
|
+
}
|
|
3150
|
+
function assertFinitePositive(value, name) {
|
|
3151
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
3152
|
+
throw createSDKError('INVALID_PARAMS', `${name} 必须是有限正数`);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
function normalizeDownloadProgress(value) {
|
|
3156
|
+
if (!value || typeof value !== 'object') {
|
|
3157
|
+
throw createSDKError('INVALID_NETWORK_RESPONSE', 'network.download 返回了无效的进度');
|
|
3158
|
+
}
|
|
3159
|
+
if (!isMiniProgramDownloadProgressPayload(value)) {
|
|
3160
|
+
throw createSDKError('INVALID_NETWORK_RESPONSE', 'network.download 返回了无效的进度');
|
|
3161
|
+
}
|
|
3162
|
+
return {
|
|
3163
|
+
loaded: value.loaded,
|
|
3164
|
+
...(value.total === undefined ? {} : { total: value.total }),
|
|
3165
|
+
lengthComputable: value.lengthComputable,
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
function normalizeDownloadPayload(options, to, isFileTarget) {
|
|
3169
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
3170
|
+
throw createSDKError('INVALID_PARAMS', 'network.download options 必须是对象');
|
|
3171
|
+
}
|
|
3172
|
+
if (typeof options.url !== 'string' || options.url.length === 0) {
|
|
3173
|
+
throw createSDKError('INVALID_PARAMS', 'url 必须是非空字符串');
|
|
3174
|
+
}
|
|
3175
|
+
if (options.params !== undefined && !isPlainObject(options.params)) {
|
|
3176
|
+
throw createSDKError('INVALID_PARAMS', 'params 必须是对象');
|
|
3177
|
+
}
|
|
3178
|
+
if (options.headers !== undefined) {
|
|
3179
|
+
if (!isPlainObject(options.headers) || Object.values(options.headers).some((value) => typeof value !== 'string')) {
|
|
3180
|
+
throw createSDKError('INVALID_PARAMS', 'headers 必须是字符串字典');
|
|
3181
|
+
}
|
|
3182
|
+
}
|
|
3183
|
+
if (options.timeout !== undefined) {
|
|
3184
|
+
assertFinitePositive(options.timeout, 'timeout');
|
|
3185
|
+
}
|
|
3186
|
+
if (options.maxBytes !== undefined && (!Number.isSafeInteger(options.maxBytes) || options.maxBytes <= 0)) {
|
|
3187
|
+
throw createSDKError('INVALID_PARAMS', 'maxBytes 必须是正的 safe integer');
|
|
3188
|
+
}
|
|
3189
|
+
if (options.withCredentials !== undefined && typeof options.withCredentials !== 'boolean') {
|
|
3190
|
+
throw createSDKError('INVALID_PARAMS', 'withCredentials 必须是 boolean');
|
|
3191
|
+
}
|
|
3192
|
+
if (options.onProgress !== undefined && typeof options.onProgress !== 'function') {
|
|
3193
|
+
throw createSDKError('INVALID_PARAMS', 'onProgress 必须是函数');
|
|
3194
|
+
}
|
|
3195
|
+
if (options.signal !== undefined &&
|
|
3196
|
+
(typeof options.signal !== 'object' ||
|
|
3197
|
+
typeof options.signal.aborted !== 'boolean' ||
|
|
3198
|
+
typeof options.signal.addEventListener !== 'function' ||
|
|
3199
|
+
typeof options.signal.removeEventListener !== 'function')) {
|
|
3200
|
+
throw createSDKError('INVALID_PARAMS', 'signal 必须是 AbortSignal');
|
|
3201
|
+
}
|
|
3202
|
+
if ('method' in options || 'data' in options || 'body' in options) {
|
|
3203
|
+
throw createSDKError('INVALID_PARAMS', 'network.download 固定使用 GET 且不接受 body');
|
|
3204
|
+
}
|
|
3205
|
+
const base = {
|
|
3206
|
+
url: options.url,
|
|
3207
|
+
to,
|
|
3208
|
+
...(options.params === undefined ? {} : { params: clonePublicValue(options.params) }),
|
|
3209
|
+
...(options.headers === undefined ? {} : { headers: clonePublicValue(options.headers) }),
|
|
3210
|
+
...(options.timeout === undefined ? {} : { timeout: options.timeout }),
|
|
3211
|
+
...(options.withCredentials === undefined ? {} : { withCredentials: options.withCredentials }),
|
|
3212
|
+
...(options.maxBytes === undefined ? {} : { maxBytes: options.maxBytes }),
|
|
3213
|
+
};
|
|
3214
|
+
if (isFileTarget) {
|
|
3215
|
+
if (options.suggestedName !== undefined || options.overwrite !== undefined) {
|
|
3216
|
+
throw createSDKError('INVALID_PARAMS', 'File target 不接受 suggestedName 或 overwrite');
|
|
3217
|
+
}
|
|
3218
|
+
return base;
|
|
3219
|
+
}
|
|
3220
|
+
if (options.overwrite !== undefined && typeof options.overwrite !== 'boolean') {
|
|
3221
|
+
throw createSDKError('INVALID_PARAMS', 'overwrite 必须是 boolean');
|
|
3222
|
+
}
|
|
3223
|
+
let suggestedName;
|
|
3224
|
+
if (options.suggestedName !== undefined) {
|
|
3225
|
+
suggestedName = normalizeRelativePath(options.suggestedName);
|
|
3226
|
+
if (suggestedName.includes('/')) {
|
|
3227
|
+
throw createSDKError('INVALID_PARAMS', 'suggestedName 必须是 basename');
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
return {
|
|
3231
|
+
...base,
|
|
3232
|
+
...(suggestedName === undefined ? {} : { suggestedName }),
|
|
3233
|
+
...(options.overwrite === undefined ? {} : { overwrite: options.overwrite }),
|
|
3234
|
+
};
|
|
3235
|
+
}
|
|
3236
|
+
function isSamePathRoot(left, right) {
|
|
3237
|
+
if (left.kind !== right.kind)
|
|
3238
|
+
return false;
|
|
3239
|
+
return left.kind === 'sandbox' || (right.kind === 'directory' && left.handleId === right.handleId);
|
|
3240
|
+
}
|
|
3241
|
+
function isSameFileRef(left, right) {
|
|
3242
|
+
if ('handleId' in left || 'handleId' in right) {
|
|
3243
|
+
return 'handleId' in left && 'handleId' in right && left.handleId === right.handleId;
|
|
3244
|
+
}
|
|
3245
|
+
return left.relativePath === right.relativePath && isSamePathRoot(left.root, right.root);
|
|
3246
|
+
}
|
|
3247
|
+
function isDirectChildFileRef(directory, file) {
|
|
3248
|
+
if ('handleId' in file)
|
|
3249
|
+
return false;
|
|
3250
|
+
if ('handleId' in directory) {
|
|
3251
|
+
return file.root.kind === 'directory' && file.root.handleId === directory.handleId && !file.relativePath.includes('/');
|
|
3252
|
+
}
|
|
3253
|
+
if (!isSamePathRoot(directory.root, file.root))
|
|
3254
|
+
return false;
|
|
3255
|
+
const prefix = `${directory.relativePath}/`;
|
|
3256
|
+
if (!file.relativePath.startsWith(prefix))
|
|
3257
|
+
return false;
|
|
3258
|
+
const childName = file.relativePath.slice(prefix.length);
|
|
3259
|
+
return childName.length > 0 && !childName.includes('/');
|
|
3260
|
+
}
|
|
3261
|
+
function invalidDownloadResult(message) {
|
|
3262
|
+
throw createSDKError('INVALID_NETWORK_RESPONSE', message);
|
|
3263
|
+
}
|
|
3264
|
+
/** 发起不使用 bridge 固定总时长 timer 的流式下载。 */
|
|
3265
|
+
async function download(requester, options) {
|
|
3266
|
+
if (options?.signal?.aborted) {
|
|
3267
|
+
throw createSDKError('DOWNLOAD_CANCELLED', '下载已取消');
|
|
3268
|
+
}
|
|
3269
|
+
const registry = getFileHandleRegistry(requester);
|
|
3270
|
+
const isFileTarget = options?.to?.kind === 'file';
|
|
3271
|
+
const target = isFileTarget ? registry.requireFile(options?.to) : registry.requireDirectory(options?.to);
|
|
3272
|
+
if (target.mode !== 'readwrite') {
|
|
3273
|
+
throw createSDKError('FILE_ACCESS_DENIED', '下载 target 必须是 readwrite');
|
|
3274
|
+
}
|
|
3275
|
+
const payload = normalizeDownloadPayload(options, target.ref, isFileTarget);
|
|
3276
|
+
const result = await requester.requestOperation(NETWORK_DOWNLOAD_METHOD, payload, {
|
|
3277
|
+
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
3278
|
+
...(options.onProgress === undefined
|
|
3279
|
+
? {}
|
|
3280
|
+
: {
|
|
3281
|
+
onProgress: (progress) => {
|
|
3282
|
+
options.onProgress?.(normalizeDownloadProgress(progress));
|
|
3283
|
+
},
|
|
3284
|
+
}),
|
|
3285
|
+
createAbortError: () => createSDKError('DOWNLOAD_CANCELLED', '下载已取消'),
|
|
3286
|
+
});
|
|
3287
|
+
const resultHandle = registry.createFile(result);
|
|
3288
|
+
const resultState = registry.requireFile(resultHandle);
|
|
3289
|
+
if (resultHandle.mode !== 'readwrite') {
|
|
3290
|
+
invalidDownloadResult('network.download 返回的 File 不是 readwrite');
|
|
3291
|
+
}
|
|
3292
|
+
if (isFileTarget) {
|
|
3293
|
+
const original = options.to;
|
|
3294
|
+
if (resultHandle.name !== original.name || !isSameFileRef(target.ref, resultState.ref)) {
|
|
3295
|
+
invalidDownloadResult('network.download 返回的 File 与 target 不一致');
|
|
3296
|
+
}
|
|
3297
|
+
return original;
|
|
3298
|
+
}
|
|
3299
|
+
if (!isDirectChildFileRef(target.ref, resultState.ref) ||
|
|
3300
|
+
'handleId' in resultState.ref ||
|
|
3301
|
+
resultHandle.name !== resultState.ref.relativePath.split('/').at(-1)) {
|
|
3302
|
+
invalidDownloadResult('network.download 返回的 File 不在 target Directory 内');
|
|
3303
|
+
}
|
|
3304
|
+
return resultHandle;
|
|
2530
3305
|
}
|
|
2531
3306
|
/**
|
|
2532
3307
|
* 创建 network 模块。
|
|
2533
3308
|
*
|
|
2534
|
-
* @param requester 底层 bridge 请求能力。
|
|
3309
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2535
3310
|
* @returns 面向业务层的网络模块对象。
|
|
2536
3311
|
*/
|
|
2537
3312
|
function createNetworkModule(requester) {
|
|
2538
3313
|
return {
|
|
2539
|
-
request: config => request(requester, config),
|
|
3314
|
+
request: (config) => request(requester, config),
|
|
3315
|
+
download: (options) => download(requester, options),
|
|
2540
3316
|
};
|
|
2541
3317
|
}
|
|
2542
3318
|
|
|
3319
|
+
function invalidParams(message) {
|
|
3320
|
+
return createSDKError('INVALID_PARAMS', message);
|
|
3321
|
+
}
|
|
3322
|
+
function normalizeMode(mode) {
|
|
3323
|
+
if (mode === undefined) {
|
|
3324
|
+
return undefined;
|
|
3325
|
+
}
|
|
3326
|
+
if (mode !== 'read' && mode !== 'readwrite') {
|
|
3327
|
+
throw invalidParams('mode 必须是 read 或 readwrite');
|
|
3328
|
+
}
|
|
3329
|
+
return mode;
|
|
3330
|
+
}
|
|
3331
|
+
function assertOptionsObject(value, name) {
|
|
3332
|
+
if (value !== undefined && (!value || typeof value !== 'object' || Array.isArray(value))) {
|
|
3333
|
+
throw invalidParams(`${name} 必须是对象`);
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
function normalizeAccept(accept) {
|
|
3337
|
+
if (accept === undefined) {
|
|
3338
|
+
return undefined;
|
|
3339
|
+
}
|
|
3340
|
+
if (!Array.isArray(accept)) {
|
|
3341
|
+
throw invalidParams('accept 必须是扩展名数组');
|
|
3342
|
+
}
|
|
3343
|
+
return accept.map((extension) => {
|
|
3344
|
+
if (typeof extension !== 'string' ||
|
|
3345
|
+
!/^\.[a-z0-9][a-z0-9._-]*$/i.test(extension)) {
|
|
3346
|
+
throw invalidParams('accept 只能包含规范扩展名,例如 .json');
|
|
3347
|
+
}
|
|
3348
|
+
return extension;
|
|
3349
|
+
});
|
|
3350
|
+
}
|
|
3351
|
+
function normalizePickFilesOptions(options) {
|
|
3352
|
+
assertOptionsObject(options, 'pickFiles options');
|
|
3353
|
+
if (options?.multiple !== undefined && typeof options.multiple !== 'boolean') {
|
|
3354
|
+
throw invalidParams('multiple 必须是 boolean');
|
|
3355
|
+
}
|
|
3356
|
+
const mode = normalizeMode(options?.mode);
|
|
3357
|
+
const accept = normalizeAccept(options?.accept);
|
|
3358
|
+
if (options === undefined) {
|
|
3359
|
+
return undefined;
|
|
3360
|
+
}
|
|
3361
|
+
return {
|
|
3362
|
+
...(mode === undefined ? {} : { mode }),
|
|
3363
|
+
...(accept === undefined ? {} : { accept }),
|
|
3364
|
+
...(options.multiple === undefined ? {} : { multiple: options.multiple }),
|
|
3365
|
+
};
|
|
3366
|
+
}
|
|
3367
|
+
function normalizePickDirectoryOptions(options) {
|
|
3368
|
+
assertOptionsObject(options, 'pickDirectory options');
|
|
3369
|
+
const mode = normalizeMode(options?.mode);
|
|
3370
|
+
return options === undefined ? undefined : mode === undefined ? {} : { mode };
|
|
3371
|
+
}
|
|
3372
|
+
function normalizeSaveFileOptions(options) {
|
|
3373
|
+
assertOptionsObject(options, 'saveFile options');
|
|
3374
|
+
if (!options || typeof options.suggestedName !== 'string') {
|
|
3375
|
+
throw invalidParams('suggestedName 必须是非空字符串');
|
|
3376
|
+
}
|
|
3377
|
+
const suggestedName = normalizeRelativePath(options.suggestedName);
|
|
3378
|
+
if (suggestedName.includes('/')) {
|
|
3379
|
+
throw invalidParams('suggestedName 必须是 basename');
|
|
3380
|
+
}
|
|
3381
|
+
if ('accept' in options) {
|
|
3382
|
+
throw invalidParams('saveFile V1 不支持 accept');
|
|
3383
|
+
}
|
|
3384
|
+
return { suggestedName };
|
|
3385
|
+
}
|
|
3386
|
+
/** 创建当前 SDK session 的 files 模块。 */
|
|
3387
|
+
function createFilesModule(requester) {
|
|
3388
|
+
const registry = getFileHandleRegistry(requester);
|
|
3389
|
+
return Object.freeze({
|
|
3390
|
+
sandbox: registry.getSandbox(),
|
|
3391
|
+
async pickFiles(options) {
|
|
3392
|
+
const normalizedOptions = normalizePickFilesOptions(options);
|
|
3393
|
+
const descriptors = await requester.request(FILES_PICK_FILES_METHOD, normalizedOptions);
|
|
3394
|
+
if (!Array.isArray(descriptors) || descriptors.length === 0 || (normalizedOptions?.multiple !== true && descriptors.length > 1)) {
|
|
3395
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的 picker 结果');
|
|
3396
|
+
}
|
|
3397
|
+
const expectedMode = normalizedOptions?.mode ?? 'read';
|
|
3398
|
+
const handles = descriptors.map((descriptor) => registry.createDirectFile(descriptor));
|
|
3399
|
+
if (handles.some((handle) => handle.mode !== expectedMode)) {
|
|
3400
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的 File mode 与 picker 请求不一致');
|
|
3401
|
+
}
|
|
3402
|
+
return handles;
|
|
3403
|
+
},
|
|
3404
|
+
async pickDirectory(options) {
|
|
3405
|
+
const normalizedOptions = normalizePickDirectoryOptions(options);
|
|
3406
|
+
const descriptor = await requester.request(FILES_PICK_DIRECTORY_METHOD, normalizedOptions);
|
|
3407
|
+
const handle = registry.createDirectDirectory(descriptor);
|
|
3408
|
+
if (handle.mode !== (normalizedOptions?.mode ?? 'read')) {
|
|
3409
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的 Directory mode 与 picker 请求不一致');
|
|
3410
|
+
}
|
|
3411
|
+
return handle;
|
|
3412
|
+
},
|
|
3413
|
+
async saveFile(options) {
|
|
3414
|
+
const descriptor = await requester.request(FILES_SAVE_FILE_METHOD, normalizeSaveFileOptions(options));
|
|
3415
|
+
const handle = registry.createDirectFile(descriptor);
|
|
3416
|
+
if (handle.mode !== 'readwrite') {
|
|
3417
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的 saveFile 不是 readwrite 授权');
|
|
3418
|
+
}
|
|
3419
|
+
return handle;
|
|
3420
|
+
},
|
|
3421
|
+
});
|
|
3422
|
+
}
|
|
3423
|
+
|
|
2543
3424
|
/**
|
|
2544
3425
|
* 获取当前小程序窗口信息。
|
|
2545
3426
|
*
|
|
2546
|
-
* @param requester 底层 bridge 请求能力。
|
|
3427
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2547
3428
|
* @returns 当前小程序可用窗口信息。
|
|
2548
|
-
* @throws {HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
3429
|
+
* @throws {@link HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2549
3430
|
*/
|
|
2550
3431
|
function getWindowInfo(requester) {
|
|
2551
3432
|
return requester.request(VIEWPORT_GET_WINDOW_INFO_METHOD);
|
|
@@ -2553,10 +3434,10 @@ function getWindowInfo(requester) {
|
|
|
2553
3434
|
/**
|
|
2554
3435
|
* 设置导航栏关闭按钮与状态栏图标/文字的前景样式。
|
|
2555
3436
|
*
|
|
2556
|
-
* @param requester 底层 bridge 请求能力。
|
|
2557
|
-
* @param options 导航栏样式配置。
|
|
3437
|
+
* @param requester - 底层 bridge 请求能力。
|
|
3438
|
+
* @param options - 导航栏样式配置。
|
|
2558
3439
|
* @returns 宿主接受设置后 resolve。
|
|
2559
|
-
* @throws {HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
3440
|
+
* @throws {@link HbMiniProgramSDKError} 当 bridge、父容器或宿主能力调用失败时抛出。
|
|
2560
3441
|
*/
|
|
2561
3442
|
function setNavigationBarStyle(requester, options) {
|
|
2562
3443
|
return requester.request(VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD, options);
|
|
@@ -2564,13 +3445,13 @@ function setNavigationBarStyle(requester, options) {
|
|
|
2564
3445
|
/**
|
|
2565
3446
|
* 创建 Viewport 模块。
|
|
2566
3447
|
*
|
|
2567
|
-
* @param requester 底层 bridge 请求能力。
|
|
3448
|
+
* @param requester - 底层 bridge 请求能力。
|
|
2568
3449
|
* @returns 面向业务层的 Viewport 模块对象。
|
|
2569
3450
|
*/
|
|
2570
3451
|
function createViewportModule(requester) {
|
|
2571
3452
|
return {
|
|
2572
3453
|
getWindowInfo: () => getWindowInfo(requester),
|
|
2573
|
-
setNavigationBarStyle: options => setNavigationBarStyle(requester, options),
|
|
3454
|
+
setNavigationBarStyle: (options) => setNavigationBarStyle(requester, options),
|
|
2574
3455
|
};
|
|
2575
3456
|
}
|
|
2576
3457
|
|
|
@@ -2583,7 +3464,7 @@ function createViewportModule(requester) {
|
|
|
2583
3464
|
* `app_user_id`,并隐式授予 `identity` 与 `profile` 后返回昵称与头像,不展示授权 UI。未登录时返回
|
|
2584
3465
|
* `{ isHeyboxAppLoggedIn: false, authorization: null, userInfo: null }`。
|
|
2585
3466
|
*
|
|
2586
|
-
* @throws {HbMiniProgramSDKError} 当 SDK 调用失败时抛出。
|
|
3467
|
+
* @throws {@link HbMiniProgramSDKError} 当 SDK 调用失败时抛出。
|
|
2587
3468
|
*/
|
|
2588
3469
|
function getInfo(requester) {
|
|
2589
3470
|
return requester.request(USER_GET_INFO_METHOD);
|
|
@@ -2596,8 +3477,8 @@ function revokeAuthorization(requester) {
|
|
|
2596
3477
|
/**
|
|
2597
3478
|
* 获取当前用户 Steam 游戏库。
|
|
2598
3479
|
*
|
|
2599
|
-
* @param requester 底层 bridge 请求能力。
|
|
2600
|
-
* @param options 查询与分页参数。
|
|
3480
|
+
* @param requester - 底层 bridge 请求能力。
|
|
3481
|
+
* @param options - 查询与分页参数。
|
|
2601
3482
|
* @returns 当前用户 Steam 游戏库;未登录或未绑定 Steam 时返回状态对象。
|
|
2602
3483
|
*/
|
|
2603
3484
|
function getSteamGameList(requester, options) {
|
|
@@ -2627,8 +3508,8 @@ function hideLoading(requester) {
|
|
|
2627
3508
|
/** 创建 UI 模块。 */
|
|
2628
3509
|
function createUiModule(requester) {
|
|
2629
3510
|
return {
|
|
2630
|
-
showToast: options => showToast(requester, options),
|
|
2631
|
-
showLoading: options => showLoading(requester, options),
|
|
3511
|
+
showToast: (options) => showToast(requester, options),
|
|
3512
|
+
showLoading: (options) => showLoading(requester, options),
|
|
2632
3513
|
hideLoading: () => hideLoading(requester),
|
|
2633
3514
|
};
|
|
2634
3515
|
}
|
|
@@ -2644,8 +3525,8 @@ function setClipboard(requester, options) {
|
|
|
2644
3525
|
/** 创建设备能力模块。 */
|
|
2645
3526
|
function createDeviceModule(requester) {
|
|
2646
3527
|
return {
|
|
2647
|
-
vibrate: options => vibrate(requester, options),
|
|
2648
|
-
setClipboard: options => setClipboard(requester, options),
|
|
3528
|
+
vibrate: (options) => vibrate(requester, options),
|
|
3529
|
+
setClipboard: (options) => setClipboard(requester, options),
|
|
2649
3530
|
};
|
|
2650
3531
|
}
|
|
2651
3532
|
|
|
@@ -2756,7 +3637,7 @@ function createNavigationModule(requester) {
|
|
|
2756
3637
|
return {
|
|
2757
3638
|
close: () => close(requester),
|
|
2758
3639
|
reload: () => reload(requester),
|
|
2759
|
-
openAppPage: options => openAppPage(requester, options),
|
|
3640
|
+
openAppPage: (options) => openAppPage(requester, options),
|
|
2760
3641
|
};
|
|
2761
3642
|
}
|
|
2762
3643
|
|
|
@@ -2786,6 +3667,8 @@ class MiniProgramSDK {
|
|
|
2786
3667
|
storage;
|
|
2787
3668
|
/** 网络请求相关开放能力。 */
|
|
2788
3669
|
network;
|
|
3670
|
+
/** 受控文件与目录能力。 */
|
|
3671
|
+
files;
|
|
2789
3672
|
/** UI 交互相关开放能力。 */
|
|
2790
3673
|
ui;
|
|
2791
3674
|
/** 设备相关开放能力。 */
|
|
@@ -2802,6 +3685,7 @@ class MiniProgramSDK {
|
|
|
2802
3685
|
this.share = createShareModule(this.client, launchHref);
|
|
2803
3686
|
this.viewport = createViewportModule(this.client);
|
|
2804
3687
|
this.storage = createStorageModule(this.client);
|
|
3688
|
+
this.files = createFilesModule(this.client);
|
|
2805
3689
|
this.network = createNetworkModule(this.client);
|
|
2806
3690
|
this.ui = createUiModule(this.client);
|
|
2807
3691
|
this.device = createDeviceModule(this.client);
|
|
@@ -2819,8 +3703,8 @@ class MiniProgramSDK {
|
|
|
2819
3703
|
/**
|
|
2820
3704
|
* 注册小程序生命周期或业务事件。
|
|
2821
3705
|
*
|
|
2822
|
-
* @param eventName 要监听的事件名。
|
|
2823
|
-
* @param handler 事件处理函数。
|
|
3706
|
+
* @param eventName - 要监听的事件名。
|
|
3707
|
+
* @param handler - 事件处理函数。
|
|
2824
3708
|
* @returns 事件解绑函数。
|
|
2825
3709
|
*/
|
|
2826
3710
|
on(eventName, handler) {
|
|
@@ -2829,8 +3713,8 @@ class MiniProgramSDK {
|
|
|
2829
3713
|
/**
|
|
2830
3714
|
* 移除小程序生命周期或业务事件。
|
|
2831
3715
|
*
|
|
2832
|
-
* @param eventName 要移除的事件名。
|
|
2833
|
-
* @param handler 对应的事件处理函数。
|
|
3716
|
+
* @param eventName - 要移除的事件名。
|
|
3717
|
+
* @param handler - 对应的事件处理函数。
|
|
2834
3718
|
*/
|
|
2835
3719
|
off(eventName, handler) {
|
|
2836
3720
|
this.client.off(eventName, handler);
|
|
@@ -2849,7 +3733,7 @@ class MiniProgramSDK {
|
|
|
2849
3733
|
/**
|
|
2850
3734
|
* 创建独立 SDK 实例。
|
|
2851
3735
|
*
|
|
2852
|
-
* @param options SDK 运行配置,例如超时时间、nonce 或测试环境注入的 window。
|
|
3736
|
+
* @param options - SDK 运行配置,例如超时时间、nonce 或测试环境注入的 window。
|
|
2853
3737
|
* @returns 可独立管理生命周期的 `MiniProgramSDK` 实例。
|
|
2854
3738
|
*
|
|
2855
3739
|
* @remarks
|
|
@@ -2875,20 +3759,59 @@ function getDefaultSDK() {
|
|
|
2875
3759
|
}
|
|
2876
3760
|
return defaultSDK;
|
|
2877
3761
|
}
|
|
2878
|
-
/**
|
|
3762
|
+
/**
|
|
3763
|
+
* 获取默认 SDK 实例的当前握手状态。
|
|
3764
|
+
*
|
|
3765
|
+
* @since 0.7.0-alpha.9
|
|
3766
|
+
* @hbPlatform android
|
|
3767
|
+
* @hbPlatform ios
|
|
3768
|
+
* @hbPlatform ohos
|
|
3769
|
+
* @hbPlatform windows
|
|
3770
|
+
* @hbPlatform macos
|
|
3771
|
+
* @hbClient android none
|
|
3772
|
+
* @hbClient ios none
|
|
3773
|
+
* @hbClient ohos none
|
|
3774
|
+
* @hbClient windows none
|
|
3775
|
+
* @hbClient macos none
|
|
3776
|
+
*/
|
|
2879
3777
|
function getHandshakeState() {
|
|
2880
3778
|
return getDefaultSDK().getHandshakeState();
|
|
2881
3779
|
}
|
|
2882
|
-
/**
|
|
3780
|
+
/**
|
|
3781
|
+
* 订阅默认 SDK 实例的握手状态,并立即接收当前状态。
|
|
3782
|
+
*
|
|
3783
|
+
* @since 0.7.0-alpha.9
|
|
3784
|
+
* @hbPlatform android
|
|
3785
|
+
* @hbPlatform ios
|
|
3786
|
+
* @hbPlatform ohos
|
|
3787
|
+
* @hbPlatform windows
|
|
3788
|
+
* @hbPlatform macos
|
|
3789
|
+
* @hbClient android none
|
|
3790
|
+
* @hbClient ios none
|
|
3791
|
+
* @hbClient ohos none
|
|
3792
|
+
* @hbClient windows none
|
|
3793
|
+
* @hbClient macos none
|
|
3794
|
+
*/
|
|
2883
3795
|
function onHandshakeStateChange(handler) {
|
|
2884
3796
|
return getDefaultSDK().onHandshakeStateChange(handler);
|
|
2885
3797
|
}
|
|
2886
3798
|
/**
|
|
2887
3799
|
* 注册默认 SDK 实例的事件监听。
|
|
2888
3800
|
*
|
|
2889
|
-
* @param eventName 要监听的事件名。
|
|
2890
|
-
* @param handler 事件处理函数。
|
|
3801
|
+
* @param eventName - 要监听的事件名。
|
|
3802
|
+
* @param handler - 事件处理函数。
|
|
2891
3803
|
* @returns 事件解绑函数。
|
|
3804
|
+
* @since 0.2.0-alpha.0
|
|
3805
|
+
* @hbPlatform android
|
|
3806
|
+
* @hbPlatform ios
|
|
3807
|
+
* @hbPlatform ohos
|
|
3808
|
+
* @hbPlatform windows
|
|
3809
|
+
* @hbPlatform macos
|
|
3810
|
+
* @hbClient android none
|
|
3811
|
+
* @hbClient ios none
|
|
3812
|
+
* @hbClient ohos none
|
|
3813
|
+
* @hbClient windows none
|
|
3814
|
+
* @hbClient macos none
|
|
2892
3815
|
*/
|
|
2893
3816
|
function on(eventName, handler) {
|
|
2894
3817
|
return getDefaultSDK().on(eventName, handler);
|
|
@@ -2896,9 +3819,20 @@ function on(eventName, handler) {
|
|
|
2896
3819
|
/**
|
|
2897
3820
|
* 移除默认 SDK 实例的事件监听。
|
|
2898
3821
|
*
|
|
2899
|
-
* @param eventName 要移除的事件名。
|
|
2900
|
-
* @param handler 对应的事件处理函数。
|
|
3822
|
+
* @param eventName - 要移除的事件名。
|
|
3823
|
+
* @param handler - 对应的事件处理函数。
|
|
2901
3824
|
* @returns 无返回值。
|
|
3825
|
+
* @since 0.2.0-alpha.0
|
|
3826
|
+
* @hbPlatform android
|
|
3827
|
+
* @hbPlatform ios
|
|
3828
|
+
* @hbPlatform ohos
|
|
3829
|
+
* @hbPlatform windows
|
|
3830
|
+
* @hbPlatform macos
|
|
3831
|
+
* @hbClient android none
|
|
3832
|
+
* @hbClient ios none
|
|
3833
|
+
* @hbClient ohos none
|
|
3834
|
+
* @hbClient windows none
|
|
3835
|
+
* @hbClient macos none
|
|
2902
3836
|
*/
|
|
2903
3837
|
function off(eventName, handler) {
|
|
2904
3838
|
getDefaultSDK().off(eventName, handler);
|
|
@@ -2933,6 +3867,16 @@ const storage = {
|
|
|
2933
3867
|
/** 默认 SDK 实例的 network 模块。 */
|
|
2934
3868
|
const network = {
|
|
2935
3869
|
request: (config) => getDefaultSDK().network.request(config),
|
|
3870
|
+
download: (options) => getDefaultSDK().network.download(options),
|
|
3871
|
+
};
|
|
3872
|
+
/** 默认 SDK 实例的 files 模块。 */
|
|
3873
|
+
const files = {
|
|
3874
|
+
get sandbox() {
|
|
3875
|
+
return getDefaultSDK().files.sandbox;
|
|
3876
|
+
},
|
|
3877
|
+
pickFiles: (options) => getDefaultSDK().files.pickFiles(options),
|
|
3878
|
+
pickDirectory: (options) => getDefaultSDK().files.pickDirectory(options),
|
|
3879
|
+
saveFile: (options) => getDefaultSDK().files.saveFile(options),
|
|
2936
3880
|
};
|
|
2937
3881
|
/** 默认 SDK 实例的 UI 模块。 */
|
|
2938
3882
|
const ui = {
|
|
@@ -2972,6 +3916,7 @@ const hbSDK = {
|
|
|
2972
3916
|
share,
|
|
2973
3917
|
viewport,
|
|
2974
3918
|
storage,
|
|
3919
|
+
files,
|
|
2975
3920
|
network,
|
|
2976
3921
|
ui,
|
|
2977
3922
|
device,
|
|
@@ -2986,6 +3931,7 @@ exports.auth = auth;
|
|
|
2986
3931
|
exports.cloud = cloud;
|
|
2987
3932
|
exports.default = hbSDK;
|
|
2988
3933
|
exports.device = device;
|
|
3934
|
+
exports.files = files;
|
|
2989
3935
|
exports.getHandshakeState = getHandshakeState;
|
|
2990
3936
|
exports.navigation = navigation;
|
|
2991
3937
|
exports.network = network;
|