@heybox/hb-sdk 0.6.3 → 0.6.4
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/README.md +4 -2
- package/dist/cli-chunks/{context-Bu0Zusj_.cjs → context-BEgbP7mM.cjs} +1 -1
- package/dist/cli-chunks/{create-CgAGcRtZ.cjs → create-BHsWWo3V.cjs} +1 -1
- package/dist/cli-chunks/{dev-g35-QcIa.cjs → dev-CSjs2OYB.cjs} +61 -47
- package/dist/cli-chunks/{doctor-klsQg82G.cjs → doctor-B3AOQgyN.cjs} +1 -1
- package/dist/cli-chunks/{index--P321c8v.cjs → index-B0zGksOA.cjs} +38 -13
- package/dist/cli-chunks/{index-CbsbCgtf.cjs → index-BGFf6g7V.cjs} +2 -2
- package/dist/cli-chunks/{login-DEoReXOe.cjs → login-ClQuL2r6.cjs} +2 -2
- package/dist/cli-chunks/{remote-CZ-G3RSH.cjs → remote-ib5THDbw.cjs} +4 -4
- package/dist/cli-chunks/{session-vlPGi1Z8.cjs → session-D7nmqMiy.cjs} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/mock-host/index.html +35 -1
- package/dist/devtools/mock-host/main.js +213 -42
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/protocol.cjs.js +54 -0
- package/dist/protocol.esm.js +53 -1
- package/dist/vite.cjs.js +1 -1
- package/dist/vite.esm.js +1 -1
- package/package.json +2 -2
- package/skill/SKILL.md +4 -2
- package/skill/references/api-protocol.md +18 -17
- package/skill/references/api-root.md +4 -2
- package/skill/references/cli.md +26 -33
- package/skill/references/recipes.md +78 -29
- package/skill/scripts/sync-references.mjs +2 -0
- package/skill/skill.json +4 -4
- package/types/protocol/runtime-permissions.d.ts +41 -0
- package/types/protocol.d.ts +4 -2
|
@@ -7,6 +7,58 @@ const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
|
7
7
|
/** SDK 向 Runtime 报告 CSP 违规的内部方法名。 */
|
|
8
8
|
const SDK_CSP_VIOLATION_METHOD = 'sdk.csp.violation';
|
|
9
9
|
|
|
10
|
+
const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
|
|
11
|
+
/**
|
|
12
|
+
* 判断 permission key 是否由 Runtime 权限快照管理。
|
|
13
|
+
*
|
|
14
|
+
* @param key 待判断的 permission key。
|
|
15
|
+
* @returns 该 key 需要读取 Runtime 权限快照时返回 `true`。
|
|
16
|
+
*/
|
|
17
|
+
function isManagedMiniProgramRuntimePermissionKey(key) {
|
|
18
|
+
return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。
|
|
22
|
+
*
|
|
23
|
+
* @param snapshot 待校验的服务端权限快照。
|
|
24
|
+
* @returns 解析状态和通过校验的受管权限。
|
|
25
|
+
*/
|
|
26
|
+
function parseMiniProgramRuntimePermissions(snapshot) {
|
|
27
|
+
if (!isRecord$2(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
|
|
28
|
+
return { valid: false, permissions: {} };
|
|
29
|
+
}
|
|
30
|
+
if (snapshot.revision !== undefined && (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
|
|
31
|
+
return { valid: false, permissions: {} };
|
|
32
|
+
}
|
|
33
|
+
const seenKeys = new Set();
|
|
34
|
+
const permissions = {};
|
|
35
|
+
for (const rawEntry of snapshot.entries) {
|
|
36
|
+
if (!isRecord$2(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
|
|
37
|
+
return { valid: false, permissions: {} };
|
|
38
|
+
}
|
|
39
|
+
const key = rawEntry.key.trim();
|
|
40
|
+
if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord$2(rawEntry.config)) {
|
|
41
|
+
return { valid: false, permissions: {} };
|
|
42
|
+
}
|
|
43
|
+
seenKeys.add(key);
|
|
44
|
+
if (!isManagedMiniProgramRuntimePermissionKey(key)) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
|
|
48
|
+
return { valid: false, permissions: {} };
|
|
49
|
+
}
|
|
50
|
+
permissions[key] = {
|
|
51
|
+
key,
|
|
52
|
+
status: rawEntry.status,
|
|
53
|
+
config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { valid: true, permissions };
|
|
57
|
+
}
|
|
58
|
+
function isRecord$2(value) {
|
|
59
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
10
62
|
/**
|
|
11
63
|
* 登录授权能力方法名。
|
|
12
64
|
*
|
|
@@ -3445,6 +3497,91 @@ function formatBlockedResource(payload) {
|
|
|
3445
3497
|
return '未知资源';
|
|
3446
3498
|
}
|
|
3447
3499
|
|
|
3500
|
+
const DEFAULT_PERMISSION_STATE = {
|
|
3501
|
+
networkRequest: {
|
|
3502
|
+
status: 'disabled',
|
|
3503
|
+
useOfficialDomain: false,
|
|
3504
|
+
},
|
|
3505
|
+
};
|
|
3506
|
+
function createMiniProgramMockPermissionState(context) {
|
|
3507
|
+
const parsed = parseMiniProgramRuntimePermissions(context.runtimePermissions);
|
|
3508
|
+
const networkRequest = parsed.permissions['network.request'];
|
|
3509
|
+
return parsed.valid && networkRequest
|
|
3510
|
+
? {
|
|
3511
|
+
networkRequest: {
|
|
3512
|
+
status: networkRequest.status,
|
|
3513
|
+
useOfficialDomain: networkRequest.config.useOfficialDomain === true,
|
|
3514
|
+
},
|
|
3515
|
+
}
|
|
3516
|
+
: cloneDefaultPermissionState();
|
|
3517
|
+
}
|
|
3518
|
+
function createMiniProgramMockRuntimePermissions(state) {
|
|
3519
|
+
return {
|
|
3520
|
+
schema_version: 1,
|
|
3521
|
+
entries: [
|
|
3522
|
+
{
|
|
3523
|
+
key: 'network.request',
|
|
3524
|
+
status: state.networkRequest.status,
|
|
3525
|
+
config: {
|
|
3526
|
+
useOfficialDomain: state.networkRequest.useOfficialDomain,
|
|
3527
|
+
},
|
|
3528
|
+
},
|
|
3529
|
+
],
|
|
3530
|
+
};
|
|
3531
|
+
}
|
|
3532
|
+
function createMiniProgramDevReleaseDiagnostics(context, localState) {
|
|
3533
|
+
if (context.source === 'anonymous') {
|
|
3534
|
+
return [
|
|
3535
|
+
{
|
|
3536
|
+
code: 'REMOTE_CONTEXT_UNAVAILABLE',
|
|
3537
|
+
severity: 'warning',
|
|
3538
|
+
message: `未获取到线上权限配置,本地模拟不代表上线状态${context.warning ? `:${context.warning}` : ''}`,
|
|
3539
|
+
},
|
|
3540
|
+
];
|
|
3541
|
+
}
|
|
3542
|
+
const parsedRemote = parseMiniProgramRuntimePermissions(context.runtimePermissions);
|
|
3543
|
+
if (!parsedRemote.valid) {
|
|
3544
|
+
return [
|
|
3545
|
+
{
|
|
3546
|
+
code: 'REMOTE_PERMISSIONS_INVALID',
|
|
3547
|
+
severity: 'warning',
|
|
3548
|
+
message: '线上权限配置不可用,本地模拟不代表上线状态。',
|
|
3549
|
+
},
|
|
3550
|
+
];
|
|
3551
|
+
}
|
|
3552
|
+
const remoteNetworkRequest = parsedRemote.permissions['network.request'];
|
|
3553
|
+
const remoteState = remoteNetworkRequest
|
|
3554
|
+
? {
|
|
3555
|
+
status: remoteNetworkRequest.status,
|
|
3556
|
+
useOfficialDomain: remoteNetworkRequest.config.useOfficialDomain === true,
|
|
3557
|
+
}
|
|
3558
|
+
: DEFAULT_PERMISSION_STATE.networkRequest;
|
|
3559
|
+
if (localState.networkRequest.status === 'enabled' && remoteState.status !== 'enabled') {
|
|
3560
|
+
return [
|
|
3561
|
+
{
|
|
3562
|
+
code: 'NETWORK_REQUEST_DISABLED_ONLINE',
|
|
3563
|
+
severity: 'warning',
|
|
3564
|
+
message: '当前本地允许 network.request,但线上权限已关闭;上线后请求会返回 PERMISSION_DENIED。',
|
|
3565
|
+
},
|
|
3566
|
+
];
|
|
3567
|
+
}
|
|
3568
|
+
if (localState.networkRequest.status === 'enabled' && localState.networkRequest.useOfficialDomain && !remoteState.useOfficialDomain) {
|
|
3569
|
+
return [
|
|
3570
|
+
{
|
|
3571
|
+
code: 'OFFICIAL_DOMAIN_DISABLED_ONLINE',
|
|
3572
|
+
severity: 'warning',
|
|
3573
|
+
message: '当前本地允许访问小黑盒官方域名,但线上权限未开放;上线后相关请求会返回 PERMISSION_DENIED。',
|
|
3574
|
+
},
|
|
3575
|
+
];
|
|
3576
|
+
}
|
|
3577
|
+
return [];
|
|
3578
|
+
}
|
|
3579
|
+
function cloneDefaultPermissionState() {
|
|
3580
|
+
return {
|
|
3581
|
+
networkRequest: { ...DEFAULT_PERMISSION_STATE.networkRequest },
|
|
3582
|
+
};
|
|
3583
|
+
}
|
|
3584
|
+
|
|
3448
3585
|
async function getMiniProgramRuntimeUserInfo(platformAdapter) {
|
|
3449
3586
|
const userId = await resolveCurrentUserId(platformAdapter);
|
|
3450
3587
|
if (!userId) {
|
|
@@ -5031,46 +5168,11 @@ function isOfficialMiniProgramNetworkUrl(value) {
|
|
|
5031
5168
|
}
|
|
5032
5169
|
}
|
|
5033
5170
|
|
|
5034
|
-
const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
|
|
5035
|
-
/** 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。 */
|
|
5036
|
-
function parseMiniProgramRuntimePermissions(snapshot) {
|
|
5037
|
-
if (!isRecord$1(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
|
|
5038
|
-
return { valid: false, permissions: {} };
|
|
5039
|
-
}
|
|
5040
|
-
if (snapshot.revision !== undefined &&
|
|
5041
|
-
(typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
|
|
5042
|
-
return { valid: false, permissions: {} };
|
|
5043
|
-
}
|
|
5044
|
-
const seenKeys = new Set();
|
|
5045
|
-
const permissions = {};
|
|
5046
|
-
for (const rawEntry of snapshot.entries) {
|
|
5047
|
-
if (!isRecord$1(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
|
|
5048
|
-
return { valid: false, permissions: {} };
|
|
5049
|
-
}
|
|
5050
|
-
const key = rawEntry.key.trim();
|
|
5051
|
-
if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord$1(rawEntry.config)) {
|
|
5052
|
-
return { valid: false, permissions: {} };
|
|
5053
|
-
}
|
|
5054
|
-
seenKeys.add(key);
|
|
5055
|
-
if (!MANAGED_RUNTIME_PERMISSION_KEYS.has(key)) {
|
|
5056
|
-
continue;
|
|
5057
|
-
}
|
|
5058
|
-
if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
|
|
5059
|
-
return { valid: false, permissions: {} };
|
|
5060
|
-
}
|
|
5061
|
-
permissions[key] = {
|
|
5062
|
-
key,
|
|
5063
|
-
status: rawEntry.status,
|
|
5064
|
-
config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
|
|
5065
|
-
};
|
|
5066
|
-
}
|
|
5067
|
-
return { valid: true, permissions };
|
|
5068
|
-
}
|
|
5069
5171
|
/** 创建只依赖启动快照的纯权限 evaluator,供正式 Runtime 与 Mock Host 共用。 */
|
|
5070
5172
|
function createMiniProgramRuntimePermissionEvaluator(snapshot) {
|
|
5071
5173
|
const parsed = parseMiniProgramRuntimePermissions(snapshot);
|
|
5072
5174
|
return (permissionKey, payload) => {
|
|
5073
|
-
if (!
|
|
5175
|
+
if (!isManagedMiniProgramRuntimePermissionKey(permissionKey)) {
|
|
5074
5176
|
return { allowed: true };
|
|
5075
5177
|
}
|
|
5076
5178
|
const entry = parsed.valid ? parsed.permissions[permissionKey] : undefined;
|
|
@@ -6306,6 +6408,7 @@ const elements = {
|
|
|
6306
6408
|
cspViolations: queryElement('#csp-violations'),
|
|
6307
6409
|
debugPageCopyStatus: queryElement('#debug-page-copy-status'),
|
|
6308
6410
|
debugPageUrl: queryElement('#debug-page-url'),
|
|
6411
|
+
devContextStatus: queryElement('#dev-context-status'),
|
|
6309
6412
|
device: queryElement('#device'),
|
|
6310
6413
|
logs: queryElement('#logs'),
|
|
6311
6414
|
macAppButton: queryElement('#mac-app-button'),
|
|
@@ -6315,6 +6418,9 @@ const elements = {
|
|
|
6315
6418
|
mobileQrImage: queryElement('#mobile-qr-image'),
|
|
6316
6419
|
mobileQrStatus: queryElement('#mobile-qr-status'),
|
|
6317
6420
|
nicknameInput: queryElement('#nickname-input'),
|
|
6421
|
+
networkPermissionSelect: queryElement('#network-permission-select'),
|
|
6422
|
+
releaseDiagnostics: queryElement('#release-diagnostics'),
|
|
6423
|
+
resetPermissionsButton: queryElement('#reset-permissions-button'),
|
|
6318
6424
|
storageSnapshot: queryElement('#storage-snapshot'),
|
|
6319
6425
|
userStatus: queryElement('#user-status'),
|
|
6320
6426
|
};
|
|
@@ -6325,13 +6431,15 @@ if (!miniUrl) {
|
|
|
6325
6431
|
}
|
|
6326
6432
|
const miniProgramUrl = miniUrl;
|
|
6327
6433
|
const bootstrap = await loadMockHostBootstrap();
|
|
6328
|
-
const
|
|
6434
|
+
const devContext = bootstrap.devContext;
|
|
6435
|
+
const initialPermissionState = createMiniProgramMockPermissionState(devContext);
|
|
6436
|
+
let permissionState = clonePermissionState(initialPermissionState);
|
|
6329
6437
|
const iframe = document.createElement('iframe');
|
|
6330
6438
|
iframe.src = appendNonce(miniProgramUrl, nonce);
|
|
6331
6439
|
iframe.allow = 'clipboard-read; clipboard-write';
|
|
6332
6440
|
elements.device.appendChild(iframe);
|
|
6333
6441
|
elements.miniUrl.textContent = miniProgramUrl;
|
|
6334
|
-
const
|
|
6442
|
+
const platformAdapter = createBrowserMockRuntimePlatformAdapter({
|
|
6335
6443
|
iframe,
|
|
6336
6444
|
getCurrentHref: () => location.href,
|
|
6337
6445
|
getCurrentUser: () => currentUser,
|
|
@@ -6353,12 +6461,11 @@ const runtime = new MiniProgramMockRuntime(createBrowserMockRuntimePlatformAdapt
|
|
|
6353
6461
|
storage.set(key, value);
|
|
6354
6462
|
updateStorageSnapshot();
|
|
6355
6463
|
},
|
|
6356
|
-
}), {
|
|
6357
|
-
onAuthChange: (result) => postEvent('authChange', result),
|
|
6358
|
-
runtimePermissions,
|
|
6359
6464
|
});
|
|
6465
|
+
let runtime = createRuntime();
|
|
6360
6466
|
updateUserStatus();
|
|
6361
6467
|
updateStorageSnapshot();
|
|
6468
|
+
updatePermissionControls();
|
|
6362
6469
|
window.addEventListener('message', handleMessage);
|
|
6363
6470
|
window.addEventListener('beforeunload', () => {
|
|
6364
6471
|
postEvent('unload', { timestamp: Date.now() });
|
|
@@ -6386,7 +6493,42 @@ elements.copyDebugPageUrlButton.addEventListener('click', () => {
|
|
|
6386
6493
|
elements.mobileLanSelect.addEventListener('change', () => {
|
|
6387
6494
|
void updateMobileQrCode();
|
|
6388
6495
|
});
|
|
6496
|
+
elements.networkPermissionSelect.addEventListener('change', () => {
|
|
6497
|
+
permissionState.networkRequest.status =
|
|
6498
|
+
elements.networkPermissionSelect.value === 'enabled' ? 'enabled' : 'disabled';
|
|
6499
|
+
rebuildRuntimeWithPermissions();
|
|
6500
|
+
});
|
|
6501
|
+
elements.resetPermissionsButton.addEventListener('click', () => {
|
|
6502
|
+
permissionState = clonePermissionState(initialPermissionState);
|
|
6503
|
+
rebuildRuntimeWithPermissions();
|
|
6504
|
+
});
|
|
6389
6505
|
const mobileAppQrReady = setupMobileAppQr();
|
|
6506
|
+
function createRuntime() {
|
|
6507
|
+
return new MiniProgramMockRuntime(platformAdapter, {
|
|
6508
|
+
onAuthChange: (result) => postEvent('authChange', result),
|
|
6509
|
+
runtimePermissions: createMiniProgramMockRuntimePermissions(permissionState),
|
|
6510
|
+
});
|
|
6511
|
+
}
|
|
6512
|
+
function rebuildRuntimeWithPermissions() {
|
|
6513
|
+
runtime = createRuntime();
|
|
6514
|
+
updatePermissionControls();
|
|
6515
|
+
}
|
|
6516
|
+
function updatePermissionControls() {
|
|
6517
|
+
elements.devContextStatus.textContent =
|
|
6518
|
+
devContext.source === 'remote'
|
|
6519
|
+
? `已连接线上配置${devContext.miniProgramId ? ` · ${devContext.miniProgramId}` : ''}`
|
|
6520
|
+
: '匿名本地沙箱';
|
|
6521
|
+
elements.networkPermissionSelect.value = permissionState.networkRequest.status;
|
|
6522
|
+
const diagnostics = createMiniProgramDevReleaseDiagnostics(devContext, permissionState);
|
|
6523
|
+
elements.releaseDiagnostics.innerHTML = diagnostics
|
|
6524
|
+
.map((diagnostic) => `<li>${escapeHtml(diagnostic.message)}</li>`)
|
|
6525
|
+
.join('');
|
|
6526
|
+
}
|
|
6527
|
+
function clonePermissionState(state) {
|
|
6528
|
+
return {
|
|
6529
|
+
networkRequest: { ...state.networkRequest },
|
|
6530
|
+
};
|
|
6531
|
+
}
|
|
6390
6532
|
function handleMessage(event) {
|
|
6391
6533
|
if (event.source !== iframe.contentWindow || !isBridgeMessage(event.data)) {
|
|
6392
6534
|
return;
|
|
@@ -6599,10 +6741,17 @@ async function loadMockHostBootstrap() {
|
|
|
6599
6741
|
});
|
|
6600
6742
|
const payload = await response.json();
|
|
6601
6743
|
if (!isRecord(payload)) {
|
|
6602
|
-
return {
|
|
6744
|
+
return {
|
|
6745
|
+
devContext: {
|
|
6746
|
+
source: 'anonymous',
|
|
6747
|
+
warning: 'dev context 响应格式无效。',
|
|
6748
|
+
},
|
|
6749
|
+
lanAddresses: [],
|
|
6750
|
+
};
|
|
6603
6751
|
}
|
|
6604
6752
|
return {
|
|
6605
6753
|
defaultLanAddressId: typeof payload.defaultLanAddressId === 'string' ? payload.defaultLanAddressId : undefined,
|
|
6754
|
+
devContext: readDevContext(payload.devContext, payload.runtimePermissions),
|
|
6606
6755
|
lanAddresses: Array.isArray(payload.lanAddresses) ? payload.lanAddresses.filter(isLanAddress) : [],
|
|
6607
6756
|
macAppProtocol: typeof payload.macAppProtocol === 'string' ? payload.macAppProtocol : undefined,
|
|
6608
6757
|
nativeAppLaunchUnavailableReason: typeof payload.nativeAppLaunchUnavailableReason === 'string'
|
|
@@ -6613,11 +6762,33 @@ async function loadMockHostBootstrap() {
|
|
|
6613
6762
|
}
|
|
6614
6763
|
catch {
|
|
6615
6764
|
return {
|
|
6765
|
+
devContext: {
|
|
6766
|
+
source: 'anonymous',
|
|
6767
|
+
warning: '真机调试配置加载失败,请重启 hb-sdk dev 后重试。',
|
|
6768
|
+
},
|
|
6616
6769
|
lanAddresses: [],
|
|
6617
6770
|
nativeAppLaunchUnavailableReason: '真机调试配置加载失败,请重启 hb-sdk dev 后重试。',
|
|
6618
6771
|
};
|
|
6619
6772
|
}
|
|
6620
6773
|
}
|
|
6774
|
+
function readDevContext(value, legacyRuntimePermissions) {
|
|
6775
|
+
if (isRecord(value) && value.source === 'remote') {
|
|
6776
|
+
return {
|
|
6777
|
+
source: 'remote',
|
|
6778
|
+
miniProgramId: typeof value.miniProgramId === 'string' ? value.miniProgramId : undefined,
|
|
6779
|
+
runtimePermissions: value.runtimePermissions ?? legacyRuntimePermissions,
|
|
6780
|
+
};
|
|
6781
|
+
}
|
|
6782
|
+
if (isRecord(value) && value.source === 'anonymous') {
|
|
6783
|
+
return {
|
|
6784
|
+
source: 'anonymous',
|
|
6785
|
+
warning: typeof value.warning === 'string' ? value.warning : undefined,
|
|
6786
|
+
};
|
|
6787
|
+
}
|
|
6788
|
+
return legacyRuntimePermissions === undefined
|
|
6789
|
+
? { source: 'anonymous', warning: '未获取到 dev context。' }
|
|
6790
|
+
: { source: 'remote', runtimePermissions: legacyRuntimePermissions };
|
|
6791
|
+
}
|
|
6621
6792
|
function setupMobileLanSelect() {
|
|
6622
6793
|
elements.mobileLanSelect.innerHTML = '';
|
|
6623
6794
|
lanAddresses.forEach((address) => {
|
package/dist/index.cjs.js
CHANGED
package/dist/index.esm.js
CHANGED
package/dist/protocol.cjs.js
CHANGED
|
@@ -32,6 +32,58 @@ function isMiniProgramBridgeMessage(value) {
|
|
|
32
32
|
typeof message.type === 'string');
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
|
|
36
|
+
/**
|
|
37
|
+
* 判断 permission key 是否由 Runtime 权限快照管理。
|
|
38
|
+
*
|
|
39
|
+
* @param key 待判断的 permission key。
|
|
40
|
+
* @returns 该 key 需要读取 Runtime 权限快照时返回 `true`。
|
|
41
|
+
*/
|
|
42
|
+
function isManagedMiniProgramRuntimePermissionKey(key) {
|
|
43
|
+
return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。
|
|
47
|
+
*
|
|
48
|
+
* @param snapshot 待校验的服务端权限快照。
|
|
49
|
+
* @returns 解析状态和通过校验的受管权限。
|
|
50
|
+
*/
|
|
51
|
+
function parseMiniProgramRuntimePermissions(snapshot) {
|
|
52
|
+
if (!isRecord(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
|
|
53
|
+
return { valid: false, permissions: {} };
|
|
54
|
+
}
|
|
55
|
+
if (snapshot.revision !== undefined && (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
|
|
56
|
+
return { valid: false, permissions: {} };
|
|
57
|
+
}
|
|
58
|
+
const seenKeys = new Set();
|
|
59
|
+
const permissions = {};
|
|
60
|
+
for (const rawEntry of snapshot.entries) {
|
|
61
|
+
if (!isRecord(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
|
|
62
|
+
return { valid: false, permissions: {} };
|
|
63
|
+
}
|
|
64
|
+
const key = rawEntry.key.trim();
|
|
65
|
+
if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord(rawEntry.config)) {
|
|
66
|
+
return { valid: false, permissions: {} };
|
|
67
|
+
}
|
|
68
|
+
seenKeys.add(key);
|
|
69
|
+
if (!isManagedMiniProgramRuntimePermissionKey(key)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
|
|
73
|
+
return { valid: false, permissions: {} };
|
|
74
|
+
}
|
|
75
|
+
permissions[key] = {
|
|
76
|
+
key,
|
|
77
|
+
status: rawEntry.status,
|
|
78
|
+
config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return { valid: true, permissions };
|
|
82
|
+
}
|
|
83
|
+
function isRecord(value) {
|
|
84
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
85
|
+
}
|
|
86
|
+
|
|
35
87
|
/**
|
|
36
88
|
* 登录授权能力方法名。
|
|
37
89
|
*
|
|
@@ -364,4 +416,6 @@ exports.USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD = USER_GET_PLATFORM_ACCOUNT_OV
|
|
|
364
416
|
exports.USER_GET_STEAM_GAME_LIST_METHOD = USER_GET_STEAM_GAME_LIST_METHOD;
|
|
365
417
|
exports.VIEWPORT_GET_WINDOW_INFO_METHOD = VIEWPORT_GET_WINDOW_INFO_METHOD;
|
|
366
418
|
exports.VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD = VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD;
|
|
419
|
+
exports.isManagedMiniProgramRuntimePermissionKey = isManagedMiniProgramRuntimePermissionKey;
|
|
367
420
|
exports.isMiniProgramBridgeMessage = isMiniProgramBridgeMessage;
|
|
421
|
+
exports.parseMiniProgramRuntimePermissions = parseMiniProgramRuntimePermissions;
|
package/dist/protocol.esm.js
CHANGED
|
@@ -30,6 +30,58 @@ function isMiniProgramBridgeMessage(value) {
|
|
|
30
30
|
typeof message.type === 'string');
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
const MANAGED_RUNTIME_PERMISSION_KEYS = new Set(['network.request']);
|
|
34
|
+
/**
|
|
35
|
+
* 判断 permission key 是否由 Runtime 权限快照管理。
|
|
36
|
+
*
|
|
37
|
+
* @param key 待判断的 permission key。
|
|
38
|
+
* @returns 该 key 需要读取 Runtime 权限快照时返回 `true`。
|
|
39
|
+
*/
|
|
40
|
+
function isManagedMiniProgramRuntimePermissionKey(key) {
|
|
41
|
+
return MANAGED_RUNTIME_PERMISSION_KEYS.has(key);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 解析服务端权限快照;格式不完整时整份快照失效并 fail closed。
|
|
45
|
+
*
|
|
46
|
+
* @param snapshot 待校验的服务端权限快照。
|
|
47
|
+
* @returns 解析状态和通过校验的受管权限。
|
|
48
|
+
*/
|
|
49
|
+
function parseMiniProgramRuntimePermissions(snapshot) {
|
|
50
|
+
if (!isRecord(snapshot) || snapshot.schema_version !== 1 || !Array.isArray(snapshot.entries)) {
|
|
51
|
+
return { valid: false, permissions: {} };
|
|
52
|
+
}
|
|
53
|
+
if (snapshot.revision !== undefined && (typeof snapshot.revision !== 'number' || !Number.isInteger(snapshot.revision) || snapshot.revision < 0)) {
|
|
54
|
+
return { valid: false, permissions: {} };
|
|
55
|
+
}
|
|
56
|
+
const seenKeys = new Set();
|
|
57
|
+
const permissions = {};
|
|
58
|
+
for (const rawEntry of snapshot.entries) {
|
|
59
|
+
if (!isRecord(rawEntry) || typeof rawEntry.key !== 'string' || !rawEntry.key.trim()) {
|
|
60
|
+
return { valid: false, permissions: {} };
|
|
61
|
+
}
|
|
62
|
+
const key = rawEntry.key.trim();
|
|
63
|
+
if (seenKeys.has(key) || (rawEntry.status !== 'enabled' && rawEntry.status !== 'disabled') || !isRecord(rawEntry.config)) {
|
|
64
|
+
return { valid: false, permissions: {} };
|
|
65
|
+
}
|
|
66
|
+
seenKeys.add(key);
|
|
67
|
+
if (!isManagedMiniProgramRuntimePermissionKey(key)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (key === 'network.request' && typeof rawEntry.config.useOfficialDomain !== 'boolean') {
|
|
71
|
+
return { valid: false, permissions: {} };
|
|
72
|
+
}
|
|
73
|
+
permissions[key] = {
|
|
74
|
+
key,
|
|
75
|
+
status: rawEntry.status,
|
|
76
|
+
config: { useOfficialDomain: rawEntry.config.useOfficialDomain },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { valid: true, permissions };
|
|
80
|
+
}
|
|
81
|
+
function isRecord(value) {
|
|
82
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
83
|
+
}
|
|
84
|
+
|
|
33
85
|
/**
|
|
34
86
|
* 登录授权能力方法名。
|
|
35
87
|
*
|
|
@@ -327,4 +379,4 @@ const MINI_PROGRAM_PROTOCOL_CAPABILITIES = [
|
|
|
327
379
|
},
|
|
328
380
|
];
|
|
329
381
|
|
|
330
|
-
export { AUTH_LOGIN_METHOD, CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_INFO_METHOD, CLOUD_LEADERBOARD_GET_LIST_METHOD, CLOUD_LEADERBOARD_SUBMIT_METHOD, DEVICE_SET_CLIPBOARD_METHOD, DEVICE_VIBRATE_METHOD, MINI_PROGRAM_BRIDGE_NONCE_PARAM, MINI_PROGRAM_MESSAGE_NAMESPACE, MINI_PROGRAM_MESSAGE_VERSION, MINI_PROGRAM_PROTOCOL_CAPABILITIES, NAVIGATION_CLOSE_METHOD, NAVIGATION_OPEN_APP_PAGE_METHOD, NAVIGATION_RELOAD_METHOD, NETWORK_REQUEST_METHOD, RUNTIME_LOCATION_PROBE_METHOD, SDK_CSP_VIOLATION_METHOD, SDK_HANDSHAKE_METHOD, SDK_LOCATION_REPORT_METHOD, SHARE_SCREENSHOT_METHOD, SHARE_SHOW_SHARE_MENU_METHOD, STORAGE_GET_STORAGE_METHOD, STORAGE_SET_STORAGE_METHOD, UI_HIDE_LOADING_METHOD, UI_SHOW_LOADING_METHOD, UI_SHOW_TOAST_METHOD, USER_GET_CURRENT_USER_DETAIL_METHOD, USER_GET_CURRENT_USER_PROFILE_METHOD, USER_GET_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD, USER_GET_STEAM_GAME_LIST_METHOD, VIEWPORT_GET_WINDOW_INFO_METHOD, VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD, isMiniProgramBridgeMessage };
|
|
382
|
+
export { AUTH_LOGIN_METHOD, CLOUD_LEADERBOARD_DELETE_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_CURRENT_USER_ENTRY_METHOD, CLOUD_LEADERBOARD_GET_INFO_METHOD, CLOUD_LEADERBOARD_GET_LIST_METHOD, CLOUD_LEADERBOARD_SUBMIT_METHOD, DEVICE_SET_CLIPBOARD_METHOD, DEVICE_VIBRATE_METHOD, MINI_PROGRAM_BRIDGE_NONCE_PARAM, MINI_PROGRAM_MESSAGE_NAMESPACE, MINI_PROGRAM_MESSAGE_VERSION, MINI_PROGRAM_PROTOCOL_CAPABILITIES, NAVIGATION_CLOSE_METHOD, NAVIGATION_OPEN_APP_PAGE_METHOD, NAVIGATION_RELOAD_METHOD, NETWORK_REQUEST_METHOD, RUNTIME_LOCATION_PROBE_METHOD, SDK_CSP_VIOLATION_METHOD, SDK_HANDSHAKE_METHOD, SDK_LOCATION_REPORT_METHOD, SHARE_SCREENSHOT_METHOD, SHARE_SHOW_SHARE_MENU_METHOD, STORAGE_GET_STORAGE_METHOD, STORAGE_SET_STORAGE_METHOD, UI_HIDE_LOADING_METHOD, UI_SHOW_LOADING_METHOD, UI_SHOW_TOAST_METHOD, USER_GET_CURRENT_USER_DETAIL_METHOD, USER_GET_CURRENT_USER_PROFILE_METHOD, USER_GET_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_INFO_METHOD, USER_GET_PLATFORM_ACCOUNT_OVERVIEW_METHOD, USER_GET_STEAM_GAME_LIST_METHOD, VIEWPORT_GET_WINDOW_INFO_METHOD, VIEWPORT_SET_NAVIGATION_BAR_STYLE_METHOD, isManagedMiniProgramRuntimePermissionKey, isMiniProgramBridgeMessage, parseMiniProgramRuntimePermissions };
|
package/dist/vite.cjs.js
CHANGED
package/dist/vite.esm.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heybox/hb-sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.4",
|
|
4
4
|
"sideEffects": [
|
|
5
5
|
"./src/index.ts",
|
|
6
6
|
"./src/core/singleton.ts",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"vue": "^2.7.16",
|
|
93
93
|
"vite": "^8.0.12",
|
|
94
94
|
"vitest": "^3.2.4",
|
|
95
|
-
"@heybox/hb-api": "~1.25.
|
|
95
|
+
"@heybox/hb-api": "~1.25.3"
|
|
96
96
|
},
|
|
97
97
|
"publishConfig": {
|
|
98
98
|
"registry": "https://registry.npmjs.org/",
|
package/skill/SKILL.md
CHANGED
|
@@ -49,7 +49,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
|
|
|
49
49
|
## Step 5: Use CLI workflows
|
|
50
50
|
|
|
51
51
|
1. Use `hb-sdk create <project-name>` to scaffold a standalone external mini-program template.
|
|
52
|
-
2. Use `hb-sdk dev` for local browser debugging through the built-in mock runtime host.
|
|
52
|
+
2. Use `hb-sdk dev` for local browser, Mac App, or mobile App debugging through the built-in mock runtime host. Vite and native dev-shell entrypoints only require the local `mini_url`: a missing project binding, CLI login, remote dev-context failure, or the 3-second context timeout must fall back to an anonymous local sandbox instead of blocking startup. Anonymous mode denies managed capabilities by default. When a verified remote dev context is available, the Mock Host uses its Runtime permission snapshot as the initial local simulation and reports local-versus-online permission differences.
|
|
53
53
|
3. Use `hb-sdk remote ...` for developer-owned remote mini-program management. Top-level `hb-sdk deploy` has been hard-cut and must not be recommended as a compatibility alias.
|
|
54
54
|
4. Use `hb-sdk remote entity list`, `hb-sdk remote entity current`, and `hb-sdk remote entity switch <entity-id>` to inspect or change the developer platform server-side current entity before remote management commands.
|
|
55
55
|
5. Use `hb-sdk remote create` to create a remote mini-program under the server-side current entity and bind the returned id into `package.json.heybox.miniProgramId`; use `hb-sdk remote bind <mini-program-id>` to bind an existing remote mini-program after current-entity manageability is verified. CLI project configuration contains only `package.json.heybox.miniProgramId`; mini-program name, icon, and cover images are maintained on the Open platform version-publish flow. On first submit without prior approved profile, the server injects a default name and default images.
|
|
@@ -64,7 +64,7 @@ Apply these instructions when writing, reviewing, or debugging code that consume
|
|
|
64
64
|
14. Add `--verbose` / `-v` only when diagnosing failures; default CLI errors are intentionally concise, while verbose output includes backend envelope, HTTP status, trace fields, raw body, or original submit-audit failure details.
|
|
65
65
|
15. Use `--json` for script consumption of `hb-sdk remote` commands. With `--json`, stdout must contain exactly one JSON object; progress, warnings, update reminders, and verbose diagnostics must not pollute stdout.
|
|
66
66
|
16. Do not expect custom base URLs to affect `hb-sdk doctor`, npm latest checks, or mock-host `network.request()`.
|
|
67
|
-
17. Use the Mock runtime host's "在 Mac 版 APP 中启动" button for Mac App debugging, or the "Mobile App" QR code after selecting a LAN interface for phone App debugging;
|
|
67
|
+
17. Use the Mock runtime host's "在 Mac 版 APP 中启动" button for Mac App debugging, or the "Mobile App" QR code after selecting a LAN interface for phone App debugging; these entrypoints remain available in anonymous mode. The phone must be on the same LAN and use a Heybox App version that supports the mini-program dev shell.
|
|
68
68
|
18. Use `--port`, `--mock-port`, and `--no-open` when the default Vite/mock ports or browser opening behavior need to be controlled.
|
|
69
69
|
19. Use `hb-sdk login`, `hb-sdk login status`, and `hb-sdk login clear` only for the CLI's own Heybox auth cache. Keep `hb-sdk login` top-level; it is not a remote mini-program command.
|
|
70
70
|
20. Treat `selectedEntity` in the CLI auth cache as a non-authoritative hint snapshot only. Every remote command must use the server-side current entity as the source of truth.
|
|
@@ -93,6 +93,8 @@ For CLI and local development:
|
|
|
93
93
|
2. Do not use `hb-sdk login` as a workaround for iframe SDK authentication.
|
|
94
94
|
3. Do not bypass `hb-sdk dev` by adding a second browser mock host.
|
|
95
95
|
4. Keep the Vite `miniappManifest()` plugin enabled so builds only start in a compatible APP Runtime. Normal deploy always runs the project build.
|
|
96
|
+
5. Keep Mock Host permission overrides in devtools-only memory. They must not rebuild the iframe, interrupt Vite HMR, mutate online permissions, or be passed through URL query parameters.
|
|
97
|
+
6. Treat remote dev context as optional diagnostics and initialization data. Do not use a public mini-program detail request as a dev startup gate.
|
|
96
98
|
|
|
97
99
|
For host/runtime/protocol-maintenance code:
|
|
98
100
|
|
|
@@ -31,6 +31,13 @@ export {
|
|
|
31
31
|
SDK_LOCATION_REPORT_METHOD,
|
|
32
32
|
} from './protocol/constants';
|
|
33
33
|
export { isMiniProgramBridgeMessage } from './protocol/guards';
|
|
34
|
+
export { isManagedMiniProgramRuntimePermissionKey, parseMiniProgramRuntimePermissions } from './protocol/runtime-permissions';
|
|
35
|
+
export type {
|
|
36
|
+
MiniProgramRuntimePermissionEntry,
|
|
37
|
+
MiniProgramRuntimePermissionStatus,
|
|
38
|
+
MiniProgramRuntimePermissionsSnapshot,
|
|
39
|
+
ParsedMiniProgramRuntimePermissions,
|
|
40
|
+
} from './protocol/runtime-permissions';
|
|
34
41
|
export type {
|
|
35
42
|
MiniProgramBridgeError,
|
|
36
43
|
MiniProgramBridgeMessage,
|
|
@@ -139,11 +146,7 @@ export type {
|
|
|
139
146
|
MiniProgramShareChannel,
|
|
140
147
|
MiniProgramShowShareMenuOptions,
|
|
141
148
|
} from './modules/share';
|
|
142
|
-
export type {
|
|
143
|
-
GetStoragePayload,
|
|
144
|
-
GetStorageResult,
|
|
145
|
-
SetStoragePayload,
|
|
146
|
-
} from './modules/storage';
|
|
149
|
+
export type { GetStoragePayload, GetStorageResult, SetStoragePayload } from './modules/storage';
|
|
147
150
|
export type {
|
|
148
151
|
GetWindowInfoPayload,
|
|
149
152
|
GetWindowInfoResult,
|
|
@@ -173,13 +176,7 @@ export type {
|
|
|
173
176
|
ShowToastPayload,
|
|
174
177
|
ShowToastResult,
|
|
175
178
|
} from './modules/ui';
|
|
176
|
-
export type {
|
|
177
|
-
MiniProgramVibrateIntensity,
|
|
178
|
-
SetClipboardPayload,
|
|
179
|
-
SetClipboardResult,
|
|
180
|
-
VibratePayload,
|
|
181
|
-
VibrateResult,
|
|
182
|
-
} from './modules/device';
|
|
179
|
+
export type { MiniProgramVibrateIntensity, SetClipboardPayload, SetClipboardResult, VibratePayload, VibrateResult } from './modules/device';
|
|
183
180
|
export type {
|
|
184
181
|
ClosePayload,
|
|
185
182
|
CloseResult,
|
|
@@ -210,18 +207,22 @@ Reference 由 `@heybox/hb-sdk` 的公开导出与源码注释自动生成,不
|
|
|
210
207
|
|
|
211
208
|
| 导出面 | 说明 |
|
|
212
209
|
| --- | --- |
|
|
213
|
-
| [Root API](api-root.md) |
|
|
214
|
-
| [Protocol API](#public-protocol-entrypoint) |
|
|
210
|
+
| [Root API](api-root.md) | `@heybox/hb-sdk` 的默认导出、命名导出与公开能力。 |
|
|
211
|
+
| [Protocol API](#public-protocol-entrypoint) | `@heybox/hb-sdk/protocol` 的协议常量、消息类型与 method 契约。 |
|
|
212
|
+
| [Miniapp Publish API](https://open.xiaoheihe.cn/docs/hb_sdk/reference/miniapp-publish/) | `@heybox/hb-sdk/miniapp-publish` 的构建产物发布前的公开校验工具。 |
|
|
213
|
+
| [Vite API](https://open.xiaoheihe.cn/docs/hb_sdk/reference/vite/) | `@heybox/hb-sdk/vite` 的Vite 工坊小程序插件。 |
|
|
215
214
|
|
|
216
215
|
## 查询建议
|
|
217
216
|
|
|
218
217
|
- 想查业务接入路径:先看 [Guide](recipes.md)。
|
|
219
|
-
-
|
|
218
|
+
- 想查导出符号:从上方对应公开入口进入分类页。
|
|
220
219
|
- 想看场景化用法:优先看 Guide / Recipes 页面中的“进一步阅读”。
|
|
221
220
|
|
|
222
221
|
## 统计
|
|
223
222
|
|
|
224
223
|
| 导出面 | Classes | Functions | Interfaces | Types | Constants |
|
|
225
224
|
| --- | ---: | ---: | ---: | ---: | ---: |
|
|
226
|
-
| Root API | 2 | 3 | 66 | 57 |
|
|
227
|
-
| Protocol API | 0 |
|
|
225
|
+
| Root API | 2 | 3 | 66 | 57 | 0 |
|
|
226
|
+
| Protocol API | 0 | 3 | 43 | 67 | 32 |
|
|
227
|
+
| Miniapp Publish API | 0 | 5 | 2 | 0 | 0 |
|
|
228
|
+
| Vite API | 0 | 1 | 0 | 0 | 0 |
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
## Package metadata
|
|
20
20
|
|
|
21
21
|
- Package: `@heybox/hb-sdk`
|
|
22
|
-
- Version at generation time: `0.6.
|
|
22
|
+
- Version at generation time: `0.6.4`
|
|
23
23
|
- Public root export: `@heybox/hb-sdk`
|
|
24
24
|
- Protocol export: `@heybox/hb-sdk/protocol`
|
|
25
25
|
- Vite plugin export: `@heybox/hb-sdk/vite`
|
|
@@ -450,7 +450,9 @@ SDK 需要在黑盒小程序 iframe 容器内运行。父容器会为页面注
|
|
|
450
450
|
npm run dev
|
|
451
451
|
```
|
|
452
452
|
|
|
453
|
-
调试页会通过 iframe 加载本地页面并补齐小程序 bridge
|
|
453
|
+
调试页会通过 iframe 加载本地页面并补齐小程序 bridge 环境。`hb-sdk dev` 的基础启动只依赖本地页面地址:即使项目未绑定、CLI 未登录或远端暂时不可用,浏览器 Mock、Mac 启动协议和手机二维码也会继续生成,真机 dev shell 以匿名本地沙箱加载 `mini_url`,不会把公开 `detail` 查询作为启动门禁。项目已绑定时会限时 3 秒读取远端 dev context;成功后 Mock Host 用真实 Runtime 权限快照初始化本地模拟,失败或超时则显示脱敏警告,并默认拒绝 `network.request` 等受管能力。需要定位降级原因时可使用 `hb-sdk dev --verbose`;详细错误只写入本地调试日志,其中 URL 用户名、密码和敏感 query/hash 会被遮蔽,不会进入 LAN bootstrap。
|
|
454
|
+
|
|
455
|
+
Mock Host 可以在内存中切换 devtools-only 的 `network.request` 权限,不会重建 iframe,因此不影响 Vite HMR;官方域名权限只读取线上快照,本地设置不会修改线上权限。调试页会对比已读取的线上快照,提示本地放开但上线后会返回 `PERMISSION_DENIED` 的差异。权限快照只通过 mock host 的同源只读 bootstrap 接口传递,URL query 不能提供或覆盖权限。真实容器加载开发 `mini_url` 前会提示“即将打开未经验证的开发网页。该页面可能由本机或局域网服务提供,请确认来源可信后继续。”,用户确认后才继续加载。Codex、VSCode 等内嵌浏览器可能无法唤起系统 APP;遇到这种情况时,请在系统浏览器中打开同一个调试页后重试。
|
|
454
456
|
|
|
455
457
|
在未使用脚手架的 Vite 项目中,可以把命令加到 `package.json`:
|
|
456
458
|
|