@heybox/hb-sdk 0.9.3-alpha.6 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/README.md +1 -0
- package/dist/cli-chunks/{build-B1YssPNl.cjs → build-DrFbb0V-.cjs} +5 -5
- package/dist/cli-chunks/{context-Deob2IZ5.cjs → context-CTlwLbwn.cjs} +2 -2
- package/dist/cli-chunks/{create-BWzY1CVT.cjs → create-Ccse2SW7.cjs} +1 -1
- package/dist/cli-chunks/{deploy-lock-ClcPcaSW.cjs → deploy-lock-DaGpgsU7.cjs} +1 -1
- package/dist/cli-chunks/{dev-wVCyeUOb.cjs → dev-BHsCFm4O.cjs} +8 -8
- package/dist/cli-chunks/{doctor-DHd5eYBr.cjs → doctor-BhTTMupn.cjs} +1 -1
- package/dist/cli-chunks/{games-DNhP1Om4.cjs → games-BOP_ZDp6.cjs} +2 -2
- package/dist/cli-chunks/{index-DfYr8g3I.cjs → index-CF6qh9ll.cjs} +17 -17
- package/dist/cli-chunks/{index-BOd8ouDY.cjs → index-DMall69X.cjs} +1 -1
- package/dist/cli-chunks/{index.esm-PlHUr780.cjs → index.esm-Cau47g22.cjs} +6 -6
- package/dist/cli-chunks/{login-D-iG6Um2.cjs → login-BGDovscU.cjs} +1 -1
- package/dist/cli-chunks/{project-vite-DyKHpc6h.cjs → project-vite-Dnev8Z34.cjs} +1 -1
- package/dist/cli-chunks/{remote-CyynHOcj.cjs → remote--MDcbz4m.cjs} +7 -7
- package/dist/cli-chunks/{runtime-gate-DPXnKdQ4.cjs → runtime-gate-BgWB2U5f.cjs} +2 -2
- package/dist/cli-chunks/{runtime-permission-env-BjO2KzGl.cjs → runtime-permission-env-DmJ1zEpa.cjs} +13 -2
- package/dist/cli-chunks/{skill-BOLOEDrj.cjs → skill-Bm1MmMN5.cjs} +2 -2
- package/dist/cli-chunks/{version-DX2glIk7.cjs → version-C6b3tkax.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-BHtf4WWG.js +102 -0
- package/dist/devtools/browser-dev-host/assets/desktop-app-launch-Dl4RfM9z.js +6 -0
- package/dist/devtools/browser-dev-host/assets/{index-Br7UpHGt.js → index-DFLYLAXj.js} +4 -4
- package/dist/devtools/browser-dev-host/assets/{index-7Cq8iNcD.css → index-h-fYisyX.css} +1 -1
- package/dist/devtools/browser-dev-host/index.html +3 -3
- package/dist/index.cjs.js +314 -28
- package/dist/index.esm.js +314 -29
- package/dist/miniapp-publish.cjs.js +11 -0
- package/dist/miniapp-publish.esm.js +11 -0
- package/dist/protocol.cjs.js +172 -0
- package/dist/protocol.esm.js +169 -1
- package/dist/vite.cjs.js +17 -3
- package/dist/vite.esm.js +17 -3
- package/package.json +7 -7
- package/skill/skill.json +4 -4
- package/types/index.d.ts +80 -2
- package/types/miniapp-publish/index.d.ts +1 -1
- package/types/protocol.d.ts +77 -2
- package/dist/devtools/browser-dev-host/assets/browser-dev-host-CD6Ado2B.js +0 -102
- package/dist/devtools/browser-dev-host/assets/desktop-app-launch-CFl6m8k-.js +0 -6
package/dist/index.cjs.js
CHANGED
|
@@ -444,6 +444,7 @@ const DIRECTORY_CREATE_METHOD = 'directory.create';
|
|
|
444
444
|
const DIRECTORY_REMOVE_METHOD = 'directory.remove';
|
|
445
445
|
const DIRECTORY_EXISTS_METHOD = 'directory.exists';
|
|
446
446
|
const DIRECTORY_LIST_METHOD = 'directory.list';
|
|
447
|
+
const MEDIA_PICK_METHOD = 'media.pick';
|
|
447
448
|
const UI_SHOW_TOAST_METHOD = 'ui.showToast';
|
|
448
449
|
const UI_SHOW_LOADING_METHOD = 'ui.showLoading';
|
|
449
450
|
const UI_HIDE_LOADING_METHOD = 'ui.hideLoading';
|
|
@@ -841,6 +842,184 @@ function isMiniProgramDirectoryRef(value) {
|
|
|
841
842
|
}
|
|
842
843
|
return (isMiniProgramPathRootRef(value.root) && (value.relativePath === '.' || isMiniProgramRelativePath(value.relativePath)) && hasOnlyKeys(value, ['kind', 'root', 'relativePath']));
|
|
843
844
|
}
|
|
845
|
+
/**
|
|
846
|
+
* 判断未知值是否具有文件 descriptor 的最低 wire 结构。
|
|
847
|
+
*
|
|
848
|
+
* @param value - 来自 Host、bridge response、目录列表或其他不可信边界的候选 descriptor。
|
|
849
|
+
* @returns 顶层字段及嵌套文件引用都满足协议结构时返回 `true`,并将 TypeScript 类型收窄为
|
|
850
|
+
* {@link MiniProgramFileDescriptor};否则返回 `false`。
|
|
851
|
+
*
|
|
852
|
+
* @remarks
|
|
853
|
+
* guard 会读取并校验以下四个属性;对通常的对象字面量,它们也是唯一允许的可枚举自有字符串字段:
|
|
854
|
+
*
|
|
855
|
+
* - `kind`:固定为 `'file'`。
|
|
856
|
+
* - `name`:非空字符串。该 guard 不检查空白、`.`、`..`、`/`、反斜杠、控制字符、平台保留名或
|
|
857
|
+
* `name` 是否等于 `ref.relativePath` 的 basename;SDK 创建 `FileHandle` 时还会执行更严格的名称校验。
|
|
858
|
+
* - `mode`:只能是 `'read'` 或 `'readwrite'`。这是 descriptor 声明值,不证明 Host 实际授予了读写权限。
|
|
859
|
+
* - `ref`:必须通过 {@link isMiniProgramFileRef}。
|
|
860
|
+
*
|
|
861
|
+
* `ref` 可以是 `{ kind: 'file', handleId }` 形式的 direct file ref,其中 `handleId` 只要求非空;也可以是
|
|
862
|
+
* `{ kind: 'file', root, relativePath }` 形式的 derived ref。derived root 只能是精确的 sandbox root 或
|
|
863
|
+
* 带非空 `handleId` 的 direct directory root,不能是 file ref 或再次派生的 directory ref;`relativePath`
|
|
864
|
+
* 必须是非空、非绝对、无尾随 `/`、无空段、`.` / `..` 点段、反斜杠或控制字符的相对路径。与目录 ref
|
|
865
|
+
* 不同,文件 ref 不接受 `relativePath: '.'`,因为它必须指向 root 下的具体文件。
|
|
866
|
+
*
|
|
867
|
+
* 顶层 descriptor、`ref` 与 `root` 的额外可枚举自有字符串键都会导致失败,包括值为 `undefined` 的额外键;
|
|
868
|
+
* 非枚举属性、symbol 键和继承属性不在 `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举
|
|
869
|
+
* 属性。该函数不会复制、冻结或规范化输入。
|
|
870
|
+
*
|
|
871
|
+
* 返回 `true` 只建立结构类型收窄,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、属于当前
|
|
872
|
+
* session/origin、仍然有效且确为预期类型,也不证明文件存在、名称真实、`filesystem` 权限已启用、mode
|
|
873
|
+
* 允许当前操作或 Host 支持对应 capability。实际文件操作仍须由 Runtime 查询 handle registry、执行权限、
|
|
874
|
+
* provenance、目标类型和存在性校验。
|
|
875
|
+
*
|
|
876
|
+
* {@link isMiniProgramFileSystemEntityDescriptor} 使用本 guard 与目录 descriptor guard 识别目录列表项。
|
|
877
|
+
* `files.pickFiles()` 与 `files.saveFile()` 的成功结果要求更窄的 direct file ref,而 sandbox/path API 返回的
|
|
878
|
+
* descriptor 可以使用 derived ref;本 guard 本身不会区分 descriptor 的来源。
|
|
879
|
+
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```ts
|
|
882
|
+
* import { isMiniProgramFileDescriptor } from '@heybox/hb-sdk/protocol';
|
|
883
|
+
*
|
|
884
|
+
* const candidate = {
|
|
885
|
+
* kind: 'file',
|
|
886
|
+
* name: 'report.json',
|
|
887
|
+
* mode: 'readwrite',
|
|
888
|
+
* ref: {
|
|
889
|
+
* kind: 'file',
|
|
890
|
+
* root: { kind: 'sandbox' },
|
|
891
|
+
* relativePath: 'exports/report.json',
|
|
892
|
+
* },
|
|
893
|
+
* };
|
|
894
|
+
* const untrusted: unknown = candidate;
|
|
895
|
+
*
|
|
896
|
+
* if (isMiniProgramFileDescriptor(untrusted)) {
|
|
897
|
+
* // 这里只能安全读取 wire 字段;文件 I/O 仍交给 files API/Runtime。
|
|
898
|
+
* console.log(untrusted.name, untrusted.mode, untrusted.ref);
|
|
899
|
+
* }
|
|
900
|
+
*
|
|
901
|
+
* isMiniProgramFileDescriptor({ ...candidate, size: 1024 }); // false:顶层存在额外键
|
|
902
|
+
* ```
|
|
903
|
+
*
|
|
904
|
+
* @see [MiniProgramFileDescriptor](/reference/symbols/protocol/interfaces/MiniProgramFileDescriptor) descriptor 字段类型。
|
|
905
|
+
* @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 嵌套 direct/derived 文件引用校验。
|
|
906
|
+
* @see [isMiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileSystemEntityDescriptor) 文件与目录 descriptor 联合 guard。
|
|
907
|
+
* @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 校验后由 SDK 建立的文件代理。
|
|
908
|
+
* @see [PickFilesResult](/reference/symbols/protocol/types/PickFilesResult) picker 的 direct-ref 文件结果。
|
|
909
|
+
* @see [SaveFileResult](/reference/symbols/protocol/types/SaveFileResult) save picker 的 direct-ref 文件结果。
|
|
910
|
+
* @since 0.8.0
|
|
911
|
+
*/
|
|
912
|
+
function isMiniProgramFileDescriptor(value) {
|
|
913
|
+
return (isRecord$3(value) &&
|
|
914
|
+
value.kind === 'file' &&
|
|
915
|
+
isNonEmptyString$1(value.name) &&
|
|
916
|
+
isFileMode(value.mode) &&
|
|
917
|
+
isMiniProgramFileRef(value.ref) &&
|
|
918
|
+
hasOnlyKeys(value, ['kind', 'name', 'mode', 'ref']));
|
|
919
|
+
}
|
|
920
|
+
/** `media.pick` 结果数组允许的最大项数,与协议 `maxCount` 上限一致。 */
|
|
921
|
+
const MAX_MEDIA_PICK_COUNT = 20;
|
|
922
|
+
/** `media.pick` 单项结果的公共 canonical key 集合。 */
|
|
923
|
+
const MEDIA_PICK_ITEM_KEYS = ['type', 'mimeType', 'size', 'width', 'height', 'previewUrl', 'file'];
|
|
924
|
+
/**
|
|
925
|
+
* 判断未知值是否为单个 `media.pick` 媒体项。
|
|
926
|
+
*
|
|
927
|
+
* @param value - 来自 Host response、bridge result 或其他不可信边界的候选媒体项。
|
|
928
|
+
* @returns 字段组合满足 {@link MediaPickItem} 判别联合的最低结构时返回 `true`,并将 TypeScript 类型
|
|
929
|
+
* 收窄为图片或视频项;否则返回 `false`。
|
|
930
|
+
*
|
|
931
|
+
* @remarks
|
|
932
|
+
* 以 `type` 为判别字段,两个分支都要求 `mimeType` 非空字符串、`size` 为非负安全整数、`width` /
|
|
933
|
+
* `height` 为正安全整数、`previewUrl` 非空字符串,且 `file` 是通过
|
|
934
|
+
* {@link isMiniProgramFileDescriptor} 的 direct file ref(`ref.kind === 'file'` 且带非空 `handleId`),
|
|
935
|
+
* `mode` 必须固定为 `'read'`。图片分支只允许公共 key;视频分支额外要求非负安全整数的 `durationMs`,
|
|
936
|
+
* 并拒绝图片携带该字段。
|
|
937
|
+
*
|
|
938
|
+
* `size` 单位为字节,`width` / `height` 是考虑方向后的显示像素尺寸,`durationMs` 单位为毫秒。
|
|
939
|
+
* 该 guard 只校验结构与数值范围,不证明元数据与 `file` 实际内容一致、`previewUrl` 属于当前会话、
|
|
940
|
+
* `file` 句柄由当前 Runtime 签发或仍在有效期内,也不检查 `media` 权限与 Host 能力。
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* ```ts
|
|
944
|
+
* import { isMiniProgramMediaPickItem } from '@heybox/hb-sdk/protocol';
|
|
945
|
+
*
|
|
946
|
+
* const video = {
|
|
947
|
+
* type: 'video',
|
|
948
|
+
* mimeType: 'video/mp4',
|
|
949
|
+
* size: 5_242_880,
|
|
950
|
+
* width: 1280,
|
|
951
|
+
* height: 720,
|
|
952
|
+
* durationMs: 12_000,
|
|
953
|
+
* previewUrl: 'hb-media-preview://session/resource_2',
|
|
954
|
+
* file: { kind: 'file', name: 'clip.mp4', mode: 'read', ref: { kind: 'file', handleId: 'media_handle_2' } },
|
|
955
|
+
* };
|
|
956
|
+
*
|
|
957
|
+
* isMiniProgramMediaPickItem(video); // true
|
|
958
|
+
* isMiniProgramMediaPickItem({ ...video, type: 'image' }); // false:图片不允许 durationMs
|
|
959
|
+
* ```
|
|
960
|
+
*
|
|
961
|
+
* @see [MediaPickItem](/reference/symbols/protocol/types/MediaPickItem) 判别联合定义。
|
|
962
|
+
* @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) 嵌套文件句柄校验。
|
|
963
|
+
* @since 0.9.3
|
|
964
|
+
*/
|
|
965
|
+
function isMiniProgramMediaPickItem(value) {
|
|
966
|
+
if (!isRecord$3(value) || (value.type !== 'image' && value.type !== 'video'))
|
|
967
|
+
return false;
|
|
968
|
+
const isVideo = value.type === 'video';
|
|
969
|
+
if (!hasOnlyKeys(value, isVideo ? [...MEDIA_PICK_ITEM_KEYS, 'durationMs'] : MEDIA_PICK_ITEM_KEYS))
|
|
970
|
+
return false;
|
|
971
|
+
if (!isNonEmptyString$1(value.mimeType) ||
|
|
972
|
+
!isNonNegativeSafeInteger(value.size) ||
|
|
973
|
+
!isPositiveSafeInteger$1(value.width) ||
|
|
974
|
+
!isPositiveSafeInteger$1(value.height) ||
|
|
975
|
+
!isNonEmptyString$1(value.previewUrl) ||
|
|
976
|
+
!isDirectFileDescriptor(value.file)) {
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
return isVideo ? isNonNegativeSafeInteger(value.durationMs) : true;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* 判断未知值是否为整批 `media.pick` 结果。
|
|
983
|
+
*
|
|
984
|
+
* @param value - 来自 Host response、bridge result 或其他不可信边界的候选结果。
|
|
985
|
+
* @returns 值为 1–20 项且每项都通过 {@link isMiniProgramMediaPickItem} 的稠密数组时返回 `true`,
|
|
986
|
+
* 并将 TypeScript 类型收窄为 {@link MediaPickResult};否则返回 `false`。
|
|
987
|
+
*
|
|
988
|
+
* @remarks
|
|
989
|
+
* 成功结果必须是非空数组:用户取消或任一项准备失败时走整批错误,不得返回空数组或部分结果。
|
|
990
|
+
* 本 guard 只校验全局上限 20,不校验调用方本次请求的 `maxCount`、相机必须恰好 1 项、项之间是否
|
|
991
|
+
* 重复或顺序,也不确认结果来自当前 request 或会话仍然有效。这些约束由 Runtime 的状态机、可信手势、
|
|
992
|
+
* 权限与句柄注册表继续执行。
|
|
993
|
+
*
|
|
994
|
+
* @example
|
|
995
|
+
* ```ts
|
|
996
|
+
* import { isMiniProgramMediaPickResult } from '@heybox/hb-sdk/protocol';
|
|
997
|
+
*
|
|
998
|
+
* isMiniProgramMediaPickResult([]); // false:取消不是空数组成功
|
|
999
|
+
* isMiniProgramMediaPickResult([imageItem]); // true
|
|
1000
|
+
* ```
|
|
1001
|
+
*
|
|
1002
|
+
* @see [MediaPickResult](/reference/symbols/protocol/types/MediaPickResult) wire 返回类型。
|
|
1003
|
+
* @see [isMiniProgramMediaPickItem](/reference/symbols/protocol/functions/isMiniProgramMediaPickItem) 单项结构与数值校验。
|
|
1004
|
+
* @since 0.9.3
|
|
1005
|
+
*/
|
|
1006
|
+
function isMiniProgramMediaPickResult(value) {
|
|
1007
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > MAX_MEDIA_PICK_COUNT)
|
|
1008
|
+
return false;
|
|
1009
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1010
|
+
if (!Object.prototype.hasOwnProperty.call(value, index) || !isMiniProgramMediaPickItem(value[index]))
|
|
1011
|
+
return false;
|
|
1012
|
+
}
|
|
1013
|
+
return true;
|
|
1014
|
+
}
|
|
1015
|
+
function isDirectFileDescriptor(value) {
|
|
1016
|
+
if (!isMiniProgramFileDescriptor(value))
|
|
1017
|
+
return false;
|
|
1018
|
+
// 媒体结果只允许只读 direct 句柄;derived ref、readwrite 与目录引用都不符合媒体契约。
|
|
1019
|
+
if (value.mode !== 'read')
|
|
1020
|
+
return false;
|
|
1021
|
+
return value.ref.kind === 'file' && 'handleId' in value.ref && isNonEmptyString$1(value.ref.handleId);
|
|
1022
|
+
}
|
|
844
1023
|
/**
|
|
845
1024
|
* 判断未知值是否为使用普通 `ArrayBuffer` 存储的 `Uint8Array` 字节视图。
|
|
846
1025
|
*
|
|
@@ -1050,9 +1229,15 @@ function isSDKHandshakePayload(value) {
|
|
|
1050
1229
|
function isBridgeError(value) {
|
|
1051
1230
|
return isRecord$3(value) && isNonEmptyString$1(value.code) && typeof value.message === 'string';
|
|
1052
1231
|
}
|
|
1232
|
+
function isFileMode(value) {
|
|
1233
|
+
return value === 'read' || value === 'readwrite';
|
|
1234
|
+
}
|
|
1053
1235
|
function isNonNegativeSafeInteger(value) {
|
|
1054
1236
|
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
1055
1237
|
}
|
|
1238
|
+
function isPositiveSafeInteger$1(value) {
|
|
1239
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
|
|
1240
|
+
}
|
|
1056
1241
|
function isNonNegativeFiniteNumber(value) {
|
|
1057
1242
|
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
1058
1243
|
}
|
|
@@ -1074,6 +1259,7 @@ const MINI_PROGRAM_PERMISSION_KEYS = [
|
|
|
1074
1259
|
'storage',
|
|
1075
1260
|
'cloud_storage',
|
|
1076
1261
|
'filesystem',
|
|
1262
|
+
'media',
|
|
1077
1263
|
'clipboard',
|
|
1078
1264
|
'leaderboard',
|
|
1079
1265
|
'network',
|
|
@@ -7299,7 +7485,7 @@ function createMessageId() {
|
|
|
7299
7485
|
/** 构建时替换为当前发布包的实际版本。 */
|
|
7300
7486
|
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
7301
7487
|
? undefined
|
|
7302
|
-
: '0.9.3
|
|
7488
|
+
: '0.9.3';
|
|
7303
7489
|
|
|
7304
7490
|
const RUNTIME_MODES = new Set(['production', 'preview', 'development', 'unknown']);
|
|
7305
7491
|
const OPERATING_SYSTEM_NAMES = new Set(['android', 'ios', 'ohos', 'windows', 'macos', 'linux', 'unknown']);
|
|
@@ -7920,7 +8106,7 @@ function createCloudModule(requester) {
|
|
|
7920
8106
|
const registries = new WeakMap();
|
|
7921
8107
|
const brandedFileSystemValues = new WeakSet();
|
|
7922
8108
|
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
7923
|
-
function invalidParams$
|
|
8109
|
+
function invalidParams$2(message, data) {
|
|
7924
8110
|
return createSDKError('INVALID_PARAMS', message, data);
|
|
7925
8111
|
}
|
|
7926
8112
|
function clonePathRootRef(ref) {
|
|
@@ -7995,13 +8181,13 @@ function assertSandboxRef(ref) {
|
|
|
7995
8181
|
}
|
|
7996
8182
|
function normalizeCreateOptions(options) {
|
|
7997
8183
|
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
7998
|
-
throw invalidParams$
|
|
8184
|
+
throw invalidParams$2('create options 必须是对象');
|
|
7999
8185
|
}
|
|
8000
8186
|
if (options?.recursive !== undefined && typeof options.recursive !== 'boolean') {
|
|
8001
|
-
throw invalidParams$
|
|
8187
|
+
throw invalidParams$2('recursive 必须是 boolean');
|
|
8002
8188
|
}
|
|
8003
8189
|
if (options?.exclusive !== undefined && typeof options.exclusive !== 'boolean') {
|
|
8004
|
-
throw invalidParams$
|
|
8190
|
+
throw invalidParams$2('exclusive 必须是 boolean');
|
|
8005
8191
|
}
|
|
8006
8192
|
return {
|
|
8007
8193
|
recursive: options?.recursive ?? false,
|
|
@@ -8010,28 +8196,28 @@ function normalizeCreateOptions(options) {
|
|
|
8010
8196
|
}
|
|
8011
8197
|
function normalizeListOptions(options) {
|
|
8012
8198
|
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
8013
|
-
throw invalidParams$
|
|
8199
|
+
throw invalidParams$2('list options 必须是对象');
|
|
8014
8200
|
}
|
|
8015
8201
|
if (options?.limit !== undefined && (!Number.isSafeInteger(options.limit) || options.limit <= 0)) {
|
|
8016
|
-
throw invalidParams$
|
|
8202
|
+
throw invalidParams$2('limit 必须是正的 safe integer');
|
|
8017
8203
|
}
|
|
8018
8204
|
return options?.limit;
|
|
8019
8205
|
}
|
|
8020
8206
|
function normalizeRemoveOptions(options) {
|
|
8021
8207
|
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
8022
|
-
throw invalidParams$
|
|
8208
|
+
throw invalidParams$2('remove options 必须是对象');
|
|
8023
8209
|
}
|
|
8024
8210
|
if (options?.recursive !== undefined && typeof options.recursive !== 'boolean') {
|
|
8025
|
-
throw invalidParams$
|
|
8211
|
+
throw invalidParams$2('recursive 必须是 boolean');
|
|
8026
8212
|
}
|
|
8027
8213
|
return { recursive: options?.recursive ?? false };
|
|
8028
8214
|
}
|
|
8029
8215
|
function cloneBytes$1(content) {
|
|
8030
8216
|
if (!(content instanceof Uint8Array)) {
|
|
8031
|
-
throw invalidParams$
|
|
8217
|
+
throw invalidParams$2('content 必须是 Uint8Array');
|
|
8032
8218
|
}
|
|
8033
8219
|
if (typeof SharedArrayBuffer !== 'undefined' && content.buffer instanceof SharedArrayBuffer) {
|
|
8034
|
-
throw invalidParams$
|
|
8220
|
+
throw invalidParams$2('content 不能由 SharedArrayBuffer 承载');
|
|
8035
8221
|
}
|
|
8036
8222
|
return new Uint8Array(content);
|
|
8037
8223
|
}
|
|
@@ -8049,7 +8235,7 @@ function normalizeStat(stat) {
|
|
|
8049
8235
|
/** 校验并返回平台无关的 `/` 分隔逻辑相对路径。 */
|
|
8050
8236
|
function normalizeRelativePath(value) {
|
|
8051
8237
|
if (!isMiniProgramRelativePath(value)) {
|
|
8052
|
-
throw invalidParams$
|
|
8238
|
+
throw invalidParams$2('relativePath 必须是规范的 `/` 分隔逻辑相对路径');
|
|
8053
8239
|
}
|
|
8054
8240
|
return value;
|
|
8055
8241
|
}
|
|
@@ -8116,7 +8302,7 @@ class MiniProgramFileHandleRegistry {
|
|
|
8116
8302
|
defineReadonlyValue(handle, 'writeText', async (content) => {
|
|
8117
8303
|
assertWritable(state.mode);
|
|
8118
8304
|
if (typeof content !== 'string') {
|
|
8119
|
-
throw invalidParams$
|
|
8305
|
+
throw invalidParams$2('content 必须是 string');
|
|
8120
8306
|
}
|
|
8121
8307
|
await this.requester.request(FILE_WRITE_TEXT_METHOD, { target: state.ref, content });
|
|
8122
8308
|
}, false);
|
|
@@ -8218,14 +8404,14 @@ class MiniProgramFileHandleRegistry {
|
|
|
8218
8404
|
requireFile(handle) {
|
|
8219
8405
|
const state = handle && typeof handle === 'object' ? this.fileStates.get(handle) : undefined;
|
|
8220
8406
|
if (!state) {
|
|
8221
|
-
throw invalidParams$
|
|
8407
|
+
throw invalidParams$2('to 必须是当前 SDK session 创建的 FileHandle');
|
|
8222
8408
|
}
|
|
8223
8409
|
return state;
|
|
8224
8410
|
}
|
|
8225
8411
|
requireDirectory(handle) {
|
|
8226
8412
|
const state = handle && typeof handle === 'object' ? this.directoryStates.get(handle) : undefined;
|
|
8227
8413
|
if (!state) {
|
|
8228
|
-
throw invalidParams$
|
|
8414
|
+
throw invalidParams$2('to 必须是当前 SDK session 创建的 DirectoryHandle');
|
|
8229
8415
|
}
|
|
8230
8416
|
return state;
|
|
8231
8417
|
}
|
|
@@ -10863,7 +11049,7 @@ async function upload(requester, options) {
|
|
|
10863
11049
|
return response;
|
|
10864
11050
|
}
|
|
10865
11051
|
|
|
10866
|
-
function invalidParams(message) {
|
|
11052
|
+
function invalidParams$1(message) {
|
|
10867
11053
|
return createSDKError('INVALID_PARAMS', message);
|
|
10868
11054
|
}
|
|
10869
11055
|
function normalizeMode(mode) {
|
|
@@ -10871,13 +11057,13 @@ function normalizeMode(mode) {
|
|
|
10871
11057
|
return undefined;
|
|
10872
11058
|
}
|
|
10873
11059
|
if (mode !== 'read' && mode !== 'readwrite') {
|
|
10874
|
-
throw invalidParams('mode 必须是 read 或 readwrite');
|
|
11060
|
+
throw invalidParams$1('mode 必须是 read 或 readwrite');
|
|
10875
11061
|
}
|
|
10876
11062
|
return mode;
|
|
10877
11063
|
}
|
|
10878
|
-
function assertOptionsObject(value, name) {
|
|
11064
|
+
function assertOptionsObject$1(value, name) {
|
|
10879
11065
|
if (value !== undefined && (!value || typeof value !== 'object' || Array.isArray(value))) {
|
|
10880
|
-
throw invalidParams(`${name} 必须是对象`);
|
|
11066
|
+
throw invalidParams$1(`${name} 必须是对象`);
|
|
10881
11067
|
}
|
|
10882
11068
|
}
|
|
10883
11069
|
function normalizeAccept(accept) {
|
|
@@ -10885,20 +11071,20 @@ function normalizeAccept(accept) {
|
|
|
10885
11071
|
return undefined;
|
|
10886
11072
|
}
|
|
10887
11073
|
if (!Array.isArray(accept)) {
|
|
10888
|
-
throw invalidParams('accept 必须是扩展名数组');
|
|
11074
|
+
throw invalidParams$1('accept 必须是扩展名数组');
|
|
10889
11075
|
}
|
|
10890
11076
|
return accept.map((extension) => {
|
|
10891
11077
|
if (typeof extension !== 'string' ||
|
|
10892
11078
|
!/^\.[a-z0-9][a-z0-9._-]*$/i.test(extension)) {
|
|
10893
|
-
throw invalidParams('accept 只能包含规范扩展名,例如 .json');
|
|
11079
|
+
throw invalidParams$1('accept 只能包含规范扩展名,例如 .json');
|
|
10894
11080
|
}
|
|
10895
11081
|
return extension;
|
|
10896
11082
|
});
|
|
10897
11083
|
}
|
|
10898
11084
|
function normalizePickFilesOptions(options) {
|
|
10899
|
-
assertOptionsObject(options, 'pickFiles options');
|
|
11085
|
+
assertOptionsObject$1(options, 'pickFiles options');
|
|
10900
11086
|
if (options?.multiple !== undefined && typeof options.multiple !== 'boolean') {
|
|
10901
|
-
throw invalidParams('multiple 必须是 boolean');
|
|
11087
|
+
throw invalidParams$1('multiple 必须是 boolean');
|
|
10902
11088
|
}
|
|
10903
11089
|
const mode = normalizeMode(options?.mode);
|
|
10904
11090
|
const accept = normalizeAccept(options?.accept);
|
|
@@ -10912,21 +11098,21 @@ function normalizePickFilesOptions(options) {
|
|
|
10912
11098
|
};
|
|
10913
11099
|
}
|
|
10914
11100
|
function normalizePickDirectoryOptions(options) {
|
|
10915
|
-
assertOptionsObject(options, 'pickDirectory options');
|
|
11101
|
+
assertOptionsObject$1(options, 'pickDirectory options');
|
|
10916
11102
|
const mode = normalizeMode(options?.mode);
|
|
10917
11103
|
return options === undefined ? undefined : mode === undefined ? {} : { mode };
|
|
10918
11104
|
}
|
|
10919
11105
|
function normalizeSaveFileOptions(options) {
|
|
10920
|
-
assertOptionsObject(options, 'saveFile options');
|
|
11106
|
+
assertOptionsObject$1(options, 'saveFile options');
|
|
10921
11107
|
if (!options || typeof options.suggestedName !== 'string') {
|
|
10922
|
-
throw invalidParams('suggestedName 必须是非空字符串');
|
|
11108
|
+
throw invalidParams$1('suggestedName 必须是非空字符串');
|
|
10923
11109
|
}
|
|
10924
11110
|
const suggestedName = normalizeRelativePath(options.suggestedName);
|
|
10925
11111
|
if (suggestedName.includes('/')) {
|
|
10926
|
-
throw invalidParams('suggestedName 必须是 basename');
|
|
11112
|
+
throw invalidParams$1('suggestedName 必须是 basename');
|
|
10927
11113
|
}
|
|
10928
11114
|
if ('accept' in options) {
|
|
10929
|
-
throw invalidParams('saveFile V1 不支持 accept');
|
|
11115
|
+
throw invalidParams$1('saveFile V1 不支持 accept');
|
|
10930
11116
|
}
|
|
10931
11117
|
return { suggestedName };
|
|
10932
11118
|
}
|
|
@@ -10968,6 +11154,97 @@ function createFilesModule(requester) {
|
|
|
10968
11154
|
});
|
|
10969
11155
|
}
|
|
10970
11156
|
|
|
11157
|
+
const MEDIA_SOURCES = ['album', 'camera'];
|
|
11158
|
+
const MEDIA_SELECTION_TYPES = ['image', 'video', 'all'];
|
|
11159
|
+
const MEDIA_PICK_MAX_COUNT = 20;
|
|
11160
|
+
const MEDIA_PICK_OPTION_KEYS = ['source', 'mediaType', 'maxCount'];
|
|
11161
|
+
function invalidParams(message) {
|
|
11162
|
+
return createSDKError('INVALID_PARAMS', message);
|
|
11163
|
+
}
|
|
11164
|
+
function assertOptionsObject(value) {
|
|
11165
|
+
if (value !== undefined && (!value || typeof value !== 'object' || Array.isArray(value))) {
|
|
11166
|
+
throw invalidParams('pick options 必须是对象');
|
|
11167
|
+
}
|
|
11168
|
+
}
|
|
11169
|
+
function assertKnownKeys(options) {
|
|
11170
|
+
for (const key of Object.keys(options)) {
|
|
11171
|
+
if (!MEDIA_PICK_OPTION_KEYS.includes(key)) {
|
|
11172
|
+
throw invalidParams(`pick options 包含未知字段 ${key}`);
|
|
11173
|
+
}
|
|
11174
|
+
}
|
|
11175
|
+
}
|
|
11176
|
+
function normalizeSource(value) {
|
|
11177
|
+
if (value === undefined) {
|
|
11178
|
+
return 'album';
|
|
11179
|
+
}
|
|
11180
|
+
if (typeof value !== 'string' || !MEDIA_SOURCES.includes(value)) {
|
|
11181
|
+
throw invalidParams('source 必须是 album 或 camera');
|
|
11182
|
+
}
|
|
11183
|
+
return value;
|
|
11184
|
+
}
|
|
11185
|
+
function normalizeSelectionType(value) {
|
|
11186
|
+
if (value === undefined) {
|
|
11187
|
+
return 'all';
|
|
11188
|
+
}
|
|
11189
|
+
if (typeof value !== 'string' || !MEDIA_SELECTION_TYPES.includes(value)) {
|
|
11190
|
+
throw invalidParams('mediaType 必须是 image、video 或 all');
|
|
11191
|
+
}
|
|
11192
|
+
return value;
|
|
11193
|
+
}
|
|
11194
|
+
function normalizeMaxCount(value, source) {
|
|
11195
|
+
if (value === undefined) {
|
|
11196
|
+
return 1;
|
|
11197
|
+
}
|
|
11198
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 1 || value > MEDIA_PICK_MAX_COUNT) {
|
|
11199
|
+
throw invalidParams(`maxCount 必须是 1 到 ${MEDIA_PICK_MAX_COUNT} 的安全整数`);
|
|
11200
|
+
}
|
|
11201
|
+
if (source === 'camera' && value > 1) {
|
|
11202
|
+
throw invalidParams('camera 的 maxCount 只能是 1');
|
|
11203
|
+
}
|
|
11204
|
+
return value;
|
|
11205
|
+
}
|
|
11206
|
+
/** 把公开选项收窄为 wire payload,缺省值显式补齐且不携带 undefined 键。 */
|
|
11207
|
+
function normalizePickOptions(options) {
|
|
11208
|
+
assertOptionsObject(options);
|
|
11209
|
+
if (options === undefined) {
|
|
11210
|
+
return { source: 'album', mediaType: 'all', maxCount: 1 };
|
|
11211
|
+
}
|
|
11212
|
+
const raw = options;
|
|
11213
|
+
assertKnownKeys(raw);
|
|
11214
|
+
const source = normalizeSource(raw.source);
|
|
11215
|
+
const mediaType = normalizeSelectionType(raw.mediaType);
|
|
11216
|
+
const maxCount = normalizeMaxCount(raw.maxCount, source);
|
|
11217
|
+
return { source, mediaType, maxCount };
|
|
11218
|
+
}
|
|
11219
|
+
/** 创建当前 SDK session 的 media 模块。 */
|
|
11220
|
+
function createMediaModule(requester) {
|
|
11221
|
+
const registry = getFileHandleRegistry(requester);
|
|
11222
|
+
return Object.freeze({
|
|
11223
|
+
async pick(options) {
|
|
11224
|
+
const payload = normalizePickOptions(options);
|
|
11225
|
+
// 图库选择与拍摄等待用户确认,不使用普通请求的固定超时;销毁仍沿用 bridge cancel。
|
|
11226
|
+
const result = await requester.requestOperation(MEDIA_PICK_METHOD, payload);
|
|
11227
|
+
if (!isMiniProgramMediaPickResult(result)) {
|
|
11228
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的媒体 picker 结果');
|
|
11229
|
+
}
|
|
11230
|
+
if (result.length > payload.maxCount) {
|
|
11231
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的媒体项数量超过本次请求上限');
|
|
11232
|
+
}
|
|
11233
|
+
if (payload.source === 'camera' && result.length !== 1) {
|
|
11234
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的相机结果必须恰好一项');
|
|
11235
|
+
}
|
|
11236
|
+
const items = result.map((item) => {
|
|
11237
|
+
const handle = registry.createDirectFile(item.file);
|
|
11238
|
+
if (handle.mode !== 'read') {
|
|
11239
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的媒体 File mode 不是 read');
|
|
11240
|
+
}
|
|
11241
|
+
return Object.freeze({ ...item, file: handle });
|
|
11242
|
+
});
|
|
11243
|
+
return Object.freeze(items);
|
|
11244
|
+
},
|
|
11245
|
+
});
|
|
11246
|
+
}
|
|
11247
|
+
|
|
10971
11248
|
/**
|
|
10972
11249
|
* 获取当前小程序窗口信息。
|
|
10973
11250
|
*
|
|
@@ -11353,6 +11630,8 @@ class MiniProgramSDK {
|
|
|
11353
11630
|
network;
|
|
11354
11631
|
/** 受控文件与目录能力。 */
|
|
11355
11632
|
files;
|
|
11633
|
+
/** 受控媒体选择能力。 */
|
|
11634
|
+
media;
|
|
11356
11635
|
/** UI 交互相关开放能力。 */
|
|
11357
11636
|
ui;
|
|
11358
11637
|
/** 设备相关开放能力。 */
|
|
@@ -11377,6 +11656,7 @@ class MiniProgramSDK {
|
|
|
11377
11656
|
this.viewport = createViewportModule(this.client);
|
|
11378
11657
|
this.storage = createStorageModule(this.client);
|
|
11379
11658
|
this.files = createFilesModule(this.client);
|
|
11659
|
+
this.media = createMediaModule(this.client);
|
|
11380
11660
|
this.network = createNetworkModule(this.client);
|
|
11381
11661
|
this.ui = createUiModule(this.client);
|
|
11382
11662
|
this.device = createDeviceModule(this.client);
|
|
@@ -11921,6 +12201,10 @@ const files = {
|
|
|
11921
12201
|
pickDirectory: (options) => getDefaultSDK().files.pickDirectory(options),
|
|
11922
12202
|
saveFile: (options) => getDefaultSDK().files.saveFile(options),
|
|
11923
12203
|
};
|
|
12204
|
+
/** 默认 SDK 实例的 media 模块。 */
|
|
12205
|
+
const media = {
|
|
12206
|
+
pick: (options) => getDefaultSDK().media.pick(options),
|
|
12207
|
+
};
|
|
11924
12208
|
/** 默认 SDK 实例的 UI 模块。 */
|
|
11925
12209
|
const ui = {
|
|
11926
12210
|
showToast: (options) => getDefaultSDK().ui.showToast(options),
|
|
@@ -11987,6 +12271,7 @@ const hbSDK = {
|
|
|
11987
12271
|
viewport,
|
|
11988
12272
|
storage,
|
|
11989
12273
|
files,
|
|
12274
|
+
media,
|
|
11990
12275
|
network,
|
|
11991
12276
|
ui,
|
|
11992
12277
|
device,
|
|
@@ -12008,6 +12293,7 @@ exports.device = device;
|
|
|
12008
12293
|
exports.environment = environment;
|
|
12009
12294
|
exports.files = files;
|
|
12010
12295
|
exports.getHandshakeState = getHandshakeState;
|
|
12296
|
+
exports.media = media;
|
|
12011
12297
|
exports.navigation = navigation;
|
|
12012
12298
|
exports.network = network;
|
|
12013
12299
|
exports.off = off;
|