@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.esm.js
CHANGED
|
@@ -440,6 +440,7 @@ const DIRECTORY_CREATE_METHOD = 'directory.create';
|
|
|
440
440
|
const DIRECTORY_REMOVE_METHOD = 'directory.remove';
|
|
441
441
|
const DIRECTORY_EXISTS_METHOD = 'directory.exists';
|
|
442
442
|
const DIRECTORY_LIST_METHOD = 'directory.list';
|
|
443
|
+
const MEDIA_PICK_METHOD = 'media.pick';
|
|
443
444
|
const UI_SHOW_TOAST_METHOD = 'ui.showToast';
|
|
444
445
|
const UI_SHOW_LOADING_METHOD = 'ui.showLoading';
|
|
445
446
|
const UI_HIDE_LOADING_METHOD = 'ui.hideLoading';
|
|
@@ -837,6 +838,184 @@ function isMiniProgramDirectoryRef(value) {
|
|
|
837
838
|
}
|
|
838
839
|
return (isMiniProgramPathRootRef(value.root) && (value.relativePath === '.' || isMiniProgramRelativePath(value.relativePath)) && hasOnlyKeys(value, ['kind', 'root', 'relativePath']));
|
|
839
840
|
}
|
|
841
|
+
/**
|
|
842
|
+
* 判断未知值是否具有文件 descriptor 的最低 wire 结构。
|
|
843
|
+
*
|
|
844
|
+
* @param value - 来自 Host、bridge response、目录列表或其他不可信边界的候选 descriptor。
|
|
845
|
+
* @returns 顶层字段及嵌套文件引用都满足协议结构时返回 `true`,并将 TypeScript 类型收窄为
|
|
846
|
+
* {@link MiniProgramFileDescriptor};否则返回 `false`。
|
|
847
|
+
*
|
|
848
|
+
* @remarks
|
|
849
|
+
* guard 会读取并校验以下四个属性;对通常的对象字面量,它们也是唯一允许的可枚举自有字符串字段:
|
|
850
|
+
*
|
|
851
|
+
* - `kind`:固定为 `'file'`。
|
|
852
|
+
* - `name`:非空字符串。该 guard 不检查空白、`.`、`..`、`/`、反斜杠、控制字符、平台保留名或
|
|
853
|
+
* `name` 是否等于 `ref.relativePath` 的 basename;SDK 创建 `FileHandle` 时还会执行更严格的名称校验。
|
|
854
|
+
* - `mode`:只能是 `'read'` 或 `'readwrite'`。这是 descriptor 声明值,不证明 Host 实际授予了读写权限。
|
|
855
|
+
* - `ref`:必须通过 {@link isMiniProgramFileRef}。
|
|
856
|
+
*
|
|
857
|
+
* `ref` 可以是 `{ kind: 'file', handleId }` 形式的 direct file ref,其中 `handleId` 只要求非空;也可以是
|
|
858
|
+
* `{ kind: 'file', root, relativePath }` 形式的 derived ref。derived root 只能是精确的 sandbox root 或
|
|
859
|
+
* 带非空 `handleId` 的 direct directory root,不能是 file ref 或再次派生的 directory ref;`relativePath`
|
|
860
|
+
* 必须是非空、非绝对、无尾随 `/`、无空段、`.` / `..` 点段、反斜杠或控制字符的相对路径。与目录 ref
|
|
861
|
+
* 不同,文件 ref 不接受 `relativePath: '.'`,因为它必须指向 root 下的具体文件。
|
|
862
|
+
*
|
|
863
|
+
* 顶层 descriptor、`ref` 与 `root` 的额外可枚举自有字符串键都会导致失败,包括值为 `undefined` 的额外键;
|
|
864
|
+
* 非枚举属性、symbol 键和继承属性不在 `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举
|
|
865
|
+
* 属性。该函数不会复制、冻结或规范化输入。
|
|
866
|
+
*
|
|
867
|
+
* 返回 `true` 只建立结构类型收窄,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、属于当前
|
|
868
|
+
* session/origin、仍然有效且确为预期类型,也不证明文件存在、名称真实、`filesystem` 权限已启用、mode
|
|
869
|
+
* 允许当前操作或 Host 支持对应 capability。实际文件操作仍须由 Runtime 查询 handle registry、执行权限、
|
|
870
|
+
* provenance、目标类型和存在性校验。
|
|
871
|
+
*
|
|
872
|
+
* {@link isMiniProgramFileSystemEntityDescriptor} 使用本 guard 与目录 descriptor guard 识别目录列表项。
|
|
873
|
+
* `files.pickFiles()` 与 `files.saveFile()` 的成功结果要求更窄的 direct file ref,而 sandbox/path API 返回的
|
|
874
|
+
* descriptor 可以使用 derived ref;本 guard 本身不会区分 descriptor 的来源。
|
|
875
|
+
*
|
|
876
|
+
* @example
|
|
877
|
+
* ```ts
|
|
878
|
+
* import { isMiniProgramFileDescriptor } from '@heybox/hb-sdk/protocol';
|
|
879
|
+
*
|
|
880
|
+
* const candidate = {
|
|
881
|
+
* kind: 'file',
|
|
882
|
+
* name: 'report.json',
|
|
883
|
+
* mode: 'readwrite',
|
|
884
|
+
* ref: {
|
|
885
|
+
* kind: 'file',
|
|
886
|
+
* root: { kind: 'sandbox' },
|
|
887
|
+
* relativePath: 'exports/report.json',
|
|
888
|
+
* },
|
|
889
|
+
* };
|
|
890
|
+
* const untrusted: unknown = candidate;
|
|
891
|
+
*
|
|
892
|
+
* if (isMiniProgramFileDescriptor(untrusted)) {
|
|
893
|
+
* // 这里只能安全读取 wire 字段;文件 I/O 仍交给 files API/Runtime。
|
|
894
|
+
* console.log(untrusted.name, untrusted.mode, untrusted.ref);
|
|
895
|
+
* }
|
|
896
|
+
*
|
|
897
|
+
* isMiniProgramFileDescriptor({ ...candidate, size: 1024 }); // false:顶层存在额外键
|
|
898
|
+
* ```
|
|
899
|
+
*
|
|
900
|
+
* @see [MiniProgramFileDescriptor](/reference/symbols/protocol/interfaces/MiniProgramFileDescriptor) descriptor 字段类型。
|
|
901
|
+
* @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 嵌套 direct/derived 文件引用校验。
|
|
902
|
+
* @see [isMiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileSystemEntityDescriptor) 文件与目录 descriptor 联合 guard。
|
|
903
|
+
* @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 校验后由 SDK 建立的文件代理。
|
|
904
|
+
* @see [PickFilesResult](/reference/symbols/protocol/types/PickFilesResult) picker 的 direct-ref 文件结果。
|
|
905
|
+
* @see [SaveFileResult](/reference/symbols/protocol/types/SaveFileResult) save picker 的 direct-ref 文件结果。
|
|
906
|
+
* @since 0.8.0
|
|
907
|
+
*/
|
|
908
|
+
function isMiniProgramFileDescriptor(value) {
|
|
909
|
+
return (isRecord$3(value) &&
|
|
910
|
+
value.kind === 'file' &&
|
|
911
|
+
isNonEmptyString$1(value.name) &&
|
|
912
|
+
isFileMode(value.mode) &&
|
|
913
|
+
isMiniProgramFileRef(value.ref) &&
|
|
914
|
+
hasOnlyKeys(value, ['kind', 'name', 'mode', 'ref']));
|
|
915
|
+
}
|
|
916
|
+
/** `media.pick` 结果数组允许的最大项数,与协议 `maxCount` 上限一致。 */
|
|
917
|
+
const MAX_MEDIA_PICK_COUNT = 20;
|
|
918
|
+
/** `media.pick` 单项结果的公共 canonical key 集合。 */
|
|
919
|
+
const MEDIA_PICK_ITEM_KEYS = ['type', 'mimeType', 'size', 'width', 'height', 'previewUrl', 'file'];
|
|
920
|
+
/**
|
|
921
|
+
* 判断未知值是否为单个 `media.pick` 媒体项。
|
|
922
|
+
*
|
|
923
|
+
* @param value - 来自 Host response、bridge result 或其他不可信边界的候选媒体项。
|
|
924
|
+
* @returns 字段组合满足 {@link MediaPickItem} 判别联合的最低结构时返回 `true`,并将 TypeScript 类型
|
|
925
|
+
* 收窄为图片或视频项;否则返回 `false`。
|
|
926
|
+
*
|
|
927
|
+
* @remarks
|
|
928
|
+
* 以 `type` 为判别字段,两个分支都要求 `mimeType` 非空字符串、`size` 为非负安全整数、`width` /
|
|
929
|
+
* `height` 为正安全整数、`previewUrl` 非空字符串,且 `file` 是通过
|
|
930
|
+
* {@link isMiniProgramFileDescriptor} 的 direct file ref(`ref.kind === 'file'` 且带非空 `handleId`),
|
|
931
|
+
* `mode` 必须固定为 `'read'`。图片分支只允许公共 key;视频分支额外要求非负安全整数的 `durationMs`,
|
|
932
|
+
* 并拒绝图片携带该字段。
|
|
933
|
+
*
|
|
934
|
+
* `size` 单位为字节,`width` / `height` 是考虑方向后的显示像素尺寸,`durationMs` 单位为毫秒。
|
|
935
|
+
* 该 guard 只校验结构与数值范围,不证明元数据与 `file` 实际内容一致、`previewUrl` 属于当前会话、
|
|
936
|
+
* `file` 句柄由当前 Runtime 签发或仍在有效期内,也不检查 `media` 权限与 Host 能力。
|
|
937
|
+
*
|
|
938
|
+
* @example
|
|
939
|
+
* ```ts
|
|
940
|
+
* import { isMiniProgramMediaPickItem } from '@heybox/hb-sdk/protocol';
|
|
941
|
+
*
|
|
942
|
+
* const video = {
|
|
943
|
+
* type: 'video',
|
|
944
|
+
* mimeType: 'video/mp4',
|
|
945
|
+
* size: 5_242_880,
|
|
946
|
+
* width: 1280,
|
|
947
|
+
* height: 720,
|
|
948
|
+
* durationMs: 12_000,
|
|
949
|
+
* previewUrl: 'hb-media-preview://session/resource_2',
|
|
950
|
+
* file: { kind: 'file', name: 'clip.mp4', mode: 'read', ref: { kind: 'file', handleId: 'media_handle_2' } },
|
|
951
|
+
* };
|
|
952
|
+
*
|
|
953
|
+
* isMiniProgramMediaPickItem(video); // true
|
|
954
|
+
* isMiniProgramMediaPickItem({ ...video, type: 'image' }); // false:图片不允许 durationMs
|
|
955
|
+
* ```
|
|
956
|
+
*
|
|
957
|
+
* @see [MediaPickItem](/reference/symbols/protocol/types/MediaPickItem) 判别联合定义。
|
|
958
|
+
* @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) 嵌套文件句柄校验。
|
|
959
|
+
* @since 0.9.3
|
|
960
|
+
*/
|
|
961
|
+
function isMiniProgramMediaPickItem(value) {
|
|
962
|
+
if (!isRecord$3(value) || (value.type !== 'image' && value.type !== 'video'))
|
|
963
|
+
return false;
|
|
964
|
+
const isVideo = value.type === 'video';
|
|
965
|
+
if (!hasOnlyKeys(value, isVideo ? [...MEDIA_PICK_ITEM_KEYS, 'durationMs'] : MEDIA_PICK_ITEM_KEYS))
|
|
966
|
+
return false;
|
|
967
|
+
if (!isNonEmptyString$1(value.mimeType) ||
|
|
968
|
+
!isNonNegativeSafeInteger(value.size) ||
|
|
969
|
+
!isPositiveSafeInteger$1(value.width) ||
|
|
970
|
+
!isPositiveSafeInteger$1(value.height) ||
|
|
971
|
+
!isNonEmptyString$1(value.previewUrl) ||
|
|
972
|
+
!isDirectFileDescriptor(value.file)) {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
return isVideo ? isNonNegativeSafeInteger(value.durationMs) : true;
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* 判断未知值是否为整批 `media.pick` 结果。
|
|
979
|
+
*
|
|
980
|
+
* @param value - 来自 Host response、bridge result 或其他不可信边界的候选结果。
|
|
981
|
+
* @returns 值为 1–20 项且每项都通过 {@link isMiniProgramMediaPickItem} 的稠密数组时返回 `true`,
|
|
982
|
+
* 并将 TypeScript 类型收窄为 {@link MediaPickResult};否则返回 `false`。
|
|
983
|
+
*
|
|
984
|
+
* @remarks
|
|
985
|
+
* 成功结果必须是非空数组:用户取消或任一项准备失败时走整批错误,不得返回空数组或部分结果。
|
|
986
|
+
* 本 guard 只校验全局上限 20,不校验调用方本次请求的 `maxCount`、相机必须恰好 1 项、项之间是否
|
|
987
|
+
* 重复或顺序,也不确认结果来自当前 request 或会话仍然有效。这些约束由 Runtime 的状态机、可信手势、
|
|
988
|
+
* 权限与句柄注册表继续执行。
|
|
989
|
+
*
|
|
990
|
+
* @example
|
|
991
|
+
* ```ts
|
|
992
|
+
* import { isMiniProgramMediaPickResult } from '@heybox/hb-sdk/protocol';
|
|
993
|
+
*
|
|
994
|
+
* isMiniProgramMediaPickResult([]); // false:取消不是空数组成功
|
|
995
|
+
* isMiniProgramMediaPickResult([imageItem]); // true
|
|
996
|
+
* ```
|
|
997
|
+
*
|
|
998
|
+
* @see [MediaPickResult](/reference/symbols/protocol/types/MediaPickResult) wire 返回类型。
|
|
999
|
+
* @see [isMiniProgramMediaPickItem](/reference/symbols/protocol/functions/isMiniProgramMediaPickItem) 单项结构与数值校验。
|
|
1000
|
+
* @since 0.9.3
|
|
1001
|
+
*/
|
|
1002
|
+
function isMiniProgramMediaPickResult(value) {
|
|
1003
|
+
if (!Array.isArray(value) || value.length < 1 || value.length > MAX_MEDIA_PICK_COUNT)
|
|
1004
|
+
return false;
|
|
1005
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
1006
|
+
if (!Object.prototype.hasOwnProperty.call(value, index) || !isMiniProgramMediaPickItem(value[index]))
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
return true;
|
|
1010
|
+
}
|
|
1011
|
+
function isDirectFileDescriptor(value) {
|
|
1012
|
+
if (!isMiniProgramFileDescriptor(value))
|
|
1013
|
+
return false;
|
|
1014
|
+
// 媒体结果只允许只读 direct 句柄;derived ref、readwrite 与目录引用都不符合媒体契约。
|
|
1015
|
+
if (value.mode !== 'read')
|
|
1016
|
+
return false;
|
|
1017
|
+
return value.ref.kind === 'file' && 'handleId' in value.ref && isNonEmptyString$1(value.ref.handleId);
|
|
1018
|
+
}
|
|
840
1019
|
/**
|
|
841
1020
|
* 判断未知值是否为使用普通 `ArrayBuffer` 存储的 `Uint8Array` 字节视图。
|
|
842
1021
|
*
|
|
@@ -1046,9 +1225,15 @@ function isSDKHandshakePayload(value) {
|
|
|
1046
1225
|
function isBridgeError(value) {
|
|
1047
1226
|
return isRecord$3(value) && isNonEmptyString$1(value.code) && typeof value.message === 'string';
|
|
1048
1227
|
}
|
|
1228
|
+
function isFileMode(value) {
|
|
1229
|
+
return value === 'read' || value === 'readwrite';
|
|
1230
|
+
}
|
|
1049
1231
|
function isNonNegativeSafeInteger(value) {
|
|
1050
1232
|
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0;
|
|
1051
1233
|
}
|
|
1234
|
+
function isPositiveSafeInteger$1(value) {
|
|
1235
|
+
return typeof value === 'number' && Number.isSafeInteger(value) && value > 0;
|
|
1236
|
+
}
|
|
1052
1237
|
function isNonNegativeFiniteNumber(value) {
|
|
1053
1238
|
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
1054
1239
|
}
|
|
@@ -1070,6 +1255,7 @@ const MINI_PROGRAM_PERMISSION_KEYS = [
|
|
|
1070
1255
|
'storage',
|
|
1071
1256
|
'cloud_storage',
|
|
1072
1257
|
'filesystem',
|
|
1258
|
+
'media',
|
|
1073
1259
|
'clipboard',
|
|
1074
1260
|
'leaderboard',
|
|
1075
1261
|
'network',
|
|
@@ -7295,7 +7481,7 @@ function createMessageId() {
|
|
|
7295
7481
|
/** 构建时替换为当前发布包的实际版本。 */
|
|
7296
7482
|
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
7297
7483
|
? undefined
|
|
7298
|
-
: '0.9.3
|
|
7484
|
+
: '0.9.3';
|
|
7299
7485
|
|
|
7300
7486
|
const RUNTIME_MODES = new Set(['production', 'preview', 'development', 'unknown']);
|
|
7301
7487
|
const OPERATING_SYSTEM_NAMES = new Set(['android', 'ios', 'ohos', 'windows', 'macos', 'linux', 'unknown']);
|
|
@@ -7916,7 +8102,7 @@ function createCloudModule(requester) {
|
|
|
7916
8102
|
const registries = new WeakMap();
|
|
7917
8103
|
const brandedFileSystemValues = new WeakSet();
|
|
7918
8104
|
const CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f]/;
|
|
7919
|
-
function invalidParams$
|
|
8105
|
+
function invalidParams$2(message, data) {
|
|
7920
8106
|
return createSDKError('INVALID_PARAMS', message, data);
|
|
7921
8107
|
}
|
|
7922
8108
|
function clonePathRootRef(ref) {
|
|
@@ -7991,13 +8177,13 @@ function assertSandboxRef(ref) {
|
|
|
7991
8177
|
}
|
|
7992
8178
|
function normalizeCreateOptions(options) {
|
|
7993
8179
|
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
7994
|
-
throw invalidParams$
|
|
8180
|
+
throw invalidParams$2('create options 必须是对象');
|
|
7995
8181
|
}
|
|
7996
8182
|
if (options?.recursive !== undefined && typeof options.recursive !== 'boolean') {
|
|
7997
|
-
throw invalidParams$
|
|
8183
|
+
throw invalidParams$2('recursive 必须是 boolean');
|
|
7998
8184
|
}
|
|
7999
8185
|
if (options?.exclusive !== undefined && typeof options.exclusive !== 'boolean') {
|
|
8000
|
-
throw invalidParams$
|
|
8186
|
+
throw invalidParams$2('exclusive 必须是 boolean');
|
|
8001
8187
|
}
|
|
8002
8188
|
return {
|
|
8003
8189
|
recursive: options?.recursive ?? false,
|
|
@@ -8006,28 +8192,28 @@ function normalizeCreateOptions(options) {
|
|
|
8006
8192
|
}
|
|
8007
8193
|
function normalizeListOptions(options) {
|
|
8008
8194
|
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
8009
|
-
throw invalidParams$
|
|
8195
|
+
throw invalidParams$2('list options 必须是对象');
|
|
8010
8196
|
}
|
|
8011
8197
|
if (options?.limit !== undefined && (!Number.isSafeInteger(options.limit) || options.limit <= 0)) {
|
|
8012
|
-
throw invalidParams$
|
|
8198
|
+
throw invalidParams$2('limit 必须是正的 safe integer');
|
|
8013
8199
|
}
|
|
8014
8200
|
return options?.limit;
|
|
8015
8201
|
}
|
|
8016
8202
|
function normalizeRemoveOptions(options) {
|
|
8017
8203
|
if (options !== undefined && (!options || typeof options !== 'object' || Array.isArray(options))) {
|
|
8018
|
-
throw invalidParams$
|
|
8204
|
+
throw invalidParams$2('remove options 必须是对象');
|
|
8019
8205
|
}
|
|
8020
8206
|
if (options?.recursive !== undefined && typeof options.recursive !== 'boolean') {
|
|
8021
|
-
throw invalidParams$
|
|
8207
|
+
throw invalidParams$2('recursive 必须是 boolean');
|
|
8022
8208
|
}
|
|
8023
8209
|
return { recursive: options?.recursive ?? false };
|
|
8024
8210
|
}
|
|
8025
8211
|
function cloneBytes$1(content) {
|
|
8026
8212
|
if (!(content instanceof Uint8Array)) {
|
|
8027
|
-
throw invalidParams$
|
|
8213
|
+
throw invalidParams$2('content 必须是 Uint8Array');
|
|
8028
8214
|
}
|
|
8029
8215
|
if (typeof SharedArrayBuffer !== 'undefined' && content.buffer instanceof SharedArrayBuffer) {
|
|
8030
|
-
throw invalidParams$
|
|
8216
|
+
throw invalidParams$2('content 不能由 SharedArrayBuffer 承载');
|
|
8031
8217
|
}
|
|
8032
8218
|
return new Uint8Array(content);
|
|
8033
8219
|
}
|
|
@@ -8045,7 +8231,7 @@ function normalizeStat(stat) {
|
|
|
8045
8231
|
/** 校验并返回平台无关的 `/` 分隔逻辑相对路径。 */
|
|
8046
8232
|
function normalizeRelativePath(value) {
|
|
8047
8233
|
if (!isMiniProgramRelativePath(value)) {
|
|
8048
|
-
throw invalidParams$
|
|
8234
|
+
throw invalidParams$2('relativePath 必须是规范的 `/` 分隔逻辑相对路径');
|
|
8049
8235
|
}
|
|
8050
8236
|
return value;
|
|
8051
8237
|
}
|
|
@@ -8112,7 +8298,7 @@ class MiniProgramFileHandleRegistry {
|
|
|
8112
8298
|
defineReadonlyValue(handle, 'writeText', async (content) => {
|
|
8113
8299
|
assertWritable(state.mode);
|
|
8114
8300
|
if (typeof content !== 'string') {
|
|
8115
|
-
throw invalidParams$
|
|
8301
|
+
throw invalidParams$2('content 必须是 string');
|
|
8116
8302
|
}
|
|
8117
8303
|
await this.requester.request(FILE_WRITE_TEXT_METHOD, { target: state.ref, content });
|
|
8118
8304
|
}, false);
|
|
@@ -8214,14 +8400,14 @@ class MiniProgramFileHandleRegistry {
|
|
|
8214
8400
|
requireFile(handle) {
|
|
8215
8401
|
const state = handle && typeof handle === 'object' ? this.fileStates.get(handle) : undefined;
|
|
8216
8402
|
if (!state) {
|
|
8217
|
-
throw invalidParams$
|
|
8403
|
+
throw invalidParams$2('to 必须是当前 SDK session 创建的 FileHandle');
|
|
8218
8404
|
}
|
|
8219
8405
|
return state;
|
|
8220
8406
|
}
|
|
8221
8407
|
requireDirectory(handle) {
|
|
8222
8408
|
const state = handle && typeof handle === 'object' ? this.directoryStates.get(handle) : undefined;
|
|
8223
8409
|
if (!state) {
|
|
8224
|
-
throw invalidParams$
|
|
8410
|
+
throw invalidParams$2('to 必须是当前 SDK session 创建的 DirectoryHandle');
|
|
8225
8411
|
}
|
|
8226
8412
|
return state;
|
|
8227
8413
|
}
|
|
@@ -10859,7 +11045,7 @@ async function upload(requester, options) {
|
|
|
10859
11045
|
return response;
|
|
10860
11046
|
}
|
|
10861
11047
|
|
|
10862
|
-
function invalidParams(message) {
|
|
11048
|
+
function invalidParams$1(message) {
|
|
10863
11049
|
return createSDKError('INVALID_PARAMS', message);
|
|
10864
11050
|
}
|
|
10865
11051
|
function normalizeMode(mode) {
|
|
@@ -10867,13 +11053,13 @@ function normalizeMode(mode) {
|
|
|
10867
11053
|
return undefined;
|
|
10868
11054
|
}
|
|
10869
11055
|
if (mode !== 'read' && mode !== 'readwrite') {
|
|
10870
|
-
throw invalidParams('mode 必须是 read 或 readwrite');
|
|
11056
|
+
throw invalidParams$1('mode 必须是 read 或 readwrite');
|
|
10871
11057
|
}
|
|
10872
11058
|
return mode;
|
|
10873
11059
|
}
|
|
10874
|
-
function assertOptionsObject(value, name) {
|
|
11060
|
+
function assertOptionsObject$1(value, name) {
|
|
10875
11061
|
if (value !== undefined && (!value || typeof value !== 'object' || Array.isArray(value))) {
|
|
10876
|
-
throw invalidParams(`${name} 必须是对象`);
|
|
11062
|
+
throw invalidParams$1(`${name} 必须是对象`);
|
|
10877
11063
|
}
|
|
10878
11064
|
}
|
|
10879
11065
|
function normalizeAccept(accept) {
|
|
@@ -10881,20 +11067,20 @@ function normalizeAccept(accept) {
|
|
|
10881
11067
|
return undefined;
|
|
10882
11068
|
}
|
|
10883
11069
|
if (!Array.isArray(accept)) {
|
|
10884
|
-
throw invalidParams('accept 必须是扩展名数组');
|
|
11070
|
+
throw invalidParams$1('accept 必须是扩展名数组');
|
|
10885
11071
|
}
|
|
10886
11072
|
return accept.map((extension) => {
|
|
10887
11073
|
if (typeof extension !== 'string' ||
|
|
10888
11074
|
!/^\.[a-z0-9][a-z0-9._-]*$/i.test(extension)) {
|
|
10889
|
-
throw invalidParams('accept 只能包含规范扩展名,例如 .json');
|
|
11075
|
+
throw invalidParams$1('accept 只能包含规范扩展名,例如 .json');
|
|
10890
11076
|
}
|
|
10891
11077
|
return extension;
|
|
10892
11078
|
});
|
|
10893
11079
|
}
|
|
10894
11080
|
function normalizePickFilesOptions(options) {
|
|
10895
|
-
assertOptionsObject(options, 'pickFiles options');
|
|
11081
|
+
assertOptionsObject$1(options, 'pickFiles options');
|
|
10896
11082
|
if (options?.multiple !== undefined && typeof options.multiple !== 'boolean') {
|
|
10897
|
-
throw invalidParams('multiple 必须是 boolean');
|
|
11083
|
+
throw invalidParams$1('multiple 必须是 boolean');
|
|
10898
11084
|
}
|
|
10899
11085
|
const mode = normalizeMode(options?.mode);
|
|
10900
11086
|
const accept = normalizeAccept(options?.accept);
|
|
@@ -10908,21 +11094,21 @@ function normalizePickFilesOptions(options) {
|
|
|
10908
11094
|
};
|
|
10909
11095
|
}
|
|
10910
11096
|
function normalizePickDirectoryOptions(options) {
|
|
10911
|
-
assertOptionsObject(options, 'pickDirectory options');
|
|
11097
|
+
assertOptionsObject$1(options, 'pickDirectory options');
|
|
10912
11098
|
const mode = normalizeMode(options?.mode);
|
|
10913
11099
|
return options === undefined ? undefined : mode === undefined ? {} : { mode };
|
|
10914
11100
|
}
|
|
10915
11101
|
function normalizeSaveFileOptions(options) {
|
|
10916
|
-
assertOptionsObject(options, 'saveFile options');
|
|
11102
|
+
assertOptionsObject$1(options, 'saveFile options');
|
|
10917
11103
|
if (!options || typeof options.suggestedName !== 'string') {
|
|
10918
|
-
throw invalidParams('suggestedName 必须是非空字符串');
|
|
11104
|
+
throw invalidParams$1('suggestedName 必须是非空字符串');
|
|
10919
11105
|
}
|
|
10920
11106
|
const suggestedName = normalizeRelativePath(options.suggestedName);
|
|
10921
11107
|
if (suggestedName.includes('/')) {
|
|
10922
|
-
throw invalidParams('suggestedName 必须是 basename');
|
|
11108
|
+
throw invalidParams$1('suggestedName 必须是 basename');
|
|
10923
11109
|
}
|
|
10924
11110
|
if ('accept' in options) {
|
|
10925
|
-
throw invalidParams('saveFile V1 不支持 accept');
|
|
11111
|
+
throw invalidParams$1('saveFile V1 不支持 accept');
|
|
10926
11112
|
}
|
|
10927
11113
|
return { suggestedName };
|
|
10928
11114
|
}
|
|
@@ -10964,6 +11150,97 @@ function createFilesModule(requester) {
|
|
|
10964
11150
|
});
|
|
10965
11151
|
}
|
|
10966
11152
|
|
|
11153
|
+
const MEDIA_SOURCES = ['album', 'camera'];
|
|
11154
|
+
const MEDIA_SELECTION_TYPES = ['image', 'video', 'all'];
|
|
11155
|
+
const MEDIA_PICK_MAX_COUNT = 20;
|
|
11156
|
+
const MEDIA_PICK_OPTION_KEYS = ['source', 'mediaType', 'maxCount'];
|
|
11157
|
+
function invalidParams(message) {
|
|
11158
|
+
return createSDKError('INVALID_PARAMS', message);
|
|
11159
|
+
}
|
|
11160
|
+
function assertOptionsObject(value) {
|
|
11161
|
+
if (value !== undefined && (!value || typeof value !== 'object' || Array.isArray(value))) {
|
|
11162
|
+
throw invalidParams('pick options 必须是对象');
|
|
11163
|
+
}
|
|
11164
|
+
}
|
|
11165
|
+
function assertKnownKeys(options) {
|
|
11166
|
+
for (const key of Object.keys(options)) {
|
|
11167
|
+
if (!MEDIA_PICK_OPTION_KEYS.includes(key)) {
|
|
11168
|
+
throw invalidParams(`pick options 包含未知字段 ${key}`);
|
|
11169
|
+
}
|
|
11170
|
+
}
|
|
11171
|
+
}
|
|
11172
|
+
function normalizeSource(value) {
|
|
11173
|
+
if (value === undefined) {
|
|
11174
|
+
return 'album';
|
|
11175
|
+
}
|
|
11176
|
+
if (typeof value !== 'string' || !MEDIA_SOURCES.includes(value)) {
|
|
11177
|
+
throw invalidParams('source 必须是 album 或 camera');
|
|
11178
|
+
}
|
|
11179
|
+
return value;
|
|
11180
|
+
}
|
|
11181
|
+
function normalizeSelectionType(value) {
|
|
11182
|
+
if (value === undefined) {
|
|
11183
|
+
return 'all';
|
|
11184
|
+
}
|
|
11185
|
+
if (typeof value !== 'string' || !MEDIA_SELECTION_TYPES.includes(value)) {
|
|
11186
|
+
throw invalidParams('mediaType 必须是 image、video 或 all');
|
|
11187
|
+
}
|
|
11188
|
+
return value;
|
|
11189
|
+
}
|
|
11190
|
+
function normalizeMaxCount(value, source) {
|
|
11191
|
+
if (value === undefined) {
|
|
11192
|
+
return 1;
|
|
11193
|
+
}
|
|
11194
|
+
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 1 || value > MEDIA_PICK_MAX_COUNT) {
|
|
11195
|
+
throw invalidParams(`maxCount 必须是 1 到 ${MEDIA_PICK_MAX_COUNT} 的安全整数`);
|
|
11196
|
+
}
|
|
11197
|
+
if (source === 'camera' && value > 1) {
|
|
11198
|
+
throw invalidParams('camera 的 maxCount 只能是 1');
|
|
11199
|
+
}
|
|
11200
|
+
return value;
|
|
11201
|
+
}
|
|
11202
|
+
/** 把公开选项收窄为 wire payload,缺省值显式补齐且不携带 undefined 键。 */
|
|
11203
|
+
function normalizePickOptions(options) {
|
|
11204
|
+
assertOptionsObject(options);
|
|
11205
|
+
if (options === undefined) {
|
|
11206
|
+
return { source: 'album', mediaType: 'all', maxCount: 1 };
|
|
11207
|
+
}
|
|
11208
|
+
const raw = options;
|
|
11209
|
+
assertKnownKeys(raw);
|
|
11210
|
+
const source = normalizeSource(raw.source);
|
|
11211
|
+
const mediaType = normalizeSelectionType(raw.mediaType);
|
|
11212
|
+
const maxCount = normalizeMaxCount(raw.maxCount, source);
|
|
11213
|
+
return { source, mediaType, maxCount };
|
|
11214
|
+
}
|
|
11215
|
+
/** 创建当前 SDK session 的 media 模块。 */
|
|
11216
|
+
function createMediaModule(requester) {
|
|
11217
|
+
const registry = getFileHandleRegistry(requester);
|
|
11218
|
+
return Object.freeze({
|
|
11219
|
+
async pick(options) {
|
|
11220
|
+
const payload = normalizePickOptions(options);
|
|
11221
|
+
// 图库选择与拍摄等待用户确认,不使用普通请求的固定超时;销毁仍沿用 bridge cancel。
|
|
11222
|
+
const result = await requester.requestOperation(MEDIA_PICK_METHOD, payload);
|
|
11223
|
+
if (!isMiniProgramMediaPickResult(result)) {
|
|
11224
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回了无效的媒体 picker 结果');
|
|
11225
|
+
}
|
|
11226
|
+
if (result.length > payload.maxCount) {
|
|
11227
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的媒体项数量超过本次请求上限');
|
|
11228
|
+
}
|
|
11229
|
+
if (payload.source === 'camera' && result.length !== 1) {
|
|
11230
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的相机结果必须恰好一项');
|
|
11231
|
+
}
|
|
11232
|
+
const items = result.map((item) => {
|
|
11233
|
+
const handle = registry.createDirectFile(item.file);
|
|
11234
|
+
if (handle.mode !== 'read') {
|
|
11235
|
+
throw createSDKError('FILE_IO_FAILED', 'Host 返回的媒体 File mode 不是 read');
|
|
11236
|
+
}
|
|
11237
|
+
return Object.freeze({ ...item, file: handle });
|
|
11238
|
+
});
|
|
11239
|
+
return Object.freeze(items);
|
|
11240
|
+
},
|
|
11241
|
+
});
|
|
11242
|
+
}
|
|
11243
|
+
|
|
10967
11244
|
/**
|
|
10968
11245
|
* 获取当前小程序窗口信息。
|
|
10969
11246
|
*
|
|
@@ -11349,6 +11626,8 @@ class MiniProgramSDK {
|
|
|
11349
11626
|
network;
|
|
11350
11627
|
/** 受控文件与目录能力。 */
|
|
11351
11628
|
files;
|
|
11629
|
+
/** 受控媒体选择能力。 */
|
|
11630
|
+
media;
|
|
11352
11631
|
/** UI 交互相关开放能力。 */
|
|
11353
11632
|
ui;
|
|
11354
11633
|
/** 设备相关开放能力。 */
|
|
@@ -11373,6 +11652,7 @@ class MiniProgramSDK {
|
|
|
11373
11652
|
this.viewport = createViewportModule(this.client);
|
|
11374
11653
|
this.storage = createStorageModule(this.client);
|
|
11375
11654
|
this.files = createFilesModule(this.client);
|
|
11655
|
+
this.media = createMediaModule(this.client);
|
|
11376
11656
|
this.network = createNetworkModule(this.client);
|
|
11377
11657
|
this.ui = createUiModule(this.client);
|
|
11378
11658
|
this.device = createDeviceModule(this.client);
|
|
@@ -11917,6 +12197,10 @@ const files = {
|
|
|
11917
12197
|
pickDirectory: (options) => getDefaultSDK().files.pickDirectory(options),
|
|
11918
12198
|
saveFile: (options) => getDefaultSDK().files.saveFile(options),
|
|
11919
12199
|
};
|
|
12200
|
+
/** 默认 SDK 实例的 media 模块。 */
|
|
12201
|
+
const media = {
|
|
12202
|
+
pick: (options) => getDefaultSDK().media.pick(options),
|
|
12203
|
+
};
|
|
11920
12204
|
/** 默认 SDK 实例的 UI 模块。 */
|
|
11921
12205
|
const ui = {
|
|
11922
12206
|
showToast: (options) => getDefaultSDK().ui.showToast(options),
|
|
@@ -11983,6 +12267,7 @@ const hbSDK = {
|
|
|
11983
12267
|
viewport,
|
|
11984
12268
|
storage,
|
|
11985
12269
|
files,
|
|
12270
|
+
media,
|
|
11986
12271
|
network,
|
|
11987
12272
|
ui,
|
|
11988
12273
|
device,
|
|
@@ -11993,4 +12278,4 @@ const hbSDK = {
|
|
|
11993
12278
|
workspaceCards,
|
|
11994
12279
|
};
|
|
11995
12280
|
|
|
11996
|
-
export { HbMiniProgramNetworkError, HbMiniProgramSDKError, USER_INFO_AUTHORIZATION_SCOPES, auth, cloud, companion, hbSDK as default, device, environment, files, getHandshakeState, navigation, network, off, on, onHandshakeStateChange, share, storage, ui, user, viewport, workspaceCards };
|
|
12281
|
+
export { HbMiniProgramNetworkError, HbMiniProgramSDKError, USER_INFO_AUTHORIZATION_SCOPES, auth, cloud, companion, hbSDK as default, device, environment, files, getHandshakeState, media, navigation, network, off, on, onHandshakeStateChange, share, storage, ui, user, viewport, workspaceCards };
|
|
@@ -14,6 +14,7 @@ const MINI_PROGRAM_PERMISSION_KEYS = [
|
|
|
14
14
|
'storage',
|
|
15
15
|
'cloud_storage',
|
|
16
16
|
'filesystem',
|
|
17
|
+
'media',
|
|
17
18
|
'clipboard',
|
|
18
19
|
'leaderboard',
|
|
19
20
|
'network',
|
|
@@ -100,6 +101,16 @@ const MINI_PROGRAM_PERMISSION_CATALOG = [
|
|
|
100
101
|
],
|
|
101
102
|
conditions: ['外部文件继续要求有效的用户选择、会话句柄和匹配的读写模式。'],
|
|
102
103
|
},
|
|
104
|
+
{
|
|
105
|
+
key: 'media',
|
|
106
|
+
name: '媒体',
|
|
107
|
+
description: '通过系统图库或相机选择图片、视频。',
|
|
108
|
+
access: 'declaration',
|
|
109
|
+
risk: 'high',
|
|
110
|
+
config: {},
|
|
111
|
+
methods: ['media.pick'],
|
|
112
|
+
conditions: ['图库授权范围仅限本次用户选择的媒体,不包含任意文件或沙盒访问权;继续执行可信手势、Host 能力与会话资源规则。'],
|
|
113
|
+
},
|
|
103
114
|
{
|
|
104
115
|
key: 'clipboard',
|
|
105
116
|
name: '剪贴板',
|
|
@@ -12,6 +12,7 @@ const MINI_PROGRAM_PERMISSION_KEYS = [
|
|
|
12
12
|
'storage',
|
|
13
13
|
'cloud_storage',
|
|
14
14
|
'filesystem',
|
|
15
|
+
'media',
|
|
15
16
|
'clipboard',
|
|
16
17
|
'leaderboard',
|
|
17
18
|
'network',
|
|
@@ -98,6 +99,16 @@ const MINI_PROGRAM_PERMISSION_CATALOG = [
|
|
|
98
99
|
],
|
|
99
100
|
conditions: ['外部文件继续要求有效的用户选择、会话句柄和匹配的读写模式。'],
|
|
100
101
|
},
|
|
102
|
+
{
|
|
103
|
+
key: 'media',
|
|
104
|
+
name: '媒体',
|
|
105
|
+
description: '通过系统图库或相机选择图片、视频。',
|
|
106
|
+
access: 'declaration',
|
|
107
|
+
risk: 'high',
|
|
108
|
+
config: {},
|
|
109
|
+
methods: ['media.pick'],
|
|
110
|
+
conditions: ['图库授权范围仅限本次用户选择的媒体,不包含任意文件或沙盒访问权;继续执行可信手势、Host 能力与会话资源规则。'],
|
|
111
|
+
},
|
|
101
112
|
{
|
|
102
113
|
key: 'clipboard',
|
|
103
114
|
name: '剪贴板',
|