@heybox/hb-sdk 0.8.1-alpha.13 → 0.8.1-alpha.14

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +25 -0
  3. package/dist/cli-chunks/{build-CBPmqCVZ.cjs → build-D-VaxzAE.cjs} +11 -7
  4. package/dist/cli-chunks/{context-CvuPJq1G.cjs → context-BH7BZPoY.cjs} +2 -2
  5. package/dist/cli-chunks/{create-CyMWFYrc.cjs → create-Bmp9E1n8.cjs} +1 -1
  6. package/dist/cli-chunks/{dev-BcAPHk9L.cjs → dev-BntAFyWR.cjs} +28 -10
  7. package/dist/cli-chunks/{doctor-CIwaVrM1.cjs → doctor-t3YMD3qv.cjs} +1 -1
  8. package/dist/cli-chunks/{index-B0KCGR9H.cjs → index-CjP8Qye_.cjs} +2 -2
  9. package/dist/cli-chunks/{index-E0qh3FUb.cjs → index-VP8boR-7.cjs} +15 -15
  10. package/dist/cli-chunks/{index.esm-DTEOIvDp.cjs → index.esm-B_5UnniK.cjs} +7 -7
  11. package/dist/cli-chunks/{login-DGvHzH9j.cjs → login-p38-WSHx.cjs} +2 -2
  12. package/dist/cli-chunks/{project-vite-CRnpRhSb.cjs → project-vite-BfJ5TGoA.cjs} +1 -1
  13. package/dist/cli-chunks/{remote-C1_Hs11w.cjs → remote-CBH04z6v.cjs} +11 -6
  14. package/dist/cli-chunks/{runtime-permission-env-Bn1ONUDU.cjs → runtime-permission-env-CVYOOxPB.cjs} +91 -1
  15. package/dist/cli-chunks/{session-BvZgS7VI.cjs → session-BEpV8CZQ.cjs} +1 -1
  16. package/dist/cli-chunks/{skill-BVT6fBpQ.cjs → skill-DuVDTrA2.cjs} +2 -2
  17. package/dist/cli-chunks/{version-CJTEIaOQ.cjs → version-Df-F-uhW.cjs} +1 -1
  18. package/dist/cli.cjs +1 -1
  19. package/dist/devtools/browser-dev-host/assets/{browser-dev-host-DOzYp6JS.js → browser-dev-host-D_nC-Zex.js} +21 -21
  20. package/dist/devtools/browser-dev-host/assets/{desktop-app-launch-DZ_umnQp.js → desktop-app-launch-C1GHFN_L.js} +3 -3
  21. package/dist/devtools/browser-dev-host/assets/{index-DoPg_zh_.js → index-mfGqyKpq.js} +4 -4
  22. package/dist/devtools/browser-dev-host/index.html +2 -2
  23. package/dist/index.cjs.js +1 -1
  24. package/dist/index.esm.js +1 -1
  25. package/dist/vite.cjs.js +141 -17
  26. package/dist/vite.esm.js +141 -18
  27. package/package.json +4 -4
  28. package/skill/SKILL.md +2 -0
  29. package/skill/references/api-protocol.md +2 -2
  30. package/skill/references/api-root.md +1 -1
  31. package/skill/references/safety-boundaries.md +2 -0
  32. package/skill/skill.json +5 -5
  33. package/types/miniapp-manifest/node.d.ts +8 -1
  34. package/types/miniapp-manifest/schema.d.ts +37 -0
  35. package/types/vite/index.d.ts +1 -0
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="theme-color" content="#f4f5f7" />
7
7
  <title>小程序工坊调试台</title>
8
- <script type="module" crossorigin src="/assets/index-DoPg_zh_.js"></script>
9
- <link rel="modulepreload" crossorigin href="/assets/desktop-app-launch-DZ_umnQp.js">
8
+ <script type="module" crossorigin src="/assets/index-mfGqyKpq.js"></script>
9
+ <link rel="modulepreload" crossorigin href="/assets/desktop-app-launch-C1GHFN_L.js">
10
10
  <link rel="stylesheet" crossorigin href="/assets/index-DXd5IZBz.css">
11
11
  </head>
12
12
  <body>
