@heybox/hb-sdk-protocol 0.8.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/dist/index.cjs.js +807 -2
- package/dist/index.esm.js +807 -2
- package/package.json +1 -1
- package/src/bridge.ts +24 -0
- package/src/capabilities.ts +19 -0
- package/src/constants.ts +25 -0
- package/src/guards.ts +645 -0
- package/src/payloads.ts +454 -34
- package/src/permissions.ts +141 -0
- package/types/bridge.d.ts +23 -0
- package/types/capabilities.d.ts +19 -0
- package/types/constants.d.ts +22 -0
- package/types/guards.d.ts +645 -0
- package/types/payloads.d.ts +393 -21
- package/types/permissions.d.ts +141 -0
package/dist/index.cjs.js
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 所有小程序 iframe bridge envelope 共用的固定 namespace。
|
|
5
|
+
*
|
|
6
|
+
* @remarks 该值用于排除无关 `postMessage` 数据,不负责验证消息来源;接收方仍须校验 source 和 nonce,
|
|
7
|
+
* 并在 origin 可以固定且协议模式要求时校验 origin。
|
|
8
|
+
*/
|
|
3
9
|
const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
|
|
10
|
+
/**
|
|
11
|
+
* 新消息默认发送的当前 bridge wire 版本。
|
|
12
|
+
*
|
|
13
|
+
* @remarks 接收 guard 为握手协商继续接受 v1 与 v2 envelope;cancel 与 operation progress 等 v2-only 消息仍必须使用 v2。
|
|
14
|
+
*/
|
|
4
15
|
const MINI_PROGRAM_MESSAGE_VERSION = 2;
|
|
16
|
+
/**
|
|
17
|
+
* Runtime 在 iframe URL 中传递 bridge nonce 的 query 参数名。
|
|
18
|
+
*
|
|
19
|
+
* @remarks nonce 用于把消息关联到当前 iframe 实例,不是独立的身份认证凭据;接收方还必须校验 source,
|
|
20
|
+
* 并在 origin 可以固定且协议模式要求时校验 origin。
|
|
21
|
+
*/
|
|
5
22
|
const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
23
|
+
/**
|
|
24
|
+
* SDK 发起 bridge 握手时使用的保留 method。
|
|
25
|
+
*
|
|
26
|
+
* @remarks `type: 'handshake'` 的消息必须使用该 method;Runtime 的可选握手响应也可携带该 method 供 SDK 识别环境快照。
|
|
27
|
+
*/
|
|
6
28
|
const SDK_HANDSHAKE_METHOD = 'sdk.handshake';
|
|
7
29
|
const RUNTIME_LOCATION_PROBE_METHOD = 'runtime.location.probe';
|
|
8
30
|
const SDK_LOCATION_REPORT_METHOD = 'sdk.location.report';
|
|
@@ -65,6 +87,19 @@ const COMPANION_SESSION_TERMINATE_METHOD = 'companion.session.terminate';
|
|
|
65
87
|
const COMPANION_SESSION_ACK_OUTPUT_METHOD = 'companion.session.ackOutput';
|
|
66
88
|
const COMPANION_OPEN_ATTACHMENT_METHOD = 'companion.openAttachment';
|
|
67
89
|
const COMPANION_SHOW_NOTIFICATION_METHOD = 'companion.showNotification';
|
|
90
|
+
/**
|
|
91
|
+
* 所有公开小程序 bridge method 的权威 method-level 目录。
|
|
92
|
+
*
|
|
93
|
+
* @remarks
|
|
94
|
+
* Host Runtime、文档生成器和一致性测试使用该目录判断 method 是否属于协议、由哪个 module
|
|
95
|
+
* 持有,以及调用前需要满足哪些声明权限。每个 {@link MiniProgramBridgeMethod} 必须且只能出现
|
|
96
|
+
* 一次;消费者不应另建 method、module、requirement 或 risk 的平行目录。
|
|
97
|
+
*
|
|
98
|
+
* 该目录只拥有 method metadata。精确 payload/result 类型分别由
|
|
99
|
+
* {@link MiniProgramCapabilityPayloadMap} 与 {@link MiniProgramCapabilityResultMap} 定义,权限的
|
|
100
|
+
* 展示名称、配置字段和平台审批属性由 `MINI_PROGRAM_PERMISSION_CATALOG` 定义。`kind: 'none'`
|
|
101
|
+
* 仅表示无需 Manifest 权限声明,不表示绕过参数校验、Host 能力、可信手势或业务策略。
|
|
102
|
+
*/
|
|
68
103
|
const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
69
104
|
{
|
|
70
105
|
method: AUTH_LOGIN_METHOD,
|
|
@@ -333,6 +368,76 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
|
333
368
|
})),
|
|
334
369
|
];
|
|
335
370
|
|
|
371
|
+
/**
|
|
372
|
+
* 判断不可信输入是否具有受支持的小程序 bridge 消息结构。
|
|
373
|
+
*
|
|
374
|
+
* @param value - 从 `MessageEvent.data`、反序列化结果或其他外部边界取得的未知值。`null`、数组和非对象值会直接返回 `false`。
|
|
375
|
+
* @returns 输入满足 v1/v2 envelope 及当前 `type` 的最低结构要求时返回 `true`,并将 TypeScript 类型收窄为
|
|
376
|
+
* {@link MiniProgramBridgeMessage};否则返回 `false`。
|
|
377
|
+
*
|
|
378
|
+
* @remarks
|
|
379
|
+
* 所有通过的消息都必须使用 {@link MINI_PROGRAM_MESSAGE_NAMESPACE},`version` 必须为 `1` 或 `2`,
|
|
380
|
+
* `nonce` 必须为非空字符串。随后按 `type` 执行以下最低校验:
|
|
381
|
+
*
|
|
382
|
+
* - `handshake`(v1/v2):`id` 非空,`method` 固定为 {@link SDK_HANDSHAKE_METHOD},`error` 必须为 `undefined`;
|
|
383
|
+
* `payload` 必须是对象,且包含非空 `href`、字符串 `userAgent` 和非空 `sdkVersion`。
|
|
384
|
+
* - `request`(v1/v2):`id` 与 `method` 都是非空字符串,且 `error` 必须为 `undefined`;`payload` 不在此处校验。
|
|
385
|
+
* - `response`(v1/v2):`id` 非空;`method` 可省略,提供时必须非空;`error` 可省略,提供时必须含非空
|
|
386
|
+
* `code` 与字符串 `message`。该最低结构允许 `payload` 与合法 `error` 同时存在,终态语义由消费端决定。
|
|
387
|
+
* - 普通 `event`(v1/v2):`method` 非空、`error` 必须为 `undefined`,`id` 可省略或为非空字符串;事件名和
|
|
388
|
+
* `payload` 不在此处校验。
|
|
389
|
+
* - `operation.progress` 与 `cancel` 只接受 v2。progress 还要求非空 `id` 和受支持的下载或 companion
|
|
390
|
+
* prepare 进度 payload;cancel 要求非空 `id`,且 `method`、`payload`、`error` 都必须为 `undefined`。
|
|
391
|
+
*
|
|
392
|
+
* 返回 `true` 只表示 wire envelope 可识别,不建立信任或授权边界。该函数不会校验
|
|
393
|
+
* `MessageEvent.source` / `origin`,只检查 nonce 非空而不会与当前 iframe 的预期 nonce 比较,也不会确认
|
|
394
|
+
* 握手状态、协商后的版本、请求 ID 是否重复或对应 pending request。除握手和 operation progress 外,它也不校验
|
|
395
|
+
* payload schema;不会把普通 `method` / 事件名限制在公开目录,不会检查 Manifest 权限、Runtime 权限状态、
|
|
396
|
+
* Host 支持、可信用户手势或业务策略。顶层 envelope、握手 payload 与 bridge error 中的额外字段也不会被拒绝。
|
|
397
|
+
*
|
|
398
|
+
* [MINI_PROGRAM_MESSAGE_VERSION](/reference/symbols/protocol/constants/MINI_PROGRAM_MESSAGE_VERSION)
|
|
399
|
+
* 是新消息的发送版本,不是本 guard 的唯一接收版本;接受 v1 是兼容行为,
|
|
400
|
+
* 调用方仍须在握手后确认消息版本与已协商版本一致。method 是否公开及其权限 requirement 应查询
|
|
401
|
+
* [MINI_PROGRAM_PROTOCOL_CAPABILITIES](/reference/symbols/protocol/constants/MINI_PROGRAM_PROTOCOL_CAPABILITIES),
|
|
402
|
+
* 权限快照应先交给
|
|
403
|
+
* [parseMiniProgramRuntimePermissions](/reference/symbols/protocol/functions/parseMiniProgramRuntimePermissions) 解析。
|
|
404
|
+
*
|
|
405
|
+
* 类型谓词只收窄到宽泛的 {@link MiniProgramBridgeMessage}:`payload` 仍为 `unknown`,`method` 等字段仍保持可选。
|
|
406
|
+
* 需要精确处理 v2 cancel 或 operation progress 时,应在检查判别字段后使用
|
|
407
|
+
* [MiniProgramBridgeCancelMessage](/reference/symbols/protocol/interfaces/MiniProgramBridgeCancelMessage) 或
|
|
408
|
+
* [MiniProgramBridgeProgressMessage](/reference/symbols/protocol/interfaces/MiniProgramBridgeProgressMessage)。
|
|
409
|
+
*
|
|
410
|
+
* @example
|
|
411
|
+
* ```ts
|
|
412
|
+
* import {
|
|
413
|
+
* MINI_PROGRAM_PROTOCOL_CAPABILITIES,
|
|
414
|
+
* isMiniProgramBridgeMessage,
|
|
415
|
+
* } from '@heybox/hb-sdk/protocol';
|
|
416
|
+
*
|
|
417
|
+
* const publicMethods = new Set<string>(
|
|
418
|
+
* MINI_PROGRAM_PROTOCOL_CAPABILITIES.map(({ method }) => method),
|
|
419
|
+
* );
|
|
420
|
+
*
|
|
421
|
+
* window.addEventListener('message', (event) => {
|
|
422
|
+
* if (event.source !== iframe.contentWindow) return;
|
|
423
|
+
* if (expectedOrigin && expectedOrigin !== '*' && event.origin !== expectedOrigin) return;
|
|
424
|
+
* if (!isMiniProgramBridgeMessage(event.data)) return;
|
|
425
|
+
* if (event.data.nonce !== expectedNonce) return;
|
|
426
|
+
*
|
|
427
|
+
* if (event.data.type === 'request') {
|
|
428
|
+
* const method = event.data.method;
|
|
429
|
+
* if (!method || !publicMethods.has(method)) return;
|
|
430
|
+
* // 继续检查握手状态、权限与 method 对应的 payload。
|
|
431
|
+
* }
|
|
432
|
+
* });
|
|
433
|
+
* ```
|
|
434
|
+
*
|
|
435
|
+
* @see [MiniProgramBridgeMessage](/reference/symbols/protocol/interfaces/MiniProgramBridgeMessage) 宽泛的顶层 envelope 类型。
|
|
436
|
+
* @see [MINI_PROGRAM_MESSAGE_VERSION](/reference/symbols/protocol/constants/MINI_PROGRAM_MESSAGE_VERSION) 新消息的默认发送版本。
|
|
437
|
+
* @see [MINI_PROGRAM_BRIDGE_NONCE_PARAM](/reference/symbols/protocol/constants/MINI_PROGRAM_BRIDGE_NONCE_PARAM) iframe URL 中的 nonce 参数名。
|
|
438
|
+
* @see [MINI_PROGRAM_PROTOCOL_CAPABILITIES](/reference/symbols/protocol/constants/MINI_PROGRAM_PROTOCOL_CAPABILITIES) 公开 method 与权限 requirement 目录。
|
|
439
|
+
* @see [parseMiniProgramRuntimePermissions](/reference/symbols/protocol/functions/parseMiniProgramRuntimePermissions) Runtime 权限快照解析器。
|
|
440
|
+
*/
|
|
336
441
|
function isMiniProgramBridgeMessage(value) {
|
|
337
442
|
if (!isRecord$2(value))
|
|
338
443
|
return false;
|
|
@@ -363,6 +468,60 @@ function isMiniProgramBridgeMessage(value) {
|
|
|
363
468
|
}
|
|
364
469
|
return false;
|
|
365
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* 判断未知值是否为以 `/` 分隔的规范逻辑相对路径。
|
|
473
|
+
*
|
|
474
|
+
* @param value - 来自 files API、derived ref、Host payload 或其他不可信边界的候选路径。
|
|
475
|
+
* @returns 值是满足协议语法的字符串时返回 `true`,并将 TypeScript 类型收窄为 `string`;否则返回 `false`。
|
|
476
|
+
*
|
|
477
|
+
* @remarks
|
|
478
|
+
* 该 guard 直接检查输入字符串,规则如下:
|
|
479
|
+
*
|
|
480
|
+
* - 必须是非空字符串;空串和非字符串值失败。
|
|
481
|
+
* - 不能以 `/` 开头或结尾,因此 `/absolute`、`nested/` 与 `/` 失败。
|
|
482
|
+
* - 不能包含反斜杠 `\\`、U+0000-U+001F 控制字符或 U+007F;Windows 风格分隔符不会被转换。
|
|
483
|
+
* - 按 `/` 分段后,每段必须非空且不能精确等于 `.` 或 `..`,因此连续 `/`、`nested/./file` 与
|
|
484
|
+
* `nested/../file` 失败。
|
|
485
|
+
*
|
|
486
|
+
* 只拒绝完整的 dot segment:`.env`、`..cache`、`file.` 与包含普通点号的名称都可通过;单独的 `'.'`
|
|
487
|
+
* 本身失败。{@link isMiniProgramDirectoryRef} 在外层显式允许 `relativePath: '.'` 表示 root 目录自身,
|
|
488
|
+
* {@link isMiniProgramFileRef} 则始终要求本 guard 通过,因此文件引用不能指向 root 自身。
|
|
489
|
+
*
|
|
490
|
+
* 该函数不 trim 空白、不 URL/percent decode、不 Unicode normalize、不折叠分隔符,也不应用平台保留名或
|
|
491
|
+
* 字符规则。空白段、`%2e%2e`、全角字符和 `C:/file` 等只要满足上述原始字符串规则就可能通过;路径长度、
|
|
492
|
+
* 段长度和段数量也没有在此限制。调用方不能把“通过”解释为操作系统级 canonical path。
|
|
493
|
+
*
|
|
494
|
+
* 返回 `true` 只证明语法可作为协议逻辑相对路径,不证明目标存在、末段是文件还是目录、root/handle 属于
|
|
495
|
+
* 当前 session/origin、`filesystem` 权限已启用、访问 mode 允许当前操作或 Host 支持该 capability。
|
|
496
|
+
* `normalizeRelativePath()` 这类 SDK/Runtime helper 只是复用本 predicate:合法时原样返回同一个字符串,非法时
|
|
497
|
+
* 转成对应 SDK/bridge 错误;它不会进一步规范化路径。
|
|
498
|
+
*
|
|
499
|
+
* @example
|
|
500
|
+
* ```ts
|
|
501
|
+
* import { isMiniProgramRelativePath } from '@heybox/hb-sdk/protocol';
|
|
502
|
+
*
|
|
503
|
+
* const paths: unknown[] = [
|
|
504
|
+
* 'data.json',
|
|
505
|
+
* 'nested/data.json',
|
|
506
|
+
* '.env',
|
|
507
|
+
* '.',
|
|
508
|
+
* '../secret',
|
|
509
|
+
* 'nested//file',
|
|
510
|
+
* 'nested\\file',
|
|
511
|
+
* ];
|
|
512
|
+
*
|
|
513
|
+
* const validPaths = paths.filter(isMiniProgramRelativePath);
|
|
514
|
+
* // ['data.json', 'nested/data.json', '.env']
|
|
515
|
+
* console.log(validPaths);
|
|
516
|
+
* ```
|
|
517
|
+
*
|
|
518
|
+
* @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 始终使用本规则的 derived 文件引用。
|
|
519
|
+
* @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 额外允许 `'.'` 的 derived 目录引用。
|
|
520
|
+
* @see [isMiniProgramPathRootRef](/reference/symbols/protocol/functions/isMiniProgramPathRootRef) 与相对路径组合的逻辑 root guard。
|
|
521
|
+
* @see [PathRoot](/reference/symbols/root/interfaces/PathRoot) 提供 `file()` / `directory()` 的 SDK 路径入口。
|
|
522
|
+
* @see [FileSystem](/reference/symbols/root/interfaces/FileSystem) `files.sandbox` 逻辑路径能力。
|
|
523
|
+
* @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 已授权目录下的相对路径能力。
|
|
524
|
+
*/
|
|
366
525
|
function isMiniProgramRelativePath(value) {
|
|
367
526
|
if (typeof value !== 'string' || !value || value.startsWith('/') || value.endsWith('/')) {
|
|
368
527
|
return false;
|
|
@@ -371,6 +530,64 @@ function isMiniProgramRelativePath(value) {
|
|
|
371
530
|
return false;
|
|
372
531
|
return value.split('/').every((segment) => Boolean(segment) && segment !== '.' && segment !== '..');
|
|
373
532
|
}
|
|
533
|
+
/**
|
|
534
|
+
* 判断未知值是否可作为 derived file/directory ref 的逻辑路径 root。
|
|
535
|
+
*
|
|
536
|
+
* @param value - 来自文件引用、目录引用或其他不可信 wire 边界的候选 root。
|
|
537
|
+
* @returns 值满足 sandbox root 或 direct directory root 任一结构时返回 `true`,并将 TypeScript 类型
|
|
538
|
+
* 收窄为 {@link MiniProgramPathRootRef} 判别联合;否则返回 `false`。
|
|
539
|
+
*
|
|
540
|
+
* @remarks
|
|
541
|
+
* 支持两个以 `kind` 区分的分支:
|
|
542
|
+
*
|
|
543
|
+
* - Sandbox root:`{ kind: 'sandbox' }`。对通常的对象字面量,除 `kind` 外不能携带任何字段。
|
|
544
|
+
* - Directory root:`{ kind: 'directory', handleId }`。`handleId` 必须是非空字符串;空白字符串仍满足
|
|
545
|
+
* 本 guard 的最低结构,格式、长度和归属由 Runtime 继续校验。
|
|
546
|
+
*
|
|
547
|
+
* direct file ref、derived directory ref、普通路径字符串以及其他 `kind` 都不能作为 PathRoot。特别是
|
|
548
|
+
* `{ kind: 'directory', root, relativePath }` 会因额外 canonical 外字段而失败:derived ref 只能引用 sandbox
|
|
549
|
+
* 或 direct directory root,不能递归嵌套另一层 derived root。
|
|
550
|
+
*
|
|
551
|
+
* 两个分支都会拒绝 canonical 集合之外的可枚举自有字符串键,包括值为 `undefined` 的额外键。检查基于
|
|
552
|
+
* `Object.keys()`,因此非枚举属性、symbol 键和继承属性不会被列入;必需属性本身也不要求是自有或可枚举
|
|
553
|
+
* 属性。该函数不会复制、冻结或规范化输入。
|
|
554
|
+
*
|
|
555
|
+
* 返回 `true` 只建立结构类型收窄,不证明 directory `handleId` 由当前 Runtime 签发、属于当前 session/origin、
|
|
556
|
+
* 仍然有效且确为目录,也不证明 `filesystem` 权限、目录访问 mode、provenance 或 Host capability。sandbox
|
|
557
|
+
* 标记同样不自行建立隔离边界;Runtime 必须把它解析到当前 Mini-program 的私有 root 并执行实际权限策略。
|
|
558
|
+
*
|
|
559
|
+
* {@link isMiniProgramFileRef} 与 {@link isMiniProgramDirectoryRef} 都用该 guard 校验 derived ref 的 `root`;
|
|
560
|
+
* 它只校验 root 身份,不校验各自的 `relativePath`。
|
|
561
|
+
*
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* import { isMiniProgramPathRootRef } from '@heybox/hb-sdk/protocol';
|
|
565
|
+
*
|
|
566
|
+
* const candidates: unknown[] = [
|
|
567
|
+
* { kind: 'sandbox' },
|
|
568
|
+
* { kind: 'directory', handleId: 'directory_1' },
|
|
569
|
+
* ];
|
|
570
|
+
*
|
|
571
|
+
* for (const candidate of candidates) {
|
|
572
|
+
* if (!isMiniProgramPathRootRef(candidate)) continue;
|
|
573
|
+
* if (candidate.kind === 'sandbox') {
|
|
574
|
+
* console.log('private sandbox root');
|
|
575
|
+
* } else {
|
|
576
|
+
* console.log('authorized directory root', candidate.handleId);
|
|
577
|
+
* }
|
|
578
|
+
* }
|
|
579
|
+
*
|
|
580
|
+
* isMiniProgramPathRootRef({ kind: 'file', handleId: 'file_1' }); // false
|
|
581
|
+
* isMiniProgramPathRootRef({ kind: 'directory', root: { kind: 'sandbox' }, relativePath: 'nested' }); // false
|
|
582
|
+
* ```
|
|
583
|
+
*
|
|
584
|
+
* @see [MiniProgramPathRootRef](/reference/symbols/protocol/types/MiniProgramPathRootRef) sandbox/direct-directory 联合类型。
|
|
585
|
+
* @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 使用 PathRoot 的 derived 文件引用。
|
|
586
|
+
* @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 使用 PathRoot 的 derived 目录引用。
|
|
587
|
+
* @see [PathRoot](/reference/symbols/root/interfaces/PathRoot) SDK 公开的逻辑路径 root 能力。
|
|
588
|
+
* @see [FilesModule](/reference/symbols/root/interfaces/FilesModule) 提供 `files.sandbox` 与目录 picker 的入口。
|
|
589
|
+
* @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 可作为 derived path root 的目录代理。
|
|
590
|
+
*/
|
|
374
591
|
function isMiniProgramPathRootRef(value) {
|
|
375
592
|
if (!isRecord$2(value))
|
|
376
593
|
return false;
|
|
@@ -378,6 +595,65 @@ function isMiniProgramPathRootRef(value) {
|
|
|
378
595
|
return hasOnlyKeys(value, ['kind']);
|
|
379
596
|
return value.kind === 'directory' && isNonEmptyString$1(value.handleId) && hasOnlyKeys(value, ['kind', 'handleId']);
|
|
380
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* 判断未知值是否为 direct handle 或 path-derived 文件引用。
|
|
600
|
+
*
|
|
601
|
+
* @param value - 来自 descriptor、capability payload 或其他不可信 wire 边界的候选文件引用。
|
|
602
|
+
* @returns 值满足 {@link MiniProgramFileRef} 任一分支的最低结构时返回 `true`,并将 TypeScript 类型
|
|
603
|
+
* 收窄为 direct/derived 文件引用联合;否则返回 `false`。
|
|
604
|
+
*
|
|
605
|
+
* @remarks
|
|
606
|
+
* 两个分支都要求 `kind === 'file'`,并对通常的对象字面量使用互斥的 canonical key 集合:
|
|
607
|
+
*
|
|
608
|
+
* - Direct ref:`{ kind: 'file', handleId }`。`handleId` 必须是非空字符串;不能同时携带 `root` 或
|
|
609
|
+
* `relativePath`。
|
|
610
|
+
* - Derived ref:`{ kind: 'file', root, relativePath }`。不能携带 `handleId`;`root` 必须通过
|
|
611
|
+
* {@link isMiniProgramPathRootRef},即精确的 `{ kind: 'sandbox' }`,或带非空 `handleId` 的 direct
|
|
612
|
+
* directory root。file ref 与再次嵌套的 derived directory ref 都不能作为 root。
|
|
613
|
+
*
|
|
614
|
+
* Derived ref 的 `relativePath` 必须通过 {@link isMiniProgramRelativePath}:非空、不以 `/` 开头或结尾,
|
|
615
|
+
* 不含空段、`.` / `..` 点段、反斜杠、U+0000-U+001F 控制字符或 U+007F。文件必须指向 root 下的具体路径,
|
|
616
|
+
* 因此与 {@link isMiniProgramDirectoryRef} 不同,`relativePath: '.'` 不合法。该 guard 不会 URL decode、
|
|
617
|
+
* Unicode normalize、解析平台分隔符、限制路径长度,或确认末段是文件而不是目录。
|
|
618
|
+
*
|
|
619
|
+
* direct 与 derived 分支都会拒绝 canonical 集合之外的可枚举自有字符串键,包括值为 `undefined` 的额外键。
|
|
620
|
+
* 检查基于 `Object.keys()`,因此非枚举属性、symbol 键和继承属性不会被列入;必需属性本身也不要求是自有
|
|
621
|
+
* 或可枚举属性。返回对象不会被复制、冻结或规范化。
|
|
622
|
+
*
|
|
623
|
+
* 返回 `true` 只证明当前结构可按 wire 类型读取,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、
|
|
624
|
+
* 属于当前 session/origin、仍然有效且类型匹配,也不证明目标文件存在、`filesystem` 权限已启用、访问 mode
|
|
625
|
+
* 允许当前操作或 Host 支持对应 capability。Runtime 在实际解析 ref 时仍须查询 handle registry,执行权限、
|
|
626
|
+
* provenance 与目标类型校验,并解析 derived path。
|
|
627
|
+
*
|
|
628
|
+
* @example
|
|
629
|
+
* ```ts
|
|
630
|
+
* import { isMiniProgramFileRef } from '@heybox/hb-sdk/protocol';
|
|
631
|
+
*
|
|
632
|
+
* const candidate: unknown = {
|
|
633
|
+
* kind: 'file',
|
|
634
|
+
* root: { kind: 'directory', handleId: 'directory_1' },
|
|
635
|
+
* relativePath: 'screenshots/cover.png',
|
|
636
|
+
* };
|
|
637
|
+
*
|
|
638
|
+
* if (isMiniProgramFileRef(candidate)) {
|
|
639
|
+
* if ('handleId' in candidate) {
|
|
640
|
+
* console.log('direct', candidate.handleId);
|
|
641
|
+
* } else {
|
|
642
|
+
* console.log('derived', candidate.root, candidate.relativePath);
|
|
643
|
+
* }
|
|
644
|
+
* }
|
|
645
|
+
*
|
|
646
|
+
* isMiniProgramFileRef({ kind: 'file', handleId: 'file_1' }); // true
|
|
647
|
+
* isMiniProgramFileRef({ kind: 'file', root: { kind: 'sandbox' }, relativePath: '.' }); // false
|
|
648
|
+
* ```
|
|
649
|
+
*
|
|
650
|
+
* @see [MiniProgramFileRef](/reference/symbols/protocol/types/MiniProgramFileRef) direct/derived 联合类型。
|
|
651
|
+
* @see [isMiniProgramPathRootRef](/reference/symbols/protocol/functions/isMiniProgramPathRootRef) derived ref 的 root guard。
|
|
652
|
+
* @see [isMiniProgramRelativePath](/reference/symbols/protocol/functions/isMiniProgramRelativePath) derived 文件路径规则。
|
|
653
|
+
* @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) 包含名称和 mode 的文件 descriptor guard。
|
|
654
|
+
* @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 可用 `'.'` 指向 root 自身的目录引用 guard。
|
|
655
|
+
* @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 使用文件引用的公开 I/O 代理。
|
|
656
|
+
*/
|
|
381
657
|
function isMiniProgramFileRef(value) {
|
|
382
658
|
if (!isRecord$2(value) || value.kind !== 'file')
|
|
383
659
|
return false;
|
|
@@ -386,6 +662,65 @@ function isMiniProgramFileRef(value) {
|
|
|
386
662
|
}
|
|
387
663
|
return (isMiniProgramPathRootRef(value.root) && isMiniProgramRelativePath(value.relativePath) && hasOnlyKeys(value, ['kind', 'root', 'relativePath']));
|
|
388
664
|
}
|
|
665
|
+
/**
|
|
666
|
+
* 判断未知值是否为 direct handle 或 path-derived 目录引用。
|
|
667
|
+
*
|
|
668
|
+
* @param value - 来自 descriptor、capability payload 或其他不可信 wire 边界的候选目录引用。
|
|
669
|
+
* @returns 值满足 {@link MiniProgramDirectoryRef} 任一分支的最低结构时返回 `true`,并将 TypeScript 类型
|
|
670
|
+
* 收窄为 direct/derived 目录引用联合;否则返回 `false`。
|
|
671
|
+
*
|
|
672
|
+
* @remarks
|
|
673
|
+
* 两个分支都要求 `kind === 'directory'`,并对通常的对象字面量使用互斥的 canonical key 集合:
|
|
674
|
+
*
|
|
675
|
+
* - Direct ref:`{ kind: 'directory', handleId }`。`handleId` 必须是非空字符串;不能同时携带 `root` 或
|
|
676
|
+
* `relativePath`。
|
|
677
|
+
* - Derived ref:`{ kind: 'directory', root, relativePath }`。不能携带 `handleId`;`root` 必须通过
|
|
678
|
+
* {@link isMiniProgramPathRootRef},即精确的 `{ kind: 'sandbox' }`,或带非空 `handleId` 的 direct
|
|
679
|
+
* directory root。file ref 与再次嵌套的 derived directory ref 都不能作为 root。
|
|
680
|
+
*
|
|
681
|
+
* Derived ref 的 `relativePath` 可以是 `'.'`,表示 root 目录自身;这是目录引用专属规则,普通
|
|
682
|
+
* {@link isMiniProgramRelativePath} 和 file ref 都不接受 `'.'`。其他相对路径必须非空,不以 `/` 开头或结尾,
|
|
683
|
+
* 不含空段、`.` / `..` 点段、反斜杠、U+0000-U+001F 控制字符或 U+007F。该校验不会 URL decode、
|
|
684
|
+
* Unicode normalize、解析平台分隔符、限制长度,或判断路径指向的目录是否存在。
|
|
685
|
+
*
|
|
686
|
+
* direct 与 derived 分支都会拒绝 canonical 集合之外的可枚举自有字符串键,包括值为 `undefined` 的额外键。
|
|
687
|
+
* 检查基于 `Object.keys()`,因此非枚举属性、symbol 键和继承属性不会被列入;必需属性本身也不要求是自有
|
|
688
|
+
* 或可枚举属性。返回对象不会被复制、冻结或规范化。
|
|
689
|
+
*
|
|
690
|
+
* 返回 `true` 只证明当前结构可按 wire 类型读取,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、
|
|
691
|
+
* 属于当前 session/origin、仍然有效且确为目录,也不证明目标存在、`filesystem` 权限已启用、访问模式允许
|
|
692
|
+
* 当前操作或 Host 支持对应 capability。Runtime 在实际解析 ref 时仍须查询 handle registry、执行权限与
|
|
693
|
+
* provenance 校验,并解析 derived path。
|
|
694
|
+
*
|
|
695
|
+
* @example
|
|
696
|
+
* ```ts
|
|
697
|
+
* import { isMiniProgramDirectoryRef } from '@heybox/hb-sdk/protocol';
|
|
698
|
+
*
|
|
699
|
+
* const candidate: unknown = {
|
|
700
|
+
* kind: 'directory',
|
|
701
|
+
* root: { kind: 'directory', handleId: 'directory_1' },
|
|
702
|
+
* relativePath: 'screenshots/2026',
|
|
703
|
+
* };
|
|
704
|
+
*
|
|
705
|
+
* if (isMiniProgramDirectoryRef(candidate)) {
|
|
706
|
+
* if ('handleId' in candidate) {
|
|
707
|
+
* console.log('direct', candidate.handleId);
|
|
708
|
+
* } else {
|
|
709
|
+
* console.log('derived', candidate.root, candidate.relativePath);
|
|
710
|
+
* }
|
|
711
|
+
* }
|
|
712
|
+
*
|
|
713
|
+
* isMiniProgramDirectoryRef({ kind: 'directory', root: { kind: 'sandbox' }, relativePath: '.' }); // true
|
|
714
|
+
* isMiniProgramDirectoryRef({ kind: 'directory', handleId: 'd1', relativePath: '.' }); // false:分支键混用
|
|
715
|
+
* ```
|
|
716
|
+
*
|
|
717
|
+
* @see [MiniProgramDirectoryRef](/reference/symbols/protocol/types/MiniProgramDirectoryRef) direct/derived 联合类型。
|
|
718
|
+
* @see [isMiniProgramPathRootRef](/reference/symbols/protocol/functions/isMiniProgramPathRootRef) derived ref 的 root guard。
|
|
719
|
+
* @see [isMiniProgramRelativePath](/reference/symbols/protocol/functions/isMiniProgramRelativePath) 非 `'.'` 相对路径规则。
|
|
720
|
+
* @see [isMiniProgramDirectoryDescriptor](/reference/symbols/protocol/functions/isMiniProgramDirectoryDescriptor) 包含名称和 mode 的目录 descriptor guard。
|
|
721
|
+
* @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 不接受 root 自身 `'.'` 的文件引用 guard。
|
|
722
|
+
* @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 使用目录引用的公开路径操作代理。
|
|
723
|
+
*/
|
|
389
724
|
function isMiniProgramDirectoryRef(value) {
|
|
390
725
|
if (!isRecord$2(value) || value.kind !== 'directory')
|
|
391
726
|
return false;
|
|
@@ -394,6 +729,72 @@ function isMiniProgramDirectoryRef(value) {
|
|
|
394
729
|
}
|
|
395
730
|
return (isMiniProgramPathRootRef(value.root) && (value.relativePath === '.' || isMiniProgramRelativePath(value.relativePath)) && hasOnlyKeys(value, ['kind', 'root', 'relativePath']));
|
|
396
731
|
}
|
|
732
|
+
/**
|
|
733
|
+
* 判断未知值是否具有文件 descriptor 的最低 wire 结构。
|
|
734
|
+
*
|
|
735
|
+
* @param value - 来自 Host、bridge response、目录列表或其他不可信边界的候选 descriptor。
|
|
736
|
+
* @returns 顶层字段及嵌套文件引用都满足协议结构时返回 `true`,并将 TypeScript 类型收窄为
|
|
737
|
+
* {@link MiniProgramFileDescriptor};否则返回 `false`。
|
|
738
|
+
*
|
|
739
|
+
* @remarks
|
|
740
|
+
* guard 会读取并校验以下四个属性;对通常的对象字面量,它们也是唯一允许的可枚举自有字符串字段:
|
|
741
|
+
*
|
|
742
|
+
* - `kind`:固定为 `'file'`。
|
|
743
|
+
* - `name`:非空字符串。该 guard 不检查空白、`.`、`..`、`/`、反斜杠、控制字符、平台保留名或
|
|
744
|
+
* `name` 是否等于 `ref.relativePath` 的 basename;SDK 创建 `FileHandle` 时还会执行更严格的名称校验。
|
|
745
|
+
* - `mode`:只能是 `'read'` 或 `'readwrite'`。这是 descriptor 声明值,不证明 Host 实际授予了读写权限。
|
|
746
|
+
* - `ref`:必须通过 {@link isMiniProgramFileRef}。
|
|
747
|
+
*
|
|
748
|
+
* `ref` 可以是 `{ kind: 'file', handleId }` 形式的 direct file ref,其中 `handleId` 只要求非空;也可以是
|
|
749
|
+
* `{ kind: 'file', root, relativePath }` 形式的 derived ref。derived root 只能是精确的 sandbox root 或
|
|
750
|
+
* 带非空 `handleId` 的 direct directory root,不能是 file ref 或再次派生的 directory ref;`relativePath`
|
|
751
|
+
* 必须是非空、非绝对、无尾随 `/`、无空段、`.` / `..` 点段、反斜杠或控制字符的相对路径。与目录 ref
|
|
752
|
+
* 不同,文件 ref 不接受 `relativePath: '.'`,因为它必须指向 root 下的具体文件。
|
|
753
|
+
*
|
|
754
|
+
* 顶层 descriptor、`ref` 与 `root` 的额外可枚举自有字符串键都会导致失败,包括值为 `undefined` 的额外键;
|
|
755
|
+
* 非枚举属性、symbol 键和继承属性不在 `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举
|
|
756
|
+
* 属性。该函数不会复制、冻结或规范化输入。
|
|
757
|
+
*
|
|
758
|
+
* 返回 `true` 只建立结构类型收窄,不证明 direct `handleId` 或 root handle 由当前 Runtime 签发、属于当前
|
|
759
|
+
* session/origin、仍然有效且确为预期类型,也不证明文件存在、名称真实、`filesystem` 权限已启用、mode
|
|
760
|
+
* 允许当前操作或 Host 支持对应 capability。实际文件操作仍须由 Runtime 查询 handle registry、执行权限、
|
|
761
|
+
* provenance、目标类型和存在性校验。
|
|
762
|
+
*
|
|
763
|
+
* {@link isMiniProgramFileSystemEntityDescriptor} 使用本 guard 与目录 descriptor guard 识别目录列表项。
|
|
764
|
+
* `files.pickFiles()` 与 `files.saveFile()` 的成功结果要求更窄的 direct file ref,而 sandbox/path API 返回的
|
|
765
|
+
* descriptor 可以使用 derived ref;本 guard 本身不会区分 descriptor 的来源。
|
|
766
|
+
*
|
|
767
|
+
* @example
|
|
768
|
+
* ```ts
|
|
769
|
+
* import { isMiniProgramFileDescriptor } from '@heybox/hb-sdk/protocol';
|
|
770
|
+
*
|
|
771
|
+
* const candidate = {
|
|
772
|
+
* kind: 'file',
|
|
773
|
+
* name: 'report.json',
|
|
774
|
+
* mode: 'readwrite',
|
|
775
|
+
* ref: {
|
|
776
|
+
* kind: 'file',
|
|
777
|
+
* root: { kind: 'sandbox' },
|
|
778
|
+
* relativePath: 'exports/report.json',
|
|
779
|
+
* },
|
|
780
|
+
* };
|
|
781
|
+
* const untrusted: unknown = candidate;
|
|
782
|
+
*
|
|
783
|
+
* if (isMiniProgramFileDescriptor(untrusted)) {
|
|
784
|
+
* // 这里只能安全读取 wire 字段;文件 I/O 仍交给 files API/Runtime。
|
|
785
|
+
* console.log(untrusted.name, untrusted.mode, untrusted.ref);
|
|
786
|
+
* }
|
|
787
|
+
*
|
|
788
|
+
* isMiniProgramFileDescriptor({ ...candidate, size: 1024 }); // false:顶层存在额外键
|
|
789
|
+
* ```
|
|
790
|
+
*
|
|
791
|
+
* @see [MiniProgramFileDescriptor](/reference/symbols/protocol/interfaces/MiniProgramFileDescriptor) descriptor 字段类型。
|
|
792
|
+
* @see [isMiniProgramFileRef](/reference/symbols/protocol/functions/isMiniProgramFileRef) 嵌套 direct/derived 文件引用校验。
|
|
793
|
+
* @see [isMiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileSystemEntityDescriptor) 文件与目录 descriptor 联合 guard。
|
|
794
|
+
* @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 校验后由 SDK 建立的文件代理。
|
|
795
|
+
* @see [PickFilesResult](/reference/symbols/protocol/types/PickFilesResult) picker 的 direct-ref 文件结果。
|
|
796
|
+
* @see [SaveFileResult](/reference/symbols/protocol/types/SaveFileResult) save picker 的 direct-ref 文件结果。
|
|
797
|
+
*/
|
|
397
798
|
function isMiniProgramFileDescriptor(value) {
|
|
398
799
|
return (isRecord$2(value) &&
|
|
399
800
|
value.kind === 'file' &&
|
|
@@ -402,6 +803,68 @@ function isMiniProgramFileDescriptor(value) {
|
|
|
402
803
|
isMiniProgramFileRef(value.ref) &&
|
|
403
804
|
hasOnlyKeys(value, ['kind', 'name', 'mode', 'ref']));
|
|
404
805
|
}
|
|
806
|
+
/**
|
|
807
|
+
* 判断未知值是否具有目录 descriptor 的精确 wire 结构。
|
|
808
|
+
*
|
|
809
|
+
* @param value - 来自 Host、bridge response、目录列表或其他不可信边界的候选 descriptor。
|
|
810
|
+
* @returns 顶层字段及嵌套目录引用都满足协议最低结构时返回 `true`,并将 TypeScript 类型收窄为
|
|
811
|
+
* {@link MiniProgramDirectoryDescriptor};否则返回 `false`。
|
|
812
|
+
*
|
|
813
|
+
* @remarks
|
|
814
|
+
* guard 会读取并校验以下四个属性;对通常的对象字面量,它们也是唯一允许的可枚举自有字符串字段:
|
|
815
|
+
*
|
|
816
|
+
* - `kind`:固定为 `'directory'`。
|
|
817
|
+
* - `name`:非空字符串。该 guard 不检查空白、`.`、`..`、斜杠、反斜杠、控制字符或平台文件名规则;
|
|
818
|
+
* SDK 将 descriptor 转为 `DirectoryHandle` 时还会执行更严格的名称校验。
|
|
819
|
+
* - `mode`:只能是 `'read'` 或 `'readwrite'`;这里只识别声明值,不证明 Host 实际授予了对应访问模式。
|
|
820
|
+
* - `ref`:必须通过 {@link isMiniProgramDirectoryRef}。
|
|
821
|
+
*
|
|
822
|
+
* `ref` 可以是 `{ kind: 'directory', handleId }` 形式的直接引用,其中 `handleId` 只要求为非空字符串;
|
|
823
|
+
* 也可以是 `{ kind: 'directory', root, relativePath }` 形式的派生引用。派生引用的 `root` 只能是精确的
|
|
824
|
+
* sandbox root,或带非空 `handleId` 的直接目录 root;`relativePath` 可用 `'.'` 表示 root 自身,其他值
|
|
825
|
+
* 必须是非空、非绝对、无尾随 `/`、无空段/点段/父目录段、反斜杠或控制字符的规范相对路径。
|
|
826
|
+
*
|
|
827
|
+
* 顶层 descriptor、`ref` 与 `root` 的额外可枚举自有字符串键都会导致失败;但非枚举属性、symbol 键和
|
|
828
|
+
* 继承属性不在 `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举属性。该函数不会复制、
|
|
829
|
+
* 冻结或规范化对象,也不会验证 `handleId` 是否由当前 Runtime 签发、是否属于当前 session/origin、目标目录
|
|
830
|
+
* 是否存在、`name` 是否与真实目录一致,或 Manifest/Runtime 权限、Host 支持及当前操作是否允许。
|
|
831
|
+
* 返回 `true` 只建立结构类型收窄,不建立信任边界。
|
|
832
|
+
*
|
|
833
|
+
* {@link isMiniProgramFileSystemEntityDescriptor} 使用本 guard 与文件 descriptor guard 共同识别目录列表项。
|
|
834
|
+
* `files.pickDirectory()` 成功结果还要求 direct ref,而 sandbox/path 派生的目录可由 `DirectoryHandle`
|
|
835
|
+
* 的路径 API 产生;这些来源和能力差异不能仅凭本 guard 判定。
|
|
836
|
+
*
|
|
837
|
+
* @example
|
|
838
|
+
* ```ts
|
|
839
|
+
* import { isMiniProgramDirectoryDescriptor } from '@heybox/hb-sdk/protocol';
|
|
840
|
+
*
|
|
841
|
+
* const candidate = {
|
|
842
|
+
* kind: 'directory',
|
|
843
|
+
* name: 'screenshots',
|
|
844
|
+
* mode: 'readwrite',
|
|
845
|
+
* ref: {
|
|
846
|
+
* kind: 'directory',
|
|
847
|
+
* root: { kind: 'sandbox' },
|
|
848
|
+
* relativePath: 'exports/screenshots',
|
|
849
|
+
* },
|
|
850
|
+
* };
|
|
851
|
+
* const untrusted: unknown = candidate;
|
|
852
|
+
*
|
|
853
|
+
* if (isMiniProgramDirectoryDescriptor(untrusted)) {
|
|
854
|
+
* // 这里只能安全读取已收窄的 wire 字段;实际操作仍交给 files API/Runtime。
|
|
855
|
+
* console.log(untrusted.name, untrusted.ref);
|
|
856
|
+
* }
|
|
857
|
+
*
|
|
858
|
+
* isMiniProgramDirectoryDescriptor({ ...candidate, size: 0 }); // false:顶层存在额外键
|
|
859
|
+
* ```
|
|
860
|
+
*
|
|
861
|
+
* @see [MiniProgramDirectoryDescriptor](/reference/symbols/protocol/interfaces/MiniProgramDirectoryDescriptor) descriptor 字段类型。
|
|
862
|
+
* @see [isMiniProgramDirectoryRef](/reference/symbols/protocol/functions/isMiniProgramDirectoryRef) 嵌套 direct/derived 引用校验。
|
|
863
|
+
* @see [isMiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileSystemEntityDescriptor) 文件与目录 descriptor 联合 guard。
|
|
864
|
+
* @see [FilesModule](/reference/symbols/root/interfaces/FilesModule) `files` 公开能力入口。
|
|
865
|
+
* @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 校验后由 SDK 建立的目录代理。
|
|
866
|
+
* @see [PickDirectoryResult](/reference/symbols/protocol/types/PickDirectoryResult) picker 的 direct-ref 返回契约。
|
|
867
|
+
*/
|
|
405
868
|
function isMiniProgramDirectoryDescriptor(value) {
|
|
406
869
|
return (isRecord$2(value) &&
|
|
407
870
|
value.kind === 'directory' &&
|
|
@@ -410,12 +873,177 @@ function isMiniProgramDirectoryDescriptor(value) {
|
|
|
410
873
|
isMiniProgramDirectoryRef(value.ref) &&
|
|
411
874
|
hasOnlyKeys(value, ['kind', 'name', 'mode', 'ref']));
|
|
412
875
|
}
|
|
876
|
+
/**
|
|
877
|
+
* 判断未知值是否为文件或目录 descriptor。
|
|
878
|
+
*
|
|
879
|
+
* @param value - 来自 `directory.list`、Host response 或其他不可信边界的候选文件系统实体 descriptor。
|
|
880
|
+
* @returns 值通过文件或目录任一子 guard 时返回 `true`,并将 TypeScript 类型收窄为
|
|
881
|
+
* {@link MiniProgramFileSystemEntityDescriptor} 判别联合;否则返回 `false`。
|
|
882
|
+
*
|
|
883
|
+
* @remarks
|
|
884
|
+
* 该函数不维护第三套 schema,而是依次调用 {@link isMiniProgramFileDescriptor} 和
|
|
885
|
+
* {@link isMiniProgramDirectoryDescriptor}:
|
|
886
|
+
*
|
|
887
|
+
* - `kind === 'file'` 的值必须同时具有非空 `name`、`'read' | 'readwrite'` mode,以及合法 direct/derived
|
|
888
|
+
* file ref。derived 文件路径必须指向 root 下的具体文件,不接受 `relativePath: '.'`。
|
|
889
|
+
* - `kind === 'directory'` 的值必须同时具有非空 `name`、`'read' | 'readwrite'` mode,以及合法
|
|
890
|
+
* direct/derived directory ref。derived 目录 ref 可用 `relativePath: '.'` 表示 root 自身。
|
|
891
|
+
*
|
|
892
|
+
* 两个分支的 `kind` 互斥,因此通过后可直接用 `descriptor.kind` 判别收窄到对应类型。各分支完整继承子 guard
|
|
893
|
+
* 的键与嵌套 ref 规则:对通常的对象字面量,顶层只允许 `kind`、`name`、`mode`、`ref`,nested ref/root
|
|
894
|
+
* 也只允许其 canonical key;额外可枚举自有字符串键会导致失败。非枚举属性、symbol 键和继承属性不在
|
|
895
|
+
* `Object.keys()` 检查范围内,必需属性本身也不要求是自有或可枚举属性。
|
|
896
|
+
*
|
|
897
|
+
* 返回 `true` 只建立单个 descriptor 的结构类型收窄。该函数不会复制、冻结或规范化输入,也不会验证
|
|
898
|
+
* `name` 的完整平台规则、handle 是否由当前 Runtime 签发并属于当前 session/origin、目标实体是否存在且类型
|
|
899
|
+
* 与 `kind` 一致、mode 是否真实、`filesystem` 权限、provenance 或 Host capability。实际创建 `FileHandle` /
|
|
900
|
+
* `DirectoryHandle` 时仍须由 SDK 与 Runtime 完成这些校验。
|
|
901
|
+
*
|
|
902
|
+
* 对 {@link DirectoryListResult},本 guard 只适合逐项验证,不验证外层值是否为稠密数组、结果数量、重复项、
|
|
903
|
+
* 子项 mode 是否继承父目录,或列表是否来自当前请求。`DirectoryHandle.list()` 调用链必须另行处理这些集合级
|
|
904
|
+
* 与请求级约束。
|
|
905
|
+
*
|
|
906
|
+
* @example
|
|
907
|
+
* ```ts
|
|
908
|
+
* import { isMiniProgramFileSystemEntityDescriptor } from '@heybox/hb-sdk/protocol';
|
|
909
|
+
*
|
|
910
|
+
* const candidates: unknown[] = [
|
|
911
|
+
* { kind: 'file', name: 'report.json', mode: 'read', ref: { kind: 'file', handleId: 'file_1' } },
|
|
912
|
+
* { kind: 'directory', name: 'exports', mode: 'read', ref: { kind: 'directory', handleId: 'directory_1' } },
|
|
913
|
+
* ];
|
|
914
|
+
*
|
|
915
|
+
* const descriptors = candidates.filter(isMiniProgramFileSystemEntityDescriptor);
|
|
916
|
+
* for (const descriptor of descriptors) {
|
|
917
|
+
* if (descriptor.kind === 'file') {
|
|
918
|
+
* console.log('file', descriptor.ref);
|
|
919
|
+
* } else {
|
|
920
|
+
* console.log('directory', descriptor.ref);
|
|
921
|
+
* }
|
|
922
|
+
* }
|
|
923
|
+
* ```
|
|
924
|
+
*
|
|
925
|
+
* @see [MiniProgramFileSystemEntityDescriptor](/reference/symbols/protocol/types/MiniProgramFileSystemEntityDescriptor) file/directory 判别联合。
|
|
926
|
+
* @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) file 分支的完整字段与边界。
|
|
927
|
+
* @see [isMiniProgramDirectoryDescriptor](/reference/symbols/protocol/functions/isMiniProgramDirectoryDescriptor) directory 分支的完整字段与边界。
|
|
928
|
+
* @see [DirectoryListResult](/reference/symbols/protocol/types/DirectoryListResult) 由 descriptor 数组组成的 wire 返回类型。
|
|
929
|
+
* @see [FileSystemEntity](/reference/symbols/root/types/FileSystemEntity) SDK 公开的 FileHandle/DirectoryHandle 联合。
|
|
930
|
+
* @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 文件 descriptor 对应的能力代理。
|
|
931
|
+
* @see [DirectoryHandle](/reference/symbols/root/interfaces/DirectoryHandle) 目录 descriptor 对应的能力代理及 `list()` 入口。
|
|
932
|
+
*/
|
|
413
933
|
function isMiniProgramFileSystemEntityDescriptor(value) {
|
|
414
934
|
return isMiniProgramFileDescriptor(value) || isMiniProgramDirectoryDescriptor(value);
|
|
415
935
|
}
|
|
936
|
+
/**
|
|
937
|
+
* 判断未知值是否为使用普通 `ArrayBuffer` 存储的 `Uint8Array` 字节视图。
|
|
938
|
+
*
|
|
939
|
+
* @param value - 来自 bridge payload、Host 返回值或其他不可信边界的候选字节数据。
|
|
940
|
+
* @returns 值是当前 realm 的 `Uint8Array`(或其子类),且 `buffer` 是 `ArrayBuffer` 时返回 `true`,
|
|
941
|
+
* 并将 TypeScript 类型收窄为 `Uint8Array`;否则返回 `false`。
|
|
942
|
+
*
|
|
943
|
+
* @remarks
|
|
944
|
+
* `Uint8Array` 的每个索引元素由 JavaScript 保证为 `0` 到 `255` 的整数。本 guard 接受空视图、非零
|
|
945
|
+
* `byteOffset` 以及只覆盖底层 buffer 一部分的视图;它不要求视图覆盖整个 `ArrayBuffer`,也不限制
|
|
946
|
+
* `byteLength`、文件大小或 Companion stdio 消息大小。
|
|
947
|
+
*
|
|
948
|
+
* 普通数组无论是否稀疏、`ArrayBuffer` 本身、`DataView`、`Int8Array`、`Uint8ClampedArray` 及其他 typed array
|
|
949
|
+
* 都会被拒绝。使用 `SharedArrayBuffer` 作为 backing buffer 的 `Uint8Array` 也会被拒绝。由于判断依赖
|
|
950
|
+
* `instanceof`,由另一个 JavaScript realm 创建且未经过 structured clone 重建的 `Uint8Array` 可能无法通过;
|
|
951
|
+
* 当前 realm 的 `Uint8Array` 子类则可能通过。
|
|
952
|
+
*
|
|
953
|
+
* 该函数不会枚举键,因此挂在 `Uint8Array` 实例上的额外自有属性不会导致失败;它也不会检查 buffer
|
|
954
|
+
* 是否已 detached、数据来源是否可信、内容是否符合业务格式,或调用方是否拥有文件/Companion 权限。
|
|
955
|
+
* 返回 `true` 后仍得到原来的可变视图,而不是副本或冻结值。跨异步边界持有或交给其他消费者前,建议使用
|
|
956
|
+
* `new Uint8Array(value)` 创建只包含当前视图字节的快照。
|
|
957
|
+
*
|
|
958
|
+
* 文件 `readBytes()` 用它识别 Host 返回的
|
|
959
|
+
* [FileReadBytesResult](/reference/symbols/protocol/types/FileReadBytesResult),`writeBytes()` 的
|
|
960
|
+
* [FileWriteBytesPayload](/reference/symbols/protocol/interfaces/FileWriteBytesPayload) `content` 也采用同一字节表示;
|
|
961
|
+
* 路径、句柄类型和访问模式由文件引用与 descriptor guards 单独校验。Companion stdio 的 stdin、stdout 和 stderr
|
|
962
|
+
* 同样传输 `Uint8Array`,但 Session ownership、controller generation、stream、sequence、framing、容量与权限
|
|
963
|
+
* 都不属于本 guard 的职责。
|
|
964
|
+
*
|
|
965
|
+
* @example
|
|
966
|
+
* ```ts
|
|
967
|
+
* import { isMiniProgramByteArray } from '@heybox/hb-sdk/protocol';
|
|
968
|
+
*
|
|
969
|
+
* const candidate: unknown = new Uint8Array([0, 128, 255]);
|
|
970
|
+
* if (!isMiniProgramByteArray(candidate)) {
|
|
971
|
+
* throw new TypeError('需要 ArrayBuffer-backed Uint8Array');
|
|
972
|
+
* }
|
|
973
|
+
*
|
|
974
|
+
* // guard 只做结构收窄;复制后再跨异步边界使用。
|
|
975
|
+
* const snapshot = new Uint8Array(candidate);
|
|
976
|
+
*
|
|
977
|
+
* isMiniProgramByteArray([0, 128, 255]); // false:普通数组不是 wire 字节视图
|
|
978
|
+
* isMiniProgramByteArray(new DataView(new ArrayBuffer(3))); // false
|
|
979
|
+
* ```
|
|
980
|
+
*
|
|
981
|
+
* @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) `readBytes()` 与 `writeBytes()` 的业务入口。
|
|
982
|
+
* @see [FileReadBytesResult](/reference/symbols/protocol/types/FileReadBytesResult) 文件二进制读取结果。
|
|
983
|
+
* @see [FileWriteBytesPayload](/reference/symbols/protocol/interfaces/FileWriteBytesPayload) 文件二进制写入 payload。
|
|
984
|
+
* @see [CompanionStdio](/reference/symbols/root/interfaces/CompanionStdio) Companion 原始字节 stdin/stdout/stderr。
|
|
985
|
+
* @see [isMiniProgramFileDescriptor](/reference/symbols/protocol/functions/isMiniProgramFileDescriptor) 文件 descriptor guard。
|
|
986
|
+
* @see [isMiniProgramDirectoryDescriptor](/reference/symbols/protocol/functions/isMiniProgramDirectoryDescriptor) 目录 descriptor guard。
|
|
987
|
+
*/
|
|
416
988
|
function isMiniProgramByteArray(value) {
|
|
417
989
|
return value instanceof Uint8Array && value.buffer instanceof ArrayBuffer;
|
|
418
990
|
}
|
|
991
|
+
/**
|
|
992
|
+
* 判断未知值是否具有文件元数据快照的最低 wire 结构。
|
|
993
|
+
*
|
|
994
|
+
* @param value - 来自 `file.stat` Host 返回值、bridge response 或其他不可信边界的候选元数据。
|
|
995
|
+
* @returns `kind`、`size` 与可选时间字段满足 {@link MiniProgramFileStat} 结构时返回 `true`,并将
|
|
996
|
+
* TypeScript 类型收窄为文件元数据;否则返回 `false`。
|
|
997
|
+
*
|
|
998
|
+
* @remarks
|
|
999
|
+
* 对通常的对象字面量,仅允许 `kind`、`size`、`modifiedAt`、`createdAt` 四个 canonical key:
|
|
1000
|
+
*
|
|
1001
|
+
* - `kind` 必须固定为 `'file'`。
|
|
1002
|
+
* - `size` 必须是 `0` 到 `Number.MAX_SAFE_INTEGER` 范围内的整数。`0` 表示空文件;负数、小数、`NaN`、
|
|
1003
|
+
* `Infinity`、unsafe integer、bigint 和数字字符串都会被拒绝。文件 API 将该值解释为字节数,但本 guard
|
|
1004
|
+
* 只验证数字结构,不能证明 Host 使用了正确单位。
|
|
1005
|
+
* - `modifiedAt` 与 `createdAt` 相互独立且都可省略,也可显式为 `undefined`;提供时必须是非负有限 number。
|
|
1006
|
+
* 与 `size` 不同,时间值不要求是整数或 safe integer,因此非负小数以及大于 `Number.MAX_SAFE_INTEGER`
|
|
1007
|
+
* 但仍有限的值都可通过。
|
|
1008
|
+
*
|
|
1009
|
+
* 文件 API 把时间字段解释为 Unix epoch milliseconds,但该 guard 不验证值是否落在 JavaScript `Date`
|
|
1010
|
+
* 可表示范围内,也不要求 `createdAt <= modifiedAt`、时间不晚于当前时刻,或两项同时存在。消费者若要排序、
|
|
1011
|
+
* 格式化或比较时间,仍须按自己的业务范围继续校验。
|
|
1012
|
+
*
|
|
1013
|
+
* canonical 集合之外的可枚举自有字符串键会导致失败,包括值为 `undefined` 的额外键;非枚举属性、
|
|
1014
|
+
* symbol 键和继承属性不在 `Object.keys()` 检查范围内,必需字段本身也不要求是自有或可枚举属性。
|
|
1015
|
+
* 该函数不会复制、冻结或规范化输入,返回的仍是原始可变对象。
|
|
1016
|
+
*
|
|
1017
|
+
* 返回 `true` 只建立结构类型收窄,不证明该快照来自可信 Host、对应当前 `FileHandle` 或当前 request,也不证明
|
|
1018
|
+
* 文件仍然存在、`size` 与磁盘内容一致、时间由文件系统提供、handle 属于当前 session/origin、`filesystem`
|
|
1019
|
+
* 权限已启用或 Host 支持 `file.stat`。这些关联、权限与 I/O 错误必须由 Runtime 和 `FileHandle.stat()` 调用链处理。
|
|
1020
|
+
*
|
|
1021
|
+
* @example
|
|
1022
|
+
* ```ts
|
|
1023
|
+
* import { isMiniProgramFileStat } from '@heybox/hb-sdk/protocol';
|
|
1024
|
+
*
|
|
1025
|
+
* const candidate: unknown = {
|
|
1026
|
+
* kind: 'file',
|
|
1027
|
+
* size: 4096,
|
|
1028
|
+
* modifiedAt: 1_795_027_200_000,
|
|
1029
|
+
* };
|
|
1030
|
+
*
|
|
1031
|
+
* if (isMiniProgramFileStat(candidate)) {
|
|
1032
|
+
* console.log(`${candidate.size} bytes`);
|
|
1033
|
+
* if (candidate.modifiedAt !== undefined) {
|
|
1034
|
+
* console.log(candidate.modifiedAt);
|
|
1035
|
+
* }
|
|
1036
|
+
* }
|
|
1037
|
+
*
|
|
1038
|
+
* isMiniProgramFileStat({ kind: 'file', size: 0 }); // true:时间字段可全部省略
|
|
1039
|
+
* isMiniProgramFileStat({ kind: 'file', size: 1.5 }); // false:size 必须是 safe integer
|
|
1040
|
+
* ```
|
|
1041
|
+
*
|
|
1042
|
+
* @see [MiniProgramFileStat](/reference/symbols/protocol/interfaces/MiniProgramFileStat) bridge 使用的元数据字段类型。
|
|
1043
|
+
* @see [FileStatResult](/reference/symbols/protocol/types/FileStatResult) `file.stat` 的 wire 返回类型。
|
|
1044
|
+
* @see [FileStat](/reference/symbols/root/interfaces/FileStat) SDK 向业务返回的元数据快照。
|
|
1045
|
+
* @see [FileHandle](/reference/symbols/root/interfaces/FileHandle) 提供 `stat()` 的当前 session 文件代理。
|
|
1046
|
+
*/
|
|
419
1047
|
function isMiniProgramFileStat(value) {
|
|
420
1048
|
return (isRecord$2(value) &&
|
|
421
1049
|
value.kind === 'file' &&
|
|
@@ -424,6 +1052,58 @@ function isMiniProgramFileStat(value) {
|
|
|
424
1052
|
(value.createdAt === undefined || isNonNegativeFiniteNumber(value.createdAt)) &&
|
|
425
1053
|
hasOnlyKeys(value, ['kind', 'size', 'modifiedAt', 'createdAt']));
|
|
426
1054
|
}
|
|
1055
|
+
/**
|
|
1056
|
+
* 判断未知值是否为单个 `network.download` 进度 payload。
|
|
1057
|
+
*
|
|
1058
|
+
* @param value - 来自 operation progress 事件、Host adapter 或其他不可信边界的候选进度快照。
|
|
1059
|
+
* @returns 字段组合满足 {@link DownloadProgressPayload} 的最低 wire 约束时返回 `true`,并将 TypeScript
|
|
1060
|
+
* 类型收窄为下载进度 payload;否则返回 `false`。
|
|
1061
|
+
*
|
|
1062
|
+
* @remarks
|
|
1063
|
+
* 对通常的对象字面量,仅允许 `loaded`、`total`、`lengthComputable` 三个 canonical key,并执行以下组合校验:
|
|
1064
|
+
*
|
|
1065
|
+
* - `loaded` 始终必需,必须是 `0` 到 `Number.MAX_SAFE_INTEGER` 范围内的整数。负数、小数、`NaN`、
|
|
1066
|
+
* `Infinity`、unsafe integer、bigint 和数字字符串都会被拒绝。
|
|
1067
|
+
* - `lengthComputable` 必须是 boolean,不能使用 `0` / `1` 或其他 truthy/falsy 值代替。
|
|
1068
|
+
* - `lengthComputable === false` 时,`total` 必须为 `undefined`;既可以省略该 key,也可以显式写成
|
|
1069
|
+
* `total: undefined`。此分支不对 `loaded` 应达到的最终值作推断。
|
|
1070
|
+
* - `lengthComputable === true` 时,`total` 必须是非负 safe integer,且 `loaded <= total`。
|
|
1071
|
+
* `{ loaded: 0, total: 0, lengthComputable: true }` 是有效的空内容进度。
|
|
1072
|
+
*
|
|
1073
|
+
* canonical 集合之外的可枚举自有字符串键会导致失败;非枚举属性、symbol 键和继承属性不在
|
|
1074
|
+
* `Object.keys()` 检查范围内,必需字段本身也不要求是自有或可枚举属性。该函数不会复制、冻结或
|
|
1075
|
+
* 规范化输入。类型谓词只收窄到字段仍为可变、`total` 仍为可选的 {@link DownloadProgressPayload},
|
|
1076
|
+
* 不会在 TypeScript 中建立以 `lengthComputable` 为判别字段的精确联合。
|
|
1077
|
+
*
|
|
1078
|
+
* 单个 payload 通过不表示进度来自当前 request、使用当前 pending operation 的 ID,或由可信 Host 产生;
|
|
1079
|
+
* 也不保证单位确为字节、事件按时间排序、`loaded` 相对前一事件单调、`total` 跨事件稳定、这是最终进度,
|
|
1080
|
+
* 或对应字节已经原子提交到目标文件。`network.download` 的 Runtime 状态机必须另行绑定 request ID,校验
|
|
1081
|
+
* 可信响应 framing、`maxBytes`、取消与终态,并过滤倒退或越界值。
|
|
1082
|
+
*
|
|
1083
|
+
* @example
|
|
1084
|
+
* ```ts
|
|
1085
|
+
* import { isMiniProgramDownloadProgressPayload } from '@heybox/hb-sdk/protocol';
|
|
1086
|
+
*
|
|
1087
|
+
* function renderProgress(candidate: unknown) {
|
|
1088
|
+
* if (!isMiniProgramDownloadProgressPayload(candidate)) return;
|
|
1089
|
+
*
|
|
1090
|
+
* if (candidate.lengthComputable && candidate.total !== undefined) {
|
|
1091
|
+
* console.log(`${candidate.loaded}/${candidate.total}`);
|
|
1092
|
+
* } else {
|
|
1093
|
+
* console.log(`${candidate.loaded} bytes`);
|
|
1094
|
+
* }
|
|
1095
|
+
* }
|
|
1096
|
+
*
|
|
1097
|
+
* renderProgress({ loaded: 512, total: 1024, lengthComputable: true }); // valid
|
|
1098
|
+
* renderProgress({ loaded: 512, lengthComputable: false }); // valid:总长度未知
|
|
1099
|
+
* renderProgress({ loaded: 5, total: 4, lengthComputable: true }); // ignored:loaded > total
|
|
1100
|
+
* ```
|
|
1101
|
+
*
|
|
1102
|
+
* @see [DownloadProgressPayload](/reference/symbols/protocol/interfaces/DownloadProgressPayload) bridge 使用的进度字段类型。
|
|
1103
|
+
* @see [DownloadProgress](/reference/symbols/root/interfaces/DownloadProgress) `onProgress` 接收的公开 SDK 进度类型。
|
|
1104
|
+
* @see [network.download](/reference/sdk/network/download) 产生并消费该进度的下载能力。
|
|
1105
|
+
* @see [isMiniProgramBridgeMessage](/reference/symbols/protocol/functions/isMiniProgramBridgeMessage) operation progress envelope 的最低校验。
|
|
1106
|
+
*/
|
|
427
1107
|
function isMiniProgramDownloadProgressPayload(value) {
|
|
428
1108
|
if (!isRecord$2(value) ||
|
|
429
1109
|
!isNonNegativeSafeInteger(value.loaded) ||
|
|
@@ -593,8 +1273,8 @@ function isOperationList(value) {
|
|
|
593
1273
|
new Set(value).size === value.length);
|
|
594
1274
|
}
|
|
595
1275
|
|
|
596
|
-
/** 当前用户信息与登录能力支持的完整 scope
|
|
597
|
-
const USER_INFO_AUTHORIZATION_SCOPES = ['identity', 'profile'];
|
|
1276
|
+
/** 当前用户信息与登录能力支持的完整 scope 目录(canonical 顺序)。 */
|
|
1277
|
+
const USER_INFO_AUTHORIZATION_SCOPES = ['identity', 'profile', 'steam_account'];
|
|
598
1278
|
/** Companion alias 的跨构建、Runtime 与 Host 规范。 */
|
|
599
1279
|
const COMPANION_ALIAS_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;
|
|
600
1280
|
/** Companion Host 可跨 bridge 保留的稳定领域错误码。 */
|
|
@@ -789,9 +1469,134 @@ function isMiniProgramPermissionKey(key) {
|
|
|
789
1469
|
}
|
|
790
1470
|
|
|
791
1471
|
const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(MINI_PROGRAM_PERMISSION_KEYS);
|
|
1472
|
+
/**
|
|
1473
|
+
* 判断字符串是否是当前协议目录管理的 canonical runtime permission key。
|
|
1474
|
+
*
|
|
1475
|
+
* @param key - 已确认类型为 `string` 的候选权限 key;若输入来自 `unknown` 边界,调用方须先完成字符串检查。
|
|
1476
|
+
* @returns 与 `MINI_PROGRAM_PERMISSION_KEYS` 中某项精确相等时返回 `true`,并将 TypeScript 类型收窄为
|
|
1477
|
+
* `MiniProgramPermissionKey`;未知、历史或格式不同的字符串返回 `false`。
|
|
1478
|
+
*
|
|
1479
|
+
* @remarks
|
|
1480
|
+
* 当前 canonical key 集合固定为:
|
|
1481
|
+
*
|
|
1482
|
+
* - `userInfo`
|
|
1483
|
+
* - `steamLibrary`
|
|
1484
|
+
* - `share`
|
|
1485
|
+
* - `storage`
|
|
1486
|
+
* - `filesystem`
|
|
1487
|
+
* - `clipboard`
|
|
1488
|
+
* - `leaderboard`
|
|
1489
|
+
* - `network`
|
|
1490
|
+
* - `companion`
|
|
1491
|
+
*
|
|
1492
|
+
* 匹配大小写敏感且按原字符串执行。该 guard 不 trim,因此 `' network '` 返回 `false`;历史权限 key
|
|
1493
|
+
* `network.request` 也返回 `false`,即使同名 capability method 仍是公开 method。消费完整 schema v1 权限快照时
|
|
1494
|
+
* 应使用 {@link parseMiniProgramRuntimePermissions},由解析边界统一 trim,并把历史 `network.request` 归一为
|
|
1495
|
+
* canonical `network`。未知的 future key 同样返回 `false`;本 guard 不负责 parser 的前向兼容忽略策略。
|
|
1496
|
+
*
|
|
1497
|
+
* 类型谓词只把字符串收窄为 `MiniProgramPermissionKey` union,不读取权限 entry、status 或 config,也不检查
|
|
1498
|
+
* Manifest。返回 `true` 不表示权限已声明、平台已批准、Runtime snapshot 中存在、status 为 `enabled`、Host 支持,
|
|
1499
|
+
* 或当前 capability 已获准执行。尤其 `network` 与 `companion` 还需要平台批准;其他 declaration 权限也继续受
|
|
1500
|
+
* 各自适用的 Host、用户授权、可信手势、参数与业务策略约束。
|
|
1501
|
+
*
|
|
1502
|
+
* `MINI_PROGRAM_PERMISSION_CATALOG` 提供每个 key 的展示信息、access、风险、配置字段和 method 集合;具体 bridge
|
|
1503
|
+
* method 是否要求某项权限应以 {@link MINI_PROGRAM_PROTOCOL_CAPABILITIES} 的 `requirement` 为准。`kind: 'none'`
|
|
1504
|
+
* 的 method 不会因为某个 key 被本 guard 识别而新增权限要求。
|
|
1505
|
+
*
|
|
1506
|
+
* @example
|
|
1507
|
+
* ```ts
|
|
1508
|
+
* import { isManagedMiniProgramRuntimePermissionKey } from '@heybox/hb-sdk/protocol';
|
|
1509
|
+
*
|
|
1510
|
+
* function acceptPermissionKey(candidate: unknown) {
|
|
1511
|
+
* if (typeof candidate !== 'string') return undefined;
|
|
1512
|
+
* return isManagedMiniProgramRuntimePermissionKey(candidate) ? candidate : undefined;
|
|
1513
|
+
* }
|
|
1514
|
+
*
|
|
1515
|
+
* acceptPermissionKey('filesystem'); // 'filesystem'
|
|
1516
|
+
* acceptPermissionKey('network'); // 'network'
|
|
1517
|
+
* acceptPermissionKey(' network '); // undefined:guard 不 trim
|
|
1518
|
+
* acceptPermissionKey('network.request'); // undefined:legacy key 只由 snapshot parser 归一化
|
|
1519
|
+
* ```
|
|
1520
|
+
*
|
|
1521
|
+
* @see [parseMiniProgramRuntimePermissions](/reference/symbols/protocol/functions/parseMiniProgramRuntimePermissions) 完整 snapshot 的 trim、legacy 归一化与 config 校验。
|
|
1522
|
+
* @see [MINI_PROGRAM_PROTOCOL_CAPABILITIES](/reference/symbols/protocol/constants/MINI_PROGRAM_PROTOCOL_CAPABILITIES) method 到 permission requirement 的权威映射。
|
|
1523
|
+
* @see [权限声明](/guide/permissions) Manifest 声明、平台批准与 Runtime 状态的完整流程。
|
|
1524
|
+
*/
|
|
792
1525
|
function isManagedMiniProgramRuntimePermissionKey(key) {
|
|
793
1526
|
return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
|
|
794
1527
|
}
|
|
1528
|
+
/**
|
|
1529
|
+
* 将不可信的 schema v1 runtime permission snapshot 解析为独立的 canonical 权限映射。
|
|
1530
|
+
*
|
|
1531
|
+
* @param snapshot - Host、服务端、dev context 或持久化边界提供的普通反序列化数据;调用方不需要先做
|
|
1532
|
+
* 类型断言。Proxy trap 或 getter 自身抛出的异常会原样传播。
|
|
1533
|
+
* @returns {@link ParsedMiniProgramRuntimePermissions}。完整 snapshot 合法时返回
|
|
1534
|
+
* `{ valid: true, permissions }`;顶层、entry、状态、重复 key 或已知 config 任一校验失败时统一
|
|
1535
|
+
* 返回 `{ valid: false, permissions: {} }`,不会保留部分权限。
|
|
1536
|
+
*
|
|
1537
|
+
* @remarks
|
|
1538
|
+
* 顶层必须是对象,`schema_version` 必须严格等于数字 `1`,`entries` 必须是数组。`revision`
|
|
1539
|
+
* 可省略;存在时必须是大于或等于 `0` 的整数。revision 只参与输入校验,不会复制到返回值。
|
|
1540
|
+
* 顶层与 entry 的其他字段会被忽略,以便 schema v1 内前向扩展。
|
|
1541
|
+
*
|
|
1542
|
+
* 每个 entry 必须是对象,`key` 必须是 trim 后非空的字符串,`status` 必须严格为
|
|
1543
|
+
* `'enabled'` 或 `'disabled'`。解析器先 trim key,再把唯一的历史别名 `network.request`
|
|
1544
|
+
* 归一为 canonical `network`;大小写不会转换。重复检查基于归一化后的所有 key,因此
|
|
1545
|
+
* `network` 与 ` network.request ` 冲突,两个相同未知 future key 也会让整个 snapshot fail
|
|
1546
|
+
* closed。
|
|
1547
|
+
*
|
|
1548
|
+
* 是否属于当前受管 key 由 {@link isManagedMiniProgramRuntimePermissionKey} 与权限目录决定。
|
|
1549
|
+
* 为兼容较新服务端,非空、状态合法且不重复的未知 key 会被忽略;其 `config` 可以缺失、为
|
|
1550
|
+
* `null`、数组或任意未来结构,均不会使已认识的权限失效。未知 entry 不会出现在返回映射中。
|
|
1551
|
+
*
|
|
1552
|
+
* 已知权限严格执行当前 schema:`network` 必须提供对象 config,且
|
|
1553
|
+
* `useOfficialDomain` 必须是 boolean;其他额外 network config 字段会丢弃,返回值只复制该
|
|
1554
|
+
* boolean。其余当前权限是无配置项权限,输入 config 可以缺失、为 `null` 或 `{}`,并统一输出
|
|
1555
|
+
* `{}`;非空对象、数组或其他值会使整个 snapshot 无效。Runtime snapshot 必须显式包含
|
|
1556
|
+
* network 的有效 boolean,即使 Manifest 目录把省略 `useOfficialDomain` 的开发者默认值定义为
|
|
1557
|
+
* `false`。
|
|
1558
|
+
*
|
|
1559
|
+
* 返回的 canonical entry/config 都是新对象;修改原 snapshot、entry 或嵌套 network config
|
|
1560
|
+
* 不会改变解析结果。`permissions` 是 Partial map:合法空 entries、只包含未知权限,或没有某个
|
|
1561
|
+
* canonical key 时,解析仍可 `valid: true`,对应属性保持缺失。
|
|
1562
|
+
*
|
|
1563
|
+
* `valid: true` 只表示 snapshot 可安全消费,不代表 entry 已启用,更不代表某个 capability 已获
|
|
1564
|
+
* 授权。Runtime consumer 应先查 {@link MINI_PROGRAM_PROTOCOL_CAPABILITIES} 中目标 method 的
|
|
1565
|
+
* `requirement`:`kind: 'none'` 不读取权限;`kind: 'all'` 要求列出的每个 canonical entry 都存在
|
|
1566
|
+
* 且 `status === 'enabled'`。之后仍需通过 Host capability、可信手势、用户授权、参数和业务
|
|
1567
|
+
* policy。当前 Runtime 对 `valid: false` 按空权限表 fail closed,受保护 method 会得到
|
|
1568
|
+
* `PERMISSION_NOT_DECLARED`,而不是沿用输入中的部分 enabled entry。
|
|
1569
|
+
*
|
|
1570
|
+
* @example
|
|
1571
|
+
* ```ts
|
|
1572
|
+
* import { parseMiniProgramRuntimePermissions } from '@heybox/hb-sdk/protocol';
|
|
1573
|
+
*
|
|
1574
|
+
* const parsed = parseMiniProgramRuntimePermissions({
|
|
1575
|
+
* schema_version: 1,
|
|
1576
|
+
* revision: 4,
|
|
1577
|
+
* entries: [
|
|
1578
|
+
* {
|
|
1579
|
+
* key: ' network.request ',
|
|
1580
|
+
* status: 'enabled',
|
|
1581
|
+
* config: { useOfficialDomain: false, futureField: 'discarded' },
|
|
1582
|
+
* },
|
|
1583
|
+
* { key: 'future.permission', status: 'disabled', config: null },
|
|
1584
|
+
* ],
|
|
1585
|
+
* });
|
|
1586
|
+
*
|
|
1587
|
+
* // legacy key 已归一化;未知 future key 被忽略。
|
|
1588
|
+
* if (parsed.valid && parsed.permissions.network?.status === 'enabled') {
|
|
1589
|
+
* console.log(parsed.permissions.network.config.useOfficialDomain); // false
|
|
1590
|
+
* // 继续执行 capability requirement、Host 与业务 policy 校验。
|
|
1591
|
+
* }
|
|
1592
|
+
* ```
|
|
1593
|
+
*
|
|
1594
|
+
* @see {@link ParsedMiniProgramRuntimePermissions}
|
|
1595
|
+
* @see {@link MiniProgramRuntimePermissionsSnapshot}
|
|
1596
|
+
* @see {@link isManagedMiniProgramRuntimePermissionKey}
|
|
1597
|
+
* @see {@link MINI_PROGRAM_PROTOCOL_CAPABILITIES}
|
|
1598
|
+
* @see {@link https://docs.xiaoheihe.cn/hb_sdk/guide/permissions | 权限声明}
|
|
1599
|
+
*/
|
|
795
1600
|
function parseMiniProgramRuntimePermissions(snapshot) {
|
|
796
1601
|
if (!isRecord(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
|
|
797
1602
|
return { valid: false, permissions: {} };
|