@heybox/hb-sdk-protocol 0.8.2-alpha.1 → 0.8.2-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/src/guards.ts CHANGED
@@ -12,6 +12,76 @@ import type {
12
12
  MiniProgramPathRootRef,
13
13
  } from './payloads';
14
14
 
15
+ /**
16
+ * 判断不可信输入是否具有受支持的小程序 bridge 消息结构。
17
+ *
18
+ * @param value - 从 `MessageEvent.data`、反序列化结果或其他外部边界取得的未知值。`null`、数组和非对象值会直接返回 `false`。
19
+ * @returns 输入满足 v1/v2 envelope 及当前 `type` 的最低结构要求时返回 `true`,并将 TypeScript 类型收窄为
20
+ * {@link MiniProgramBridgeMessage};否则返回 `false`。
21
+ *
22
+ * @remarks
23
+ * 所有通过的消息都必须使用 {@link MINI_PROGRAM_MESSAGE_NAMESPACE},`version` 必须为 `1` 或 `2`,
24
+ * `nonce` 必须为非空字符串。随后按 `type` 执行以下最低校验:
25
+ *
26
+ * - `handshake`(v1/v2):`id` 非空,`method` 固定为 {@link SDK_HANDSHAKE_METHOD},`error` 必须为 `undefined`;
27
+ * `payload` 必须是对象,且包含非空 `href`、字符串 `userAgent` 和非空 `sdkVersion`。
28
+ * - `request`(v1/v2):`id` 与 `method` 都是非空字符串,且 `error` 必须为 `undefined`;`payload` 不在此处校验。
29
+ * - `response`(v1/v2):`id` 非空;`method` 可省略,提供时必须非空;`error` 可省略,提供时必须含非空
30
+ * `code` 与字符串 `message`。该最低结构允许 `payload` 与合法 `error` 同时存在,终态语义由消费端决定。
31
+ * - 普通 `event`(v1/v2):`method` 非空、`error` 必须为 `undefined`,`id` 可省略或为非空字符串;事件名和
32
+ * `payload` 不在此处校验。
33
+ * - `operation.progress` 与 `cancel` 只接受 v2。progress 还要求非空 `id` 和受支持的下载或 companion
34
+ * prepare 进度 payload;cancel 要求非空 `id`,且 `method`、`payload`、`error` 都必须为 `undefined`。
35
+ *
36
+ * 返回 `true` 只表示 wire envelope 可识别,不建立信任或授权边界。该函数不会校验
37
+ * `MessageEvent.source` / `origin`,只检查 nonce 非空而不会与当前 iframe 的预期 nonce 比较,也不会确认
38
+ * 握手状态、协商后的版本、请求 ID 是否重复或对应 pending request。除握手和 operation progress 外,它也不校验
39
+ * payload schema;不会把普通 `method` / 事件名限制在公开目录,不会检查 Manifest 权限、Runtime 权限状态、
40
+ * Host 支持、可信用户手势或业务策略。顶层 envelope、握手 payload 与 bridge error 中的额外字段也不会被拒绝。
41
+ *
42
+ * [MINI_PROGRAM_MESSAGE_VERSION](/reference/symbols/protocol/constants/MINI_PROGRAM_MESSAGE_VERSION)
43
+ * 是新消息的发送版本,不是本 guard 的唯一接收版本;接受 v1 是兼容行为,
44
+ * 调用方仍须在握手后确认消息版本与已协商版本一致。method 是否公开及其权限 requirement 应查询
45
+ * [MINI_PROGRAM_PROTOCOL_CAPABILITIES](/reference/symbols/protocol/constants/MINI_PROGRAM_PROTOCOL_CAPABILITIES),
46
+ * 权限快照应先交给
47
+ * [parseMiniProgramRuntimePermissions](/reference/symbols/protocol/functions/parseMiniProgramRuntimePermissions) 解析。
48
+ *
49
+ * 类型谓词只收窄到宽泛的 {@link MiniProgramBridgeMessage}:`payload` 仍为 `unknown`,`method` 等字段仍保持可选。
50
+ * 需要精确处理 v2 cancel 或 operation progress 时,应在检查判别字段后使用
51
+ * [MiniProgramBridgeCancelMessage](/reference/symbols/protocol/interfaces/MiniProgramBridgeCancelMessage) 或
52
+ * [MiniProgramBridgeProgressMessage](/reference/symbols/protocol/interfaces/MiniProgramBridgeProgressMessage)。
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import {
57
+ * MINI_PROGRAM_PROTOCOL_CAPABILITIES,
58
+ * isMiniProgramBridgeMessage,
59
+ * } from '@heybox/hb-sdk/protocol';
60
+ *
61
+ * const publicMethods = new Set<string>(
62
+ * MINI_PROGRAM_PROTOCOL_CAPABILITIES.map(({ method }) => method),
63
+ * );
64
+ *
65
+ * window.addEventListener('message', (event) => {
66
+ * if (event.source !== iframe.contentWindow) return;
67
+ * if (expectedOrigin && expectedOrigin !== '*' && event.origin !== expectedOrigin) return;
68
+ * if (!isMiniProgramBridgeMessage(event.data)) return;
69
+ * if (event.data.nonce !== expectedNonce) return;
70
+ *
71
+ * if (event.data.type === 'request') {
72
+ * const method = event.data.method;
73
+ * if (!method || !publicMethods.has(method)) return;
74
+ * // 继续检查握手状态、权限与 method 对应的 payload。
75
+ * }
76
+ * });
77
+ * ```
78
+ *
79
+ * @see [MiniProgramBridgeMessage](/reference/symbols/protocol/interfaces/MiniProgramBridgeMessage) 宽泛的顶层 envelope 类型。
80
+ * @see [MINI_PROGRAM_MESSAGE_VERSION](/reference/symbols/protocol/constants/MINI_PROGRAM_MESSAGE_VERSION) 新消息的默认发送版本。
81
+ * @see [MINI_PROGRAM_BRIDGE_NONCE_PARAM](/reference/symbols/protocol/constants/MINI_PROGRAM_BRIDGE_NONCE_PARAM) iframe URL 中的 nonce 参数名。
82
+ * @see [MINI_PROGRAM_PROTOCOL_CAPABILITIES](/reference/symbols/protocol/constants/MINI_PROGRAM_PROTOCOL_CAPABILITIES) 公开 method 与权限 requirement 目录。
83
+ * @see [parseMiniProgramRuntimePermissions](/reference/symbols/protocol/functions/parseMiniProgramRuntimePermissions) Runtime 权限快照解析器。
84
+ */
15
85
  export function isMiniProgramBridgeMessage(value: unknown): value is MiniProgramBridgeMessage {
16
86
  if (!isRecord(value)) return false;
17
87
  if (value.namespace !== MINI_PROGRAM_MESSAGE_NAMESPACE || (value.version !== 1 && value.version !== 2) || !isNonEmptyString(value.nonce)) {
@@ -46,6 +116,60 @@ export function isMiniProgramBridgeMessage(value: unknown): value is MiniProgram
46
116
  return false;
47
117
  }
48
118
 
119
+ /**
120
+ * 判断未知值是否为以 `/` 分隔的规范逻辑相对路径。
121
+ *
122
+ * @param value - 来自 files API、derived ref、Host payload 或其他不可信边界的候选路径。
123
+ * @returns 值是满足协议语法的字符串时返回 `true`,并将 TypeScript 类型收窄为 `string`;否则返回 `false`。
124
+ *
125
+ * @remarks
126
+ * 该 guard 直接检查输入字符串,规则如下:
127
+ *
128
+ * - 必须是非空字符串;空串和非字符串值失败。
129
+ * - 不能以 `/` 开头或结尾,因此 `/absolute`、`nested/` 与 `/` 失败。
130
+ * - 不能包含反斜杠 `\\`、U+0000-U+001F 控制字符或 U+007F;Windows 风格分隔符不会被转换。
131
+ * - 按 `/` 分段后,每段必须非空且不能精确等于 `.` 或 `..`,因此连续 `/`、`nested/./file` 与
132
+ * `nested/../file` 失败。
133
+ *
134
+ * 只拒绝完整的 dot segment:`.env`、`..cache`、`file.` 与包含普通点号的名称都可通过;单独的 `'.'`
135
+ * 本身失败。{@link isMiniProgramDirectoryRef} 在外层显式允许 `relativePath: '.'` 表示 root 目录自身,
136
+ * {@link isMiniProgramFileRef} 则始终要求本 guard 通过,因此文件引用不能指向 root 自身。
137
+ *
138
+ * 该函数不 trim 空白、不 URL/percent decode、不 Unicode normalize、不折叠分隔符,也不应用平台保留名或
139
+ * 字符规则。空白段、`%2e%2e`、全角字符和 `C:/file` 等只要满足上述原始字符串规则就可能通过;路径长度、
140
+ * 段长度和段数量也没有在此限制。调用方不能把“通过”解释为操作系统级 canonical path。
141
+ *
142
+ * 返回 `true` 只证明语法可作为协议逻辑相对路径,不证明目标存在、末段是文件还是目录、root/handle 属于
143
+ * 当前 session/origin、`filesystem` 权限已启用、访问 mode 允许当前操作或 Host 支持该 capability。
144
+ * `normalizeRelativePath()` 这类 SDK/Runtime helper 只是复用本 predicate:合法时原样返回同一个字符串,非法时
145
+ * 转成对应 SDK/bridge 错误;它不会进一步规范化路径。
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * import { isMiniProgramRelativePath } from '@heybox/hb-sdk/protocol';
150
+ *
151
+ * const paths: unknown[] = [
152
+ * 'data.json',
153
+ * 'nested/data.json',
154
+ * '.env',
155
+ * '.',
156
+ * '../secret',
157
+ * 'nested//file',
158
+ * 'nested\\file',
159
+ * ];
160
+ *
161
+ * const validPaths = paths.filter(isMiniProgramRelativePath);
162
+ * // ['data.json', 'nested/data.json', '.env']
163
+ * console.log(validPaths);
164
+ * ```
165
+ *
166
+ * @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 始终使用本规则的 derived 文件引用。
167
+ * @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 额外允许 `'.'` 的 derived 目录引用。
168
+ * @see [isMiniProgramPathRootRef](/reference/symbols/protocol/functions/isMiniProgramPathRootRef) 与相对路径组合的逻辑 root guard。
169
+ * @see [PathRoot](/reference/symbols/root/interfaces/PathRoot) 提供 `file()` / `directory()` 的 SDK 路径入口。
170
+ * @see [FileSystem](/reference/symbols/root/interfaces/FileSystem) `files.sandbox` 逻辑路径能力。
171
+ * @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 已授权目录下的相对路径能力。
172
+ */
49
173
  export function isMiniProgramRelativePath(value: unknown): value is string {
50
174
  if (typeof value !== 'string' || !value || value.startsWith('/') || value.endsWith('/')) {
51
175
  return false;
@@ -54,12 +178,129 @@ export function isMiniProgramRelativePath(value: unknown): value is string {
54
178
  return value.split('/').every((segment) => Boolean(segment) && segment !== '.' && segment !== '..');
55
179
  }
56
180
 
181
+ /**
182
+ * 判断未知值是否可作为 derived file/directory ref 的逻辑路径 root。
183
+ *
184
+ * @param value - 来自文件引用、目录引用或其他不可信 wire 边界的候选 root。
185
+ * @returns 值满足 sandbox root 或 direct directory root 任一结构时返回 `true`,并将 TypeScript 类型
186
+ * 收窄为 {@link MiniProgramPathRootRef} 判别联合;否则返回 `false`。
187
+ *
188
+ * @remarks
189
+ * 支持两个以 `kind` 区分的分支:
190
+ *
191
+ * - Sandbox root:`{ kind: 'sandbox' }`。对通常的对象字面量,除 `kind` 外不能携带任何字段。
192
+ * - Directory root:`{ kind: 'directory', handleId }`。`handleId` 必须是非空字符串;空白字符串仍满足
193
+ * 本 guard 的最低结构,格式、长度和归属由 Runtime 继续校验。
194
+ *
195
+ * direct file ref、derived directory ref、普通路径字符串以及其他 `kind` 都不能作为 PathRoot。特别是
196
+ * `{ kind: 'directory', root, relativePath }` 会因额外 canonical 外字段而失败:derived ref 只能引用 sandbox
197
+ * 或 direct directory root,不能递归嵌套另一层 derived root。
198
+ *
199
+ * 两个分支都会拒绝 canonical 集合之外的可枚举自有字符串键,包括值为 `undefined` 的额外键。检查基于
200
+ * `Object.keys()`,因此非枚举属性、symbol 键和继承属性不会被列入;必需属性本身也不要求是自有或可枚举
201
+ * 属性。该函数不会复制、冻结或规范化输入。
202
+ *
203
+ * 返回 `true` 只建立结构类型收窄,不证明 directory `handleId` 由当前 Runtime 签发、属于当前 session/origin、
204
+ * 仍然有效且确为目录,也不证明 `filesystem` 权限、目录访问 mode、provenance 或 Host capability。sandbox
205
+ * 标记同样不自行建立隔离边界;Runtime 必须把它解析到当前 Mini-program 的私有 root 并执行实际权限策略。
206
+ *
207
+ * {@link isMiniProgramFileRef} 与 {@link isMiniProgramDirectoryRef} 都用该 guard 校验 derived ref 的 `root`;
208
+ * 它只校验 root 身份,不校验各自的 `relativePath`。
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * import { isMiniProgramPathRootRef } from '@heybox/hb-sdk/protocol';
213
+ *
214
+ * const candidates: unknown[] = [
215
+ * { kind: 'sandbox' },
216
+ * { kind: 'directory', handleId: 'directory_1' },
217
+ * ];
218
+ *
219
+ * for (const candidate of candidates) {
220
+ * if (!isMiniProgramPathRootRef(candidate)) continue;
221
+ * if (candidate.kind === 'sandbox') {
222
+ * console.log('private sandbox root');
223
+ * } else {
224
+ * console.log('authorized directory root', candidate.handleId);
225
+ * }
226
+ * }
227
+ *
228
+ * isMiniProgramPathRootRef({ kind: 'file', handleId: 'file_1' }); // false
229
+ * isMiniProgramPathRootRef({ kind: 'directory', root: { kind: 'sandbox' }, relativePath: 'nested' }); // false
230
+ * ```
231
+ *
232
+ * @see [MiniProgramPathRootRef](/reference/symbols/protocol/types/MiniProgramPathRootRef) sandbox/direct-directory 联合类型。
233
+ * @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 使用 PathRoot 的 derived 文件引用。
234
+ * @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 使用 PathRoot 的 derived 目录引用。
235
+ * @see [PathRoot](/reference/symbols/root/interfaces/PathRoot) SDK 公开的逻辑路径 root 能力。
236
+ * @see [FilesModule](/reference/symbols/root/interfaces/FilesModule) 提供 `files.sandbox` 与目录 picker 的入口。
237
+ * @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 可作为 derived path root 的目录代理。
238
+ */
57
239
  export function isMiniProgramPathRootRef(value: unknown): value is MiniProgramPathRootRef {
58
240
  if (!isRecord(value)) return false;
59
241
  if (value.kind === 'sandbox') return hasOnlyKeys(value, ['kind']);
60
242
  return value.kind === 'directory' && isNonEmptyString(value.handleId) && hasOnlyKeys(value, ['kind', 'handleId']);
61
243
  }
62
244
 
245
+ /**
246
+ * 判断未知值是否为 direct handle 或 path-derived 文件引用。
247
+ *
248
+ * @param value - 来自 descriptor、capability payload 或其他不可信 wire 边界的候选文件引用。
249
+ * @returns 值满足 {@link MiniProgramFileRef} 任一分支的最低结构时返回 `true`,并将 TypeScript 类型
250
+ * 收窄为 direct/derived 文件引用联合;否则返回 `false`。
251
+ *
252
+ * @remarks
253
+ * 两个分支都要求 `kind === 'file'`,并对通常的对象字面量使用互斥的 canonical key 集合:
254
+ *
255
+ * - Direct ref:`{ kind: 'file', handleId }`。`handleId` 必须是非空字符串;不能同时携带 `root` 或
256
+ * `relativePath`。
257
+ * - Derived ref:`{ kind: 'file', root, relativePath }`。不能携带 `handleId`;`root` 必须通过
258
+ * {@link isMiniProgramPathRootRef},即精确的 `{ kind: 'sandbox' }`,或带非空 `handleId` 的 direct
259
+ * directory root。file ref 与再次嵌套的 derived directory ref 都不能作为 root。
260
+ *
261
+ * Derived ref 的 `relativePath` 必须通过 {@link isMiniProgramRelativePath}:非空、不以 `/` 开头或结尾,
262
+ * 不含空段、`.` / `..` 点段、反斜杠、U+0000-U+001F 控制字符或 U+007F。文件必须指向 root 下的具体路径,
263
+ * 因此与 {@link isMiniProgramDirectoryRef} 不同,`relativePath: '.'` 不合法。该 guard 不会 URL decode、
264
+ * Unicode normalize、解析平台分隔符、限制路径长度,或确认末段是文件而不是目录。
265
+ *
266
+ * direct 与 derived 分支都会拒绝 canonical 集合之外的可枚举自有字符串键,包括值为 `undefined` 的额外键。
267
+ * 检查基于 `Object.keys()`,因此非枚举属性、symbol 键和继承属性不会被列入;必需属性本身也不要求是自有
268
+ * 或可枚举属性。返回对象不会被复制、冻结或规范化。
269
+ *
270
+ * 返回 `true` 只证明当前结构可按 wire 类型读取,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、
271
+ * 属于当前 session/origin、仍然有效且类型匹配,也不证明目标文件存在、`filesystem` 权限已启用、访问 mode
272
+ * 允许当前操作或 Host 支持对应 capability。Runtime 在实际解析 ref 时仍须查询 handle registry,执行权限、
273
+ * provenance 与目标类型校验,并解析 derived path。
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * import { isMiniProgramFileRef } from '@heybox/hb-sdk/protocol';
278
+ *
279
+ * const candidate: unknown = {
280
+ * kind: 'file',
281
+ * root: { kind: 'directory', handleId: 'directory_1' },
282
+ * relativePath: 'screenshots/cover.png',
283
+ * };
284
+ *
285
+ * if (isMiniProgramFileRef(candidate)) {
286
+ * if ('handleId' in candidate) {
287
+ * console.log('direct', candidate.handleId);
288
+ * } else {
289
+ * console.log('derived', candidate.root, candidate.relativePath);
290
+ * }
291
+ * }
292
+ *
293
+ * isMiniProgramFileRef({ kind: 'file', handleId: 'file_1' }); // true
294
+ * isMiniProgramFileRef({ kind: 'file', root: { kind: 'sandbox' }, relativePath: '.' }); // false
295
+ * ```
296
+ *
297
+ * @see [MiniProgramFileRef](/reference/symbols/protocol/types/MiniProgramFileRef) direct/derived 联合类型。
298
+ * @see [isMiniProgramPathRootRef](/reference/symbols/protocol/functions/isMiniProgramPathRootRef) derived ref 的 root guard。
299
+ * @see [isMiniProgramRelativePath](/reference/symbols/protocol/functions/isMiniProgramRelativePath) derived 文件路径规则。
300
+ * @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) 包含名称和 mode 的文件 descriptor guard。
301
+ * @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 可用 `'.'` 指向 root 自身的目录引用 guard。
302
+ * @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 使用文件引用的公开 I/O 代理。
303
+ */
63
304
  export function isMiniProgramFileRef(value: unknown): value is MiniProgramFileRef {
64
305
  if (!isRecord(value) || value.kind !== 'file') return false;
65
306
  if (isNonEmptyString(value.handleId)) {
@@ -70,6 +311,65 @@ export function isMiniProgramFileRef(value: unknown): value is MiniProgramFileRe
70
311
  );
71
312
  }
72
313
 
314
+ /**
315
+ * 判断未知值是否为 direct handle 或 path-derived 目录引用。
316
+ *
317
+ * @param value - 来自 descriptor、capability payload 或其他不可信 wire 边界的候选目录引用。
318
+ * @returns 值满足 {@link MiniProgramDirectoryRef} 任一分支的最低结构时返回 `true`,并将 TypeScript 类型
319
+ * 收窄为 direct/derived 目录引用联合;否则返回 `false`。
320
+ *
321
+ * @remarks
322
+ * 两个分支都要求 `kind === 'directory'`,并对通常的对象字面量使用互斥的 canonical key 集合:
323
+ *
324
+ * - Direct ref:`{ kind: 'directory', handleId }`。`handleId` 必须是非空字符串;不能同时携带 `root` 或
325
+ * `relativePath`。
326
+ * - Derived ref:`{ kind: 'directory', root, relativePath }`。不能携带 `handleId`;`root` 必须通过
327
+ * {@link isMiniProgramPathRootRef},即精确的 `{ kind: 'sandbox' }`,或带非空 `handleId` 的 direct
328
+ * directory root。file ref 与再次嵌套的 derived directory ref 都不能作为 root。
329
+ *
330
+ * Derived ref 的 `relativePath` 可以是 `'.'`,表示 root 目录自身;这是目录引用专属规则,普通
331
+ * {@link isMiniProgramRelativePath} 和 file ref 都不接受 `'.'`。其他相对路径必须非空,不以 `/` 开头或结尾,
332
+ * 不含空段、`.` / `..` 点段、反斜杠、U+0000-U+001F 控制字符或 U+007F。该校验不会 URL decode、
333
+ * Unicode normalize、解析平台分隔符、限制长度,或判断路径指向的目录是否存在。
334
+ *
335
+ * direct 与 derived 分支都会拒绝 canonical 集合之外的可枚举自有字符串键,包括值为 `undefined` 的额外键。
336
+ * 检查基于 `Object.keys()`,因此非枚举属性、symbol 键和继承属性不会被列入;必需属性本身也不要求是自有
337
+ * 或可枚举属性。返回对象不会被复制、冻结或规范化。
338
+ *
339
+ * 返回 `true` 只证明当前结构可按 wire 类型读取,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、
340
+ * 属于当前 session/origin、仍然有效且确为目录,也不证明目标存在、`filesystem` 权限已启用、访问模式允许
341
+ * 当前操作或 Host 支持对应 capability。Runtime 在实际解析 ref 时仍须查询 handle registry、执行权限与
342
+ * provenance 校验,并解析 derived path。
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * import { isMiniProgramDirectoryRef } from '@heybox/hb-sdk/protocol';
347
+ *
348
+ * const candidate: unknown = {
349
+ * kind: 'directory',
350
+ * root: { kind: 'directory', handleId: 'directory_1' },
351
+ * relativePath: 'screenshots/2026',
352
+ * };
353
+ *
354
+ * if (isMiniProgramDirectoryRef(candidate)) {
355
+ * if ('handleId' in candidate) {
356
+ * console.log('direct', candidate.handleId);
357
+ * } else {
358
+ * console.log('derived', candidate.root, candidate.relativePath);
359
+ * }
360
+ * }
361
+ *
362
+ * isMiniProgramDirectoryRef({ kind: 'directory', root: { kind: 'sandbox' }, relativePath: '.' }); // true
363
+ * isMiniProgramDirectoryRef({ kind: 'directory', handleId: 'd1', relativePath: '.' }); // false:分支键混用
364
+ * ```
365
+ *
366
+ * @see [MiniProgramDirectoryRef](/reference/symbols/protocol/types/MiniProgramDirectoryRef) direct/derived 联合类型。
367
+ * @see [isMiniProgramPathRootRef](/reference/symbols/protocol/functions/isMiniProgramPathRootRef) derived ref 的 root guard。
368
+ * @see [isMiniProgramRelativePath](/reference/symbols/protocol/functions/isMiniProgramRelativePath) 非 `'.'` 相对路径规则。
369
+ * @see [isMiniProgramDirectoryDescriptor](/reference/symbols/protocol/functions/isMiniProgramDirectoryDescriptor) 包含名称和 mode 的目录 descriptor guard。
370
+ * @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 不接受 root 自身 `'.'` 的文件引用 guard。
371
+ * @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 使用目录引用的公开路径操作代理。
372
+ */
73
373
  export function isMiniProgramDirectoryRef(value: unknown): value is MiniProgramDirectoryRef {
74
374
  if (!isRecord(value) || value.kind !== 'directory') return false;
75
375
  if (isNonEmptyString(value.handleId)) {
@@ -80,6 +380,72 @@ export function isMiniProgramDirectoryRef(value: unknown): value is MiniProgramD
80
380
  );
81
381
  }
82
382
 
383
+ /**
384
+ * 判断未知值是否具有文件 descriptor 的最低 wire 结构。
385
+ *
386
+ * @param value - 来自 Host、bridge response、目录列表或其他不可信边界的候选 descriptor。
387
+ * @returns 顶层字段及嵌套文件引用都满足协议结构时返回 `true`,并将 TypeScript 类型收窄为
388
+ * {@link MiniProgramFileDescriptor};否则返回 `false`。
389
+ *
390
+ * @remarks
391
+ * guard 会读取并校验以下四个属性;对通常的对象字面量,它们也是唯一允许的可枚举自有字符串字段:
392
+ *
393
+ * - `kind`:固定为 `'file'`。
394
+ * - `name`:非空字符串。该 guard 不检查空白、`.`、`..`、`/`、反斜杠、控制字符、平台保留名或
395
+ * `name` 是否等于 `ref.relativePath` 的 basename;SDK 创建 `FileHandle` 时还会执行更严格的名称校验。
396
+ * - `mode`:只能是 `'read'` 或 `'readwrite'`。这是 descriptor 声明值,不证明 Host 实际授予了读写权限。
397
+ * - `ref`:必须通过 {@link isMiniProgramFileRef}。
398
+ *
399
+ * `ref` 可以是 `{ kind: 'file', handleId }` 形式的 direct file ref,其中 `handleId` 只要求非空;也可以是
400
+ * `{ kind: 'file', root, relativePath }` 形式的 derived ref。derived root 只能是精确的 sandbox root 或
401
+ * 带非空 `handleId` 的 direct directory root,不能是 file ref 或再次派生的 directory ref;`relativePath`
402
+ * 必须是非空、非绝对、无尾随 `/`、无空段、`.` / `..` 点段、反斜杠或控制字符的相对路径。与目录 ref
403
+ * 不同,文件 ref 不接受 `relativePath: '.'`,因为它必须指向 root 下的具体文件。
404
+ *
405
+ * 顶层 descriptor、`ref` 与 `root` 的额外可枚举自有字符串键都会导致失败,包括值为 `undefined` 的额外键;
406
+ * 非枚举属性、symbol 键和继承属性不在 `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举
407
+ * 属性。该函数不会复制、冻结或规范化输入。
408
+ *
409
+ * 返回 `true` 只建立结构类型收窄,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、属于当前
410
+ * session/origin、仍然有效且确为预期类型,也不证明文件存在、名称真实、`filesystem` 权限已启用、mode
411
+ * 允许当前操作或 Host 支持对应 capability。实际文件操作仍须由 Runtime 查询 handle registry、执行权限、
412
+ * provenance、目标类型和存在性校验。
413
+ *
414
+ * {@link isMiniProgramFileSystemEntityDescriptor} 使用本 guard 与目录 descriptor guard 识别目录列表项。
415
+ * `files.pickFiles()` 与 `files.saveFile()` 的成功结果要求更窄的 direct file ref,而 sandbox/path API 返回的
416
+ * descriptor 可以使用 derived ref;本 guard 本身不会区分 descriptor 的来源。
417
+ *
418
+ * @example
419
+ * ```ts
420
+ * import { isMiniProgramFileDescriptor } from '@heybox/hb-sdk/protocol';
421
+ *
422
+ * const candidate = {
423
+ * kind: 'file',
424
+ * name: 'report.json',
425
+ * mode: 'readwrite',
426
+ * ref: {
427
+ * kind: 'file',
428
+ * root: { kind: 'sandbox' },
429
+ * relativePath: 'exports/report.json',
430
+ * },
431
+ * };
432
+ * const untrusted: unknown = candidate;
433
+ *
434
+ * if (isMiniProgramFileDescriptor(untrusted)) {
435
+ * // 这里只能安全读取 wire 字段;文件 I/O 仍交给 files API/Runtime。
436
+ * console.log(untrusted.name, untrusted.mode, untrusted.ref);
437
+ * }
438
+ *
439
+ * isMiniProgramFileDescriptor({ ...candidate, size: 1024 }); // false:顶层存在额外键
440
+ * ```
441
+ *
442
+ * @see [MiniProgramFileDescriptor](/reference/symbols/protocol/interfaces/MiniProgramFileDescriptor) descriptor 字段类型。
443
+ * @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 嵌套 direct/derived 文件引用校验。
444
+ * @see [isMiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileSystemEntityDescriptor) 文件与目录 descriptor 联合 guard。
445
+ * @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 校验后由 SDK 建立的文件代理。
446
+ * @see [PickFilesResult](/reference/symbols/protocol/types/PickFilesResult) picker 的 direct-ref 文件结果。
447
+ * @see [SaveFileResult](/reference/symbols/protocol/types/SaveFileResult) save picker 的 direct-ref 文件结果。
448
+ */
83
449
  export function isMiniProgramFileDescriptor(value: unknown): value is MiniProgramFileDescriptor {
84
450
  return (
85
451
  isRecord(value) &&
@@ -91,6 +457,68 @@ export function isMiniProgramFileDescriptor(value: unknown): value is MiniProgra
91
457
  );
92
458
  }
93
459
 
460
+ /**
461
+ * 判断未知值是否具有目录 descriptor 的精确 wire 结构。
462
+ *
463
+ * @param value - 来自 Host、bridge response、目录列表或其他不可信边界的候选 descriptor。
464
+ * @returns 顶层字段及嵌套目录引用都满足协议最低结构时返回 `true`,并将 TypeScript 类型收窄为
465
+ * {@link MiniProgramDirectoryDescriptor};否则返回 `false`。
466
+ *
467
+ * @remarks
468
+ * guard 会读取并校验以下四个属性;对通常的对象字面量,它们也是唯一允许的可枚举自有字符串字段:
469
+ *
470
+ * - `kind`:固定为 `'directory'`。
471
+ * - `name`:非空字符串。该 guard 不检查空白、`.`、`..`、斜杠、反斜杠、控制字符或平台文件名规则;
472
+ * SDK 将 descriptor 转为 `DirectoryHandle` 时还会执行更严格的名称校验。
473
+ * - `mode`:只能是 `'read'` 或 `'readwrite'`;这里只识别声明值,不证明 Host 实际授予了对应访问模式。
474
+ * - `ref`:必须通过 {@link isMiniProgramDirectoryRef}。
475
+ *
476
+ * `ref` 可以是 `{ kind: 'directory', handleId }` 形式的直接引用,其中 `handleId` 只要求为非空字符串;
477
+ * 也可以是 `{ kind: 'directory', root, relativePath }` 形式的派生引用。派生引用的 `root` 只能是精确的
478
+ * sandbox root,或带非空 `handleId` 的直接目录 root;`relativePath` 可用 `'.'` 表示 root 自身,其他值
479
+ * 必须是非空、非绝对、无尾随 `/`、无空段/点段/父目录段、反斜杠或控制字符的规范相对路径。
480
+ *
481
+ * 顶层 descriptor、`ref` 与 `root` 的额外可枚举自有字符串键都会导致失败;但非枚举属性、symbol 键和
482
+ * 继承属性不在 `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举属性。该函数不会复制、
483
+ * 冻结或规范化对象,也不会验证 `handleId` 是否由当前 Runtime 签发、是否属于当前 session/origin、目标目录
484
+ * 是否存在、`name` 是否与真实目录一致,或 Manifest/Runtime 权限、Host 支持及当前操作是否允许。
485
+ * 返回 `true` 只建立结构类型收窄,不建立信任边界。
486
+ *
487
+ * {@link isMiniProgramFileSystemEntityDescriptor} 使用本 guard 与文件 descriptor guard 共同识别目录列表项。
488
+ * `files.pickDirectory()` 成功结果还要求 direct ref,而 sandbox/path 派生的目录可由 `DirectoryHandle`
489
+ * 的路径 API 产生;这些来源和能力差异不能仅凭本 guard 判定。
490
+ *
491
+ * @example
492
+ * ```ts
493
+ * import { isMiniProgramDirectoryDescriptor } from '@heybox/hb-sdk/protocol';
494
+ *
495
+ * const candidate = {
496
+ * kind: 'directory',
497
+ * name: 'screenshots',
498
+ * mode: 'readwrite',
499
+ * ref: {
500
+ * kind: 'directory',
501
+ * root: { kind: 'sandbox' },
502
+ * relativePath: 'exports/screenshots',
503
+ * },
504
+ * };
505
+ * const untrusted: unknown = candidate;
506
+ *
507
+ * if (isMiniProgramDirectoryDescriptor(untrusted)) {
508
+ * // 这里只能安全读取已收窄的 wire 字段;实际操作仍交给 files API/Runtime。
509
+ * console.log(untrusted.name, untrusted.ref);
510
+ * }
511
+ *
512
+ * isMiniProgramDirectoryDescriptor({ ...candidate, size: 0 }); // false:顶层存在额外键
513
+ * ```
514
+ *
515
+ * @see [MiniProgramDirectoryDescriptor](/reference/symbols/protocol/interfaces/MiniProgramDirectoryDescriptor) descriptor 字段类型。
516
+ * @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 嵌套 direct/derived 引用校验。
517
+ * @see [isMiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileSystemEntityDescriptor) 文件与目录 descriptor 联合 guard。
518
+ * @see [FilesModule](/reference/symbols/root/interfaces/FilesModule) `files` 公开能力入口。
519
+ * @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 校验后由 SDK 建立的目录代理。
520
+ * @see [PickDirectoryResult](/reference/symbols/protocol/types/PickDirectoryResult) picker 的 direct-ref 返回契约。
521
+ */
94
522
  export function isMiniProgramDirectoryDescriptor(value: unknown): value is MiniProgramDirectoryDescriptor {
95
523
  return (
96
524
  isRecord(value) &&
@@ -102,14 +530,179 @@ export function isMiniProgramDirectoryDescriptor(value: unknown): value is MiniP
102
530
  );
103
531
  }
104
532
 
533
+ /**
534
+ * 判断未知值是否为文件或目录 descriptor。
535
+ *
536
+ * @param value - 来自 `directory.list`、Host response 或其他不可信边界的候选文件系统实体 descriptor。
537
+ * @returns 值通过文件或目录任一子 guard 时返回 `true`,并将 TypeScript 类型收窄为
538
+ * {@link MiniProgramFileSystemEntityDescriptor} 判别联合;否则返回 `false`。
539
+ *
540
+ * @remarks
541
+ * 该函数不维护第三套 schema,而是依次调用 {@link isMiniProgramFileDescriptor} 和
542
+ * {@link isMiniProgramDirectoryDescriptor}:
543
+ *
544
+ * - `kind === 'file'` 的值必须同时具有非空 `name`、`'read' | 'readwrite'` mode,以及合法 direct/derived
545
+ * file ref。derived 文件路径必须指向 root 下的具体文件,不接受 `relativePath: '.'`。
546
+ * - `kind === 'directory'` 的值必须同时具有非空 `name`、`'read' | 'readwrite'` mode,以及合法
547
+ * direct/derived directory ref。derived 目录 ref 可用 `relativePath: '.'` 表示 root 自身。
548
+ *
549
+ * 两个分支的 `kind` 互斥,因此通过后可直接用 `descriptor.kind` 判别收窄到对应类型。各分支完整继承子 guard
550
+ * 的键与嵌套 ref 规则:对通常的对象字面量,顶层只允许 `kind`、`name`、`mode`、`ref`,nested ref/root
551
+ * 也只允许其 canonical key;额外可枚举自有字符串键会导致失败。非枚举属性、symbol 键和继承属性不在
552
+ * `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举属性。
553
+ *
554
+ * 返回 `true` 只建立单个 descriptor 的结构类型收窄。该函数不会复制、冻结或规范化输入,也不会验证
555
+ * `name` 的完整平台规则、handle 是否由当前 Runtime 签发并属于当前 session/origin、目标实体是否存在且类型
556
+ * 与 `kind` 一致、mode 是否真实、`filesystem` 权限、provenance 或 Host capability。实际创建 `FileHandle` /
557
+ * `DirectoryHandle` 时仍须由 SDK 与 Runtime 完成这些校验。
558
+ *
559
+ * 对 {@link DirectoryListResult},本 guard 只适合逐项验证,不验证外层值是否为稠密数组、结果数量、重复项、
560
+ * 子项 mode 是否继承父目录,或列表是否来自当前请求。`DirectoryHandle.list()` 调用链必须另行处理这些集合级
561
+ * 与请求级约束。
562
+ *
563
+ * @example
564
+ * ```ts
565
+ * import { isMiniProgramFileSystemEntityDescriptor } from '@heybox/hb-sdk/protocol';
566
+ *
567
+ * const candidates: unknown[] = [
568
+ * { kind: 'file', name: 'report.json', mode: 'read', ref: { kind: 'file', handleId: 'file_1' } },
569
+ * { kind: 'directory', name: 'exports', mode: 'read', ref: { kind: 'directory', handleId: 'directory_1' } },
570
+ * ];
571
+ *
572
+ * const descriptors = candidates.filter(isMiniProgramFileSystemEntityDescriptor);
573
+ * for (const descriptor of descriptors) {
574
+ * if (descriptor.kind === 'file') {
575
+ * console.log('file', descriptor.ref);
576
+ * } else {
577
+ * console.log('directory', descriptor.ref);
578
+ * }
579
+ * }
580
+ * ```
581
+ *
582
+ * @see [MiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/types/MiniProgramFileSystemEntityDescriptor) file/directory 判别联合。
583
+ * @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) file 分支的完整字段与边界。
584
+ * @see [isMiniProgramDirectoryDescriptor](/reference/symbols/protocol/functions/isMiniProgramDirectoryDescriptor) directory 分支的完整字段与边界。
585
+ * @see [DirectoryListResult](/reference/symbols/protocol/types/DirectoryListResult) 由 descriptor 数组组成的 wire 返回类型。
586
+ * @see [FileSystemEntity](/reference/symbols/root/types/FileSystemEntity) SDK 公开的 FileHandle/DirectoryHandle 联合。
587
+ * @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 文件 descriptor 对应的能力代理。
588
+ * @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 目录 descriptor 对应的能力代理及 `list()` 入口。
589
+ */
105
590
  export function isMiniProgramFileSystemEntityDescriptor(value: unknown): value is MiniProgramFileSystemEntityDescriptor {
106
591
  return isMiniProgramFileDescriptor(value) || isMiniProgramDirectoryDescriptor(value);
107
592
  }
108
593
 
594
+ /**
595
+ * 判断未知值是否为使用普通 `ArrayBuffer` 存储的 `Uint8Array` 字节视图。
596
+ *
597
+ * @param value - 来自 bridge payload、Host 返回值或其他不可信边界的候选字节数据。
598
+ * @returns 值是当前 realm 的 `Uint8Array`(或其子类),且 `buffer` 是 `ArrayBuffer` 时返回 `true`,
599
+ * 并将 TypeScript 类型收窄为 `Uint8Array`;否则返回 `false`。
600
+ *
601
+ * @remarks
602
+ * `Uint8Array` 的每个索引元素由 JavaScript 保证为 `0` 到 `255` 的整数。本 guard 接受空视图、非零
603
+ * `byteOffset` 以及只覆盖底层 buffer 一部分的视图;它不要求视图覆盖整个 `ArrayBuffer`,也不限制
604
+ * `byteLength`、文件大小或 Companion stdio 消息大小。
605
+ *
606
+ * 普通数组无论是否稀疏、`ArrayBuffer` 本身、`DataView`、`Int8Array`、`Uint8ClampedArray` 及其他 typed array
607
+ * 都会被拒绝。使用 `SharedArrayBuffer` 作为 backing buffer 的 `Uint8Array` 也会被拒绝。由于判断依赖
608
+ * `instanceof`,由另一个 JavaScript realm 创建且未经过 structured clone 重建的 `Uint8Array` 可能无法通过;
609
+ * 当前 realm 的 `Uint8Array` 子类则可能通过。
610
+ *
611
+ * 该函数不会枚举键,因此挂在 `Uint8Array` 实例上的额外自有属性不会导致失败;它也不会检查 buffer
612
+ * 是否已 detached、数据来源是否可信、内容是否符合业务格式,或调用方是否拥有文件/Companion 权限。
613
+ * 返回 `true` 后仍得到原来的可变视图,而不是副本或冻结值。跨异步边界持有或交给其他消费者前,建议使用
614
+ * `new Uint8Array(value)` 创建只包含当前视图字节的快照。
615
+ *
616
+ * 文件 `readBytes()` 用它识别 Host 返回的
617
+ * [FileReadBytesResult](/reference/symbols/protocol/types/FileReadBytesResult),`writeBytes()` 的
618
+ * [FileWriteBytesPayload](/reference/symbols/protocol/interfaces/FileWriteBytesPayload) `content` 也采用同一字节表示;
619
+ * 路径、句柄类型和访问模式由文件引用与 descriptor guards 单独校验。Companion stdio 的 stdin、stdout 和 stderr
620
+ * 同样传输 `Uint8Array`,但 Session ownership、controller generation、stream、sequence、framing、容量与权限
621
+ * 都不属于本 guard 的职责。
622
+ *
623
+ * @example
624
+ * ```ts
625
+ * import { isMiniProgramByteArray } from '@heybox/hb-sdk/protocol';
626
+ *
627
+ * const candidate: unknown = new Uint8Array([0, 128, 255]);
628
+ * if (!isMiniProgramByteArray(candidate)) {
629
+ * throw new TypeError('需要 ArrayBuffer-backed Uint8Array');
630
+ * }
631
+ *
632
+ * // guard 只做结构收窄;复制后再跨异步边界使用。
633
+ * const snapshot = new Uint8Array(candidate);
634
+ *
635
+ * isMiniProgramByteArray([0, 128, 255]); // false:普通数组不是 wire 字节视图
636
+ * isMiniProgramByteArray(new DataView(new ArrayBuffer(3))); // false
637
+ * ```
638
+ *
639
+ * @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) `readBytes()` 与 `writeBytes()` 的业务入口。
640
+ * @see [FileReadBytesResult](/reference/symbols/protocol/types/FileReadBytesResult) 文件二进制读取结果。
641
+ * @see [FileWriteBytesPayload](/reference/symbols/protocol/interfaces/FileWriteBytesPayload) 文件二进制写入 payload。
642
+ * @see [CompanionStdio](/reference/symbols/root/interfaces/CompanionStdio) Companion 原始字节 stdin/stdout/stderr。
643
+ * @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) 文件 descriptor guard。
644
+ * @see [isMiniProgramDirectoryDescriptor](/reference/symbols/protocol/functions/isMiniProgramDirectoryDescriptor) 目录 descriptor guard。
645
+ */
109
646
  export function isMiniProgramByteArray(value: unknown): value is Uint8Array {
110
647
  return value instanceof Uint8Array && value.buffer instanceof ArrayBuffer;
111
648
  }
112
649
 
650
+ /**
651
+ * 判断未知值是否具有文件元数据快照的最低 wire 结构。
652
+ *
653
+ * @param value - 来自 `file.stat` Host 返回值、bridge response 或其他不可信边界的候选元数据。
654
+ * @returns `kind`、`size` 与可选时间字段满足 {@link MiniProgramFileStat} 结构时返回 `true`,并将
655
+ * TypeScript 类型收窄为文件元数据;否则返回 `false`。
656
+ *
657
+ * @remarks
658
+ * 对通常的对象字面量,仅允许 `kind`、`size`、`modifiedAt`、`createdAt` 四个 canonical key:
659
+ *
660
+ * - `kind` 必须固定为 `'file'`。
661
+ * - `size` 必须是 `0` 到 `Number.MAX_SAFE_INTEGER` 范围内的整数。`0` 表示空文件;负数、小数、`NaN`、
662
+ * `Infinity`、unsafe integer、bigint 和数字字符串都会被拒绝。文件 API 将该值解释为字节数,但本 guard
663
+ * 只验证数字结构,不能证明 Host 使用了正确单位。
664
+ * - `modifiedAt` 与 `createdAt` 相互独立且都可省略,也可显式为 `undefined`;提供时必须是非负有限 number。
665
+ * 与 `size` 不同,时间值不要求是整数或 safe integer,因此非负小数以及大于 `Number.MAX_SAFE_INTEGER`
666
+ * 但仍有限的值都可通过。
667
+ *
668
+ * 文件 API 把时间字段解释为 Unix epoch milliseconds,但该 guard 不验证值是否落在 JavaScript `Date`
669
+ * 可表示范围内,也不要求 `createdAt <= modifiedAt`、时间不晚于当前时刻,或两项同时存在。消费者若要排序、
670
+ * 格式化或比较时间,仍须按自己的业务范围继续校验。
671
+ *
672
+ * canonical 集合之外的可枚举自有字符串键会导致失败,包括值为 `undefined` 的额外键;非枚举属性、
673
+ * symbol 键和继承属性不在 `Object.keys()` 检查范围内,必需字段本身也不要求是自有或可枚举属性。
674
+ * 该函数不会复制、冻结或规范化输入,返回的仍是原始可变对象。
675
+ *
676
+ * 返回 `true` 只建立结构类型收窄,不证明该快照来自可信 Host、对应当前 `FileHandle` 或当前 request,也不证明
677
+ * 文件仍然存在、`size` 与磁盘内容一致、时间由文件系统提供、handle 属于当前 session/origin、`filesystem`
678
+ * 权限已启用或 Host 支持 `file.stat`。这些关联、权限与 I/O 错误必须由 Runtime 和 `FileHandle.stat()` 调用链处理。
679
+ *
680
+ * @example
681
+ * ```ts
682
+ * import { isMiniProgramFileStat } from '@heybox/hb-sdk/protocol';
683
+ *
684
+ * const candidate: unknown = {
685
+ * kind: 'file',
686
+ * size: 4096,
687
+ * modifiedAt: 1_795_027_200_000,
688
+ * };
689
+ *
690
+ * if (isMiniProgramFileStat(candidate)) {
691
+ * console.log(`${candidate.size} bytes`);
692
+ * if (candidate.modifiedAt !== undefined) {
693
+ * console.log(candidate.modifiedAt);
694
+ * }
695
+ * }
696
+ *
697
+ * isMiniProgramFileStat({ kind: 'file', size: 0 }); // true:时间字段可全部省略
698
+ * isMiniProgramFileStat({ kind: 'file', size: 1.5 }); // false:size 必须是 safe integer
699
+ * ```
700
+ *
701
+ * @see [MiniProgramFileStat](/reference/symbols/protocol/interfaces/MiniProgramFileStat) bridge 使用的元数据字段类型。
702
+ * @see [FileStatResult](/reference/symbols/protocol/types/FileStatResult) `file.stat` 的 wire 返回类型。
703
+ * @see [FileStat](/reference/symbols/root/interfaces/FileStat) SDK 向业务返回的元数据快照。
704
+ * @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 提供 `stat()` 的当前 session 文件代理。
705
+ */
113
706
  export function isMiniProgramFileStat(value: unknown): value is MiniProgramFileStat {
114
707
  return (
115
708
  isRecord(value) &&
@@ -121,6 +714,58 @@ export function isMiniProgramFileStat(value: unknown): value is MiniProgramFileS
121
714
  );
122
715
  }
123
716
 
717
+ /**
718
+ * 判断未知值是否为单个 `network.download` 进度 payload。
719
+ *
720
+ * @param value - 来自 operation progress 事件、Host adapter 或其他不可信边界的候选进度快照。
721
+ * @returns 字段组合满足 {@link DownloadProgressPayload} 的最低 wire 约束时返回 `true`,并将 TypeScript
722
+ * 类型收窄为下载进度 payload;否则返回 `false`。
723
+ *
724
+ * @remarks
725
+ * 对通常的对象字面量,仅允许 `loaded`、`total`、`lengthComputable` 三个 canonical key,并执行以下组合校验:
726
+ *
727
+ * - `loaded` 始终必需,必须是 `0` 到 `Number.MAX_SAFE_INTEGER` 范围内的整数。负数、小数、`NaN`、
728
+ * `Infinity`、unsafe integer、bigint 和数字字符串都会被拒绝。
729
+ * - `lengthComputable` 必须是 boolean,不能使用 `0` / `1` 或其他 truthy/falsy 值代替。
730
+ * - `lengthComputable === false` 时,`total` 必须为 `undefined`;既可以省略该 key,也可以显式写成
731
+ * `total: undefined`。此分支不对 `loaded` 应达到的最终值作推断。
732
+ * - `lengthComputable === true` 时,`total` 必须是非负 safe integer,且 `loaded <= total`。
733
+ * `{ loaded: 0, total: 0, lengthComputable: true }` 是有效的空内容进度。
734
+ *
735
+ * canonical 集合之外的可枚举自有字符串键会导致失败;非枚举属性、symbol 键和继承属性不在
736
+ * `Object.keys()` 检查范围内,必需字段本身也不要求是自有或可枚举属性。该函数不会复制、冻结或
737
+ * 规范化输入。类型谓词只收窄到字段仍为可变、`total` 仍为可选的 {@link DownloadProgressPayload},
738
+ * 不会在 TypeScript 中建立以 `lengthComputable` 为判别字段的精确联合。
739
+ *
740
+ * 单个 payload 通过不表示进度来自当前 request、使用当前 pending operation 的 ID,或由可信 Host 产生;
741
+ * 也不保证单位确为字节、事件按时间排序、`loaded` 相对前一事件单调、`total` 跨事件稳定、这是最终进度,
742
+ * 或对应字节已经原子提交到目标文件。`network.download` 的 Runtime 状态机必须另行绑定 request ID,校验
743
+ * 可信响应 framing、`maxBytes`、取消与终态,并过滤倒退或越界值。
744
+ *
745
+ * @example
746
+ * ```ts
747
+ * import { isMiniProgramDownloadProgressPayload } from '@heybox/hb-sdk/protocol';
748
+ *
749
+ * function renderProgress(candidate: unknown) {
750
+ * if (!isMiniProgramDownloadProgressPayload(candidate)) return;
751
+ *
752
+ * if (candidate.lengthComputable && candidate.total !== undefined) {
753
+ * console.log(`${candidate.loaded}/${candidate.total}`);
754
+ * } else {
755
+ * console.log(`${candidate.loaded} bytes`);
756
+ * }
757
+ * }
758
+ *
759
+ * renderProgress({ loaded: 512, total: 1024, lengthComputable: true }); // valid
760
+ * renderProgress({ loaded: 512, lengthComputable: false }); // valid:总长度未知
761
+ * renderProgress({ loaded: 5, total: 4, lengthComputable: true }); // ignored:loaded > total
762
+ * ```
763
+ *
764
+ * @see [DownloadProgressPayload](/reference/symbols/protocol/interfaces/DownloadProgressPayload) bridge 使用的进度字段类型。
765
+ * @see [DownloadProgress](/reference/symbols/root/interfaces/DownloadProgress) `onProgress` 接收的公开 SDK 进度类型。
766
+ * @see [network.download](/reference/sdk/network/download) 产生并消费该进度的下载能力。
767
+ * @see [isMiniProgramBridgeMessage](/reference/symbols/protocol/functions/isMiniProgramBridgeMessage) operation progress envelope 的最低校验。
768
+ */
124
769
  export function isMiniProgramDownloadProgressPayload(value: unknown): value is DownloadProgressPayload {
125
770
  if (
126
771
  !isRecord(value) ||