@heybox/hb-sdk 0.6.7-alpha.0 → 0.6.7-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +3 -3
  2. package/dist/cli-chunks/{build-mTmtBJbp.cjs → build-CQmYxfxm.cjs} +25 -29
  3. package/dist/cli-chunks/{context-wI8oXc58.cjs → context-ChkYWwH_.cjs} +23 -122
  4. package/dist/cli-chunks/{create-DEwECU7u.cjs → create-CC5r_8bL.cjs} +1 -1
  5. package/dist/cli-chunks/{dev-BZ7FJEeZ.cjs → dev-CD-38TgO.cjs} +110 -21
  6. package/dist/cli-chunks/{doctor-DbJQPPs2.cjs → doctor-43yEfVr0.cjs} +1 -1
  7. package/dist/cli-chunks/{index-UecHob74.cjs → index-BM6oH2Hs.cjs} +3 -3
  8. package/dist/cli-chunks/{runtime-permission-env-BSs4uCNa.cjs → index-BQm5XmMC.cjs} +0 -71
  9. package/dist/cli-chunks/{index-Df14XqmW.cjs → index-CQnkS0Z8.cjs} +14 -14
  10. package/dist/cli-chunks/{login-BzSUtgRH.cjs → login-BmisXAdp.cjs} +2 -2
  11. package/dist/cli-chunks/{project-vite-D2Hx7XPp.cjs → project-vite-D9_v57vp.cjs} +1 -1
  12. package/dist/cli-chunks/{remote-BZiSfXOm.cjs → remote-BXtT6M2f.cjs} +22 -40
  13. package/dist/cli-chunks/{runtime-gate--rPXjiG6.cjs → runtime-gate-B2MQf1vc.cjs} +3 -3
  14. package/dist/cli-chunks/{session-CqycMFvy.cjs → session-BjtjhtPg.cjs} +1 -1
  15. package/dist/cli.cjs +1 -1
  16. package/dist/devtools/mock-host/main.js +187 -21
  17. package/dist/index.cjs.js +1 -1
  18. package/dist/index.esm.js +1 -1
  19. package/dist/vite.cjs.js +50 -28
  20. package/dist/vite.esm.js +50 -28
  21. package/package.json +1 -1
  22. package/skill/references/api-root.md +7 -9
  23. package/skill/skill.json +4 -4
  24. package/types/vite/html-policy.d.ts +0 -1
  25. package/types/vite/index.d.ts +1 -0
  26. package/types/vite/runtime-permission-env.d.ts +0 -9
@@ -3707,25 +3707,61 @@ function stringifyMiniProgramRuntimeJsonData(data, options) {
3707
3707
  }
3708
3708
 
3709
3709
  const LEADERBOARD_KEY_RE = /^[A-Za-z0-9_-]{1,64}$/;
3710
+ const DEFAULT_LEADERBOARD_KEY = 'default';
3710
3711
  const DEFAULT_LEADERBOARD_LIST_LIMIT = 20;
3711
3712
  const MAX_LEADERBOARD_LIST_LIMIT = 100;
3712
3713
  const MAX_LEADERBOARD_EXTRA_BYTES = 2048;
3713
3714
  const MAX_LEADERBOARD_SAFE_SCORE = Number.MAX_SAFE_INTEGER;
