@ttmg/cli 0.3.4 → 0.3.5-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -38,6 +38,7 @@ var FormData$1 = require('form-data');
38
38
  var ttmgPack = require('ttmg-pack');
39
39
  var expressStaticGzip = require('express-static-gzip');
40
40
  var fileUpload = require('express-fileupload');
41
+ var async_hooks = require('async_hooks');
41
42
  var fs$1 = require('node:fs');
42
43
  var path$1 = require('node:path');
43
44
  var zlib = require('zlib');
@@ -6532,7 +6533,7 @@ function getAxiosProxyConfig() {
6532
6533
  }
6533
6534
  return {};
6534
6535
  }
6535
- async function request({ url, method, data, headers, params, }) {
6536
+ async function request$1({ url, method, data, headers, params, }) {
6536
6537
  const config = getTTMGRC();
6537
6538
  const cookie = config?.cookie;
6538
6539
  const proxyConfig = getAxiosProxyConfig();
@@ -6572,7 +6573,20 @@ async function request({ url, method, data, headers, params, }) {
6572
6573
  };
6573
6574
  }
6574
6575
  }
6575
- async function download(url, filePath) {
6576
+ /** wasm/archive 大文件下载超时(毫秒),防止无限卡住 */
6577
+ const DOWNLOAD_TIMEOUT_MS = 10 * 60 * 1000; // 10 分钟
6578
+ function formatBytes(n) {
6579
+ if (n >= 1024 * 1024)
6580
+ return `${(n / 1024 / 1024).toFixed(2)}MB`;
6581
+ if (n >= 1024)
6582
+ return `${(n / 1024).toFixed(2)}KB`;
6583
+ return `${n}B`;
6584
+ }
6585
+ async function download$1(url, filePath, headers) {
6586
+ const t0 = Date.now();
6587
+ const baseName = filePath.split(/[/\\]/).pop() ?? filePath;
6588
+ const urlShort = url.length > 120 ? url.slice(0, 100) + '...' + url.slice(-20) : url;
6589
+ console.log(`[download] start out=${baseName} url=${urlShort}`);
6576
6590
  // 清理旧文件
6577
6591
  if (fs.existsSync(filePath)) {
6578
6592
  try {
@@ -6584,40 +6598,88 @@ async function download(url, filePath) {
6584
6598
  try {
6585
6599
  const res = await axios.get(url, {
6586
6600
  responseType: 'stream',
6587
- // 让非 2xx 进入 catch
6588
6601
  validateStatus: s => s >= 200 && s < 300,
6602
+ headers: headers || {},
6603
+ timeout: DOWNLOAD_TIMEOUT_MS,
6589
6604
  ...proxyConfig,
6590
6605
  });
6591
- // 关键:把“流事件”封装为 Promise,并 await 它
6592
- await new Promise((resolve, reject) => {
6606
+ const contentLength = res.headers['content-length'];
6607
+ const expectedSize = contentLength ? parseInt(contentLength, 10) : null;
6608
+ console.log(`[download] response ok out=${baseName} content-length=${expectedSize != null ? formatBytes(expectedSize) : 'unknown'} elapsed=${Date.now() - t0}ms`);
6609
+ // 流写入 + 超时保护,防止 pipe 卡死
6610
+ const pipePromise = new Promise((resolve, reject) => {
6593
6611
  const writer = fs.createWriteStream(filePath);
6612
+ let settled = false;
6613
+ let receivedBytes = 0;
6614
+ let lastLogBytes = 0;
6615
+ const LOG_INTERVAL = 1024 * 1024; // 每 1MB 打一次日志
6616
+ const settle = (fn) => {
6617
+ if (settled)
6618
+ return;
6619
+ settled = true;
6620
+ fn();
6621
+ };
6594
6622
  const onError = (e) => {
6595
6623
  cleanup();
6596
- try {
6597
- if (fs.existsSync(filePath))
6598
- fs.unlinkSync(filePath);
6599
- }
6600
- catch { }
6601
- reject(e);
6624
+ console.error(`[download] stream error out=${baseName} received=${formatBytes(receivedBytes)} err=`, e);
6625
+ settle(() => {
6626
+ try {
6627
+ if (fs.existsSync(filePath))
6628
+ fs.unlinkSync(filePath);
6629
+ }
6630
+ catch { }
6631
+ reject(e);
6632
+ });
6602
6633
  };
6603
6634
  const onClose = () => {
6604
6635
  cleanup();
6605
- resolve();
6636
+ const elapsed = Date.now() - t0;
6637
+ const speed = elapsed > 0 ? (receivedBytes / 1024 / elapsed).toFixed(1) : '?';
6638
+ console.log(`[download] pipe done out=${baseName} size=${formatBytes(receivedBytes)} elapsed=${elapsed}ms speed=${speed}KB/s`);
6639
+ settle(resolve);
6606
6640
  };
6607
6641
  const cleanup = () => {
6642
+ res.data.off('data', onData);
6608
6643
  writer.off('error', onError);
6609
6644
  writer.off('close', onClose);
6610
6645
  res.data.off('error', onError);
6646
+ if (timeoutId)
6647
+ clearTimeout(timeoutId);
6611
6648
  };
6649
+ const onData = (chunk) => {
6650
+ receivedBytes += Buffer.isBuffer(chunk) ? chunk.length : Buffer.byteLength(chunk);
6651
+ if (receivedBytes - lastLogBytes >= LOG_INTERVAL) {
6652
+ lastLogBytes = receivedBytes;
6653
+ const elapsed = Date.now() - t0;
6654
+ const speed = elapsed > 0 ? (receivedBytes / 1024 / elapsed).toFixed(1) : '?';
6655
+ console.log(`[download] progress out=${baseName} received=${formatBytes(receivedBytes)} elapsed=${elapsed}ms speed=${speed}KB/s`);
6656
+ }
6657
+ };
6658
+ const timeoutId = setTimeout(() => {
6659
+ cleanup();
6660
+ console.error(`[download] timeout out=${baseName} received=${formatBytes(receivedBytes)} expected=${expectedSize != null ? formatBytes(expectedSize) : 'unknown'}`);
6661
+ settle(() => {
6662
+ try {
6663
+ if (fs.existsSync(filePath))
6664
+ fs.unlinkSync(filePath);
6665
+ }
6666
+ catch { }
6667
+ res.data?.destroy();
6668
+ reject(new Error(`下载超时(${DOWNLOAD_TIMEOUT_MS / 60000} 分钟),请检查网络后重试`));
6669
+ });
6670
+ }, DOWNLOAD_TIMEOUT_MS);
6671
+ res.data.on('data', onData);
6612
6672
  res.data.on('error', onError);
6613
6673
  writer.on('error', onError);
6614
6674
  writer.on('close', onClose);
6615
6675
  res.data.pipe(writer);
6616
6676
  });
6617
- // 成功
6677
+ await pipePromise;
6618
6678
  return { ok: true };
6619
6679
  }
6620
6680
  catch (err) {
6681
+ const elapsed = Date.now() - t0;
6682
+ console.error(`[download] failed out=${baseName} elapsed=${elapsed}ms err=`, err);
6621
6683
  // 403 等受控处理
6622
6684
  if (isAxiosError(err) && err.response?.status === 403) {
6623
6685
  // 不抛出,让上层自行决定
@@ -6633,7 +6695,7 @@ function isAxiosError(e) {
6633
6695
 
6634
6696
  async function fetchGameInfo(clientKey) {
6635
6697
  // 访问 V4 接口
6636
- const response = await request({
6698
+ const response = await request$1({
6637
6699
  url: `https://developers.tiktok.com/tiktok/v4/devportal/mini_game/devtool/info`,
6638
6700
  method: 'GET',
6639
6701
  params: {
@@ -6690,7 +6752,7 @@ async function uploadGameToPlatform({ data, name, clientKey, note = '--', appId,
6690
6752
  formData.append('file', new Blob([data], { type: 'application/zip' }), sanitized);
6691
6753
  formData.append('client_key', clientKey);
6692
6754
  formData.append('note', note);
6693
- const response = await request({
6755
+ const response = await request$1({
6694
6756
  method: 'POST',
6695
6757
  url: 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/devtool/upload',
6696
6758
  data: formData,
@@ -8680,8 +8742,34 @@ async function listen(app, options) {
8680
8742
  return { server, port, url, version };
8681
8743
  }
8682
8744
 
8745
+ const asyncLocalStorage = new async_hooks.AsyncLocalStorage();
8746
+ /** 在请求作用域内运行,供 middleware 调用 */
8747
+ function runWithRequest(req, fn) {
8748
+ return asyncLocalStorage.run({ req }, fn);
8749
+ }
8750
+ /** Express 中间件:将 req 注入 AsyncLocalStorage,供 api.request/download 内部读取 ppeHeaders */
8751
+ function requestContextMiddleware(req, res, next) {
8752
+ runWithRequest(req, () => next());
8753
+ }
8754
+ /** 从当前请求中提取 x-use-ppe、x-tt-env,仅当浏览器有传时才返回 */
8755
+ function getPpeHeaders() {
8756
+ const store = asyncLocalStorage.getStore();
8757
+ if (!store?.req?.headers)
8758
+ return {};
8759
+ const h = store.req.headers;
8760
+ const result = {};
8761
+ const v1 = h['x-use-ppe'];
8762
+ const v2 = h['x-tt-env'];
8763
+ if (typeof v1 === 'string')
8764
+ result['x-use-ppe'] = v1;
8765
+ if (typeof v2 === 'string')
8766
+ result['x-tt-env'] = v2;
8767
+ return result;
8768
+ }
8769
+
8683
8770
  function setupMiddlewares(app, options) {
8684
8771
  const { publicPath, outputDir } = options;
8772
+ app.use(requestContextMiddleware);
8685
8773
  app.use(fileUpload());
8686
8774
  app.use(expressStaticGzip(publicPath, {
8687
8775
  enableBrotli: true,
@@ -8883,6 +8971,8 @@ const UNITY_WASM_SPLIT_CONFIG_FIELD_SCHEME = {
8883
8971
  IOS_CODE_FILE_MD5: `$IOS_CODE_FILE_MD5`,
8884
8972
  ANDROID_CODE_FILE_MD5: `$ANDROID_CODE_FILE_MD5`,
8885
8973
  ANDROID_SUB_CODE_FILE_MD5: `$SUB_CODE_FILE_MD5`,
8974
+ ARCHIVE_CODE_FILE_MD5: `$ARCHIVE_CODE_FILE_MD5`,
8975
+ enableArchiveMode: `"$ENABLEARCHIVEMODE"`,
8886
8976
  WASMSPLITVERSION: `"$WASMSPLITVERSION"`,
8887
8977
  ENABLEWASMSPLIT: `"$ENABLEWASMSPLIT"`,
8888
8978
  IOS_SUB_JS_FILE_CONFIG: `"$IOS_SUB_JS_FILE_CONFIG"`,
@@ -8902,9 +8992,11 @@ const WASM_SPLIT_SUBPACKAGE_CONFIG = {
8902
8992
  name: 'wasmcode1-android',
8903
8993
  root: 'wasmcode1-android/',
8904
8994
  },
8905
- ios: {
8906
- root: 'wasmcode-ios/',
8995
+ archive: {
8996
+ name: 'wasmcode-archive',
8997
+ root: 'wasmcode-archive/',
8907
8998
  },
8999
+ /** iOS 与 archive 互斥:有 archive 时 iOS URL 为空,反之亦然 */
8908
9000
  iosMain: {
8909
9001
  name: 'wasmcode-ios',
8910
9002
  root: 'wasmcode-ios/',
@@ -8915,6 +9007,7 @@ const WASM_SPLIT_SUBPACKAGE_CONFIG = {
8915
9007
  },
8916
9008
  };
8917
9009
  const WASM_FILENAME_SUFFIX = '.webgl.wasm.code.unityweb.wasm';
9010
+ const WASM_ARCHIVE_FILENAME_SUFFIX = '.webgl.wasm.code.unityweb.bin';
8918
9011
  const BR_SUFFIX = '.br';
8919
9012
  // 输出 JSON 格式
8920
9013
  const JSON_INDENT = 2;
@@ -8924,6 +9017,21 @@ const CONCURRENCY_LIMIT = 2;
8924
9017
  const DOWNLOAD_RETRY = 3;
8925
9018
  const WASM_SPLIT_CONFIG_FILE_NAME = 'webgl-wasm-split.js';
8926
9019
 
9020
+ /**
9021
+ * Unity 模块统一的 HTTP 请求封装
9022
+ * DEV_HEADERS 与 ppeHeaders(从当前请求 AsyncLocalStorage 读取)的合并逻辑集中在此处
9023
+ */
9024
+ const request = (opts) => {
9025
+ const ppeHeaders = getPpeHeaders();
9026
+ const headers = { ...DEV_HEADERS, ...ppeHeaders, ...opts.headers };
9027
+ return request$1({ ...opts, headers });
9028
+ };
9029
+ const download = (url, filePath) => {
9030
+ const ppeHeaders = getPpeHeaders();
9031
+ const headers = ppeHeaders && Object.keys(ppeHeaders).length > 0 ? ppeHeaders : undefined;
9032
+ return download$1(url, filePath, headers);
9033
+ };
9034
+
8927
9035
  // prepare.ts
8928
9036
  // 若你的 request 是 axios:你可以添加 maxBodyLength/ maxContentLength 等参数
8929
9037
  // 若是 got:可直接传 form 实例
@@ -8974,16 +9082,9 @@ async function startPrepare(params) {
8974
9082
  return request({
8975
9083
  url: `${BASE_URL}/api/stark_wasm/v4/post/prepare`,
8976
9084
  method: 'POST',
8977
- headers: {
8978
- ...DEV_HEADERS,
8979
- ...formHeaders, // 包含正确的 multipart/form-data; boundary=...
8980
- },
8981
- params: {
8982
- client_key: params.client_key,
8983
- with_ios: true,
8984
- },
9085
+ headers: formHeaders,
9086
+ params: { client_key: params.client_key, with_ios: true },
8985
9087
  data: form,
8986
- // 若 request 基于 axios,建议加上以下两项以支持大文件:
8987
9088
  });
8988
9089
  }
8989
9090
 
@@ -9069,7 +9170,6 @@ async function downloadPrepared(data) {
9069
9170
  const res = await request({
9070
9171
  url: `${BASE_URL}/api/stark_wasm/v4/post/download_prepared`,
9071
9172
  method: 'POST',
9072
- headers: DEV_HEADERS,
9073
9173
  data,
9074
9174
  });
9075
9175
  wsServer.sendUnitySplitStatus({
@@ -9091,9 +9191,6 @@ async function downloadPrepared(data) {
9091
9191
  url: downloadUrl,
9092
9192
  });
9093
9193
  await download(downloadUrl, tempWasmPath);
9094
- /**
9095
- * 下载完成后需要进行 br 并替换 codePath 对应的文件后再返回成功
9096
- */
9097
9194
  fs$1.copyFileSync(tempWasmPath, willReplaceWasmPath);
9098
9195
  wsServer.sendUnitySplitStatus({
9099
9196
  status: 'download_prepared_wasm_done',
@@ -9170,11 +9267,7 @@ async function getCollectedFuncIds({ client_key, wasm_md5, }) {
9170
9267
  return request({
9171
9268
  url: `${BASE_URL}/api/stark_wasm/v4/get/collectedfuncids`,
9172
9269
  method: 'GET',
9173
- headers: DEV_HEADERS,
9174
- params: {
9175
- client_key,
9176
- wasm_md5,
9177
- },
9270
+ params: { client_key, wasm_md5 },
9178
9271
  });
9179
9272
  }
9180
9273
 
@@ -9182,11 +9275,7 @@ async function setCollect({ client_key, wasm_md5, }) {
9182
9275
  return request({
9183
9276
  url: `${BASE_URL}/api/stark_wasm/v4/post/set_collecting`,
9184
9277
  method: 'POST',
9185
- data: {
9186
- client_key,
9187
- wasm_md5,
9188
- },
9189
- headers: DEV_HEADERS,
9278
+ data: { client_key, wasm_md5 },
9190
9279
  });
9191
9280
  }
9192
9281
 
@@ -9194,26 +9283,15 @@ async function getCollecttingInfo({ client_key, wasm_md5, }) {
9194
9283
  return request({
9195
9284
  url: `${BASE_URL}/api/stark_wasm/v4/get/funccollect`,
9196
9285
  method: 'GET',
9197
- headers: DEV_HEADERS,
9198
- params: {
9199
- client_key,
9200
- wasm_md5,
9201
- },
9286
+ params: { client_key, wasm_md5 },
9202
9287
  });
9203
9288
  }
9204
9289
 
9205
- // /api/stark_wasm/v4/post/split
9206
9290
  async function startSplit({ client_key, wasm_md5, }) {
9207
9291
  return request({
9208
9292
  url: `${BASE_URL}/api/stark_wasm/v4/post/split`,
9209
9293
  method: 'POST',
9210
- headers: {
9211
- ...DEV_HEADERS,
9212
- },
9213
- data: {
9214
- client_key,
9215
- wasm_md5,
9216
- },
9294
+ data: { client_key, wasm_md5 },
9217
9295
  });
9218
9296
  }
9219
9297
 
@@ -9383,7 +9461,8 @@ function pLimit(concurrency) {
9383
9461
  return generator;
9384
9462
  }
9385
9463
 
9386
- function updateSubpackageConfigSync() {
9464
+ /** iOS 与 archive 互斥,二者只会存在其一 */
9465
+ function updateSubpackageConfigSync(options) {
9387
9466
  const gameJsonPath = path__namespace.join(process.cwd(), SUBPACKAGE_CONFIG_FILE_NAME);
9388
9467
  const raw = fs__namespace.readFileSync(gameJsonPath, 'utf-8');
9389
9468
  const gameJson = JSON.parse(raw);
@@ -9396,11 +9475,17 @@ function updateSubpackageConfigSync() {
9396
9475
  const filtered = subpackages.filter(s => s.name !== WASM_SPLIT_SUBPACKAGE_CONFIG.origin.name);
9397
9476
  /**
9398
9477
  * 基于 SUBPACKAGE_CONFIG_FILE_NAME 更新 subpackages
9478
+ * iOS 与 archive 互斥:有 archive 加 archive,否则有 iOS 加 iosMain+iosSub
9399
9479
  */
9400
9480
  filtered.push(WASM_SPLIT_SUBPACKAGE_CONFIG.androidMain);
9401
9481
  filtered.push(WASM_SPLIT_SUBPACKAGE_CONFIG.androidSub);
9402
- filtered.push(WASM_SPLIT_SUBPACKAGE_CONFIG.iosMain);
9403
- filtered.push(WASM_SPLIT_SUBPACKAGE_CONFIG.iosSub);
9482
+ if (options?.hasArchive) {
9483
+ filtered.push(WASM_SPLIT_SUBPACKAGE_CONFIG.archive);
9484
+ }
9485
+ else if (options?.hasIos) {
9486
+ filtered.push(WASM_SPLIT_SUBPACKAGE_CONFIG.iosMain);
9487
+ filtered.push(WASM_SPLIT_SUBPACKAGE_CONFIG.iosSub);
9488
+ }
9404
9489
  // 合并去重:存在则更新 root,不存在则新增
9405
9490
  const map = new Map(filtered.map(s => [s.name, s]));
9406
9491
  gameJson[fieldName] = Array.from(map.values());
@@ -9452,12 +9537,28 @@ async function downloadSplited(context) {
9452
9537
  ensureDirSync(splitTempDir);
9453
9538
  const mainAndroidDir = path.join(splitTempDir, WASM_SPLIT_SUBPACKAGE_CONFIG.androidMain.root);
9454
9539
  const subAndroidDir = path.join(splitTempDir, WASM_SPLIT_SUBPACKAGE_CONFIG.androidSub.root);
9455
- const mainIosDir = path.join(splitTempDir, WASM_SPLIT_SUBPACKAGE_CONFIG.iosMain.root);
9456
- const subIosDir = path.join(splitTempDir, WASM_SPLIT_SUBPACKAGE_CONFIG.iosSub.root);
9457
- [mainAndroidDir, subAndroidDir, mainIosDir, subIosDir].forEach(ensureDirSync);
9540
+ /** iOS archive 互斥:有 archive 时 iOS URL 为空,反之亦然 */
9541
+ const hasArchive = Boolean(context.archive_wasm_download_url);
9542
+ const hasIos = !hasArchive && Boolean(context.main_wasm_h5_download_url);
9543
+ const archiveDir = hasArchive
9544
+ ? path.join(splitTempDir, WASM_SPLIT_SUBPACKAGE_CONFIG.archive.root)
9545
+ : null;
9546
+ const mainIosDir = hasIos
9547
+ ? path.join(splitTempDir, WASM_SPLIT_SUBPACKAGE_CONFIG.iosMain.root)
9548
+ : null;
9549
+ const subIosDir = hasIos
9550
+ ? path.join(splitTempDir, WASM_SPLIT_SUBPACKAGE_CONFIG.iosSub.root)
9551
+ : null;
9552
+ const dirsToEnsure = [
9553
+ mainAndroidDir,
9554
+ subAndroidDir,
9555
+ ...(archiveDir ? [archiveDir] : []),
9556
+ ...(mainIosDir ? [mainIosDir] : []),
9557
+ ...(subIosDir ? [subIosDir] : []),
9558
+ ];
9559
+ dirsToEnsure.forEach(ensureDirSync);
9458
9560
  const mainAndroidWasmCodeTempPath = path.join(mainAndroidDir, `${context.main_wasm_md5}${WASM_FILENAME_SUFFIX}`);
9459
9561
  const subAndroidWasmCodeTempPath = path.join(subAndroidDir, `${context.sub_wasm_md5}${WASM_FILENAME_SUFFIX}`);
9460
- const mainIosWasmCodeTempPath = path.join(mainIosDir, `${context.main_wasm_h5_md5}${WASM_FILENAME_SUFFIX}`);
9461
9562
  const limit = pLimit(CONCURRENCY_LIMIT);
9462
9563
  try {
9463
9564
  console.log('downloadWasmSplit', context);
@@ -9468,7 +9569,6 @@ async function downloadSplited(context) {
9468
9569
  wsServer.sendUnitySplitStatus({
9469
9570
  status: 'start_download_android_sub_wasm_code',
9470
9571
  });
9471
- wsServer.sendUnitySplitStatus({ status: 'start_download_ios_main_wasm' });
9472
9572
  /**
9473
9573
  * 需要做个保护,只有 有 URL 时才下载
9474
9574
  */
@@ -9490,28 +9590,41 @@ async function downloadSplited(context) {
9490
9590
  url: context.sub_wasm_download_url,
9491
9591
  out: subAndroidWasmCodeTempPath,
9492
9592
  })),
9493
- limit(() => downloadAndCompress({
9494
- startDownloadStatus: 'start_download_ios_main_wasm',
9495
- downloadDoneStatus: 'download_ios_main_wasm_done',
9496
- startCompressStatus: 'start_compress_ios_main_wasm',
9497
- compressDoneStatus: 'compress_ios_main_wasm_done',
9498
- url: context.main_wasm_h5_download_url,
9499
- out: mainIosWasmCodeTempPath,
9500
- })),
9501
- // 下载 ios sub js range json
9502
- limit(() => downloadAndCompress({
9503
- startDownloadStatus: 'start_download_ios_range_json',
9504
- downloadDoneStatus: 'download_ios_range_json_done',
9505
- url: context.sub_js_range_download_url,
9506
- out: path.join(subIosDir, 'func_bytes_range.json'),
9507
- })),
9508
- // 下载 ios sub js data br
9509
- limit(() => downloadAndCompress({
9510
- startDownloadStatus: 'start_download_ios_js_data_br',
9511
- downloadDoneStatus: 'download_ios_js_data_br_done',
9512
- url: context.sub_js_data_download_url,
9513
- out: path.join(subIosDir, 'subjs.data'),
9514
- })),
9593
+ ...(hasArchive && archiveDir && context.wasm_archive_md5
9594
+ ? [
9595
+ limit(() => downloadAndCompress({
9596
+ startDownloadStatus: 'start_download_archive_wasm',
9597
+ downloadDoneStatus: 'download_archive_wasm_done',
9598
+ url: context.archive_wasm_download_url,
9599
+ out: path.join(archiveDir, `${context.wasm_archive_md5}${WASM_ARCHIVE_FILENAME_SUFFIX}`),
9600
+ })),
9601
+ ]
9602
+ : []),
9603
+ // iOS 子包(与 archive 互斥,仅当 hasIos 时下载)
9604
+ ...(hasIos && mainIosDir && subIosDir
9605
+ ? [
9606
+ limit(() => downloadAndCompress({
9607
+ startDownloadStatus: 'start_download_ios_main_wasm',
9608
+ downloadDoneStatus: 'download_ios_main_wasm_done',
9609
+ startCompressStatus: 'start_compress_ios_main_wasm',
9610
+ compressDoneStatus: 'compress_ios_main_wasm_done',
9611
+ url: context.main_wasm_h5_download_url,
9612
+ out: path.join(mainIosDir, `${context.main_wasm_h5_md5}${WASM_FILENAME_SUFFIX}`),
9613
+ })),
9614
+ limit(() => downloadAndCompress({
9615
+ startDownloadStatus: 'start_download_ios_range_json',
9616
+ downloadDoneStatus: 'download_ios_range_json_done',
9617
+ url: context.sub_js_range_download_url,
9618
+ out: path.join(subIosDir, 'func_bytes_range.json'),
9619
+ })),
9620
+ limit(() => downloadAndCompress({
9621
+ startDownloadStatus: 'start_download_ios_js_data_br',
9622
+ downloadDoneStatus: 'download_ios_js_data_br_done',
9623
+ url: context.sub_js_data_download_url,
9624
+ out: path.join(subIosDir, 'subjs.data'),
9625
+ })),
9626
+ ]
9627
+ : []),
9515
9628
  ]);
9516
9629
  // 复制 split/* 到项目根目录(递归、覆盖)——避免 EISDIR
9517
9630
  console.log('copy splitTempDir to root start');
@@ -9529,25 +9642,35 @@ async function downloadSplited(context) {
9529
9642
  console.log('copy splitTempDir to root end');
9530
9643
  // 更新分包配置(幂等)
9531
9644
  console.log('updateSubpackageConfigSync start');
9532
- updateSubpackageConfigSync();
9645
+ updateSubpackageConfigSync({
9646
+ hasArchive,
9647
+ hasIos,
9648
+ });
9533
9649
  console.log('updateSubpackageConfigSync end');
9534
9650
  // 更新 wasm split 配置(保持原始状态文案)
9535
9651
  console.log('updateWasmSplitConfig start');
9536
9652
  wsServer.sendUnitySplitStatus({ status: 'start_update_wasm_split_config' });
9537
- updateWasmSplitConfig({
9653
+ const wasmSplitConfigFields = {
9538
9654
  ENABLEWASMCOLLECT: true,
9539
9655
  ORIGINALWASMMD5: `${context.original_wasm_md5}`,
9540
9656
  WASMTABLESIZE: context.table_size,
9541
9657
  GLOBALVARLIST: JSON.stringify(context.global_var_list ?? []),
9542
- SUBJSURL: `${context.sub_js_download_url}`,
9543
- IOS_CODE_FILE_MD5: `${context.main_wasm_h5_md5}`,
9544
9658
  ANDROID_CODE_FILE_MD5: `${context.main_wasm_md5}`,
9545
9659
  ANDROID_SUB_CODE_FILE_MD5: `${context.sub_wasm_md5}`,
9546
9660
  WASMSPLITVERSION: `${context.version}`,
9547
- USINGWASMH5: Boolean(context.main_wasm_h5_md5),
9548
9661
  ENABLEWASMSPLIT: true,
9549
- // IOS_SUB_JS_FILE_CONFIG: JSON.stringify(context.merged_js ?? {}),
9550
- });
9662
+ };
9663
+ // iOS 与 archive 互斥:走 iOS 时写入 iOS 相关字段,走 archive 时写入 archive 相关字段
9664
+ if (hasIos) {
9665
+ wasmSplitConfigFields.IOS_CODE_FILE_MD5 = `${context.main_wasm_h5_md5}`;
9666
+ wasmSplitConfigFields.USINGWASMH5 = true;
9667
+ wasmSplitConfigFields.SUBJSURL = `${context.sub_js_download_url ?? ''}`;
9668
+ }
9669
+ if (hasArchive && context.wasm_archive_md5) {
9670
+ wasmSplitConfigFields.ARCHIVE_CODE_FILE_MD5 = `${context.wasm_archive_md5}`;
9671
+ wasmSplitConfigFields.enableArchiveMode = true;
9672
+ }
9673
+ updateWasmSplitConfig(wasmSplitConfigFields);
9551
9674
  wsServer.sendUnitySplitStatus({ status: 'update_wasm_split_config_done' });
9552
9675
  console.log('updateWasmSplitConfig end');
9553
9676
  return {
@@ -9590,7 +9713,6 @@ async function getSplitResult({ client_key, wasm_md5, wasm_path, }) {
9590
9713
  return request({
9591
9714
  url: `${BASE_URL}/api/stark_wasm/v4/post/download`,
9592
9715
  method: 'POST',
9593
- headers: { ...DEV_HEADERS },
9594
9716
  data: { client_key, wasm_md5, wasm_path },
9595
9717
  });
9596
9718
  }
@@ -9636,13 +9758,7 @@ async function resetWasmSplit(data) {
9636
9758
  const res = await request({
9637
9759
  url: `${BASE_URL}/api/stark_wasm/v4/post/reset`,
9638
9760
  method: 'POST',
9639
- headers: {
9640
- ...DEV_HEADERS,
9641
- },
9642
- data: {
9643
- client_key: data.clientkey,
9644
- wasm_md5: data.wasmMd5,
9645
- },
9761
+ data: { client_key: data.clientkey, wasm_md5: data.wasmMd5 },
9646
9762
  });
9647
9763
  /**
9648
9764
  * 把— __TTMG_TEMP__/wasmcode/ 目录下的所有文件恢复到原本的位置,进行重置
@@ -9694,9 +9810,17 @@ async function resetWasmSplit(data) {
9694
9810
  if (fs.existsSync(androidSubpackageSubDir)) {
9695
9811
  fs.rmSync(androidSubpackageSubDir, { recursive: true });
9696
9812
  }
9697
- const iosSubpackageDir = path.join(process.cwd(), WASM_SPLIT_SUBPACKAGE_CONFIG.ios.root);
9698
- if (fs.existsSync(iosSubpackageDir)) {
9699
- fs.rmSync(iosSubpackageDir, { recursive: true });
9813
+ const archiveSubpackageDir = path.join(process.cwd(), WASM_SPLIT_SUBPACKAGE_CONFIG.archive.root);
9814
+ if (fs.existsSync(archiveSubpackageDir)) {
9815
+ fs.rmSync(archiveSubpackageDir, { recursive: true });
9816
+ }
9817
+ const iosMainDir = path.join(process.cwd(), WASM_SPLIT_SUBPACKAGE_CONFIG.iosMain.root);
9818
+ if (fs.existsSync(iosMainDir)) {
9819
+ fs.rmSync(iosMainDir, { recursive: true });
9820
+ }
9821
+ const iosSubDir = path.join(process.cwd(), WASM_SPLIT_SUBPACKAGE_CONFIG.iosSub.root);
9822
+ if (fs.existsSync(iosSubDir)) {
9823
+ fs.rmSync(iosSubDir, { recursive: true });
9700
9824
  }
9701
9825
  return res;
9702
9826
  }
@@ -9737,7 +9861,6 @@ const getTaskStatus = (params) => {
9737
9861
  return request({
9738
9862
  url: `${BASE_URL}/api/stark_wasm/v4/get/status`,
9739
9863
  method: 'GET',
9740
- headers: DEV_HEADERS,
9741
9864
  params,
9742
9865
  });
9743
9866
  };
@@ -9746,7 +9869,6 @@ const getTaskInfo = async (params) => {
9746
9869
  return request({
9747
9870
  url: `${BASE_URL}/api/stark_wasm/v4/get/taskinfo`,
9748
9871
  method: 'GET',
9749
- headers: DEV_HEADERS,
9750
9872
  params,
9751
9873
  });
9752
9874
  };
@@ -9963,13 +10085,12 @@ const gameWasmSplitDownloadResultRoute = {
9963
10085
  }
9964
10086
  else {
9965
10087
  const splitResult = (response.data?.result || {});
10088
+ // iOS 与 archive 互斥:有 archive 时校验 archive 字段,否则校验 iOS 字段
10089
+ const hasArchive = typeof splitResult.archive_wasm_download_url === 'string' && String(splitResult.archive_wasm_download_url).trim() !== '';
9966
10090
  const requiredDownloadFields = [
9967
10091
  'main_wasm_download_url',
9968
- 'main_wasm_h5_download_url',
9969
- // 'sub_wasm_download_url',
9970
- // 'sub_js_download_url',
9971
- // 'sub_js_data_download_url',
9972
- // 'sub_js_range_download_url',
10092
+ 'sub_wasm_download_url',
10093
+ ...(hasArchive ? ['archive_wasm_download_url'] : ['main_wasm_h5_download_url']),
9973
10094
  ];
9974
10095
  const missingFields = requiredDownloadFields.filter(field => {
9975
10096
  const value = splitResult[field];
@@ -10643,7 +10764,7 @@ async function upload({ clientKey, note = '--', dir, }) {
10643
10764
  }
10644
10765
  }
10645
10766
 
10646
- var version = "0.3.4";
10767
+ var version = "0.3.5-beta.1";
10647
10768
  var pkg = {
10648
10769
  version: version};
10649
10770