package/dist/index.cjs.js CHANGED
@@ -835,7 +835,7 @@ function createMessageId() {
835
835
  /** 构建时替换为当前发布包的实际版本。 */
836
836
  const HB_SDK_VERSION = typeof undefined === 'string'
837
837
  ? undefined
838
- : '0.8.1-alpha.13';
838
+ : '0.8.1-alpha.14';
839
839
 
840
840
  const RUNTIME_MODES = new Set(['production', 'preview', 'development', 'unknown']);
841
841
  const OPERATING_SYSTEM_NAMES = new Set(['android', 'ios', 'ohos', 'windows', 'macos', 'linux', 'unknown']);
package/dist/index.esm.js CHANGED
@@ -831,7 +831,7 @@ function createMessageId() {
831
831
  /** 构建时替换为当前发布包的实际版本。 */
832
832
  const HB_SDK_VERSION = typeof undefined === 'string'
833
833
  ? undefined
834
- : '0.8.1-alpha.13';
834
+ : '0.8.1-alpha.14';
835
835
 
836
836
  const RUNTIME_MODES = new Set(['production', 'preview', 'development', 'unknown']);
837
837
  const OPERATING_SYSTEM_NAMES = new Set(['android', 'ios', 'ohos', 'windows', 'macos', 'linux', 'unknown']);
package/dist/vite.cjs.js CHANGED
@@ -11,7 +11,7 @@ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentS
11
11
  /** 构建时替换为当前发布包的实际版本。 */
12
12
  const HB_SDK_VERSION = typeof undefined === 'string'
13
13
  ? undefined
14
- : '0.8.1-alpha.13';
14
+ : '0.8.1-alpha.14';
15
15
 
16
16
  /**
17
17
  * iframe 与调试台页面的开发期 console 捕获转发。
@@ -3281,7 +3281,7 @@ function parseMiniappPermissions(value, options) {
3281
3281
  }
3282
3282
  return { declared: false };
3283
3283
  }
3284
- if (!isRecord$2(value)) {
3284
+ if (!isRecord$3(value)) {
3285
3285
  throw new Error(`${sourceLabel} 必须是 JSON 对象`);
3286
3286
  }
3287
3287
  const knownKeys = new Set(MINIAPP_PERMISSION_KEYS);
@@ -3295,7 +3295,7 @@ function parseMiniappPermissions(value, options) {
3295
3295
  continue;
3296
3296
  const declaration = value[key];
3297
3297
  const itemLabel = `${sourceLabel}.${key}`;
3298
- if (!isRecord$2(declaration))
3298
+ if (!isRecord$3(declaration))
3299
3299
  throw new Error(`${itemLabel} 必须是 JSON 对象`);
3300
3300
  const definition = MINI_PROGRAM_PERMISSION_CATALOG.find((item) => item.key === key);
3301
3301
  if (!definition)
@@ -3329,11 +3329,15 @@ function requiresExplicitPermissions(sdkVersion) {
3329
3329
  function getMissingPermissionsWarning(sourceLabel = 'package.json#heybox.permissions') {
3330
3330
  return `${sourceLabel} 尚未声明。当前按 0.8 兼容行为构建;0.9.0 起将拒绝构建,请尽快补充权限声明(无权限需求时配置 {})。`;
3331
3331
  }
3332
- function isRecord$2(value) {
3332
+ function isRecord$3(value) {
3333
3333
  return typeof value === 'object' && value !== null && !Array.isArray(value);
3334
3334
  }
3335
3335
 
3336
3336
  const MINIAPP_PLATFORM_VALUES = ['android', 'ios', 'ohos', 'windows', 'macos', 'linux'];
3337
+ /** 窗口尺寸字段的脏值兜底上限;真正的上限是宿主侧显示器 workArea。 */
3338
+ const MINIAPP_WINDOW_SIZE_MAX = 16_384;
3339
+ const MINIAPP_WINDOW_NUMBER_FIELDS = ['defaultWidth', 'defaultHeight', 'minWidth', 'minHeight'];
3340
+ const MINIAPP_WINDOW_ALLOWED_FIELDS = [...MINIAPP_WINDOW_NUMBER_FIELDS, 'resizable'];
3337
3341
  function validateMiniappPackageVersionForBuild(version) {
3338
3342
  return validateMiniappManifestVersion(version, 'package.json.version');
3339
3343
  }
@@ -3346,12 +3350,14 @@ function validateMiniappManifestVersion(version, sourceLabel = 'manifest.version
3346
3350
  }
3347
3351
  function renderMiniappManifest(manifest) {
3348
3352
  const platforms = validateMiniappPlatforms(manifest.platforms, 'manifest.platforms');
3353
+ const window = validateMiniappWindow(manifest.window, 'manifest.window');
3349
3354
  const output = {
3350
3355
  version: manifest.version,
3351
3356
  sdkVersion: manifest.sdkVersion,
3352
3357
  platforms,
3353
3358
  ...(manifest.permissions === undefined ? {} : { permissions: manifest.permissions }),
3354
3359
  ...(manifest.companions === undefined ? {} : { companions: manifest.companions }),
3360
+ ...(window === undefined ? {} : { window }),
3355
3361
  };
3356
3362
  return `${JSON.stringify(output, null, 2)}\n`;
3357
3363
  }
@@ -3372,6 +3378,70 @@ function validateMiniappPlatforms(platforms, sourceLabel = 'platforms') {
3372
3378
  }
3373
3379
  return Object.freeze(MINIAPP_PLATFORM_VALUES.filter((platform) => seen.has(platform)));
3374
3380
  }
3381
+ /**
3382
+ * 校验并归一化 `package.json#heybox.window` / `manifest.window` 声明。
3383
+ *
3384
+ * @remarks
3385
+ * 与 `heybox.permissions` 同样的严格风格:未知字段报错,不做静默忽略。
3386
+ * `width` / `height` 显式给出改名提示;`x` / `y` 按未知字段显式报错(`INV-NO-DEV-POSITION`)。
3387
+ * 未声明时返回 `undefined`,由调用方决定不写入产物。
3388
+ * @param value - 待校验的原始声明(不可信输入)。
3389
+ * @param sourceLabel - 报错前缀,默认指向开发者声明路径。
3390
+ * @throws 当声明不是非空对象、含未知字段、数值/布尔类型不合法或 min/default 不配对时抛出。
3391
+ */
3392
+ function validateMiniappWindow(value, sourceLabel = 'package.json#heybox.window') {
3393
+ if (value === undefined)
3394
+ return undefined;
3395
+ if (!isRecord$2(value) || Object.keys(value).length === 0) {
3396
+ throw new Error(`${sourceLabel} 至少需要声明 defaultWidth/defaultHeight/minWidth/minHeight/resizable 之一`);
3397
+ }
3398
+ if (Object.prototype.hasOwnProperty.call(value, 'width') || Object.prototype.hasOwnProperty.call(value, 'height')) {
3399
+ throw new Error(`${sourceLabel} 请使用 defaultWidth/defaultHeight,而不是 width/height`);
3400
+ }
3401
+ for (const key of Object.keys(value)) {
3402
+ if (!MINIAPP_WINDOW_ALLOWED_FIELDS.includes(key)) {
3403
+ throw new Error(`${sourceLabel} 包含未知字段:${key}`);
3404
+ }
3405
+ }
3406
+ const window = {};
3407
+ for (const field of MINIAPP_WINDOW_NUMBER_FIELDS) {
3408
+ if (!Object.prototype.hasOwnProperty.call(value, field))
3409
+ continue;
3410
+ const size = value[field];
3411
+ if (typeof size !== 'number' || !Number.isSafeInteger(size) || size <= 0) {
3412
+ throw new Error(`${sourceLabel}.${field} 必须是正整数`);
3413
+ }
3414
+ if (size > MINIAPP_WINDOW_SIZE_MAX) {
3415
+ throw new Error(`${sourceLabel}.${field} 不能大于 ${MINIAPP_WINDOW_SIZE_MAX}`);
3416
+ }
3417
+ window[field] = size;
3418
+ }
3419
+ if (Object.prototype.hasOwnProperty.call(value, 'resizable')) {
3420
+ if (typeof value.resizable !== 'boolean') {
3421
+ throw new Error(`${sourceLabel}.resizable 必须是 boolean`);
3422
+ }
3423
+ window.resizable = value.resizable;
3424
+ }
3425
+ for (const [minimum, fallback] of [
3426
+ ['minWidth', 'defaultWidth'],
3427
+ ['minHeight', 'defaultHeight'],
3428
+ ]) {
3429
+ const minimumValue = window[minimum];
3430
+ if (minimumValue === undefined)
3431
+ continue;
3432
+ const fallbackValue = window[fallback];
3433
+ if (fallbackValue === undefined) {
3434
+ throw new Error(`${sourceLabel}.${minimum} 必须与 ${fallback} 同时声明`);
3435
+ }
3436
+ if (minimumValue > fallbackValue) {
3437
+ throw new Error(`${sourceLabel}.${minimum} 必须小于等于 ${fallback}`);
3438
+ }
3439
+ }
3440
+ return window;
3441
+ }
3442
+ function isRecord$2(value) {
3443
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3444
+ }
3375
3445
  function getMiniappManifestVersionError(version) {
3376
3446
  if (typeof version !== 'string' || version.trim() === '') {
3377
3447
  return `必须是合法 SemVer:${String(version)}`;
@@ -3429,6 +3499,17 @@ function readMiniappPlatformsFromPackageJson(root) {
3429
3499
  const heybox = readMiniappHeyboxConfig(packageJson);
3430
3500
  return validateMiniappPlatforms(heybox?.platforms, 'package.json#heybox.platforms');
3431
3501
  }
3502
+ /**
3503
+ * 读取并校验 `package.json#heybox.window`。
3504
+ *
3505
+ * @returns 合法声明;未声明时返回 `undefined`,调用方不得向 manifest 写入空对象或 `null`。
3506
+ * @throws 当声明违反 {@link validateMiniappWindow} 的规则时抛出,报错前缀为 `package.json#heybox.window`。
3507
+ */
3508
+ function readMiniappWindowFromPackageJson(root) {
3509
+ const { packageJson } = readMiniappPackageJson(root, false);
3510
+ const heybox = readMiniappHeyboxConfig(packageJson);
3511
+ return validateMiniappWindow(heybox?.window, 'package.json#heybox.window');
3512
+ }
3432
3513
  function readMiniappPermissionsFromPackageJson(root, sdkVersion) {
3433
3514
  const { packageJson } = readMiniappPackageJson(root, false);
3434
3515
  const heybox = readMiniappHeyboxConfig(packageJson);
@@ -12893,11 +12974,23 @@ const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
12893
12974
  const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
12894
12975
  const miniDevLoggingConstant = ['__HB_SDK_DEV', 'LOGGING__'].join('_');
12895
12976
  const DEV_MANIFEST_PATH = '/__hb_sdk__/companion/manifest.json';
12977
+ /**
12978
+ * 本地 PC 调试开窗时的 manifest 入口。
12979
+ *
12980
+ * 开窗发生在扩展拿到 `LocalDevelopmentBinding` 之前,所以不能用 Companion 会话令牌守护;
12981
+ * 该入口改由 CLI 随本地调试启动上下文(`mini_url` query)下发的专用令牌守护,
12982
+ * 只回最新快照,不参与 Companion 会话的 digest 钉扎。
12983
+ */
12984
+ const DEV_MINI_PROGRAM_MANIFEST_PATH = '/__hb_sdk__/mini-program/manifest.json';
12896
12985
  const DEV_TOKEN_HEADER = 'x-hb-sdk-companion-dev-token';
12986
+ const DEV_MINI_PROGRAM_MANIFEST_TOKEN_HEADER = 'x-hb-sdk-mini-program-manifest-token';
12897
12987
  const DEV_DIGEST_HEADER = 'x-hb-sdk-companion-manifest-sha256';
12898
12988
  const DEV_ERROR_MESSAGE = 'Companion 开发资源暂时不可用';
12989
+ /** 与 Companion manifest 一致的单响应上限,避免本地调试入口被大体积声明撑爆。 */
12990
+ const DEV_MINI_PROGRAM_MANIFEST_MAX_BYTES = 1024 * 1024;
12899
12991
  const DEV_COMPANION_PATH_PATTERN = /^\/__hb_sdk__\/companions\/([a-z][a-z0-9-]{0,31})\/(windows-x64|macos-arm64)\.zip$/;
12900
12992
  const HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV = 'HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN';
12993
+ const HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV = 'HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN';
12901
12994
  const devSnapshotStoreKey = Symbol.for('@heybox/hb-sdk/dev-companion-snapshots');
12902
12995
  const devSnapshotRegistry = globalThis;
12903
12996
  if (!devSnapshotRegistry[devSnapshotStoreKey]) {
@@ -12914,7 +13007,8 @@ function miniappManifest() {
12914
13007
  const includeCompanions = process.env.HB_SDK_SKIP_COMPANIONS !== '1';
12915
13008
  const deviceLogs = new DeviceLogStore();
12916
13009
  const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
12917
- let companionArtifactAccessToken = parseCompanionArtifactAccessToken(process.env[HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV]);
13010
+ let companionArtifactAccessToken = parseDevAccessToken(process.env[HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV]);
13011
+ let miniProgramManifestAccessToken = parseDevAccessToken(process.env[HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV]);
12918
13012
  let root = process.cwd();
12919
13013
  let outDir = 'dist';
12920
13014
  let command = 'build';
@@ -12962,8 +13056,16 @@ function miniappManifest() {
12962
13056
  registerLogs();
12963
13057
  let store = devSnapshotStores.get(root);
12964
13058
  companionArtifactAccessToken ??= store?.token;
12965
- if (!store || store.token !== companionArtifactAccessToken) {
12966
- store = { token: companionArtifactAccessToken ?? '', snapshots: new Map() };
13059
+ // Companion 令牌同因:配置重载时 env 已不在,只能从同一服务的注册表恢复。
13060
+ miniProgramManifestAccessToken ??= store?.manifestToken;
13061
+ if (!store ||
13062
+ store.token !== companionArtifactAccessToken ||
13063
+ store.manifestToken !== miniProgramManifestAccessToken) {
13064
+ store = {
13065
+ token: companionArtifactAccessToken ?? '',
13066
+ ...(miniProgramManifestAccessToken ? { manifestToken: miniProgramManifestAccessToken } : {}),
13067
+ snapshots: new Map(),
13068
+ };
12967
13069
  devSnapshotStores.set(root, store);
12968
13070
  }
12969
13071
  const snapshots = store.snapshots;
@@ -13068,7 +13170,8 @@ function miniappManifest() {
13068
13170
  return;
13069
13171
  }
13070
13172
  const companionMatch = pathname?.match(DEV_COMPANION_PATH_PATTERN);
13071
- if (pathname !== DEV_MANIFEST_PATH && !companionMatch) {
13173
+ const miniProgramManifest = pathname === DEV_MINI_PROGRAM_MANIFEST_PATH;
13174
+ if (pathname !== DEV_MANIFEST_PATH && !companionMatch && !miniProgramManifest) {
13072
13175
  next();
13073
13176
  return;
13074
13177
  }
@@ -13078,16 +13181,25 @@ function miniappManifest() {
13078
13181
  response.end();
13079
13182
  return;
13080
13183
  }
13081
- if (!companionArtifactAccessToken ||
13082
- !isNumericLoopbackRequest(request) ||
13083
- !isNumericLoopbackHost(request.headers.host) ||
13084
- !hasValidCompanionArtifactToken(request, companionArtifactAccessToken)) {
13184
+ // 两条链路共用「仅数值 loopback」边界,凭据各自独立。
13185
+ if (!isNumericLoopbackRequest(request) || !isNumericLoopbackHost(request.headers.host)) {
13186
+ response.statusCode = 403;
13187
+ response.end();
13188
+ return;
13189
+ }
13190
+ const authorized = miniProgramManifest
13191
+ ? miniProgramManifestAccessToken !== undefined &&
13192
+ hasValidDevToken(request, DEV_MINI_PROGRAM_MANIFEST_TOKEN_HEADER, miniProgramManifestAccessToken)
13193
+ : companionArtifactAccessToken !== undefined &&
13194
+ hasValidDevToken(request, DEV_TOKEN_HEADER, companionArtifactAccessToken);
13195
+ if (!authorized) {
13085
13196
  response.statusCode = 403;
13086
13197
  response.end();
13087
13198
  return;
13088
13199
  }
13089
13200
  try {
13090
- const requestedDigest = request.headers[DEV_DIGEST_HEADER];
13201
+ // 本地调试入口只回最新快照;digest 钉扎仍然只服务 Companion 会话。
13202
+ const requestedDigest = miniProgramManifest ? undefined : request.headers[DEV_DIGEST_HEADER];
13091
13203
  if (requestedDigest !== undefined && (typeof requestedDigest !== 'string' || !/^[a-f0-9]{64}$/.test(requestedDigest))) {
13092
13204
  response.statusCode = 400;
13093
13205
  response.end();
@@ -13099,9 +13211,15 @@ function miniappManifest() {
13099
13211
  response.end();
13100
13212
  return;
13101
13213
  }
13214
+ if (miniProgramManifest && snapshot.manifestBytes.length > DEV_MINI_PROGRAM_MANIFEST_MAX_BYTES) {
13215
+ response.statusCode = 500;
13216
+ response.setHeader('Content-Type', 'text/plain; charset=utf-8');
13217
+ response.end(DEV_ERROR_MESSAGE);
13218
+ return;
13219
+ }
13102
13220
  response.setHeader('Cache-Control', 'no-store');
13103
13221
  response.setHeader('X-Content-Type-Options', 'nosniff');
13104
- if (pathname === DEV_MANIFEST_PATH) {
13222
+ if (pathname === DEV_MANIFEST_PATH || miniProgramManifest) {
13105
13223
  response.statusCode = 200;
13106
13224
  response.setHeader('Content-Type', 'application/json; charset=utf-8');
13107
13225
  response.setHeader('Content-Length', snapshot.manifestBytes.length);
@@ -13181,6 +13299,7 @@ function miniappManifest() {
13181
13299
  const sdkVersion = resolveSdkVersion();
13182
13300
  const platforms = readMiniappPlatformsFromPackageJson(root);
13183
13301
  const parsedPermissions = readMiniappPermissionsFromPackageJson(root, sdkVersion);
13302
+ const windowDeclaration = readMiniappWindowFromPackageJson(root);
13184
13303
  const companionConfig = readMiniappCompanionsFromPackageJson(root);
13185
13304
  const companionDeclarations = includeCompanions ? companionConfig.declarations : undefined;
13186
13305
  if (!parsedPermissions.declared)
@@ -13210,6 +13329,7 @@ function miniappManifest() {
13210
13329
  platforms,
13211
13330
  ...(parsedPermissions.permissions === undefined ? {} : { permissions: parsedPermissions.permissions }),
13212
13331
  ...(companions === undefined ? {} : { companions }),
13332
+ ...(windowDeclaration === undefined ? {} : { window: windowDeclaration }),
13213
13333
  }));
13214
13334
  },
13215
13335
  };
@@ -13218,6 +13338,7 @@ function prepareDevSnapshot(root, outDir, platforms, declarations, snapshotRoot,
13218
13338
  const version = validateMiniappPackageVersionForBuild(readMiniappVersionFromPackageJson(root));
13219
13339
  const sdkVersion = resolveSdkVersion();
13220
13340
  const permissions = readMiniappPermissionsFromPackageJson(root, sdkVersion).permissions;
13341
+ const windowDeclaration = readMiniappWindowFromPackageJson(root);
13221
13342
  if (declarations && !permissions?.companion) {
13222
13343
  throw new Error('配置 companions 时必须声明 package.json#heybox.permissions.companion.enabled=true');
13223
13344
  }
@@ -13241,6 +13362,7 @@ function prepareDevSnapshot(root, outDir, platforms, declarations, snapshotRoot,
13241
13362
  platforms,
13242
13363
  ...(permissions === undefined ? {} : { permissions }),
13243
13364
  ...(companions === undefined ? {} : { companions }),
13365
+ ...(windowDeclaration === undefined ? {} : { window: windowDeclaration }),
13244
13366
  };
13245
13367
  return { artifacts, manifestBytes: Buffer.from(renderMiniappManifest(manifest)) };
13246
13368
  }
@@ -13289,11 +13411,12 @@ function isNumericLoopbackHost(host) {
13289
13411
  const port = Number(match[1]);
13290
13412
  return port >= 1 && port <= 65_535;
13291
13413
  }
13292
- function parseCompanionArtifactAccessToken(value) {
13414
+ /** 两条本地调试链路共用的凭据形状:32 字节 base64url,恰好 43 个字符。 */
13415
+ function parseDevAccessToken(value) {
13293
13416
  return value && /^[A-Za-z0-9_-]{43}$/.test(value) ? value : undefined;
13294
13417
  }
13295
- function hasValidCompanionArtifactToken(request, expected) {
13296
- const value = request.headers[DEV_TOKEN_HEADER];
13418
+ function hasValidDevToken(request, header, expected) {
13419
+ const value = request.headers[header];
13297
13420
  const candidate = Buffer.alloc(43);
13298
13421
  if (typeof value === 'string')
13299
13422
  Buffer.from(value).copy(candidate, 0, 0, candidate.length);
@@ -13343,5 +13466,6 @@ function resolveHmrWebSocketUrl(resolved) {
13343
13466
  }
13344
13467
 
13345
13468
  exports.HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV = HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV;
13469
+ exports.HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV = HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV;
13346
13470
  exports.MINIAPP_PLATFORM_VALUES = MINIAPP_PLATFORM_VALUES;
13347
13471
  exports.miniappManifest = miniappManifest;
package/dist/vite.esm.js CHANGED
@@ -8,7 +8,7 @@ import { AsyncLocalStorage } from 'node:async_hooks';
8
8
  /** 构建时替换为当前发布包的实际版本。 */
9
9
  const HB_SDK_VERSION = typeof undefined === 'string'
10
10
  ? undefined
11
- : '0.8.1-alpha.13';
11
+ : '0.8.1-alpha.14';
12
12
 
13
13
  /**
14
14
  * iframe 与调试台页面的开发期 console 捕获转发。
@@ -3278,7 +3278,7 @@ function parseMiniappPermissions(value, options) {
3278
3278
  }
3279
3279
  return { declared: false };
3280
3280
  }
3281
- if (!isRecord$2(value)) {
3281
+ if (!isRecord$3(value)) {
3282
3282
  throw new Error(`${sourceLabel} 必须是 JSON 对象`);
3283
3283
  }
3284
3284
  const knownKeys = new Set(MINIAPP_PERMISSION_KEYS);
@@ -3292,7 +3292,7 @@ function parseMiniappPermissions(value, options) {
3292
3292
  continue;
3293
3293
  const declaration = value[key];
3294
3294
  const itemLabel = `${sourceLabel}.${key}`;
3295
- if (!isRecord$2(declaration))
3295
+ if (!isRecord$3(declaration))
3296
3296
  throw new Error(`${itemLabel} 必须是 JSON 对象`);
3297
3297
  const definition = MINI_PROGRAM_PERMISSION_CATALOG.find((item) => item.key === key);
3298
3298
  if (!definition)
@@ -3326,11 +3326,15 @@ function requiresExplicitPermissions(sdkVersion) {
3326
3326
  function getMissingPermissionsWarning(sourceLabel = 'package.json#heybox.permissions') {
3327
3327
  return `${sourceLabel} 尚未声明。当前按 0.8 兼容行为构建;0.9.0 起将拒绝构建,请尽快补充权限声明(无权限需求时配置 {})。`;
3328
3328
  }
3329
- function isRecord$2(value) {
3329
+ function isRecord$3(value) {
3330
3330
  return typeof value === 'object' && value !== null && !Array.isArray(value);
3331
3331
  }
3332
3332
 
3333
3333
  const MINIAPP_PLATFORM_VALUES = ['android', 'ios', 'ohos', 'windows', 'macos', 'linux'];
3334
+ /** 窗口尺寸字段的脏值兜底上限;真正的上限是宿主侧显示器 workArea。 */
3335
+ const MINIAPP_WINDOW_SIZE_MAX = 16_384;
3336
+ const MINIAPP_WINDOW_NUMBER_FIELDS = ['defaultWidth', 'defaultHeight', 'minWidth', 'minHeight'];
3337
+ const MINIAPP_WINDOW_ALLOWED_FIELDS = [...MINIAPP_WINDOW_NUMBER_FIELDS, 'resizable'];
3334
3338
  function validateMiniappPackageVersionForBuild(version) {
3335
3339
  return validateMiniappManifestVersion(version, 'package.json.version');
3336
3340
  }
@@ -3343,12 +3347,14 @@ function validateMiniappManifestVersion(version, sourceLabel = 'manifest.version
3343
3347
  }
3344
3348
  function renderMiniappManifest(manifest) {
3345
3349
  const platforms = validateMiniappPlatforms(manifest.platforms, 'manifest.platforms');
3350
+ const window = validateMiniappWindow(manifest.window, 'manifest.window');
3346
3351
  const output = {
3347
3352
  version: manifest.version,
3348
3353
  sdkVersion: manifest.sdkVersion,
3349
3354
  platforms,
3350
3355
  ...(manifest.permissions === undefined ? {} : { permissions: manifest.permissions }),
3351
3356
  ...(manifest.companions === undefined ? {} : { companions: manifest.companions }),
3357
+ ...(window === undefined ? {} : { window }),
3352
3358
  };
3353
3359
  return `${JSON.stringify(output, null, 2)}\n`;
3354
3360
  }
@@ -3369,6 +3375,70 @@ function validateMiniappPlatforms(platforms, sourceLabel = 'platforms') {
3369
3375
  }
3370
3376
  return Object.freeze(MINIAPP_PLATFORM_VALUES.filter((platform) => seen.has(platform)));
3371
3377
  }
3378
+ /**
3379
+ * 校验并归一化 `package.json#heybox.window` / `manifest.window` 声明。
3380
+ *
3381
+ * @remarks
3382
+ * 与 `heybox.permissions` 同样的严格风格:未知字段报错,不做静默忽略。
3383
+ * `width` / `height` 显式给出改名提示;`x` / `y` 按未知字段显式报错(`INV-NO-DEV-POSITION`)。
3384
+ * 未声明时返回 `undefined`,由调用方决定不写入产物。
3385
+ * @param value - 待校验的原始声明(不可信输入)。
3386
+ * @param sourceLabel - 报错前缀,默认指向开发者声明路径。
3387
+ * @throws 当声明不是非空对象、含未知字段、数值/布尔类型不合法或 min/default 不配对时抛出。
3388
+ */
3389
+ function validateMiniappWindow(value, sourceLabel = 'package.json#heybox.window') {
3390
+ if (value === undefined)
3391
+ return undefined;
3392
+ if (!isRecord$2(value) || Object.keys(value).length === 0) {
3393
+ throw new Error(`${sourceLabel} 至少需要声明 defaultWidth/defaultHeight/minWidth/minHeight/resizable 之一`);
3394
+ }
3395
+ if (Object.prototype.hasOwnProperty.call(value, 'width') || Object.prototype.hasOwnProperty.call(value, 'height')) {
3396
+ throw new Error(`${sourceLabel} 请使用 defaultWidth/defaultHeight,而不是 width/height`);
3397
+ }
3398
+ for (const key of Object.keys(value)) {
3399
+ if (!MINIAPP_WINDOW_ALLOWED_FIELDS.includes(key)) {
3400
+ throw new Error(`${sourceLabel} 包含未知字段:${key}`);
3401
+ }
3402
+ }
3403
+ const window = {};
3404
+ for (const field of MINIAPP_WINDOW_NUMBER_FIELDS) {
3405
+ if (!Object.prototype.hasOwnProperty.call(value, field))
3406
+ continue;
3407
+ const size = value[field];
3408
+ if (typeof size !== 'number' || !Number.isSafeInteger(size) || size <= 0) {
3409
+ throw new Error(`${sourceLabel}.${field} 必须是正整数`);
3410
+ }
3411
+ if (size > MINIAPP_WINDOW_SIZE_MAX) {
3412
+ throw new Error(`${sourceLabel}.${field} 不能大于 ${MINIAPP_WINDOW_SIZE_MAX}`);
3413
+ }
3414
+ window[field] = size;
3415
+ }
3416
+ if (Object.prototype.hasOwnProperty.call(value, 'resizable')) {
3417
+ if (typeof value.resizable !== 'boolean') {
3418
+ throw new Error(`${sourceLabel}.resizable 必须是 boolean`);
3419
+ }
3420
+ window.resizable = value.resizable;
3421
+ }
3422
+ for (const [minimum, fallback] of [
3423
+ ['minWidth', 'defaultWidth'],
3424
+ ['minHeight', 'defaultHeight'],
3425
+ ]) {
3426
+ const minimumValue = window[minimum];
3427
+ if (minimumValue === undefined)
3428
+ continue;
3429
+ const fallbackValue = window[fallback];
3430
+ if (fallbackValue === undefined) {
3431
+ throw new Error(`${sourceLabel}.${minimum} 必须与 ${fallback} 同时声明`);
3432
+ }
3433
+ if (minimumValue > fallbackValue) {
3434
+ throw new Error(`${sourceLabel}.${minimum} 必须小于等于 ${fallback}`);
3435
+ }
3436
+ }
3437
+ return window;
3438
+ }
3439
+ function isRecord$2(value) {
3440
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3441
+ }
3372
3442
  function getMiniappManifestVersionError(version) {
3373
3443
  if (typeof version !== 'string' || version.trim() === '') {
3374
3444
  return `必须是合法 SemVer:${String(version)}`;
@@ -3426,6 +3496,17 @@ function readMiniappPlatformsFromPackageJson(root) {
3426
3496
  const heybox = readMiniappHeyboxConfig(packageJson);
3427
3497
  return validateMiniappPlatforms(heybox?.platforms, 'package.json#heybox.platforms');
3428
3498
  }
3499
+ /**
3500
+ * 读取并校验 `package.json#heybox.window`。
3501
+ *
3502
+ * @returns 合法声明;未声明时返回 `undefined`,调用方不得向 manifest 写入空对象或 `null`。
3503
+ * @throws 当声明违反 {@link validateMiniappWindow} 的规则时抛出,报错前缀为 `package.json#heybox.window`。
3504
+ */
3505
+ function readMiniappWindowFromPackageJson(root) {
3506
+ const { packageJson } = readMiniappPackageJson(root, false);
3507
+ const heybox = readMiniappHeyboxConfig(packageJson);
3508
+ return validateMiniappWindow(heybox?.window, 'package.json#heybox.window');
3509
+ }
3429
3510
  function readMiniappPermissionsFromPackageJson(root, sdkVersion) {
3430
3511
  const { packageJson } = readMiniappPackageJson(root, false);
3431
3512
  const heybox = readMiniappHeyboxConfig(packageJson);
@@ -12890,11 +12971,23 @@ const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
12890
12971
  const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
12891
12972
  const miniDevLoggingConstant = ['__HB_SDK_DEV', 'LOGGING__'].join('_');
12892
12973
  const DEV_MANIFEST_PATH = '/__hb_sdk__/companion/manifest.json';
12974
+ /**
12975
+ * 本地 PC 调试开窗时的 manifest 入口。
12976
+ *
12977
+ * 开窗发生在扩展拿到 `LocalDevelopmentBinding` 之前,所以不能用 Companion 会话令牌守护;
12978
+ * 该入口改由 CLI 随本地调试启动上下文(`mini_url` query)下发的专用令牌守护,
12979
+ * 只回最新快照,不参与 Companion 会话的 digest 钉扎。
12980
+ */
12981
+ const DEV_MINI_PROGRAM_MANIFEST_PATH = '/__hb_sdk__/mini-program/manifest.json';
12893
12982
  const DEV_TOKEN_HEADER = 'x-hb-sdk-companion-dev-token';
12983
+ const DEV_MINI_PROGRAM_MANIFEST_TOKEN_HEADER = 'x-hb-sdk-mini-program-manifest-token';
12894
12984
  const DEV_DIGEST_HEADER = 'x-hb-sdk-companion-manifest-sha256';
12895
12985
  const DEV_ERROR_MESSAGE = 'Companion 开发资源暂时不可用';
12986
+ /** 与 Companion manifest 一致的单响应上限,避免本地调试入口被大体积声明撑爆。 */
12987
+ const DEV_MINI_PROGRAM_MANIFEST_MAX_BYTES = 1024 * 1024;
12896
12988
  const DEV_COMPANION_PATH_PATTERN = /^\/__hb_sdk__\/companions\/([a-z][a-z0-9-]{0,31})\/(windows-x64|macos-arm64)\.zip$/;
12897
12989
  const HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV = 'HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN';
12990
+ const HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV = 'HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN';
12898
12991
  const devSnapshotStoreKey = Symbol.for('@heybox/hb-sdk/dev-companion-snapshots');
12899
12992
  const devSnapshotRegistry = globalThis;
12900
12993
  if (!devSnapshotRegistry[devSnapshotStoreKey]) {
@@ -12911,7 +13004,8 @@ function miniappManifest() {
12911
13004
  const includeCompanions = process.env.HB_SDK_SKIP_COMPANIONS !== '1';
12912
13005
  const deviceLogs = new DeviceLogStore();
12913
13006
  const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
12914
- let companionArtifactAccessToken = parseCompanionArtifactAccessToken(process.env[HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV]);
13007
+ let companionArtifactAccessToken = parseDevAccessToken(process.env[HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV]);
13008
+ let miniProgramManifestAccessToken = parseDevAccessToken(process.env[HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV]);
12915
13009
  let root = process.cwd();
12916
13010
  let outDir = 'dist';
12917
13011
  let command = 'build';
@@ -12959,8 +13053,16 @@ function miniappManifest() {
12959
13053
  registerLogs();
12960
13054
  let store = devSnapshotStores.get(root);
12961
13055
  companionArtifactAccessToken ??= store?.token;
12962
- if (!store || store.token !== companionArtifactAccessToken) {
12963
- store = { token: companionArtifactAccessToken ?? '', snapshots: new Map() };
13056
+ // Companion 令牌同因:配置重载时 env 已不在,只能从同一服务的注册表恢复。
13057
+ miniProgramManifestAccessToken ??= store?.manifestToken;
13058
+ if (!store ||
13059
+ store.token !== companionArtifactAccessToken ||
13060
+ store.manifestToken !== miniProgramManifestAccessToken) {
13061
+ store = {
13062
+ token: companionArtifactAccessToken ?? '',
13063
+ ...(miniProgramManifestAccessToken ? { manifestToken: miniProgramManifestAccessToken } : {}),
13064
+ snapshots: new Map(),
13065
+ };
12964
13066
  devSnapshotStores.set(root, store);
12965
13067
  }
12966
13068
  const snapshots = store.snapshots;
@@ -13065,7 +13167,8 @@ function miniappManifest() {
13065
13167
  return;
13066
13168
  }
13067
13169
  const companionMatch = pathname?.match(DEV_COMPANION_PATH_PATTERN);
13068
- if (pathname !== DEV_MANIFEST_PATH && !companionMatch) {
13170
+ const miniProgramManifest = pathname === DEV_MINI_PROGRAM_MANIFEST_PATH;
13171
+ if (pathname !== DEV_MANIFEST_PATH && !companionMatch && !miniProgramManifest) {
13069
13172
  next();
13070
13173
  return;
13071
13174
  }
@@ -13075,16 +13178,25 @@ function miniappManifest() {
13075
13178
  response.end();
13076
13179
  return;
13077
13180
  }
13078
- if (!companionArtifactAccessToken ||
13079
- !isNumericLoopbackRequest(request) ||
13080
- !isNumericLoopbackHost(request.headers.host) ||
13081
- !hasValidCompanionArtifactToken(request, companionArtifactAccessToken)) {
13181
+ // 两条链路共用「仅数值 loopback」边界,凭据各自独立。
13182
+ if (!isNumericLoopbackRequest(request) || !isNumericLoopbackHost(request.headers.host)) {
13183
+ response.statusCode = 403;
13184
+ response.end();
13185
+ return;
13186
+ }
13187
+ const authorized = miniProgramManifest
13188
+ ? miniProgramManifestAccessToken !== undefined &&
13189
+ hasValidDevToken(request, DEV_MINI_PROGRAM_MANIFEST_TOKEN_HEADER, miniProgramManifestAccessToken)
13190
+ : companionArtifactAccessToken !== undefined &&
13191
+ hasValidDevToken(request, DEV_TOKEN_HEADER, companionArtifactAccessToken);
13192
+ if (!authorized) {
13082
13193
  response.statusCode = 403;
13083
13194
  response.end();
13084
13195
  return;
13085
13196
  }
13086
13197
  try {
13087
- const requestedDigest = request.headers[DEV_DIGEST_HEADER];
13198
+ // 本地调试入口只回最新快照;digest 钉扎仍然只服务 Companion 会话。
13199
+ const requestedDigest = miniProgramManifest ? undefined : request.headers[DEV_DIGEST_HEADER];
13088
13200
  if (requestedDigest !== undefined && (typeof requestedDigest !== 'string' || !/^[a-f0-9]{64}$/.test(requestedDigest))) {
13089
13201
  response.statusCode = 400;
13090
13202
  response.end();
@@ -13096,9 +13208,15 @@ function miniappManifest() {
13096
13208
  response.end();
13097
13209
  return;
13098
13210
  }
13211
+ if (miniProgramManifest && snapshot.manifestBytes.length > DEV_MINI_PROGRAM_MANIFEST_MAX_BYTES) {
13212
+ response.statusCode = 500;
13213
+ response.setHeader('Content-Type', 'text/plain; charset=utf-8');
13214
+ response.end(DEV_ERROR_MESSAGE);
13215
+ return;
13216
+ }
13099
13217
  response.setHeader('Cache-Control', 'no-store');
13100
13218
  response.setHeader('X-Content-Type-Options', 'nosniff');
13101
- if (pathname === DEV_MANIFEST_PATH) {
13219
+ if (pathname === DEV_MANIFEST_PATH || miniProgramManifest) {
13102
13220
  response.statusCode = 200;
13103
13221
  response.setHeader('Content-Type', 'application/json; charset=utf-8');
13104
13222
  response.setHeader('Content-Length', snapshot.manifestBytes.length);
@@ -13178,6 +13296,7 @@ function miniappManifest() {
13178
13296
  const sdkVersion = resolveSdkVersion();
13179
13297
  const platforms = readMiniappPlatformsFromPackageJson(root);
13180
13298
  const parsedPermissions = readMiniappPermissionsFromPackageJson(root, sdkVersion);
13299
+ const windowDeclaration = readMiniappWindowFromPackageJson(root);
13181
13300
  const companionConfig = readMiniappCompanionsFromPackageJson(root);
13182
13301
  const companionDeclarations = includeCompanions ? companionConfig.declarations : undefined;
13183
13302
  if (!parsedPermissions.declared)
@@ -13207,6 +13326,7 @@ function miniappManifest() {
13207
13326
  platforms,
13208
13327
  ...(parsedPermissions.permissions === undefined ? {} : { permissions: parsedPermissions.permissions }),
13209
13328
  ...(companions === undefined ? {} : { companions }),
13329
+ ...(windowDeclaration === undefined ? {} : { window: windowDeclaration }),
13210
13330
  }));
13211
13331
  },
13212
13332
  };
@@ -13215,6 +13335,7 @@ function prepareDevSnapshot(root, outDir, platforms, declarations, snapshotRoot,
13215
13335
  const version = validateMiniappPackageVersionForBuild(readMiniappVersionFromPackageJson(root));
13216
13336
  const sdkVersion = resolveSdkVersion();
13217
13337
  const permissions = readMiniappPermissionsFromPackageJson(root, sdkVersion).permissions;
13338
+ const windowDeclaration = readMiniappWindowFromPackageJson(root);
13218
13339
  if (declarations && !permissions?.companion) {
13219
13340
  throw new Error('配置 companions 时必须声明 package.json#heybox.permissions.companion.enabled=true');
13220
13341
  }
@@ -13238,6 +13359,7 @@ function prepareDevSnapshot(root, outDir, platforms, declarations, snapshotRoot,
13238
13359
  platforms,
13239
13360
  ...(permissions === undefined ? {} : { permissions }),
13240
13361
  ...(companions === undefined ? {} : { companions }),
13362
+ ...(windowDeclaration === undefined ? {} : { window: windowDeclaration }),
13241
13363
  };
13242
13364
  return { artifacts, manifestBytes: Buffer.from(renderMiniappManifest(manifest)) };
13243
13365
  }
@@ -13286,11 +13408,12 @@ function isNumericLoopbackHost(host) {
13286
13408
  const port = Number(match[1]);
13287
13409
  return port >= 1 && port <= 65_535;
13288
13410
  }
13289
- function parseCompanionArtifactAccessToken(value) {
13411
+ /** 两条本地调试链路共用的凭据形状:32 字节 base64url,恰好 43 个字符。 */
13412
+ function parseDevAccessToken(value) {
13290
13413
  return value && /^[A-Za-z0-9_-]{43}$/.test(value) ? value : undefined;
13291
13414
  }
13292
- function hasValidCompanionArtifactToken(request, expected) {
13293
- const value = request.headers[DEV_TOKEN_HEADER];
13415
+ function hasValidDevToken(request, header, expected) {
13416
+ const value = request.headers[header];
13294
13417
  const candidate = Buffer.alloc(43);
13295
13418
  if (typeof value === 'string')
13296
13419
  Buffer.from(value).copy(candidate, 0, 0, candidate.length);
@@ -13339,4 +13462,4 @@ function resolveHmrWebSocketUrl(resolved) {
13339
13462
  return `${protocol}://${host}${port ? `:${port}` : ''}`;
13340
13463
  }
13341
13464
 
13342
- export { HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV, MINIAPP_PLATFORM_VALUES, miniappManifest };
13465
+ export { HB_SDK_COMPANION_ARTIFACT_ACCESS_TOKEN_ENV, HB_SDK_MINI_PROGRAM_MANIFEST_ACCESS_TOKEN_ENV, MINIAPP_PLATFORM_VALUES, miniappManifest };