@heybox/hb-sdk 0.5.18 → 0.6.0
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 +29 -25
- package/dist/cli-chunks/{create-OZOIdeBz.cjs → create-DYb53EQ-.cjs} +1 -1
- package/dist/cli-chunks/{dev-DXz8izSJ.cjs → dev-CPKTuA6q.cjs} +35 -6
- package/dist/cli-chunks/{doctor-DjMSJoQ5.cjs → doctor-TshFvqHC.cjs} +1 -1
- package/dist/cli-chunks/{index-CsOcGUzF.cjs → index-CCeHouBU.cjs} +21 -16
- package/dist/cli-chunks/{index-Cl5HFTBW.cjs → index-NOO3q8_C.cjs} +3 -3
- package/dist/cli-chunks/{login-Ct-46gLx.cjs → login-DtXHSm8R.cjs} +2 -2
- package/dist/cli-chunks/remote-BWfn7qIN.cjs +9915 -0
- package/dist/cli-chunks/{context-CKJlNcSN.cjs → sdk-version-policy-B1CFC1Dk.cjs} +122 -1
- package/dist/cli-chunks/{session-dRGPpyS1.cjs → session-DIcRjyDY.cjs} +2 -1
- package/dist/cli.cjs +1 -1
- package/dist/devtools/mock-host/index.html +40 -0
- package/dist/devtools/mock-host/main.js +85 -0
- package/dist/index.cjs.js +232 -4
- package/dist/index.esm.js +233 -3
- package/dist/miniapp-publish.cjs.js +4 -0
- package/dist/miniapp-publish.esm.js +4 -1
- package/dist/protocol.cjs.js +9 -0
- package/dist/protocol.esm.js +7 -1
- package/dist/vite.cjs.js +9159 -22
- package/dist/vite.esm.js +9159 -23
- package/package.json +19 -7
- package/skill/SKILL.md +23 -23
- package/skill/references/api-protocol.md +11 -2
- package/skill/references/api-root.md +145 -19
- package/skill/references/cli.md +13 -15
- package/skill/references/recipes.md +12 -44
- package/skill/references/safety-boundaries.md +1 -2
- package/skill/scripts/sync-references.mjs +2 -2
- package/skill/skill.json +4 -4
- package/types/cli/auth/base-url.d.ts +20 -0
- package/types/cli/config.d.ts +11 -0
- package/types/core/client.d.ts +7 -0
- package/types/core/csp-violation.d.ts +5 -0
- package/types/core/history-observer.d.ts +4 -0
- package/types/core/version.d.ts +2 -0
- package/types/index.d.ts +0 -2
- package/types/miniapp-manifest/schema.d.ts +6 -1
- package/types/miniapp-manifest/sdk-version-policy.d.ts +9 -0
- package/types/miniapp-publish/index.d.ts +1 -0
- package/types/protocol/constants.d.ts +6 -0
- package/types/protocol/types.d.ts +45 -0
- package/types/protocol.d.ts +2 -2
- package/types/vite/html-policy.d.ts +4 -0
- package/types/vite/index.d.ts +23 -3
- package/types/vite/runtime-gate.d.ts +5 -0
- package/types/vite/sdk-version-gate.d.ts +8 -0
- package/dist/cli-chunks/remote-C77axNSw.cjs +0 -1604
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var session = require('./session-
|
|
3
|
+
var session = require('./session-DIcRjyDY.cjs');
|
|
4
4
|
var fs$1 = require('node:fs');
|
|
5
5
|
var fs = require('node:fs/promises');
|
|
6
6
|
var path = require('node:path');
|
|
7
|
+
var index = require('./index-CCeHouBU.cjs');
|
|
8
|
+
|
|
9
|
+
const APIUserMiniprogramPublicSdkConfigPath = '/user_miniprogram/public/sdk_config';
|
|
10
|
+
function createUserMiniprogramPublicSdkConfigRequest(serviceTag) {
|
|
11
|
+
return {
|
|
12
|
+
path: APIUserMiniprogramPublicSdkConfigPath,
|
|
13
|
+
searchParams: serviceTag ? { special_tag: serviceTag } : {},
|
|
14
|
+
headers: serviceTag ? { 'x-rylai-service-tag': serviceTag } : {},
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function parseUserMiniprogramPublicSdkConfigResult(value) {
|
|
18
|
+
if (!isRecord(value) ||
|
|
19
|
+
typeof value.minimum_sdk_version !== 'string') {
|
|
20
|
+
throw new Error('公共 SDK 配置响应无效');
|
|
21
|
+
}
|
|
22
|
+
return value;
|
|
23
|
+
}
|
|
24
|
+
function isRecord(value) {
|
|
25
|
+
return typeof value === 'object' && value !== null;
|
|
26
|
+
}
|
|
7
27
|
|
|
8
28
|
var re = {exports: {}};
|
|
9
29
|
|
|
@@ -2794,6 +2814,7 @@ const MINIAPP_UPLOAD_SCOPE = 'activity';
|
|
|
2794
2814
|
const ACTIVITY_UPLOAD_KEY_MAX_LENGTH = 64;
|
|
2795
2815
|
const MINIAPP_UPLOAD_BATCH_SIZE = 50;
|
|
2796
2816
|
const MINIAPP_UPLOAD_TOTAL_SIZE_LIMIT_BYTES = 100 * 1024 * 1024;
|
|
2817
|
+
const PUBLIC_USER_MINIPROGRAM_SDK_CONFIG_API_PATH = APIUserMiniprogramPublicSdkConfigPath;
|
|
2797
2818
|
const USER_MINIPROGRAM_ACCESS_STATUS_API_PATH = '/mall/developer/user_miniprogram/access_status';
|
|
2798
2819
|
const DEVELOPER_ENTITY_LIST_API_PATH = '/mall/developer/entity/list';
|
|
2799
2820
|
const DEVELOPER_ENTITY_SWITCH_API_PATH = '/mall/developer/entity/switch';
|
|
@@ -2909,6 +2930,20 @@ function createRemoteApiClient(options) {
|
|
|
2909
2930
|
listUserMiniprogramVersions: (requestOptions) => listUserMiniprogramVersions(requestOptions, options),
|
|
2910
2931
|
};
|
|
2911
2932
|
}
|
|
2933
|
+
async function getPublicSdkConfig(runtime) {
|
|
2934
|
+
const fetchImpl = runtime.fetchImpl ?? fetch;
|
|
2935
|
+
const serviceTag = index.resolveHeyboxRylaiServiceTag(PUBLIC_USER_MINIPROGRAM_SDK_CONFIG_API_PATH);
|
|
2936
|
+
const request = createUserMiniprogramPublicSdkConfigRequest(serviceTag);
|
|
2937
|
+
const response = await fetchImpl(session.createHeyboxApiUrl(runtime.baseUrl ?? 'https://api.xiaoheihe.cn', request.path, request.searchParams), {
|
|
2938
|
+
method: 'GET',
|
|
2939
|
+
headers: {
|
|
2940
|
+
Referer: session.HEYBOX_WEB_REFERER,
|
|
2941
|
+
...request.headers,
|
|
2942
|
+
},
|
|
2943
|
+
});
|
|
2944
|
+
const result = await readRequiredResult(response, request.path);
|
|
2945
|
+
return parseUserMiniprogramPublicSdkConfigResult(result);
|
|
2946
|
+
}
|
|
2912
2947
|
async function listDeveloperEntities(runtime) {
|
|
2913
2948
|
return remoteGet(DEVELOPER_ENTITY_LIST_API_PATH, {}, runtime);
|
|
2914
2949
|
}
|
|
@@ -3179,6 +3214,91 @@ function isTruthyEnvFlag(value) {
|
|
|
3179
3214
|
.toLowerCase());
|
|
3180
3215
|
}
|
|
3181
3216
|
|
|
3217
|
+
const CACHE_MAX_AGE_MS = 24 * 60 * 60 * 1_000;
|
|
3218
|
+
const CACHE_FILE_NAME = 'minimum-sdk-versions.json';
|
|
3219
|
+
async function resolveMinimumSdkVersion(options) {
|
|
3220
|
+
const now = options.now ?? Date.now();
|
|
3221
|
+
const cachePath = path.join(options.cacheDir, CACHE_FILE_NAME);
|
|
3222
|
+
const cache = await readCache(cachePath);
|
|
3223
|
+
let minimumSdkVersion;
|
|
3224
|
+
try {
|
|
3225
|
+
minimumSdkVersion = validateStableSdkVersion(await options.fetchMinimumSdkVersion(), 'minimumSdkVersion');
|
|
3226
|
+
}
|
|
3227
|
+
catch {
|
|
3228
|
+
const cached = cache.environments[options.environment];
|
|
3229
|
+
const cacheAge = cached ? now - cached.fetchedAt : Number.NaN;
|
|
3230
|
+
if (cached && Number.isFinite(cached.fetchedAt) && cacheAge >= 0 && cacheAge <= CACHE_MAX_AGE_MS) {
|
|
3231
|
+
try {
|
|
3232
|
+
return validateStableSdkVersion(cached.minimumSdkVersion, 'cached minimumSdkVersion');
|
|
3233
|
+
}
|
|
3234
|
+
catch {
|
|
3235
|
+
// 非法缓存按不存在处理。
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
throw new Error('MINIMUM_SDK_VERSION_UNAVAILABLE:无法获取最低 SDK 版本,且没有 24 小时内的有效缓存');
|
|
3239
|
+
}
|
|
3240
|
+
cache.environments[options.environment] = { fetchedAt: now, minimumSdkVersion };
|
|
3241
|
+
try {
|
|
3242
|
+
await writeCache(cachePath, cache);
|
|
3243
|
+
}
|
|
3244
|
+
catch {
|
|
3245
|
+
// 已校验的远端策略仍然可信;缓存只是离线回退优化。
|
|
3246
|
+
}
|
|
3247
|
+
return minimumSdkVersion;
|
|
3248
|
+
}
|
|
3249
|
+
function validateCurrentSdkVersion(currentSdkVersion, minimumSdkVersion, options) {
|
|
3250
|
+
const current = validateSdkVersion(currentSdkVersion, '当前 SDK 版本');
|
|
3251
|
+
const minimum = validateStableSdkVersion(minimumSdkVersion, 'minimumSdkVersion');
|
|
3252
|
+
if (!options.allowPrerelease && semverExports.prerelease(current) !== null) {
|
|
3253
|
+
throw new Error('正式发布不允许使用预发布 SDK,请运行 pnpm add @heybox/hb-sdk@latest 后重新构建');
|
|
3254
|
+
}
|
|
3255
|
+
if (!semverExports.satisfies(current, `>=${minimum}`, { includePrerelease: options.allowPrerelease })) {
|
|
3256
|
+
throw new Error(`SDK_VERSION_TOO_LOW:当前 @heybox/hb-sdk@${current} 低于最低版本 ${minimum},请运行 pnpm add @heybox/hb-sdk@latest`);
|
|
3257
|
+
}
|
|
3258
|
+
return current;
|
|
3259
|
+
}
|
|
3260
|
+
function validateSdkVersion(value, label) {
|
|
3261
|
+
const normalized = String(value).trim();
|
|
3262
|
+
const parsed = semverExports.parse(normalized);
|
|
3263
|
+
if (!parsed || parsed.version !== normalized || parsed.build.length > 0) {
|
|
3264
|
+
throw new Error(`${label} 必须是合法 SemVer:${normalized}`);
|
|
3265
|
+
}
|
|
3266
|
+
return normalized;
|
|
3267
|
+
}
|
|
3268
|
+
function validateStableSdkVersion(value, label) {
|
|
3269
|
+
const normalized = validateSdkVersion(value, label);
|
|
3270
|
+
if (semverExports.prerelease(normalized) !== null)
|
|
3271
|
+
throw new Error(`${label} 必须是稳定 SemVer:${normalized}`);
|
|
3272
|
+
return normalized;
|
|
3273
|
+
}
|
|
3274
|
+
async function readCache(cachePath) {
|
|
3275
|
+
try {
|
|
3276
|
+
const parsed = JSON.parse(await fs.readFile(cachePath, 'utf8'));
|
|
3277
|
+
if (parsed && typeof parsed === 'object' && parsed.environments && typeof parsed.environments === 'object')
|
|
3278
|
+
return parsed;
|
|
3279
|
+
}
|
|
3280
|
+
catch {
|
|
3281
|
+
// 缓存缺失或损坏时继续请求最新配置。
|
|
3282
|
+
}
|
|
3283
|
+
return { environments: {} };
|
|
3284
|
+
}
|
|
3285
|
+
async function writeCache(cachePath, cache) {
|
|
3286
|
+
await fs.mkdir(path.dirname(cachePath), { recursive: true });
|
|
3287
|
+
const temporaryPath = `${cachePath}.${process.pid}.tmp`;
|
|
3288
|
+
await fs.writeFile(temporaryPath, `${JSON.stringify(cache, null, 2)}\n`, { mode: 0o600 });
|
|
3289
|
+
await fs.rename(temporaryPath, cachePath);
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
const paths = index.envPaths('hb-sdk');
|
|
3293
|
+
async function enforceRemoteSdkVersion(options) {
|
|
3294
|
+
const minimumSdkVersion = await resolveMinimumSdkVersion({
|
|
3295
|
+
cacheDir: options.cacheDir ?? paths.cache,
|
|
3296
|
+
environment: options.environment,
|
|
3297
|
+
fetchMinimumSdkVersion: async () => (await getPublicSdkConfig({ baseUrl: options.environment, fetchImpl: options.fetchImpl })).minimum_sdk_version,
|
|
3298
|
+
});
|
|
3299
|
+
return validateCurrentSdkVersion(options.sdkVersion, minimumSdkVersion, { allowPrerelease: options.allowPrerelease });
|
|
3300
|
+
}
|
|
3301
|
+
|
|
3182
3302
|
exports.ACTIVITY_UPLOAD_KEY_MAX_LENGTH = ACTIVITY_UPLOAD_KEY_MAX_LENGTH;
|
|
3183
3303
|
exports.MINIAPP_UPLOAD_BATCH_SIZE = MINIAPP_UPLOAD_BATCH_SIZE;
|
|
3184
3304
|
exports.MINIAPP_UPLOAD_SCOPE = MINIAPP_UPLOAD_SCOPE;
|
|
@@ -3186,6 +3306,7 @@ exports.MiniProgramProjectBindingError = MiniProgramProjectBindingError;
|
|
|
3186
3306
|
exports.PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH = PRECHECK_USER_MINIPROGRAM_VERSION_API_PATH;
|
|
3187
3307
|
exports.SUBMIT_USER_MINIPROGRAM_AUDIT_API_PATH = SUBMIT_USER_MINIPROGRAM_AUDIT_API_PATH;
|
|
3188
3308
|
exports.createRemoteApiClientForEnvironment = createRemoteApiClientForEnvironment;
|
|
3309
|
+
exports.enforceRemoteSdkVersion = enforceRemoteSdkVersion;
|
|
3189
3310
|
exports.getBoundMiniProgram = getBoundMiniProgram;
|
|
3190
3311
|
exports.getMiniappUploadKey = getMiniappUploadKey;
|
|
3191
3312
|
exports.normalizeRelativePath = normalizeRelativePath;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var index = require('./index-
|
|
3
|
+
var index = require('./index-CCeHouBU.cjs');
|
|
4
4
|
var node_crypto = require('node:crypto');
|
|
5
5
|
var path = require('node:path');
|
|
6
6
|
var require$$0$2 = require('fs');
|
|
@@ -3321,6 +3321,7 @@ function isRecord(value) {
|
|
|
3321
3321
|
return Object.prototype.toString.call(value) === '[object Object]';
|
|
3322
3322
|
}
|
|
3323
3323
|
|
|
3324
|
+
exports.HEYBOX_WEB_REFERER = HEYBOX_WEB_REFERER;
|
|
3324
3325
|
exports.clearHeyboxAuthSession = clearHeyboxAuthSession;
|
|
3325
3326
|
exports.createHeyboxApiUrl = createHeyboxApiUrl;
|
|
3326
3327
|
exports.createHeyboxAuthSession = createHeyboxAuthSession;
|
package/dist/cli.cjs
CHANGED
|
@@ -212,6 +212,42 @@
|
|
|
212
212
|
overflow: auto;
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
.csp-violations {
|
|
216
|
+
border: 1px solid #dc2626;
|
|
217
|
+
border-radius: 8px;
|
|
218
|
+
padding: 12px;
|
|
219
|
+
color: #7f1d1d;
|
|
220
|
+
background: #fef2f2;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.csp-violations[hidden] {
|
|
224
|
+
display: none;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.csp-violations > strong {
|
|
228
|
+
display: block;
|
|
229
|
+
color: #991b1b;
|
|
230
|
+
font-size: 14px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.csp-violations ol {
|
|
234
|
+
display: grid;
|
|
235
|
+
gap: 8px;
|
|
236
|
+
margin: 8px 0 0;
|
|
237
|
+
padding-left: 20px;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.csp-violations li strong,
|
|
241
|
+
.csp-violations li span {
|
|
242
|
+
display: block;
|
|
243
|
+
overflow-wrap: anywhere;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
.csp-violations li span {
|
|
247
|
+
margin-top: 2px;
|
|
248
|
+
font-size: 12px;
|
|
249
|
+
}
|
|
250
|
+
|
|
215
251
|
.log {
|
|
216
252
|
border: 1px solid #d7dee8;
|
|
217
253
|
border-radius: 8px;
|
|
@@ -317,6 +353,10 @@
|
|
|
317
353
|
<strong id="mini-url"></strong>
|
|
318
354
|
</section>
|
|
319
355
|
|
|
356
|
+
<section class="csp-violations" id="csp-violations" hidden>
|
|
357
|
+
<strong>网络或资源请求已被 CSP 阻止</strong>
|
|
358
|
+
</section>
|
|
359
|
+
|
|
320
360
|
<section class="block">
|
|
321
361
|
<span>Mac App</span>
|
|
322
362
|
<div class="actions" style="margin-top: 10px">
|
|
@@ -4,6 +4,8 @@ const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
|
|
|
4
4
|
const MINI_PROGRAM_MESSAGE_VERSION = 1;
|
|
5
5
|
/** 父容器注入到小程序 URL 上的通信 nonce 参数名。 */
|
|
6
6
|
const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
7
|
+
/** SDK 向 Runtime 报告 CSP 违规的内部方法名。 */
|
|
8
|
+
const SDK_CSP_VIOLATION_METHOD = 'sdk.csp.violation';
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* 登录授权能力方法名。
|
|
@@ -3373,6 +3375,76 @@ function requireBrowser () {
|
|
|
3373
3375
|
|
|
3374
3376
|
var browserExports = requireBrowser();
|
|
3375
3377
|
|
|
3378
|
+
function createCSPViolationPanel(container) {
|
|
3379
|
+
const seen = new Set();
|
|
3380
|
+
const list = document.createElement('ol');
|
|
3381
|
+
container.setAttribute('role', 'alert');
|
|
3382
|
+
container.setAttribute('aria-live', 'assertive');
|
|
3383
|
+
container.appendChild(list);
|
|
3384
|
+
return {
|
|
3385
|
+
report(payload) {
|
|
3386
|
+
if (!isCSPViolationPayload(payload)) {
|
|
3387
|
+
return false;
|
|
3388
|
+
}
|
|
3389
|
+
const key = createViolationKey(payload);
|
|
3390
|
+
if (seen.has(key)) {
|
|
3391
|
+
return false;
|
|
3392
|
+
}
|
|
3393
|
+
seen.add(key);
|
|
3394
|
+
const item = document.createElement('li');
|
|
3395
|
+
const directive = document.createElement('strong');
|
|
3396
|
+
directive.textContent = payload.effectiveDirective;
|
|
3397
|
+
const detail = document.createElement('span');
|
|
3398
|
+
detail.textContent = formatBlockedResource(payload);
|
|
3399
|
+
item.append(directive, detail);
|
|
3400
|
+
list.appendChild(item);
|
|
3401
|
+
container.hidden = false;
|
|
3402
|
+
return true;
|
|
3403
|
+
},
|
|
3404
|
+
};
|
|
3405
|
+
}
|
|
3406
|
+
function isCSPViolationPayload(payload) {
|
|
3407
|
+
if (!payload || typeof payload !== 'object') {
|
|
3408
|
+
return false;
|
|
3409
|
+
}
|
|
3410
|
+
const candidate = payload;
|
|
3411
|
+
return (typeof candidate.effectiveDirective === 'string' &&
|
|
3412
|
+
typeof candidate.violatedDirective === 'string' &&
|
|
3413
|
+
typeof candidate.disposition === 'string' &&
|
|
3414
|
+
['url', 'inline', 'eval', 'unknown'].includes(candidate.blockedResourceType ?? '') &&
|
|
3415
|
+
typeof candidate.sdkVersion === 'string' &&
|
|
3416
|
+
typeof candidate.timestamp === 'number' &&
|
|
3417
|
+
isOptionalString(candidate.blockedOrigin) &&
|
|
3418
|
+
isOptionalString(candidate.redactedPathname) &&
|
|
3419
|
+
isOptionalString(candidate.sanitizedPathnameHash));
|
|
3420
|
+
}
|
|
3421
|
+
function isOptionalString(value) {
|
|
3422
|
+
return value === undefined || typeof value === 'string';
|
|
3423
|
+
}
|
|
3424
|
+
function createViolationKey(payload) {
|
|
3425
|
+
return [
|
|
3426
|
+
payload.effectiveDirective,
|
|
3427
|
+
payload.violatedDirective,
|
|
3428
|
+
payload.disposition,
|
|
3429
|
+
payload.blockedResourceType,
|
|
3430
|
+
payload.blockedOrigin,
|
|
3431
|
+
payload.redactedPathname,
|
|
3432
|
+
payload.sanitizedPathnameHash,
|
|
3433
|
+
].join('\n');
|
|
3434
|
+
}
|
|
3435
|
+
function formatBlockedResource(payload) {
|
|
3436
|
+
if (payload.blockedResourceType === 'url') {
|
|
3437
|
+
return `${payload.blockedOrigin ?? '未知来源'}${payload.redactedPathname ?? ''}`;
|
|
3438
|
+
}
|
|
3439
|
+
if (payload.blockedResourceType === 'inline') {
|
|
3440
|
+
return '内联资源';
|
|
3441
|
+
}
|
|
3442
|
+
if (payload.blockedResourceType === 'eval') {
|
|
3443
|
+
return '动态代码执行';
|
|
3444
|
+
}
|
|
3445
|
+
return '未知资源';
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3376
3448
|
async function getMiniProgramRuntimeUserInfo(platformAdapter) {
|
|
3377
3449
|
const userId = await resolveCurrentUserId(platformAdapter);
|
|
3378
3450
|
if (!userId) {
|
|
@@ -6211,6 +6283,7 @@ function isPlainRecord(value) {
|
|
|
6211
6283
|
|
|
6212
6284
|
const NETWORK_PROXY_PATH = '/__hb_sdk_mock_network__';
|
|
6213
6285
|
const MOCK_HOST_BOOTSTRAP_PATH = '/__hb_sdk_bootstrap__';
|
|
6286
|
+
const RUNTIME_GATE_PROBE_METHOD = 'runtime.gate.probe';
|
|
6214
6287
|
const params = new URL(location.href).searchParams;
|
|
6215
6288
|
const miniUrl = params.get('mini_url');
|
|
6216
6289
|
let lanAddresses = [];
|
|
@@ -6230,6 +6303,7 @@ let currentUser = {
|
|
|
6230
6303
|
const elements = {
|
|
6231
6304
|
bridgeStatus: queryElement('#bridge-status'),
|
|
6232
6305
|
copyDebugPageUrlButton: queryElement('#copy-debug-page-url-button'),
|
|
6306
|
+
cspViolations: queryElement('#csp-violations'),
|
|
6233
6307
|
debugPageCopyStatus: queryElement('#debug-page-copy-status'),
|
|
6234
6308
|
debugPageUrl: queryElement('#debug-page-url'),
|
|
6235
6309
|
device: queryElement('#device'),
|
|
@@ -6244,6 +6318,7 @@ const elements = {
|
|
|
6244
6318
|
storageSnapshot: queryElement('#storage-snapshot'),
|
|
6245
6319
|
userStatus: queryElement('#user-status'),
|
|
6246
6320
|
};
|
|
6321
|
+
const cspViolationPanel = createCSPViolationPanel(elements.cspViolations);
|
|
6247
6322
|
if (!miniUrl) {
|
|
6248
6323
|
elements.device.innerHTML = '<div class="error">Missing mini_url query.</div>';
|
|
6249
6324
|
throw new Error('Missing mini_url query.');
|
|
@@ -6323,6 +6398,12 @@ function handleMessage(event) {
|
|
|
6323
6398
|
}
|
|
6324
6399
|
if (message.type === 'request') {
|
|
6325
6400
|
void handleRequest(message);
|
|
6401
|
+
return;
|
|
6402
|
+
}
|
|
6403
|
+
if (message.type === 'event' && message.method === SDK_CSP_VIOLATION_METHOD) {
|
|
6404
|
+
if (cspViolationPanel.report(message.payload)) {
|
|
6405
|
+
pushLog(message.method, message.payload);
|
|
6406
|
+
}
|
|
6326
6407
|
}
|
|
6327
6408
|
}
|
|
6328
6409
|
function handleHandshake() {
|
|
@@ -6344,6 +6425,10 @@ async function handleRequest(message) {
|
|
|
6344
6425
|
if (!message.id || !message.method) {
|
|
6345
6426
|
return;
|
|
6346
6427
|
}
|
|
6428
|
+
if (!handshaken && message.method === RUNTIME_GATE_PROBE_METHOD) {
|
|
6429
|
+
postResponse(message, { ready: true });
|
|
6430
|
+
return;
|
|
6431
|
+
}
|
|
6347
6432
|
try {
|
|
6348
6433
|
const payload = await runtime.runMethod(message.method, message.payload);
|
|
6349
6434
|
postResponse(message, payload);
|
package/dist/index.cjs.js
CHANGED
|
@@ -74,6 +74,54 @@ class HbMiniProgramNetworkError extends Error {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/** 将浏览器 CSP 事件转换为不包含原始 URL 敏感部分的 bridge 载荷。 */
|
|
78
|
+
async function createCSPViolationPayload(event, baseHref, sdkVersion, cryptoImplementation) {
|
|
79
|
+
const { pathnameForHash, ...blockedResource } = normalizeBlockedResource(event.blockedURI, baseHref);
|
|
80
|
+
return {
|
|
81
|
+
effectiveDirective: event.effectiveDirective,
|
|
82
|
+
violatedDirective: event.violatedDirective,
|
|
83
|
+
disposition: event.disposition,
|
|
84
|
+
...blockedResource,
|
|
85
|
+
sanitizedPathnameHash: pathnameForHash
|
|
86
|
+
? await hashPathname(pathnameForHash, cryptoImplementation)
|
|
87
|
+
: undefined,
|
|
88
|
+
sdkVersion,
|
|
89
|
+
timestamp: Date.now(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function normalizeBlockedResource(blockedURI, baseHref) {
|
|
93
|
+
if (blockedURI === 'inline' || blockedURI === 'eval' || blockedURI === 'wasm-eval') {
|
|
94
|
+
return {
|
|
95
|
+
blockedResourceType: blockedURI === 'inline' ? 'inline' : 'eval',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!blockedURI) {
|
|
99
|
+
return { blockedResourceType: 'unknown' };
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const url = new URL(blockedURI, baseHref);
|
|
103
|
+
const segmentCount = url.pathname.split('/').filter(Boolean).length;
|
|
104
|
+
return {
|
|
105
|
+
blockedResourceType: 'url',
|
|
106
|
+
blockedOrigin: url.origin === 'null' ? undefined : url.origin,
|
|
107
|
+
redactedPathname: segmentCount === 0 ? '/' : `/${Array(segmentCount).fill(':segment').join('/')}`,
|
|
108
|
+
pathnameForHash: url.pathname,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return { blockedResourceType: 'unknown' };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
async function hashPathname(pathname, cryptoImplementation) {
|
|
116
|
+
try {
|
|
117
|
+
const digest = await cryptoImplementation.subtle.digest('SHA-256', new TextEncoder().encode(pathname));
|
|
118
|
+
return Array.from(new Uint8Array(digest), value => value.toString(16).padStart(2, '0')).join('');
|
|
119
|
+
}
|
|
120
|
+
catch {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
77
125
|
/** SDK 内部事件总线,负责管理沙盒生命周期与业务事件监听。 */
|
|
78
126
|
class MiniProgramEventBus {
|
|
79
127
|
eventHandlers = new Map();
|
|
@@ -107,6 +155,100 @@ class MiniProgramEventBus {
|
|
|
107
155
|
}
|
|
108
156
|
}
|
|
109
157
|
|
|
158
|
+
const historyObservers = new WeakMap();
|
|
159
|
+
/** 共享同一个 History patch,并在最后一个订阅者退出时精确恢复原属性。 */
|
|
160
|
+
function subscribeHistoryChanges(history, listener) {
|
|
161
|
+
let state = historyObservers.get(history);
|
|
162
|
+
if (!state) {
|
|
163
|
+
state = createHistoryObserver(history);
|
|
164
|
+
if (!state) {
|
|
165
|
+
return () => undefined;
|
|
166
|
+
}
|
|
167
|
+
historyObservers.set(history, state);
|
|
168
|
+
}
|
|
169
|
+
state.listeners.add(listener);
|
|
170
|
+
let active = true;
|
|
171
|
+
return () => {
|
|
172
|
+
if (!active) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
active = false;
|
|
176
|
+
state.listeners.delete(listener);
|
|
177
|
+
if (state.listeners.size === 0) {
|
|
178
|
+
restoreHistoryObserver(history, state);
|
|
179
|
+
historyObservers.delete(history);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function createHistoryObserver(history) {
|
|
184
|
+
const listeners = new Set();
|
|
185
|
+
const originalPushState = readOriginalMethod(history, 'pushState');
|
|
186
|
+
const originalReplaceState = readOriginalMethod(history, 'replaceState');
|
|
187
|
+
const notify = () => {
|
|
188
|
+
[...listeners].forEach(currentListener => {
|
|
189
|
+
try {
|
|
190
|
+
currentListener();
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// 单个 SDK 诊断失败不能影响 History API 或其他订阅者。
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
const patchedPushState = (...args) => {
|
|
198
|
+
originalPushState.value.apply(history, args);
|
|
199
|
+
notify();
|
|
200
|
+
};
|
|
201
|
+
const patchedReplaceState = (...args) => {
|
|
202
|
+
originalReplaceState.value.apply(history, args);
|
|
203
|
+
notify();
|
|
204
|
+
};
|
|
205
|
+
const state = {
|
|
206
|
+
listeners,
|
|
207
|
+
originalPushState,
|
|
208
|
+
originalReplaceState,
|
|
209
|
+
patchedPushState,
|
|
210
|
+
patchedReplaceState,
|
|
211
|
+
};
|
|
212
|
+
try {
|
|
213
|
+
definePatchedMethod(history, 'pushState', patchedPushState, originalPushState.descriptor);
|
|
214
|
+
definePatchedMethod(history, 'replaceState', patchedReplaceState, originalReplaceState.descriptor);
|
|
215
|
+
return state;
|
|
216
|
+
}
|
|
217
|
+
catch {
|
|
218
|
+
restoreHistoryObserver(history, state);
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function readOriginalMethod(history, name) {
|
|
223
|
+
return {
|
|
224
|
+
descriptor: Object.getOwnPropertyDescriptor(history, name),
|
|
225
|
+
value: history[name],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function definePatchedMethod(history, name, value, originalDescriptor) {
|
|
229
|
+
Object.defineProperty(history, name, {
|
|
230
|
+
configurable: true,
|
|
231
|
+
enumerable: originalDescriptor?.enumerable ?? false,
|
|
232
|
+
writable: true,
|
|
233
|
+
value,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function restoreHistoryObserver(history, state) {
|
|
237
|
+
restoreMethod(history, 'pushState', state.patchedPushState, state.originalPushState);
|
|
238
|
+
restoreMethod(history, 'replaceState', state.patchedReplaceState, state.originalReplaceState);
|
|
239
|
+
}
|
|
240
|
+
function restoreMethod(history, name, patchedValue, original) {
|
|
241
|
+
if (history[name] !== patchedValue) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (original.descriptor) {
|
|
245
|
+
Object.defineProperty(history, name, original.descriptor);
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
delete history[name];
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
110
252
|
/** 小程序沙盒通信命名空间,父容器与 iframe 内 SDK 必须保持一致。 */
|
|
111
253
|
const MINI_PROGRAM_MESSAGE_NAMESPACE = 'heybox:miniprogram';
|
|
112
254
|
/** 小程序沙盒通信协议版本。 */
|
|
@@ -115,6 +257,12 @@ const MINI_PROGRAM_MESSAGE_VERSION = 1;
|
|
|
115
257
|
const MINI_PROGRAM_BRIDGE_NONCE_PARAM = 'hb_mini_bridge_nonce';
|
|
116
258
|
/** SDK 初始化时发给父容器的握手方法名。 */
|
|
117
259
|
const SDK_HANDSHAKE_METHOD = 'sdk.handshake';
|
|
260
|
+
/** Runtime 主动探测 SDK 当前文档地址的内部方法名。 */
|
|
261
|
+
const RUNTIME_LOCATION_PROBE_METHOD = 'runtime.location.probe';
|
|
262
|
+
/** SDK 向 Runtime 报告当前文档地址的内部方法名。 */
|
|
263
|
+
const SDK_LOCATION_REPORT_METHOD = 'sdk.location.report';
|
|
264
|
+
/** SDK 向 Runtime 报告 CSP 违规的内部方法名。 */
|
|
265
|
+
const SDK_CSP_VIOLATION_METHOD = 'sdk.csp.violation';
|
|
118
266
|
|
|
119
267
|
/** 获取浏览器全局 window。 */
|
|
120
268
|
function getGlobalWindow() {
|
|
@@ -174,6 +322,11 @@ function createMessageId() {
|
|
|
174
322
|
return `${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`;
|
|
175
323
|
}
|
|
176
324
|
|
|
325
|
+
/** 构建时替换为当前发布包的实际版本。 */
|
|
326
|
+
const HB_SDK_VERSION = typeof undefined === 'string'
|
|
327
|
+
? undefined
|
|
328
|
+
: '0.6.0';
|
|
329
|
+
|
|
177
330
|
/**
|
|
178
331
|
* 判断未知数据是否符合小程序 bridge 消息信封。
|
|
179
332
|
*
|
|
@@ -203,6 +356,13 @@ class MiniProgramBridgeClient {
|
|
|
203
356
|
pendingRequests = new Map();
|
|
204
357
|
eventBus = new MiniProgramEventBus();
|
|
205
358
|
handleMessage;
|
|
359
|
+
handlePopState = () => this.postLocationReport('popstate');
|
|
360
|
+
handleHashChange = () => this.postLocationReport('hashchange');
|
|
361
|
+
handlePageShow = () => this.postLocationReport('pageshow');
|
|
362
|
+
handleSecurityPolicyViolation = (event) => {
|
|
363
|
+
this.postCSPViolation(event);
|
|
364
|
+
};
|
|
365
|
+
unsubscribeHistoryChanges;
|
|
206
366
|
started = false;
|
|
207
367
|
readySettled = false;
|
|
208
368
|
readyPromise;
|
|
@@ -290,6 +450,12 @@ class MiniProgramBridgeClient {
|
|
|
290
450
|
destroy() {
|
|
291
451
|
this.destroyed = true;
|
|
292
452
|
this.selfWindow?.removeEventListener('message', this.handleMessage);
|
|
453
|
+
this.selfWindow?.removeEventListener('popstate', this.handlePopState);
|
|
454
|
+
this.selfWindow?.removeEventListener('hashchange', this.handleHashChange);
|
|
455
|
+
this.selfWindow?.removeEventListener('pageshow', this.handlePageShow);
|
|
456
|
+
this.selfWindow?.removeEventListener('securitypolicyviolation', this.handleSecurityPolicyViolation);
|
|
457
|
+
this.unsubscribeHistoryChanges?.();
|
|
458
|
+
this.unsubscribeHistoryChanges = undefined;
|
|
293
459
|
const error = createSDKError('SDK_DESTROYED', '小程序 SDK 已销毁');
|
|
294
460
|
this.failReady(error);
|
|
295
461
|
this.rejectAllPending(error);
|
|
@@ -316,6 +482,13 @@ class MiniProgramBridgeClient {
|
|
|
316
482
|
return;
|
|
317
483
|
}
|
|
318
484
|
this.selfWindow.addEventListener('message', this.handleMessage);
|
|
485
|
+
this.selfWindow.addEventListener('popstate', this.handlePopState);
|
|
486
|
+
this.selfWindow.addEventListener('hashchange', this.handleHashChange);
|
|
487
|
+
this.selfWindow.addEventListener('pageshow', this.handlePageShow);
|
|
488
|
+
this.selfWindow.addEventListener('securitypolicyviolation', this.handleSecurityPolicyViolation);
|
|
489
|
+
this.unsubscribeHistoryChanges = subscribeHistoryChanges(this.selfWindow.history, () => {
|
|
490
|
+
this.postLocationReport('history');
|
|
491
|
+
});
|
|
319
492
|
this.readyTimer = setTimeout(() => {
|
|
320
493
|
this.failReady(createSDKError('READY_TIMEOUT', '小程序沙盒握手超时'));
|
|
321
494
|
}, this.timeout);
|
|
@@ -352,11 +525,12 @@ class MiniProgramBridgeClient {
|
|
|
352
525
|
payload: {
|
|
353
526
|
href: this.selfWindow.location.href,
|
|
354
527
|
userAgent: this.selfWindow.navigator.userAgent,
|
|
528
|
+
sdkVersion: HB_SDK_VERSION,
|
|
355
529
|
},
|
|
356
530
|
});
|
|
357
531
|
}
|
|
358
532
|
onMessage(event) {
|
|
359
|
-
if (event.source
|
|
533
|
+
if (event.source !== this.targetWindow) {
|
|
360
534
|
return;
|
|
361
535
|
}
|
|
362
536
|
if (!isMiniProgramBridgeMessage(event.data) || event.data.nonce !== this.nonce) {
|
|
@@ -368,6 +542,59 @@ class MiniProgramBridgeClient {
|
|
|
368
542
|
}
|
|
369
543
|
if (event.data.type === 'response') {
|
|
370
544
|
this.handleResponse(event.data);
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (event.data.type === 'request' && event.data.method === RUNTIME_LOCATION_PROBE_METHOD) {
|
|
548
|
+
const payload = event.data.payload;
|
|
549
|
+
if (typeof payload?.probeId === 'string') {
|
|
550
|
+
this.postLocationReport('probe', payload.probeId);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
postLocationReport(trigger, probeId) {
|
|
555
|
+
if (this.destroyed || !this.selfWindow) {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
try {
|
|
559
|
+
this.postMessage({
|
|
560
|
+
namespace: MINI_PROGRAM_MESSAGE_NAMESPACE,
|
|
561
|
+
version: MINI_PROGRAM_MESSAGE_VERSION,
|
|
562
|
+
nonce: this.nonce,
|
|
563
|
+
type: 'event',
|
|
564
|
+
method: SDK_LOCATION_REPORT_METHOD,
|
|
565
|
+
payload: {
|
|
566
|
+
probeId,
|
|
567
|
+
href: this.selfWindow.location.href,
|
|
568
|
+
sdkVersion: HB_SDK_VERSION,
|
|
569
|
+
trigger,
|
|
570
|
+
timestamp: Date.now(),
|
|
571
|
+
},
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
catch {
|
|
575
|
+
// 内部诊断消息失败不能改变 Mini-program 自身的导航语义。
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
async postCSPViolation(event) {
|
|
579
|
+
if (!this.selfWindow) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
try {
|
|
583
|
+
const payload = await createCSPViolationPayload(event, this.selfWindow.location.href, HB_SDK_VERSION, this.selfWindow.crypto);
|
|
584
|
+
if (this.destroyed) {
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
this.postMessage({
|
|
588
|
+
namespace: MINI_PROGRAM_MESSAGE_NAMESPACE,
|
|
589
|
+
version: MINI_PROGRAM_MESSAGE_VERSION,
|
|
590
|
+
nonce: this.nonce,
|
|
591
|
+
type: 'event',
|
|
592
|
+
method: SDK_CSP_VIOLATION_METHOD,
|
|
593
|
+
payload,
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
catch {
|
|
597
|
+
// CSP 违规已经由浏览器阻断,诊断发送失败不影响页面继续降级运行。
|
|
371
598
|
}
|
|
372
599
|
}
|
|
373
600
|
handleEvent(message) {
|
|
@@ -378,6 +605,9 @@ class MiniProgramBridgeClient {
|
|
|
378
605
|
else if (eventName === 'ready' && !this.runtimeUnavailable) {
|
|
379
606
|
this.resolveReadyOnce();
|
|
380
607
|
}
|
|
608
|
+
else if (eventName === 'show' && !this.runtimeUnavailable) {
|
|
609
|
+
this.postLocationReport('resume');
|
|
610
|
+
}
|
|
381
611
|
this.eventBus.emit(eventName, message.payload);
|
|
382
612
|
}
|
|
383
613
|
handleResponse(message) {
|
|
@@ -1120,7 +1350,7 @@ function createMiniProgramSDK(options) {
|
|
|
1120
1350
|
return new MiniProgramSDK(options);
|
|
1121
1351
|
}
|
|
1122
1352
|
|
|
1123
|
-
let defaultSDK =
|
|
1353
|
+
let defaultSDK = createMiniProgramSDK();
|
|
1124
1354
|
function getDefaultSDK() {
|
|
1125
1355
|
if (!defaultSDK) {
|
|
1126
1356
|
defaultSDK = createMiniProgramSDK();
|
|
@@ -1234,10 +1464,8 @@ const hbSDK = {
|
|
|
1234
1464
|
|
|
1235
1465
|
exports.HbMiniProgramNetworkError = HbMiniProgramNetworkError;
|
|
1236
1466
|
exports.HbMiniProgramSDKError = HbMiniProgramSDKError;
|
|
1237
|
-
exports.MiniProgramSDK = MiniProgramSDK;
|
|
1238
1467
|
exports.auth = auth;
|
|
1239
1468
|
exports.cloud = cloud;
|
|
1240
|
-
exports.createMiniProgramSDK = createMiniProgramSDK;
|
|
1241
1469
|
exports.default = hbSDK;
|
|
1242
1470
|
exports.device = device;
|
|
1243
1471
|
exports.navigation = navigation;
|