3714
- /** 提交当前用户排行榜分数。 */
3715
- async function submitMiniProgramRuntimeLeaderboardEntry(payload, options) {
3716
- const request = readLeaderboardPayload(payload, 'cloud.leaderboard.submit', 'submit');
3715
+ const LEADERBOARD_READ_CACHE_TTL_MS = 5 * 60 * 1000;
3716
+ const MAX_LEADERBOARD_LIST_CACHE_ENTRIES = 250;
3717
+ const MAX_LEADERBOARD_CACHE_BUCKETS = 3;
3718
+ /** 创建一组共享读缓存的排行榜 handler。 */
3719
+ function createMiniProgramRuntimeLeaderboardHandlers(options) {
3720
+ const store = createLeaderboardReadStore();
3721
+ return {
3722
+ async submit(payload) {
3723
+ const request = readLeaderboardPayload(payload, 'cloud.leaderboard.submit', 'submit');
3724
+ const bucketKey = createLeaderboardBucketKey(options, request.key);
3725
+ return submitLeaderboardEntry(request, options, () => store.invalidateMutableReads(bucketKey));
3726
+ },
3727
+ async getList(payload) {
3728
+ const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getList', 'list');
3729
+ const bucketKey = createLeaderboardBucketKey(options, request.key);
3730
+ const cacheKey = createLeaderboardCacheKey('list', request.limit, request.cursor ?? null);
3731
+ return store.read(bucketKey, 'list', cacheKey, () => requestLeaderboardList(request, options));
3732
+ },
3733
+ async getCurrentUserEntry(payload) {
3734
+ const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getCurrentUserEntry', 'keyOnly');
3735
+ const userId = await readCurrentUserId(options.platformAdapter);
3736
+ const bucketKey = createLeaderboardBucketKey(options, request.key);
3737
+ const cacheKey = createLeaderboardCacheKey('current', userId);
3738
+ return store.read(bucketKey, 'current', cacheKey, () => requestCurrentUserLeaderboardEntry(request, userId, options));
3739
+ },
3740
+ async deleteCurrentUserEntry(payload) {
3741
+ const request = readLeaderboardPayload(payload, 'cloud.leaderboard.deleteCurrentUserEntry', 'keyOnly');
3742
+ const bucketKey = createLeaderboardBucketKey(options, request.key);
3743
+ return deleteCurrentUserLeaderboardEntry(request, options, () => store.invalidateMutableReads(bucketKey));
3744
+ },
3745
+ async getInfo(payload) {
3746
+ const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getInfo', 'keyOnly');
3747
+ const bucketKey = createLeaderboardBucketKey(options, request.key);
3748
+ const cacheKey = createLeaderboardCacheKey('info');
3749
+ return store.read(bucketKey, 'info', cacheKey, () => requestLeaderboardInfo(request, options));
3750
+ },
3751
+ };
3752
+ }
3753
+ async function submitLeaderboardEntry(request, options, onAdapterSuccess) {
3717
3754
  const userId = await readCurrentUserId(options.platformAdapter);
3718
3755
  const entry = await options.platformAdapter.cloud.leaderboard.submit({
3719
3756
  ...request,
3720
3757
  miniProgramId: options.identity?.miniProgramId,
3721
3758
  userId,
3722
3759
  });
3760
+ onAdapterSuccess?.();
3723
3761
  assertLeaderboardEntry(entry, 'cloud.leaderboard.submit');
3724
3762
  return entry;
3725
3763
  }
3726
- /** 读取排行榜列表。 */
3727
- async function getMiniProgramRuntimeLeaderboardList(payload, options) {
3728
- const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getList', 'list');
3764
+ async function requestLeaderboardList(request, options) {
3729
3765
  const result = await options.platformAdapter.cloud.leaderboard.getList({
3730
3766
  ...request,
3731
3767
  miniProgramId: options.identity?.miniProgramId,
@@ -3743,10 +3779,7 @@ async function getMiniProgramRuntimeLeaderboardList(payload, options) {
3743
3779
  }
3744
3780
  return result;
3745
3781
  }
3746
- /** 读取当前用户在排行榜中的记录。 */
3747
- async function getMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, options) {
3748
- const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getCurrentUserEntry', 'keyOnly');
3749
- const userId = await readCurrentUserId(options.platformAdapter);
3782
+ async function requestCurrentUserLeaderboardEntry(request, userId, options) {
3750
3783
  const entry = await options.platformAdapter.cloud.leaderboard.getCurrentUserEntry({
3751
3784
  ...request,
3752
3785
  miniProgramId: options.identity?.miniProgramId,
@@ -3757,24 +3790,21 @@ async function getMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, options
3757
3790
  }
3758
3791
  return entry;
3759
3792
  }
3760
- /** 删除当前用户在排行榜中的记录。 */
3761
- async function deleteMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, options) {
3762
- const request = readLeaderboardPayload(payload, 'cloud.leaderboard.deleteCurrentUserEntry', 'keyOnly');
3793
+ async function deleteCurrentUserLeaderboardEntry(request, options, onAdapterSuccess) {
3763
3794
  const userId = await readCurrentUserId(options.platformAdapter);
3764
3795
  const result = await options.platformAdapter.cloud.leaderboard.deleteCurrentUserEntry({
3765
3796
  ...request,
3766
3797
  miniProgramId: options.identity?.miniProgramId,
3767
3798
  userId,
3768
3799
  });
3800
+ onAdapterSuccess?.();
3769
3801
  assertMiniProgramRuntimeRecord(result, 'cloud.leaderboard.deleteCurrentUserEntry 返回值必须是对象');
3770
3802
  if (typeof result.deleted !== 'boolean') {
3771
3803
  throw createMiniProgramRuntimeBridgeError('INVALID_RESPONSE', 'cloud.leaderboard.deleteCurrentUserEntry 返回的 deleted 必须是布尔值');
3772
3804
  }
3773
3805
  return result;
3774
3806
  }
3775
- /** 读取排行榜基础信息。 */
3776
- async function getMiniProgramRuntimeLeaderboardInfo(payload, options) {
3777
- const request = readLeaderboardPayload(payload, 'cloud.leaderboard.getInfo', 'keyOnly');
3807
+ async function requestLeaderboardInfo(request, options) {
3778
3808
  const info = await options.platformAdapter.cloud.leaderboard.getInfo({
3779
3809
  ...request,
3780
3810
  miniProgramId: options.identity?.miniProgramId,
@@ -3791,6 +3821,138 @@ async function getMiniProgramRuntimeLeaderboardInfo(payload, options) {
3791
3821
  }
3792
3822
  return info;
3793
3823
  }
3824
+ function createLeaderboardReadStore() {
3825
+ const buckets = new Map();
3826
+ const inflight = new Map();
3827
+ function getOrCreateBucket(bucketKey) {
3828
+ const bucket = buckets.get(bucketKey);
3829
+ if (bucket) {
3830
+ return bucket;
3831
+ }
3832
+ pruneExpiredBuckets();
3833
+ if (buckets.size >= MAX_LEADERBOARD_CACHE_BUCKETS) {
3834
+ return undefined;
3835
+ }
3836
+ const nextBucket = {
3837
+ generation: 0,
3838
+ caches: {
3839
+ list: new Map(),
3840
+ current: new Map(),
3841
+ info: new Map(),
3842
+ },
3843
+ };
3844
+ buckets.set(bucketKey, nextBucket);
3845
+ return nextBucket;
3846
+ }
3847
+ function read(bucketKey, kind, cacheKey, request) {
3848
+ const bucket = getOrCreateBucket(bucketKey);
3849
+ const cache = bucket?.caches[kind];
3850
+ const cached = cache?.get(cacheKey);
3851
+ if (cached && cached.expiresAt > Date.now()) {
3852
+ return Promise.resolve(cached.value);
3853
+ }
3854
+ cache?.delete(cacheKey);
3855
+ const inflightKey = createLeaderboardInflightKey(bucketKey, cacheKey);
3856
+ const existingInflight = inflight.get(inflightKey);
3857
+ if (existingInflight) {
3858
+ return existingInflight.promise;
3859
+ }
3860
+ const requestGeneration = bucket?.generation;
3861
+ const promise = request()
3862
+ .then((result) => {
3863
+ if (bucket && cache && bucket.generation === requestGeneration) {
3864
+ if (kind === 'list') {
3865
+ if (cache.size >= MAX_LEADERBOARD_LIST_CACHE_ENTRIES) {
3866
+ cache.delete(cache.keys().next().value);
3867
+ }
3868
+ }
3869
+ else {
3870
+ cache.clear();
3871
+ }
3872
+ cache.set(cacheKey, {
3873
+ expiresAt: Date.now() + LEADERBOARD_READ_CACHE_TTL_MS,
3874
+ value: result,
3875
+ });
3876
+ }
3877
+ return result;
3878
+ })
3879
+ .finally(() => {
3880
+ if (inflight.get(inflightKey)?.promise === promise) {
3881
+ inflight.delete(inflightKey);
3882
+ }
3883
+ if (bucket) {
3884
+ removeEmptyBucket(bucketKey, bucket);
3885
+ }
3886
+ });
3887
+ inflight.set(inflightKey, { bucketKey, promise });
3888
+ return promise;
3889
+ }
3890
+ function invalidateMutableReads(bucketKey) {
3891
+ invalidateInflight(bucketKey);
3892
+ const bucket = buckets.get(bucketKey);
3893
+ if (!bucket) {
3894
+ return;
3895
+ }
3896
+ bucket.generation += 1;
3897
+ bucket.caches.list.clear();
3898
+ bucket.caches.current.clear();
3899
+ }
3900
+ function invalidateInflight(bucketKey) {
3901
+ inflight.forEach((entry, inflightKey) => {
3902
+ if (entry.bucketKey === bucketKey) {
3903
+ inflight.delete(inflightKey);
3904
+ }
3905
+ });
3906
+ }
3907
+ function pruneExpiredBuckets() {
3908
+ const now = Date.now();
3909
+ for (const [bucketKey, bucket] of buckets) {
3910
+ removeExpiredCacheEntries(bucket, now);
3911
+ if (isBucketEmpty(bucketKey, bucket)) {
3912
+ buckets.delete(bucketKey);
3913
+ }
3914
+ }
3915
+ }
3916
+ function removeEmptyBucket(bucketKey, bucket) {
3917
+ removeExpiredCacheEntries(bucket, Date.now());
3918
+ if (buckets.get(bucketKey) === bucket && isBucketEmpty(bucketKey, bucket)) {
3919
+ buckets.delete(bucketKey);
3920
+ }
3921
+ }
3922
+ function removeExpiredCacheEntries(bucket, now) {
3923
+ Object.values(bucket.caches).forEach((cache) => {
3924
+ cache.forEach((entry, cacheKey) => {
3925
+ if (entry.expiresAt <= now) {
3926
+ cache.delete(cacheKey);
3927
+ }
3928
+ });
3929
+ });
3930
+ }
3931
+ function isBucketEmpty(bucketKey, bucket) {
3932
+ return !hasInflight(bucketKey) && Object.values(bucket.caches).every(cache => cache.size === 0);
3933
+ }
3934
+ function hasInflight(bucketKey) {
3935
+ for (const entry of inflight.values()) {
3936
+ if (entry.bucketKey === bucketKey) {
3937
+ return true;
3938
+ }
3939
+ }
3940
+ return false;
3941
+ }
3942
+ return {
3943
+ read,
3944
+ invalidateMutableReads,
3945
+ };
3946
+ }
3947
+ function createLeaderboardBucketKey(options, key) {
3948
+ return JSON.stringify([options.identity?.miniProgramId ?? null, key ?? DEFAULT_LEADERBOARD_KEY]);
3949
+ }
3950
+ function createLeaderboardCacheKey(kind, ...parts) {
3951
+ return JSON.stringify([kind, ...parts]);
3952
+ }
3953
+ function createLeaderboardInflightKey(bucketKey, cacheKey) {
3954
+ return JSON.stringify([bucketKey, cacheKey]);
3955
+ }
3794
3956
  function readLeaderboardPayload(payload, methodName, shape) {
3795
3957
  if (payload === undefined && shape !== 'submit') {
3796
3958
  return shape === 'list' ? { limit: DEFAULT_LEADERBOARD_LIST_LIMIT } : {};
@@ -5505,6 +5667,10 @@ class MiniProgramMockRuntime {
5505
5667
  this.vibrates = [];
5506
5668
  this.openAppPages = [];
5507
5669
  this.evaluateRuntimePermission = createMiniProgramRuntimePermissionEvaluator(options.runtimePermissions);
5670
+ this.leaderboardHandlers = createMiniProgramRuntimeLeaderboardHandlers({
5671
+ identity: options.identity,
5672
+ platformAdapter: adapter,
5673
+ });
5508
5674
  }
5509
5675
  async runMethod(method, payload) {
5510
5676
  if (!isMiniProgramMockRuntimeMethod(method)) {
@@ -5634,19 +5800,19 @@ class MiniProgramMockRuntime {
5634
5800
  return requestMiniProgramRuntimeNetwork(payload, this.adapter);
5635
5801
  }
5636
5802
  submitLeaderboardEntry(payload) {
5637
- return submitMiniProgramRuntimeLeaderboardEntry(payload, { platformAdapter: this.adapter });
5803
+ return this.leaderboardHandlers.submit(payload);
5638
5804
  }
5639
5805
  getLeaderboardList(payload) {
5640
- return getMiniProgramRuntimeLeaderboardList(payload, { platformAdapter: this.adapter });
5806
+ return this.leaderboardHandlers.getList(payload);
5641
5807
  }
5642
5808
  getCurrentUserLeaderboardEntry(payload) {
5643
- return getMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, { platformAdapter: this.adapter });
5809
+ return this.leaderboardHandlers.getCurrentUserEntry(payload);
5644
5810
  }
5645
5811
  deleteCurrentUserLeaderboardEntry(payload) {
5646
- return deleteMiniProgramRuntimeCurrentUserLeaderboardEntry(payload, { platformAdapter: this.adapter });
5812
+ return this.leaderboardHandlers.deleteCurrentUserEntry(payload);
5647
5813
  }
5648
5814
  getLeaderboardInfo(payload) {
5649
- return getMiniProgramRuntimeLeaderboardInfo(payload, { platformAdapter: this.adapter });
5815
+ return this.leaderboardHandlers.getInfo(payload);
5650
5816
  }
5651
5817
  async showToast(payload) {
5652
5818
  const toast = readShowToastPayload(payload);
package/dist/index.cjs.js CHANGED
@@ -325,7 +325,7 @@ function createMessageId() {
325
325
  /** 构建时替换为当前发布包的实际版本。 */
326
326
  const HB_SDK_VERSION = typeof undefined === 'string'
327
327
  ? undefined
328
- : '0.6.7-alpha.0';
328
+ : '0.6.7-alpha.2';
329
329
 
330
330
  /**
331
331
  * 判断未知数据是否符合小程序 bridge 消息信封。
package/dist/index.esm.js CHANGED
@@ -321,7 +321,7 @@ function createMessageId() {
321
321
  /** 构建时替换为当前发布包的实际版本。 */
322
322
  const HB_SDK_VERSION = typeof undefined === 'string'
323
323
  ? undefined
324
- : '0.6.7-alpha.0';
324
+ : '0.6.7-alpha.2';
325
325
 
326
326
  /**
327
327
  * 判断未知数据是否符合小程序 bridge 消息信封。
package/dist/vite.cjs.js CHANGED
@@ -2,13 +2,12 @@
2
2
 
3
3
  var node_fs = require('node:fs');
4
4
  var path = require('node:path');
5
- var node_async_hooks = require('node:async_hooks');
6
5
 
7
6
  var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
8
7
  /** 构建时替换为当前发布包的实际版本。 */
9
8
  const HB_SDK_VERSION = typeof undefined === 'string'
10
9
  ? undefined
11
- : '0.6.7-alpha.0';
10
+ : '0.6.7-alpha.2';
12
11
 
13
12
  var re = {exports: {}};
14
13
 
@@ -11433,13 +11432,17 @@ function walk$1(node, visit) {
11433
11432
 
11434
11433
  const FORBIDDEN_RESOURCE_HINTS = new Set(['dns-prefetch', 'preconnect', 'prerender']);
11435
11434
  const RELATIVE_URL_BASE = new URL('https://heybox-relative.invalid/');
11435
+ const OFFICIAL_RESOURCE_ROOT_DOMAINS = ['xiaoheihe.cn', 'max-c.com', 'debugmode.cn', 'maxjia.com'];
11436
+ const OFFICIAL_RESOURCE_SOURCES = OFFICIAL_RESOURCE_ROOT_DOMAINS.flatMap(rootDomain => [
11437
+ `https://${rootDomain}`,
11438
+ `https://*.${rootDomain}`,
11439
+ ]);
11436
11440
  const URL_ATTRIBUTES = {
11437
11441
  audio: ['src'],
11438
11442
  embed: ['src'],
11439
11443
  iframe: ['src'],
11440
11444
  img: ['src'],
11441
11445
  input: ['src'],
11442
- link: ['href'],
11443
11446
  object: ['data'],
11444
11447
  script: ['src'],
11445
11448
  source: ['src', 'srcset'],
@@ -11449,11 +11452,11 @@ const URL_ATTRIBUTES = {
11449
11452
  const PRODUCTION_CSP = [
11450
11453
  "default-src 'none'",
11451
11454
  "connect-src 'none'",
11452
- "script-src 'self' 'unsafe-inline'",
11453
- "style-src 'self' 'unsafe-inline'",
11454
- "img-src 'self' data: blob:",
11455
- "font-src 'self' data:",
11456
- "media-src 'self' blob:",
11455
+ `script-src 'self' 'unsafe-inline' ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11456
+ `style-src 'self' 'unsafe-inline' ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11457
+ `img-src 'self' data: blob: ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11458
+ `font-src 'self' data: ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11459
+ `media-src 'self' blob: ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11457
11460
  "manifest-src 'self'",
11458
11461
  "frame-src 'none'",
11459
11462
  "child-src 'none'",
@@ -11472,9 +11475,6 @@ function enforceMiniappHtmlPolicy(html, options = {}) {
11472
11475
  removePlatformCspMarkers(document);
11473
11476
  walk(document, element => validateElement(element));
11474
11477
  injectMiniappRuntimeGate(document);
11475
- if (options.skipPlatformCsp) {
11476
- return serialize(document);
11477
- }
11478
11478
  const content = options.hmrWebSocketUrl
11479
11479
  ? PRODUCTION_CSP.replace("connect-src 'none'", `connect-src ${options.hmrWebSocketUrl}`)
11480
11480
  : PRODUCTION_CSP;
@@ -11511,19 +11511,25 @@ function validateElement(element) {
11511
11511
  if (forbidden) {
11512
11512
  throw new Error(`index.html 不允许 rel="${forbidden}"`);
11513
11513
  }
11514
- if (rels.includes('preload') || rels.includes('modulepreload')) {
11514
+ if (rels.includes('stylesheet')) {
11515
+ validateResourceUrl(attrs.get('href'), '<link rel="stylesheet" href>', false, true);
11516
+ }
11517
+ else if (rels.includes('preload') || rels.includes('modulepreload')) {
11515
11518
  validateRelativeUrl(attrs.get('href'), `<link rel="${rels.includes('modulepreload') ? 'modulepreload' : 'preload'}" href>`, false);
11516
11519
  }
11520
+ else {
11521
+ validateRelativeUrl(attrs.get('href'), '<link href>', false);
11522
+ }
11517
11523
  }
11518
11524
  for (const attribute of URL_ATTRIBUTES[tagName] ?? []) {
11519
11525
  const value = attrs.get(attribute);
11520
11526
  if (attribute === 'srcset') {
11521
11527
  for (const candidate of (value ?? '').split(',').map(item => item.trim().split(/\s+/)[0]).filter(Boolean)) {
11522
- validateRelativeUrl(candidate, `<${tagName} ${attribute}>`, true);
11528
+ validateResourceUrl(candidate, `<${tagName} ${attribute}>`, true, supportsOfficialResourceUrl(tagName, attribute));
11523
11529
  }
11524
11530
  }
11525
11531
  else {
11526
- validateRelativeUrl(value, `<${tagName} ${attribute}>`, true);
11532
+ validateResourceUrl(value, `<${tagName} ${attribute}>`, true, supportsOfficialResourceUrl(tagName, attribute));
11527
11533
  }
11528
11534
  }
11529
11535
  }
@@ -11538,16 +11544,43 @@ function removePlatformCspMarkers(node) {
11538
11544
  for (const child of node.childNodes)
11539
11545
  removePlatformCspMarkers(child);
11540
11546
  }
11541
- function validateRelativeUrl(value, label, allowDataBlob) {
11547
+ function supportsOfficialResourceUrl(tagName, attribute) {
11548
+ return ((tagName === 'script' && attribute === 'src') ||
11549
+ (tagName === 'img' && attribute === 'src') ||
11550
+ (tagName === 'source' && (attribute === 'src' || attribute === 'srcset')) ||
11551
+ (tagName === 'audio' && attribute === 'src') ||
11552
+ (tagName === 'track' && attribute === 'src') ||
11553
+ (tagName === 'video' && (attribute === 'src' || attribute === 'poster')));
11554
+ }
11555
+ function validateResourceUrl(value, label, allowDataBlob, allowOfficialResourceUrl) {
11542
11556
  if (!value || value.startsWith('#'))
11543
11557
  return;
11544
11558
  const normalized = value.trim().toLowerCase();
11545
11559
  if (allowDataBlob && (normalized.startsWith('data:') || normalized.startsWith('blob:')))
11546
11560
  return;
11561
+ if (allowOfficialResourceUrl && isOfficialResourceUrl(value))
11562
+ return;
11547
11563
  if (!isRelativeUrl(value, normalized)) {
11548
11564
  throw new Error(`${label} 必须使用相对路径${allowDataBlob ? '或明确的 data:/blob: URL' : ''}:${value}`);
11549
11565
  }
11550
11566
  }
11567
+ function validateRelativeUrl(value, label, allowDataBlob) {
11568
+ validateResourceUrl(value, label, allowDataBlob, false);
11569
+ }
11570
+ function isOfficialResourceUrl(value) {
11571
+ try {
11572
+ const url = new URL(value);
11573
+ if (url.protocol !== 'https:' || url.username || url.password)
11574
+ return false;
11575
+ const hostname = url.hostname.toLowerCase();
11576
+ if (hostname.endsWith('.'))
11577
+ return false;
11578
+ return OFFICIAL_RESOURCE_ROOT_DOMAINS.some(rootDomain => hostname === rootDomain || hostname.endsWith(`.${rootDomain}`));
11579
+ }
11580
+ catch {
11581
+ return false;
11582
+ }
11583
+ }
11551
11584
  function isRelativeUrl(value, normalized) {
11552
11585
  const browserNormalized = normalized.replace(/[\t\n\r]/g, '');
11553
11586
  if (browserNormalized.includes('\\') || browserNormalized.startsWith('//'))
@@ -11584,19 +11617,9 @@ function walk(node, visit) {
11584
11617
  walk(node.content, visit);
11585
11618
  }
11586
11619
 
11587
- const HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV = 'HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN';
11588
- const HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV = 'HB_SDK_RUNTIME_PERMISSION_CONTEXT';
11589
- const VERIFIED_RUNTIME_PERMISSION_CONTEXT = 'verified';
11590
- new node_async_hooks.AsyncLocalStorage();
11591
- Promise.resolve();
11592
- function shouldSkipMiniappPlatformCspFromEnv(env = process.env) {
11593
- return env[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV] === VERIFIED_RUNTIME_PERMISSION_CONTEXT && env[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV] === '1';
11594
- }
11595
-
11596
11620
  const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
11597
11621
  const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
11598
11622
  function miniappManifest() {
11599
- const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
11600
11623
  let root = process.cwd();
11601
11624
  let outDir = 'dist';
11602
11625
  let command = 'build';
@@ -11628,7 +11651,6 @@ function miniappManifest() {
11628
11651
  transformIndexHtml(html) {
11629
11652
  return enforceMiniappHtmlPolicy(html, {
11630
11653
  ...(command === 'serve' ? { hmrWebSocketUrl } : {}),
11631
- skipPlatformCsp,
11632
11654
  });
11633
11655
  },
11634
11656
  async closeBundle() {
@@ -11651,7 +11673,7 @@ function miniappManifest() {
11651
11673
  if (!htmlFiles.includes(indexHtmlPath)) {
11652
11674
  throw new Error('构建产物必须包含入口 dist/index.html');
11653
11675
  }
11654
- node_fs.writeFileSync(indexHtmlPath, enforceMiniappHtmlPolicy(node_fs.readFileSync(indexHtmlPath, 'utf8'), { skipPlatformCsp }));
11676
+ node_fs.writeFileSync(indexHtmlPath, enforceMiniappHtmlPolicy(node_fs.readFileSync(indexHtmlPath, 'utf8')));
11655
11677
  const manifestPath = path.join(outputRoot, 'manifest.json');
11656
11678
  node_fs.mkdirSync(path.dirname(manifestPath), { recursive: true });
11657
11679
  node_fs.writeFileSync(manifestPath, renderMiniappManifest({ version, sdkVersion }));
@@ -11693,7 +11715,7 @@ function resolveHmrWebSocketUrl(resolved) {
11693
11715
  if (hmr === false)
11694
11716
  return undefined;
11695
11717
  const options = typeof hmr === 'object' ? hmr : {};
11696
- const protocol = options.protocol ?? 'ws';
11718
+ const protocol = options.protocol ?? (resolved.server.https ? 'wss' : 'ws');
11697
11719
  const configuredHost = options.host ?? resolved.server.host;
11698
11720
  const host = typeof configuredHost === 'string' && configuredHost !== '0.0.0.0' ? configuredHost : '127.0.0.1';
11699
11721
  const port = options.clientPort ?? options.port ?? resolved.server.port;
package/dist/vite.esm.js CHANGED
@@ -1,11 +1,10 @@
1
1
  import { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { AsyncLocalStorage } from 'node:async_hooks';
4
3
 
5
4
  /** 构建时替换为当前发布包的实际版本。 */
6
5
  const HB_SDK_VERSION = typeof undefined === 'string'
7
6
  ? undefined
8
- : '0.6.7-alpha.0';
7
+ : '0.6.7-alpha.2';
9
8
 
10
9
  var re = {exports: {}};
11
10
 
@@ -11430,13 +11429,17 @@ function walk$1(node, visit) {
11430
11429
 
11431
11430
  const FORBIDDEN_RESOURCE_HINTS = new Set(['dns-prefetch', 'preconnect', 'prerender']);
11432
11431
  const RELATIVE_URL_BASE = new URL('https://heybox-relative.invalid/');
11432
+ const OFFICIAL_RESOURCE_ROOT_DOMAINS = ['xiaoheihe.cn', 'max-c.com', 'debugmode.cn', 'maxjia.com'];
11433
+ const OFFICIAL_RESOURCE_SOURCES = OFFICIAL_RESOURCE_ROOT_DOMAINS.flatMap(rootDomain => [
11434
+ `https://${rootDomain}`,
11435
+ `https://*.${rootDomain}`,
11436
+ ]);
11433
11437
  const URL_ATTRIBUTES = {
11434
11438
  audio: ['src'],
11435
11439
  embed: ['src'],
11436
11440
  iframe: ['src'],
11437
11441
  img: ['src'],
11438
11442
  input: ['src'],
11439
- link: ['href'],
11440
11443
  object: ['data'],
11441
11444
  script: ['src'],
11442
11445
  source: ['src', 'srcset'],
@@ -11446,11 +11449,11 @@ const URL_ATTRIBUTES = {
11446
11449
  const PRODUCTION_CSP = [
11447
11450
  "default-src 'none'",
11448
11451
  "connect-src 'none'",
11449
- "script-src 'self' 'unsafe-inline'",
11450
- "style-src 'self' 'unsafe-inline'",
11451
- "img-src 'self' data: blob:",
11452
- "font-src 'self' data:",
11453
- "media-src 'self' blob:",
11452
+ `script-src 'self' 'unsafe-inline' ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11453
+ `style-src 'self' 'unsafe-inline' ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11454
+ `img-src 'self' data: blob: ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11455
+ `font-src 'self' data: ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11456
+ `media-src 'self' blob: ${OFFICIAL_RESOURCE_SOURCES.join(' ')}`,
11454
11457
  "manifest-src 'self'",
11455
11458
  "frame-src 'none'",
11456
11459
  "child-src 'none'",
@@ -11469,9 +11472,6 @@ function enforceMiniappHtmlPolicy(html, options = {}) {
11469
11472
  removePlatformCspMarkers(document);
11470
11473
  walk(document, element => validateElement(element));
11471
11474
  injectMiniappRuntimeGate(document);
11472
- if (options.skipPlatformCsp) {
11473
- return serialize(document);
11474
- }
11475
11475
  const content = options.hmrWebSocketUrl
11476
11476
  ? PRODUCTION_CSP.replace("connect-src 'none'", `connect-src ${options.hmrWebSocketUrl}`)
11477
11477
  : PRODUCTION_CSP;
@@ -11508,19 +11508,25 @@ function validateElement(element) {
11508
11508
  if (forbidden) {
11509
11509
  throw new Error(`index.html 不允许 rel="${forbidden}"`);
11510
11510
  }
11511
- if (rels.includes('preload') || rels.includes('modulepreload')) {
11511
+ if (rels.includes('stylesheet')) {
11512
+ validateResourceUrl(attrs.get('href'), '<link rel="stylesheet" href>', false, true);
11513
+ }
11514
+ else if (rels.includes('preload') || rels.includes('modulepreload')) {
11512
11515
  validateRelativeUrl(attrs.get('href'), `<link rel="${rels.includes('modulepreload') ? 'modulepreload' : 'preload'}" href>`, false);
11513
11516
  }
11517
+ else {
11518
+ validateRelativeUrl(attrs.get('href'), '<link href>', false);
11519
+ }
11514
11520
  }
11515
11521
  for (const attribute of URL_ATTRIBUTES[tagName] ?? []) {
11516
11522
  const value = attrs.get(attribute);
11517
11523
  if (attribute === 'srcset') {
11518
11524
  for (const candidate of (value ?? '').split(',').map(item => item.trim().split(/\s+/)[0]).filter(Boolean)) {
11519
- validateRelativeUrl(candidate, `<${tagName} ${attribute}>`, true);
11525
+ validateResourceUrl(candidate, `<${tagName} ${attribute}>`, true, supportsOfficialResourceUrl(tagName, attribute));
11520
11526
  }
11521
11527
  }
11522
11528
  else {
11523
- validateRelativeUrl(value, `<${tagName} ${attribute}>`, true);
11529
+ validateResourceUrl(value, `<${tagName} ${attribute}>`, true, supportsOfficialResourceUrl(tagName, attribute));
11524
11530
  }
11525
11531
  }
11526
11532
  }
@@ -11535,16 +11541,43 @@ function removePlatformCspMarkers(node) {
11535
11541
  for (const child of node.childNodes)
11536
11542
  removePlatformCspMarkers(child);
11537
11543
  }
11538
- function validateRelativeUrl(value, label, allowDataBlob) {
11544
+ function supportsOfficialResourceUrl(tagName, attribute) {
11545
+ return ((tagName === 'script' && attribute === 'src') ||
11546
+ (tagName === 'img' && attribute === 'src') ||
11547
+ (tagName === 'source' && (attribute === 'src' || attribute === 'srcset')) ||
11548
+ (tagName === 'audio' && attribute === 'src') ||
11549
+ (tagName === 'track' && attribute === 'src') ||
11550
+ (tagName === 'video' && (attribute === 'src' || attribute === 'poster')));
11551
+ }
11552
+ function validateResourceUrl(value, label, allowDataBlob, allowOfficialResourceUrl) {
11539
11553
  if (!value || value.startsWith('#'))
11540
11554
  return;
11541
11555
  const normalized = value.trim().toLowerCase();
11542
11556
  if (allowDataBlob && (normalized.startsWith('data:') || normalized.startsWith('blob:')))
11543
11557
  return;
11558
+ if (allowOfficialResourceUrl && isOfficialResourceUrl(value))
11559
+ return;
11544
11560
  if (!isRelativeUrl(value, normalized)) {
11545
11561
  throw new Error(`${label} 必须使用相对路径${allowDataBlob ? '或明确的 data:/blob: URL' : ''}:${value}`);
11546
11562
  }
11547
11563
  }
11564
+ function validateRelativeUrl(value, label, allowDataBlob) {
11565
+ validateResourceUrl(value, label, allowDataBlob, false);
11566
+ }
11567
+ function isOfficialResourceUrl(value) {
11568
+ try {
11569
+ const url = new URL(value);
11570
+ if (url.protocol !== 'https:' || url.username || url.password)
11571
+ return false;
11572
+ const hostname = url.hostname.toLowerCase();
11573
+ if (hostname.endsWith('.'))
11574
+ return false;
11575
+ return OFFICIAL_RESOURCE_ROOT_DOMAINS.some(rootDomain => hostname === rootDomain || hostname.endsWith(`.${rootDomain}`));
11576
+ }
11577
+ catch {
11578
+ return false;
11579
+ }
11580
+ }
11548
11581
  function isRelativeUrl(value, normalized) {
11549
11582
  const browserNormalized = normalized.replace(/[\t\n\r]/g, '');
11550
11583
  if (browserNormalized.includes('\\') || browserNormalized.startsWith('//'))
@@ -11581,19 +11614,9 @@ function walk(node, visit) {
11581
11614
  walk(node.content, visit);
11582
11615
  }
11583
11616
 
11584
- const HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV = 'HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN';
11585
- const HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV = 'HB_SDK_RUNTIME_PERMISSION_CONTEXT';
11586
- const VERIFIED_RUNTIME_PERMISSION_CONTEXT = 'verified';
11587
- new AsyncLocalStorage();
11588
- Promise.resolve();
11589
- function shouldSkipMiniappPlatformCspFromEnv(env = process.env) {
11590
- return env[HB_SDK_RUNTIME_PERMISSION_CONTEXT_ENV] === VERIFIED_RUNTIME_PERMISSION_CONTEXT && env[HB_SDK_RUNTIME_USE_OFFICIAL_DOMAIN_ENV] === '1';
11591
- }
11592
-
11593
11617
  const sdkVersionPlaceholder = ['__HB_SDK', 'VERSION__'].join('_');
11594
11618
  const sdkVersionBuildConstant = ['__HB_SDK_BUILD', 'VERSION__'].join('_');
11595
11619
  function miniappManifest() {
11596
- const skipPlatformCsp = shouldSkipMiniappPlatformCspFromEnv();
11597
11620
  let root = process.cwd();
11598
11621
  let outDir = 'dist';
11599
11622
  let command = 'build';
@@ -11625,7 +11648,6 @@ function miniappManifest() {
11625
11648
  transformIndexHtml(html) {
11626
11649
  return enforceMiniappHtmlPolicy(html, {
11627
11650
  ...(command === 'serve' ? { hmrWebSocketUrl } : {}),
11628
- skipPlatformCsp,
11629
11651
  });
11630
11652
  },
11631
11653
  async closeBundle() {
@@ -11648,7 +11670,7 @@ function miniappManifest() {
11648
11670
  if (!htmlFiles.includes(indexHtmlPath)) {
11649
11671
  throw new Error('构建产物必须包含入口 dist/index.html');
11650
11672
  }
11651
- writeFileSync(indexHtmlPath, enforceMiniappHtmlPolicy(readFileSync(indexHtmlPath, 'utf8'), { skipPlatformCsp }));
11673
+ writeFileSync(indexHtmlPath, enforceMiniappHtmlPolicy(readFileSync(indexHtmlPath, 'utf8')));
11652
11674
  const manifestPath = path.join(outputRoot, 'manifest.json');
11653
11675
  mkdirSync(path.dirname(manifestPath), { recursive: true });
11654
11676
  writeFileSync(manifestPath, renderMiniappManifest({ version, sdkVersion }));
@@ -11690,7 +11712,7 @@ function resolveHmrWebSocketUrl(resolved) {
11690
11712
  if (hmr === false)
11691
11713
  return undefined;
11692
11714
  const options = typeof hmr === 'object' ? hmr : {};
11693
- const protocol = options.protocol ?? 'ws';
11715
+ const protocol = options.protocol ?? (resolved.server.https ? 'wss' : 'ws');
11694
11716
  const configuredHost = options.host ?? resolved.server.host;
11695
11717
  const host = typeof configuredHost === 'string' && configuredHost !== '0.0.0.0' ? configuredHost : '127.0.0.1';
11696
11718
  const port = options.clientPort ?? options.port ?? resolved.server.port;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heybox/hb-sdk",
3
- "version": "0.6.7-alpha.0",
3
+ "version": "0.6.7-alpha.2",
4
4
  "sideEffects": [
5
5
  "./src/index.ts",
6
6
  "./src/core/singleton.ts",