@ttmg/cli 0.4.0 → 0.4.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/CHANGELOG.md +21 -0
- package/dist/index.js +544 -39
- package/dist/index.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/public/assets/{Card-BMss5cuV.js → Card-ASGGlJ8S.js} +1 -1
- package/dist/public/assets/Detail-DY__Zw0r.js +1 -0
- package/dist/public/assets/Detail-DY__Zw0r.js.br +0 -0
- package/dist/public/assets/MonetizationMode-sHN_4ybQ.js +1 -0
- package/dist/public/assets/MonetizationModeSummary-DLvyriLM.js +1 -0
- package/dist/public/assets/{SectionHeader-3b39hv3n.js → SectionHeader-B7dueBCf.js} +1 -1
- package/dist/public/assets/{Tag-B5gYnANu.js → Tag-BsX0d6UL.js} +1 -1
- package/dist/public/assets/{arrow-left-B3swWB64.js → arrow-left-uixm7Sd1.js} +1 -1
- package/dist/public/assets/{baseForm-Dsi8Ia0k.js → baseForm-C8XfTFyG.js} +1 -1
- package/dist/public/assets/baseForm-C8XfTFyG.js.br +0 -0
- package/dist/public/assets/boxes-C7RoIbId.js +1 -0
- package/dist/public/assets/{chevron-right-WZeuFw11.js → chevron-right-B1WBZrrD.js} +1 -1
- package/dist/public/assets/circle-check-wUxXeINd.js +1 -0
- package/dist/public/assets/{compass-CCsIev11.js → compass-VJNhHwqW.js} +1 -1
- package/dist/public/assets/index-BBR3CPkN.js +1 -0
- package/dist/public/assets/index-BBR3CPkN.js.br +0 -0
- package/dist/public/assets/{index-uquhwGkB.js → index-BH099q_U.js} +1 -1
- package/dist/public/assets/{index-ROKxx4f7.css → index-BJZ4gOGZ.css} +1 -1
- package/dist/public/assets/index-BJZ4gOGZ.css.br +0 -0
- package/dist/public/assets/{index-D0xEiy7C.js → index-BNc8JtET.js} +1 -1
- package/dist/public/assets/{index-JNUqDBWt.js → index-BO5MBsdm.js} +1 -1
- package/dist/public/assets/index-Bd-f8ERX.js +1 -0
- package/dist/public/assets/{index-DeL2bgxo.js → index-C1C34gtn.js} +1 -1
- package/dist/public/assets/index-C7Tmy9g3.js +1 -0
- package/dist/public/assets/index-CCKb9Y-j.js +14 -0
- package/dist/public/assets/index-CCKb9Y-j.js.br +0 -0
- package/dist/public/assets/index-CKhfKyA3.js +1 -0
- package/dist/public/assets/index-CKhfKyA3.js.br +0 -0
- package/dist/public/assets/{index-C9Un1hFP.js → index-C_npSvtQ.js} +1 -1
- package/dist/public/assets/{index-BOl1-Siv.css → index-DHXlsTU6.css} +1 -1
- package/dist/public/assets/index-DHXlsTU6.css.br +0 -0
- package/dist/public/assets/index-DMsjcIN8.js +1 -0
- package/dist/public/assets/index-DNd8ZTok.js +1 -0
- package/dist/public/assets/index-DNd8ZTok.js.br +0 -0
- package/dist/public/assets/{index-DjY3Igd6.js → index-DbxY65Om.js} +1 -1
- package/dist/public/assets/index-Dn5HHf-0.js +1 -0
- package/dist/public/assets/index-DpkktYot.css +1 -0
- package/dist/public/assets/index-DtoDrber.js +1 -0
- package/dist/public/assets/index-DtoDrber.js.br +0 -0
- package/dist/public/assets/index-Dx5VGEQm.css +1 -0
- package/dist/public/assets/{index-Cigxnpav.js → index-ZHPtAdJM.js} +1 -1
- package/dist/public/assets/index-ZHPtAdJM.js.br +0 -0
- package/dist/public/assets/{index-C9HXjiVu.js → index-ZtlCPCV9.js} +1 -1
- package/dist/public/assets/{index-B-AeuNlL.css → index-_6n0s04V.css} +1 -1
- package/dist/public/assets/index-_6n0s04V.css.br +0 -0
- package/dist/public/assets/{index-C2NWNiPX.js → index-a-rMUS2V.js} +1 -1
- package/dist/public/assets/index-bQUQ2Wmz.js +1 -0
- package/dist/public/assets/{index-HYSh4-Ri.js → index-qqnHwFjO.js} +1 -1
- package/dist/public/assets/{index-aqIfJUqW.js → index-r3aSeNL1.js} +1 -1
- package/dist/public/assets/{index-D1oGAPRa.js → index-s_30KWiA.js} +1 -1
- package/dist/public/assets/index-t4JeqZ4a.js +1 -0
- package/dist/public/assets/layers-Biv61qtE.js +1 -0
- package/dist/public/assets/{sparkles-D2QPhLAu.js → sparkles-Bv2GxrFr.js} +1 -1
- package/dist/public/assets/{zap-Cv4X8yRx.js → zap-CjkC_qWZ.js} +1 -1
- package/dist/public/index.html +2 -2
- package/package.json +1 -1
- package/dist/public/assets/Detail-Lyas-t6F.js +0 -1
- package/dist/public/assets/Detail-Lyas-t6F.js.br +0 -0
- package/dist/public/assets/MonetizationMode-DI4fy3U7.js +0 -1
- package/dist/public/assets/MonetizationModeSummary-B6yEB26H.js +0 -1
- package/dist/public/assets/baseForm-Dsi8Ia0k.js.br +0 -0
- package/dist/public/assets/index-B-AeuNlL.css.br +0 -0
- package/dist/public/assets/index-B3-gsm5-.js +0 -1
- package/dist/public/assets/index-B3L1GnMP.js +0 -1
- package/dist/public/assets/index-B3L1GnMP.js.br +0 -0
- package/dist/public/assets/index-B6NHbQwP.js +0 -1
- package/dist/public/assets/index-BJV8-tz8.js +0 -1
- package/dist/public/assets/index-BOl1-Siv.css.br +0 -0
- package/dist/public/assets/index-BqSAtEgq.js +0 -1
- package/dist/public/assets/index-CV2u_S0E.js +0 -1
- package/dist/public/assets/index-CV2u_S0E.js.br +0 -0
- package/dist/public/assets/index-Ci-lqt1V.js +0 -1
- package/dist/public/assets/index-Ci-lqt1V.js.br +0 -0
- package/dist/public/assets/index-Cigxnpav.js.br +0 -0
- package/dist/public/assets/index-DpWpqPKC.js +0 -1
- package/dist/public/assets/index-DpWpqPKC.js.br +0 -0
- package/dist/public/assets/index-Dvg_oNs7.css +0 -1
- package/dist/public/assets/index-DxISN0Xc.js +0 -14
- package/dist/public/assets/index-DxISN0Xc.js.br +0 -0
- package/dist/public/assets/index-ROKxx4f7.css.br +0 -0
package/dist/index.js
CHANGED
|
@@ -6924,10 +6924,8 @@ function isAxiosError(e) {
|
|
|
6924
6924
|
}
|
|
6925
6925
|
|
|
6926
6926
|
const CREATE_DIRECT_FEED_CARD_URL = 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/fyf_card/create_direct_feed_card';
|
|
6927
|
-
const DIRECT_FEED_CARD_HEADERS$
|
|
6927
|
+
const DIRECT_FEED_CARD_HEADERS$3 = {
|
|
6928
6928
|
'Content-Type': 'application/json',
|
|
6929
|
-
'x-use-ppe': '1',
|
|
6930
|
-
'x-tt-env': 'ppe_feed_play',
|
|
6931
6929
|
};
|
|
6932
6930
|
async function createDirectFeedCard({ appId, clientKey, directFeedCard, }) {
|
|
6933
6931
|
const payload = {
|
|
@@ -6938,22 +6936,20 @@ async function createDirectFeedCard({ appId, clientKey, directFeedCard, }) {
|
|
|
6938
6936
|
return request({
|
|
6939
6937
|
url: CREATE_DIRECT_FEED_CARD_URL,
|
|
6940
6938
|
method: 'POST',
|
|
6941
|
-
headers: DIRECT_FEED_CARD_HEADERS$
|
|
6939
|
+
headers: DIRECT_FEED_CARD_HEADERS$3,
|
|
6942
6940
|
data: payload,
|
|
6943
6941
|
});
|
|
6944
6942
|
}
|
|
6945
6943
|
|
|
6946
6944
|
const GET_DIRECT_FEED_SCENARIOS_URL = 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/fyf_card/get_direct_feed_scenario';
|
|
6947
|
-
const DIRECT_FEED_CARD_HEADERS$
|
|
6945
|
+
const DIRECT_FEED_CARD_HEADERS$2 = {
|
|
6948
6946
|
'Content-Type': 'application/json',
|
|
6949
|
-
'x-use-ppe': '1',
|
|
6950
|
-
'x-tt-env': 'ppe_feed_play',
|
|
6951
6947
|
};
|
|
6952
6948
|
async function fetchDirectFeedScenarios({ appId, clientKey, }) {
|
|
6953
6949
|
return request({
|
|
6954
6950
|
url: GET_DIRECT_FEED_SCENARIOS_URL,
|
|
6955
6951
|
method: 'POST',
|
|
6956
|
-
headers: DIRECT_FEED_CARD_HEADERS$
|
|
6952
|
+
headers: DIRECT_FEED_CARD_HEADERS$2,
|
|
6957
6953
|
data: {
|
|
6958
6954
|
app_id: appId,
|
|
6959
6955
|
client_key: clientKey,
|
|
@@ -6973,16 +6969,14 @@ async function fetchGameAssetPreviewUrl({ assetId, }) {
|
|
|
6973
6969
|
}
|
|
6974
6970
|
|
|
6975
6971
|
const GET_DIRECT_FEED_CARD_ASSET_PREVIEW_URL = 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/fyf_card/get_direct_feed_card_asset_preview_url';
|
|
6976
|
-
const DIRECT_FEED_CARD_HEADERS = {
|
|
6972
|
+
const DIRECT_FEED_CARD_HEADERS$1 = {
|
|
6977
6973
|
'Content-Type': 'application/json',
|
|
6978
|
-
'x-use-ppe': '1',
|
|
6979
|
-
'x-tt-env': 'ppe_feed_play',
|
|
6980
6974
|
};
|
|
6981
6975
|
async function fetchDirectFeedCardAssetPreviewUrl({ assetId, contentId, }) {
|
|
6982
6976
|
return request({
|
|
6983
6977
|
url: GET_DIRECT_FEED_CARD_ASSET_PREVIEW_URL,
|
|
6984
6978
|
method: 'POST',
|
|
6985
|
-
headers: DIRECT_FEED_CARD_HEADERS,
|
|
6979
|
+
headers: DIRECT_FEED_CARD_HEADERS$1,
|
|
6986
6980
|
data: {
|
|
6987
6981
|
asset_id: assetId,
|
|
6988
6982
|
content_id: contentId,
|
|
@@ -7040,8 +7034,6 @@ async function fetchGameInfo(clientKey) {
|
|
|
7040
7034
|
const LIST_DIRECT_FEED_CARD_URL = 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/fyf_card/list_direct_feed_card';
|
|
7041
7035
|
const LIST_DIRECT_FEED_CARD_HEADERS = {
|
|
7042
7036
|
'Content-Type': 'application/json',
|
|
7043
|
-
'x-use-ppe': '1',
|
|
7044
|
-
'x-tt-env': 'ppe_feed_play',
|
|
7045
7037
|
};
|
|
7046
7038
|
async function listDirectFeedCards({ appId, clientKey, }) {
|
|
7047
7039
|
return request({
|
|
@@ -7055,6 +7047,23 @@ async function listDirectFeedCards({ appId, clientKey, }) {
|
|
|
7055
7047
|
});
|
|
7056
7048
|
}
|
|
7057
7049
|
|
|
7050
|
+
const CHANGE_DIRECT_FEED_CARD_STATUS_URL = 'https://developers.tiktok.com/tiktok/v4/devportal/mini_game/fyf_card/change_direct_feed_card_status';
|
|
7051
|
+
const DIRECT_FEED_CARD_HEADERS = {
|
|
7052
|
+
'Content-Type': 'application/json',
|
|
7053
|
+
};
|
|
7054
|
+
async function changeDirectFeedCardStatus({ appId, clientKey, contentStatus, }) {
|
|
7055
|
+
return request({
|
|
7056
|
+
url: CHANGE_DIRECT_FEED_CARD_STATUS_URL,
|
|
7057
|
+
method: 'POST',
|
|
7058
|
+
headers: DIRECT_FEED_CARD_HEADERS,
|
|
7059
|
+
data: {
|
|
7060
|
+
app_id: appId,
|
|
7061
|
+
client_key: clientKey,
|
|
7062
|
+
content_status: contentStatus,
|
|
7063
|
+
},
|
|
7064
|
+
});
|
|
7065
|
+
}
|
|
7066
|
+
|
|
7058
7067
|
async function uploadGameToPlatform({ data, name, clientKey, note = '--', appId, }) {
|
|
7059
7068
|
if (!appId) {
|
|
7060
7069
|
return {
|
|
@@ -9431,6 +9440,49 @@ const gameCheckRoute = {
|
|
|
9431
9440
|
};
|
|
9432
9441
|
|
|
9433
9442
|
const changelog = [
|
|
9443
|
+
{
|
|
9444
|
+
title: '0.4.1',
|
|
9445
|
+
target: {
|
|
9446
|
+
iOS: '>=43.1',
|
|
9447
|
+
Android: '>=43.1',
|
|
9448
|
+
},
|
|
9449
|
+
changes: {
|
|
9450
|
+
optimize: [
|
|
9451
|
+
{
|
|
9452
|
+
desc: {
|
|
9453
|
+
'zh-CN': '优化上传代码包流程:上传后的平台处理提示改为更易理解的文案,不再向开发者暴露平台内部状态码及 asset / ready 等技术字段,进度与失败原因表达更清晰',
|
|
9454
|
+
'en-US': 'Improve the code package upload flow: post-upload platform status messages are now easier to understand and no longer expose internal status codes or technical fields like asset / ready, making progress and failure reasons clearer.',
|
|
9455
|
+
},
|
|
9456
|
+
module: 'upload',
|
|
9457
|
+
},
|
|
9458
|
+
{
|
|
9459
|
+
desc: {
|
|
9460
|
+
'zh-CN': '重构上传成功卡片布局:二维码与版本信息分区展示并以分隔线区隔,新增成功状态标识,排版更紧凑协调',
|
|
9461
|
+
'en-US': 'Rework the upload success card layout: the QR code and version info are separated into clear sections with a divider, a success indicator is added, and the layout is more compact and balanced.',
|
|
9462
|
+
},
|
|
9463
|
+
module: 'upload',
|
|
9464
|
+
},
|
|
9465
|
+
],
|
|
9466
|
+
},
|
|
9467
|
+
},
|
|
9468
|
+
{
|
|
9469
|
+
title: '0.4.0',
|
|
9470
|
+
target: {
|
|
9471
|
+
iOS: '>=43.1',
|
|
9472
|
+
Android: '>=43.1',
|
|
9473
|
+
},
|
|
9474
|
+
changes: {
|
|
9475
|
+
bugfix: [
|
|
9476
|
+
{
|
|
9477
|
+
desc: {
|
|
9478
|
+
'zh-CN': '修复游戏无开发权限时提示信息展示异常的问题,引导更准确',
|
|
9479
|
+
'en-US': 'Fix incorrect prompt messaging when the developer has no permission to develop the game, providing more accurate guidance.',
|
|
9480
|
+
},
|
|
9481
|
+
module: 'detail',
|
|
9482
|
+
},
|
|
9483
|
+
],
|
|
9484
|
+
},
|
|
9485
|
+
},
|
|
9434
9486
|
{
|
|
9435
9487
|
title: '0.3.9',
|
|
9436
9488
|
target: {
|
|
@@ -9912,7 +9964,7 @@ const gameDirectFeedCardAssetPreviewRoute = {
|
|
|
9912
9964
|
},
|
|
9913
9965
|
};
|
|
9914
9966
|
|
|
9915
|
-
async function resolveAppIdentity$
|
|
9967
|
+
async function resolveAppIdentity$3(body) {
|
|
9916
9968
|
const clientKey = body?.clientKey?.trim() ||
|
|
9917
9969
|
body?.client_key?.trim() ||
|
|
9918
9970
|
getClientKey().clientKey?.trim();
|
|
@@ -9979,7 +10031,7 @@ const gameDirectFeedCardCreateRoute = {
|
|
|
9979
10031
|
method: 'post',
|
|
9980
10032
|
path: '/game/direct-feed-card/create',
|
|
9981
10033
|
handler: async (req, res) => {
|
|
9982
|
-
const identity = await resolveAppIdentity$
|
|
10034
|
+
const identity = await resolveAppIdentity$3(req.body);
|
|
9983
10035
|
if (identity.error) {
|
|
9984
10036
|
res.send({
|
|
9985
10037
|
code: errorCode,
|
|
@@ -10035,7 +10087,7 @@ function isUsableDirectFeedCard(card) {
|
|
|
10035
10087
|
}
|
|
10036
10088
|
return true;
|
|
10037
10089
|
}
|
|
10038
|
-
async function resolveAppIdentity$
|
|
10090
|
+
async function resolveAppIdentity$2(body) {
|
|
10039
10091
|
const clientKey = body?.clientKey?.trim() ||
|
|
10040
10092
|
body?.client_key?.trim() ||
|
|
10041
10093
|
getClientKey().clientKey?.trim();
|
|
@@ -10080,7 +10132,7 @@ const gameDirectFeedCardListRoute = {
|
|
|
10080
10132
|
method: 'post',
|
|
10081
10133
|
path: '/game/direct-feed-card/list',
|
|
10082
10134
|
handler: async (req, res) => {
|
|
10083
|
-
const identity = await resolveAppIdentity$
|
|
10135
|
+
const identity = await resolveAppIdentity$2(req.body);
|
|
10084
10136
|
if (identity.error) {
|
|
10085
10137
|
res.send({
|
|
10086
10138
|
code: errorCode,
|
|
@@ -10185,7 +10237,7 @@ function extractScenarioOptions(data) {
|
|
|
10185
10237
|
}
|
|
10186
10238
|
return [];
|
|
10187
10239
|
}
|
|
10188
|
-
async function resolveAppIdentity(body) {
|
|
10240
|
+
async function resolveAppIdentity$1(body) {
|
|
10189
10241
|
const clientKey = body?.clientKey?.trim() ||
|
|
10190
10242
|
body?.client_key?.trim() ||
|
|
10191
10243
|
getClientKey().clientKey?.trim();
|
|
@@ -10230,7 +10282,7 @@ const gameDirectFeedCardScenariosRoute = {
|
|
|
10230
10282
|
method: 'post',
|
|
10231
10283
|
path: '/game/direct-feed-card/scenarios',
|
|
10232
10284
|
handler: async (req, res) => {
|
|
10233
|
-
const identity = await resolveAppIdentity(req.body);
|
|
10285
|
+
const identity = await resolveAppIdentity$1(req.body);
|
|
10234
10286
|
if (identity.error) {
|
|
10235
10287
|
res.send({
|
|
10236
10288
|
code: errorCode,
|
|
@@ -10270,6 +10322,112 @@ const gameDirectFeedCardScenariosRoute = {
|
|
|
10270
10322
|
},
|
|
10271
10323
|
};
|
|
10272
10324
|
|
|
10325
|
+
async function resolveAppIdentity(body) {
|
|
10326
|
+
const clientKey = body?.clientKey?.trim() ||
|
|
10327
|
+
body?.client_key?.trim() ||
|
|
10328
|
+
getClientKey().clientKey?.trim();
|
|
10329
|
+
let appId = body?.appId?.trim() ||
|
|
10330
|
+
body?.app_id?.trim() ||
|
|
10331
|
+
store.getState().appId?.trim();
|
|
10332
|
+
if (!clientKey) {
|
|
10333
|
+
return {
|
|
10334
|
+
clientKey: '',
|
|
10335
|
+
appId,
|
|
10336
|
+
error: 'Missing client key. Please run `ttmg init` or pass `clientKey` from IDE.',
|
|
10337
|
+
};
|
|
10338
|
+
}
|
|
10339
|
+
if (!appId) {
|
|
10340
|
+
const gameInfoResponse = await fetchGameInfo(clientKey);
|
|
10341
|
+
if (gameInfoResponse.error) {
|
|
10342
|
+
return {
|
|
10343
|
+
clientKey,
|
|
10344
|
+
appId: '',
|
|
10345
|
+
error: gameInfoResponse.error,
|
|
10346
|
+
};
|
|
10347
|
+
}
|
|
10348
|
+
appId = gameInfoResponse.data?.app_id?.trim() || '';
|
|
10349
|
+
if (appId) {
|
|
10350
|
+
store.setState({ appId });
|
|
10351
|
+
}
|
|
10352
|
+
}
|
|
10353
|
+
if (!appId) {
|
|
10354
|
+
return {
|
|
10355
|
+
clientKey,
|
|
10356
|
+
appId: '',
|
|
10357
|
+
error: 'Missing app id. Please open project detail first or pass `appId` from IDE.',
|
|
10358
|
+
};
|
|
10359
|
+
}
|
|
10360
|
+
return {
|
|
10361
|
+
clientKey,
|
|
10362
|
+
appId,
|
|
10363
|
+
error: null,
|
|
10364
|
+
};
|
|
10365
|
+
}
|
|
10366
|
+
function normalizeContentStatus(body) {
|
|
10367
|
+
const source = body?.contentStatus || body?.content_status || {};
|
|
10368
|
+
const contentStatus = Object.entries(source).reduce((result, [contentId, status]) => {
|
|
10369
|
+
const normalizedContentId = String(contentId || '').trim();
|
|
10370
|
+
const normalizedStatus = Number(status);
|
|
10371
|
+
if (normalizedContentId && Number.isFinite(normalizedStatus)) {
|
|
10372
|
+
result[normalizedContentId] = normalizedStatus;
|
|
10373
|
+
}
|
|
10374
|
+
return result;
|
|
10375
|
+
}, {});
|
|
10376
|
+
if (Object.keys(contentStatus).length === 0) {
|
|
10377
|
+
return {
|
|
10378
|
+
contentStatus,
|
|
10379
|
+
error: 'Missing direct-play card status. Please pass a non-empty `contentStatus` from IDE.',
|
|
10380
|
+
};
|
|
10381
|
+
}
|
|
10382
|
+
return {
|
|
10383
|
+
contentStatus,
|
|
10384
|
+
error: null,
|
|
10385
|
+
};
|
|
10386
|
+
}
|
|
10387
|
+
const gameDirectFeedCardStatusRoute = {
|
|
10388
|
+
method: 'post',
|
|
10389
|
+
path: '/game/direct-feed-card/status',
|
|
10390
|
+
handler: async (req, res) => {
|
|
10391
|
+
const body = req.body;
|
|
10392
|
+
const identity = await resolveAppIdentity(body);
|
|
10393
|
+
if (identity.error) {
|
|
10394
|
+
res.send({
|
|
10395
|
+
code: errorCode,
|
|
10396
|
+
error: identity.error,
|
|
10397
|
+
data: null,
|
|
10398
|
+
});
|
|
10399
|
+
return;
|
|
10400
|
+
}
|
|
10401
|
+
const normalized = normalizeContentStatus(body);
|
|
10402
|
+
if (normalized.error) {
|
|
10403
|
+
res.send({
|
|
10404
|
+
code: errorCode,
|
|
10405
|
+
error: normalized.error,
|
|
10406
|
+
data: null,
|
|
10407
|
+
});
|
|
10408
|
+
return;
|
|
10409
|
+
}
|
|
10410
|
+
const response = await changeDirectFeedCardStatus({
|
|
10411
|
+
appId: identity.appId,
|
|
10412
|
+
clientKey: identity.clientKey,
|
|
10413
|
+
contentStatus: normalized.contentStatus,
|
|
10414
|
+
});
|
|
10415
|
+
if (response.error) {
|
|
10416
|
+
res.send({
|
|
10417
|
+
code: errorCode,
|
|
10418
|
+
error: response.error,
|
|
10419
|
+
ctx: response.ctx,
|
|
10420
|
+
});
|
|
10421
|
+
return;
|
|
10422
|
+
}
|
|
10423
|
+
res.send({
|
|
10424
|
+
code: successCode,
|
|
10425
|
+
data: response.data,
|
|
10426
|
+
ctx: response.ctx,
|
|
10427
|
+
});
|
|
10428
|
+
},
|
|
10429
|
+
};
|
|
10430
|
+
|
|
10273
10431
|
const gameDetailRoute = {
|
|
10274
10432
|
method: 'get',
|
|
10275
10433
|
path: '/game/detail',
|
|
@@ -11203,36 +11361,79 @@ async function startSplit$1({ client_key, wasm_md5, }) {
|
|
|
11203
11361
|
ctx: { logid: 'local', httpStatusCode: 400 },
|
|
11204
11362
|
};
|
|
11205
11363
|
}
|
|
11364
|
+
// ── Local boot-miss feedback (no server change) ──────────────────────
|
|
11365
|
+
// The runtime logs every sub-package function it had to pull in BEFORE the
|
|
11366
|
+
// first frame as `[wasmcode2] ... firstFrame=BEFORE` — each one is a
|
|
11367
|
+
// first-screen hitch. Drop those func ids into
|
|
11368
|
+
// `__TTMG_TEMP__/boot_extra_func_ids.txt` (one per line) and they get
|
|
11369
|
+
// merged into `alwaysInclude` so the next split pins them into the main
|
|
11370
|
+
// package, closing the gap WITHOUT re-enabling the indirect-closure flood.
|
|
11371
|
+
// No-op when the file is absent.
|
|
11372
|
+
const bootExtraPath = path$1.join(tempDir, 'boot_extra_func_ids.txt');
|
|
11373
|
+
let bootExtra = [];
|
|
11374
|
+
if (fs$1.existsSync(bootExtraPath)) {
|
|
11375
|
+
bootExtra = fs$1
|
|
11376
|
+
.readFileSync(bootExtraPath, 'utf-8')
|
|
11377
|
+
.split('\n')
|
|
11378
|
+
.map(l => parseInt(l.trim(), 10))
|
|
11379
|
+
.filter(n => Number.isInteger(n) && n > 0);
|
|
11380
|
+
}
|
|
11381
|
+
const alwaysIncludeIds = Array.from(new Set([...bootFuncIds, ...bootExtra]));
|
|
11382
|
+
// ── Collect coverage signal ──────────────────────────────────────────
|
|
11383
|
+
// With the indirect-call closure off, the main package ≈ the collected
|
|
11384
|
+
// set, so collect coverage now directly determines first-screen safety.
|
|
11385
|
+
// Surface it, and warn (never block) when it looks too thin to cover boot.
|
|
11386
|
+
const totalFuncs = Number(getGameJson()?.wasmFuncCount) || getLocalState().totalWasmFuncCount || 0;
|
|
11387
|
+
const coveragePct = totalFuncs ? (funcIds.length / totalFuncs) * 100 : 0;
|
|
11206
11388
|
verboseLog(`[wasmtool] splitting with ${funcIds.length} func IDs` +
|
|
11389
|
+
` (coverage ${coveragePct.toFixed(1)}% of ${totalFuncs})` +
|
|
11207
11390
|
(bootFuncIds.length > 0
|
|
11208
|
-
? `, ${bootFuncIds.length} boot-phase func IDs
|
|
11209
|
-
: ', no boot-phase info (legacy server,
|
|
11210
|
-
`,
|
|
11391
|
+
? `, ${bootFuncIds.length} boot-phase func IDs`
|
|
11392
|
+
: ', no boot-phase info (legacy server, relying on collect + callClosure)') +
|
|
11393
|
+
(bootExtra.length > 0 ? `, +${bootExtra.length} boot-extra(local)` : '') +
|
|
11394
|
+
`, ${alwaysIncludeIds.length} → alwaysInclude, archive=${archive}`);
|
|
11395
|
+
// THIN_COLLECT_FLOOR is a heuristic, not a hard rule — tune from real
|
|
11396
|
+
// first-screen sizes. Warn-only: a thin collect with no boot hints means
|
|
11397
|
+
// first-screen functions may be served from the sub package before the
|
|
11398
|
+
// first frame (a visible hitch).
|
|
11399
|
+
const THIN_COLLECT_FLOOR = 8000;
|
|
11400
|
+
if (alwaysIncludeIds.length === 0 && funcIds.length < THIN_COLLECT_FLOOR) {
|
|
11401
|
+
verboseWarn(`[wasmtool] collect looks thin (${funcIds.length} funcs, no boot hints). ` +
|
|
11402
|
+
`First package may miss first-screen functions → pre-first-frame sub-package loads. ` +
|
|
11403
|
+
`Collect more scenarios (resume) before splitting, or add ids to ${bootExtraPath}.`);
|
|
11404
|
+
}
|
|
11211
11405
|
try {
|
|
11212
11406
|
const result = ttmgWasmtool.split({
|
|
11213
11407
|
input: rawWasmPath,
|
|
11214
11408
|
funcIds,
|
|
11215
|
-
// Boot-phase func ids
|
|
11216
|
-
// `
|
|
11217
|
-
//
|
|
11218
|
-
//
|
|
11219
|
-
// observability signal
|
|
11220
|
-
alwaysInclude:
|
|
11409
|
+
// Boot-phase func ids (server) ∪ local boot-extra feedback →
|
|
11410
|
+
// `alwaysInclude`. These seed the direct-call closure BFS with the
|
|
11411
|
+
// exact set needed for the first frame and force first-screen
|
|
11412
|
+
// functions into main; the split tool's `alwaysIncludeAdded` counter
|
|
11413
|
+
// is the observability signal (0 = no boot hints from any source).
|
|
11414
|
+
alwaysInclude: alwaysIncludeIds.length > 0 ? alwaysIncludeIds : undefined,
|
|
11221
11415
|
// Always-on direct-call closure over (collect ∪ alwaysInclude ∪
|
|
11222
11416
|
// start_func). Folds in func ids that collect missed (untaken
|
|
11223
11417
|
// branches, race conditions during collect) so first-screen code
|
|
11224
11418
|
// paths don't trap on archive trampolines. See the split tool's
|
|
11225
11419
|
// `closure_added` counter for the per-build size impact.
|
|
11226
11420
|
callClosure: true,
|
|
11227
|
-
//
|
|
11228
|
-
//
|
|
11229
|
-
//
|
|
11230
|
-
//
|
|
11231
|
-
//
|
|
11232
|
-
//
|
|
11233
|
-
//
|
|
11234
|
-
//
|
|
11235
|
-
|
|
11421
|
+
// OFF: the indirect-call type-closure is a static over-approximation
|
|
11422
|
+
// that pulls EVERY table function whose signature matches one used by
|
|
11423
|
+
// a boot root. In IL2CPP builds ~90% of functions live in the indirect
|
|
11424
|
+
// table and share only a few hundred signatures, so any non-trivial
|
|
11425
|
+
// boot set touches them all and this broadcasts almost the entire
|
|
11426
|
+
// module into main — measured ~135k/136k funcs (~50MB) main, defeating
|
|
11427
|
+
// the split. First-screen indirect-call targets are instead covered
|
|
11428
|
+
// precisely by the runtime collect (logCall is injected into every
|
|
11429
|
+
// function body, so any function that actually runs before first frame
|
|
11430
|
+
// — including call_indirect targets — is already in `funcIds`) plus the
|
|
11431
|
+
// direct-call closure and `alwaysInclude`. Any residual first-frame
|
|
11432
|
+
// indirect hit is served on-demand by the archive loader at runtime;
|
|
11433
|
+
// keep those rare by improving collect-session coverage, not by
|
|
11434
|
+
// re-enabling this flood. Set explicitly so the wasmtool default
|
|
11435
|
+
// (currently `true`) can't silently turn it back on.
|
|
11436
|
+
callIndirectClosure: false,
|
|
11236
11437
|
outputDir: splitOutputDir,
|
|
11237
11438
|
archive,
|
|
11238
11439
|
compress: true,
|
|
@@ -12826,6 +13027,306 @@ const gamePipelineModeGetRoute = {
|
|
|
12826
13027
|
},
|
|
12827
13028
|
};
|
|
12828
13029
|
|
|
13030
|
+
/**
|
|
13031
|
+
* 启动链路子包收集 —— DevTool CLI 侧契约类型。
|
|
13032
|
+
*
|
|
13033
|
+
* 协议字段与设计方案保持一致(见
|
|
13034
|
+
* topics/performance/startup-subpackage-collection-technical-design.md)。
|
|
13035
|
+
* `entry` 在协议层用单字段 name 表示:主包入口为内部约定 `__GAME__`,
|
|
13036
|
+
* 独立分包入口为该独立分包在源码 `subpackages[]` 中的 name。
|
|
13037
|
+
*/
|
|
13038
|
+
/** 平台内部主包标识,只活在 DevTool / 客户端 / 编译服务等平台组件之间。 */
|
|
13039
|
+
const GAME_ENTRY = '__GAME__';
|
|
13040
|
+
/** 启动子包预下载配置字段名(源码侧 + 编译产物侧同名)。 */
|
|
13041
|
+
const PRELOAD_FIELD_NAME = 'parallelPreloadSubpackages';
|
|
13042
|
+
|
|
13043
|
+
/**
|
|
13044
|
+
* 从磁盘**新鲜读取**源码 game.json(不走 getGameJson 缓存,因为本流程要写)。
|
|
13045
|
+
* game.json 不存在或解析失败时抛错,由上层转成 failed 状态。
|
|
13046
|
+
*/
|
|
13047
|
+
function readRawGameJson() {
|
|
13048
|
+
const filePath = path__namespace.join(process.cwd(), SUBPACKAGE_CONFIG_FILE_NAME);
|
|
13049
|
+
if (!fs__namespace.existsSync(filePath)) {
|
|
13050
|
+
throw new Error('game.json 不存在');
|
|
13051
|
+
}
|
|
13052
|
+
const raw = fs__namespace.readFileSync(filePath, 'utf-8');
|
|
13053
|
+
let json;
|
|
13054
|
+
try {
|
|
13055
|
+
json = JSON.parse(raw);
|
|
13056
|
+
}
|
|
13057
|
+
catch {
|
|
13058
|
+
throw new Error('game.json 解析失败');
|
|
13059
|
+
}
|
|
13060
|
+
const subpackagesField = SUBPACKAGE_FIELD_NAMES.find(k => Array.isArray(json[k])) ??
|
|
13061
|
+
SUBPACKAGE_FIELD_NAMES[0];
|
|
13062
|
+
return { filePath, raw, json, subpackagesField };
|
|
13063
|
+
}
|
|
13064
|
+
/** 只读读取一份 game.json;不存在 / 解析失败时返回 null,不抛错。 */
|
|
13065
|
+
function tryReadGameJson() {
|
|
13066
|
+
try {
|
|
13067
|
+
return readRawGameJson();
|
|
13068
|
+
}
|
|
13069
|
+
catch {
|
|
13070
|
+
return null;
|
|
13071
|
+
}
|
|
13072
|
+
}
|
|
13073
|
+
/** 取 subpackages 数组(保证返回数组)。 */
|
|
13074
|
+
function getSubpackages(game) {
|
|
13075
|
+
const list = game.json[game.subpackagesField];
|
|
13076
|
+
return Array.isArray(list) ? list : [];
|
|
13077
|
+
}
|
|
13078
|
+
/** 按 name 在 subpackages[] 中定位 entry。 */
|
|
13079
|
+
function findSubpackage(game, name) {
|
|
13080
|
+
return getSubpackages(game).find(s => s?.name === name);
|
|
13081
|
+
}
|
|
13082
|
+
/**
|
|
13083
|
+
* 把对象就地写回磁盘,沿用仓库 JSON 缩进 / 换行约定。
|
|
13084
|
+
*/
|
|
13085
|
+
function writeGameJson(game) {
|
|
13086
|
+
fs__namespace.writeFileSync(game.filePath, JSON.stringify(game.json, null, JSON_INDENT) + JSON_EOL);
|
|
13087
|
+
}
|
|
13088
|
+
/** 失败回滚:把原始文本原样写回。 */
|
|
13089
|
+
function restoreGameJson(game) {
|
|
13090
|
+
fs__namespace.writeFileSync(game.filePath, game.raw);
|
|
13091
|
+
}
|
|
13092
|
+
/** entry 是否为主包入口。 */
|
|
13093
|
+
function isGameEntry(entry) {
|
|
13094
|
+
return entry === GAME_ENTRY;
|
|
13095
|
+
}
|
|
13096
|
+
|
|
13097
|
+
/**
|
|
13098
|
+
* 写入前校验:通用规则 + 独立分包入口增量规则。
|
|
13099
|
+
* 任意一条不满足直接抛 Error(上层转 failed,不写 game.json)。
|
|
13100
|
+
* 返回过滤掉防御性条目后的待写入列表(含 root)。
|
|
13101
|
+
*/
|
|
13102
|
+
function validateCollect(game, entry, reported) {
|
|
13103
|
+
const subpackages = getSubpackages(game);
|
|
13104
|
+
if (!Array.isArray(game.json[game.subpackagesField])) {
|
|
13105
|
+
throw new Error('game.json.subpackages 缺失或不是数组');
|
|
13106
|
+
}
|
|
13107
|
+
// 独立分包入口增量校验
|
|
13108
|
+
if (!isGameEntry(entry)) {
|
|
13109
|
+
const entryPkg = findSubpackage(game, entry);
|
|
13110
|
+
if (!entryPkg) {
|
|
13111
|
+
throw new Error(`独立分包入口 ${entry} 不存在于 game.json.subpackages`);
|
|
13112
|
+
}
|
|
13113
|
+
if (entryPkg.independent !== true) {
|
|
13114
|
+
throw new Error(`入口 ${entry} 不是独立分包(independent !== true),不可作为启动入口采集`);
|
|
13115
|
+
}
|
|
13116
|
+
}
|
|
13117
|
+
const nameToPkg = new Map(subpackages.map(s => [s?.name, s]));
|
|
13118
|
+
const result = [];
|
|
13119
|
+
const seen = new Set();
|
|
13120
|
+
for (const item of reported) {
|
|
13121
|
+
const subPkgName = item?.subPkgName;
|
|
13122
|
+
// 客户端不应上报 __GAME__ 作为子包名:防御性忽略,不入库、不阻塞
|
|
13123
|
+
if (!subPkgName || subPkgName === GAME_ENTRY) {
|
|
13124
|
+
continue;
|
|
13125
|
+
}
|
|
13126
|
+
// 独立分包不预下载自己
|
|
13127
|
+
if (!isGameEntry(entry) && subPkgName === entry) {
|
|
13128
|
+
throw new Error(`独立分包入口 ${entry} 不能预下载自身`);
|
|
13129
|
+
}
|
|
13130
|
+
if (seen.has(subPkgName)) {
|
|
13131
|
+
continue;
|
|
13132
|
+
}
|
|
13133
|
+
seen.add(subPkgName);
|
|
13134
|
+
const pkg = nameToPkg.get(subPkgName);
|
|
13135
|
+
if (!pkg) {
|
|
13136
|
+
throw new Error(`子包 ${subPkgName} 未定义在 game.json.subpackages`);
|
|
13137
|
+
}
|
|
13138
|
+
if (!pkg.root || typeof pkg.root !== 'string') {
|
|
13139
|
+
throw new Error(`子包 ${subPkgName} 缺少 root 字段`);
|
|
13140
|
+
}
|
|
13141
|
+
const absDir = path__namespace.isAbsolute(pkg.root)
|
|
13142
|
+
? pkg.root
|
|
13143
|
+
: path__namespace.join(process.cwd(), pkg.root);
|
|
13144
|
+
if (!fs__namespace.existsSync(absDir) || !fs__namespace.statSync(absDir).isDirectory()) {
|
|
13145
|
+
throw new Error(`子包 ${subPkgName} 的目录不存在:${pkg.root}`);
|
|
13146
|
+
}
|
|
13147
|
+
result.push({
|
|
13148
|
+
subPkgName,
|
|
13149
|
+
isStartup: !!item.isStartup,
|
|
13150
|
+
root: pkg.root,
|
|
13151
|
+
});
|
|
13152
|
+
}
|
|
13153
|
+
return result;
|
|
13154
|
+
}
|
|
13155
|
+
|
|
13156
|
+
/** 读出某个 entry 当前的 parallelPreloadSubpackages(保证返回数组)。 */
|
|
13157
|
+
function readEntryConfig(game, entry) {
|
|
13158
|
+
const container = isGameEntry(entry)
|
|
13159
|
+
? game.json
|
|
13160
|
+
: findSubpackage(game, entry);
|
|
13161
|
+
const list = container?.[PRELOAD_FIELD_NAME];
|
|
13162
|
+
return Array.isArray(list) ? list : [];
|
|
13163
|
+
}
|
|
13164
|
+
/**
|
|
13165
|
+
* 按 entry 只读读取源码 game.json 中已有的 parallelPreloadSubpackages,
|
|
13166
|
+
* 用于 UI 初始化配置预览。game.json 不存在 / 字段缺失时返回空数组,不阻塞。
|
|
13167
|
+
*/
|
|
13168
|
+
function readPreloadSubpackagesConfig(entry) {
|
|
13169
|
+
const game = tryReadGameJson();
|
|
13170
|
+
if (!game) {
|
|
13171
|
+
return { entry, parallelPreloadSubpackages: [] };
|
|
13172
|
+
}
|
|
13173
|
+
return { entry, parallelPreloadSubpackages: readEntryConfig(game, entry) };
|
|
13174
|
+
}
|
|
13175
|
+
/**
|
|
13176
|
+
* 列出某个 entry 下可被采集(可预下载)的候选子包,供 UI 基于真实可用包选择。
|
|
13177
|
+
*
|
|
13178
|
+
* 候选 = game.json.subpackages 中 root 目录真实存在、且 name 不等于当前 entry
|
|
13179
|
+
* 的子包(独立分包入口不可预下载自身)。仅供 mock 上报基于真实可用包选择,
|
|
13180
|
+
* 不含 size —— size 在编译期由 ttmg-compile 按压缩加密后体积回填。
|
|
13181
|
+
* game.json 不存在 / 字段缺失时返回空数组,不阻塞。
|
|
13182
|
+
*/
|
|
13183
|
+
function listPreloadSubpackageCandidates(entry) {
|
|
13184
|
+
const game = tryReadGameJson();
|
|
13185
|
+
if (!game) {
|
|
13186
|
+
return { entry, candidates: [], totalSubpackages: 0 };
|
|
13187
|
+
}
|
|
13188
|
+
const subpackages = getSubpackages(game);
|
|
13189
|
+
const candidates = [];
|
|
13190
|
+
for (const sub of subpackages) {
|
|
13191
|
+
const subPkgName = sub?.name;
|
|
13192
|
+
const root = sub?.root;
|
|
13193
|
+
if (typeof subPkgName !== 'string' || !subPkgName)
|
|
13194
|
+
continue;
|
|
13195
|
+
if (subPkgName === entry)
|
|
13196
|
+
continue;
|
|
13197
|
+
if (typeof root !== 'string' || !root)
|
|
13198
|
+
continue;
|
|
13199
|
+
const absDir = path__namespace.isAbsolute(root)
|
|
13200
|
+
? root
|
|
13201
|
+
: path__namespace.join(process.cwd(), root);
|
|
13202
|
+
if (!fs__namespace.existsSync(absDir) || !fs__namespace.statSync(absDir).isDirectory())
|
|
13203
|
+
continue;
|
|
13204
|
+
candidates.push({
|
|
13205
|
+
subPkgName,
|
|
13206
|
+
root,
|
|
13207
|
+
independent: sub.independent === true,
|
|
13208
|
+
});
|
|
13209
|
+
}
|
|
13210
|
+
return { entry, candidates, totalSubpackages: subpackages.length };
|
|
13211
|
+
}
|
|
13212
|
+
/**
|
|
13213
|
+
* 收集快照写入:校验 → 按 entry 覆盖写入对应位置 → 失败回滚。
|
|
13214
|
+
*
|
|
13215
|
+
* 只写入 `subPkgName` + `isStartup`,**不写 size**:size 是压缩+加密后的真实
|
|
13216
|
+
* 下发体积,由编译服务(ttmg-compile)在产出 STTPKG 时回填到编译产物,收集期
|
|
13217
|
+
* 拿不到也不写(见技术方案 3.4 / 3.5)。
|
|
13218
|
+
*
|
|
13219
|
+
* - 写入前校验失败:抛错,不修改 game.json。
|
|
13220
|
+
* - 进入写入阶段后异常:回滚原始 game.json 再抛错。
|
|
13221
|
+
* 返回本次 entry 下最终写入的 parallelPreloadSubpackages。
|
|
13222
|
+
*/
|
|
13223
|
+
function collectPreloadSubpackages(entry, reported) {
|
|
13224
|
+
// readRawGameJson 抛错即「game.json 不存在 / 解析失败」,写入前阶段,不动文件
|
|
13225
|
+
const game = readRawGameJson();
|
|
13226
|
+
const validated = validateCollect(game, entry, reported);
|
|
13227
|
+
const parallelPreloadSubpackages = validated.map(item => ({
|
|
13228
|
+
subPkgName: item.subPkgName,
|
|
13229
|
+
isStartup: item.isStartup,
|
|
13230
|
+
}));
|
|
13231
|
+
let started = false;
|
|
13232
|
+
try {
|
|
13233
|
+
const container = isGameEntry(entry)
|
|
13234
|
+
? game.json
|
|
13235
|
+
: findSubpackage(game, entry);
|
|
13236
|
+
if (!container) {
|
|
13237
|
+
// 独立分包入口已在 validate 阶段确认存在,这里是防御
|
|
13238
|
+
throw new Error(`入口 ${entry} 不存在`);
|
|
13239
|
+
}
|
|
13240
|
+
started = true;
|
|
13241
|
+
container[PRELOAD_FIELD_NAME] = parallelPreloadSubpackages;
|
|
13242
|
+
writeGameJson(game);
|
|
13243
|
+
}
|
|
13244
|
+
catch (err) {
|
|
13245
|
+
if (started) {
|
|
13246
|
+
restoreGameJson(game);
|
|
13247
|
+
}
|
|
13248
|
+
throw err;
|
|
13249
|
+
}
|
|
13250
|
+
return { entry, parallelPreloadSubpackages };
|
|
13251
|
+
}
|
|
13252
|
+
|
|
13253
|
+
/**
|
|
13254
|
+
* 读取某个启动入口已有的 parallelPreloadSubpackages(只读),用于 UI 初始化
|
|
13255
|
+
* 配置预览。对应设计方案的 `getPreloadSubpackagesConfig`。
|
|
13256
|
+
*
|
|
13257
|
+
* query: `entry`(缺省按主包 `__GAME__` 处理)。
|
|
13258
|
+
*/
|
|
13259
|
+
const gamePreloadSubpackagesConfigRoute = {
|
|
13260
|
+
method: 'get',
|
|
13261
|
+
path: '/game/preload-subpackages-config',
|
|
13262
|
+
handler: async (req, res) => {
|
|
13263
|
+
const entry = req.query.entry || GAME_ENTRY;
|
|
13264
|
+
try {
|
|
13265
|
+
const data = readPreloadSubpackagesConfig(entry);
|
|
13266
|
+
res.send({ code: successCode, data });
|
|
13267
|
+
}
|
|
13268
|
+
catch (err) {
|
|
13269
|
+
res.send({
|
|
13270
|
+
code: errorCode,
|
|
13271
|
+
error: { message: err.message },
|
|
13272
|
+
});
|
|
13273
|
+
}
|
|
13274
|
+
},
|
|
13275
|
+
};
|
|
13276
|
+
|
|
13277
|
+
/**
|
|
13278
|
+
* 列出某个启动入口下可被采集(可预下载)的候选子包,供 UI mock 上报时
|
|
13279
|
+
* 基于真实可用包选择,而不是手填名字。候选直接来自 game.json.subpackages
|
|
13280
|
+
* 中 root 真实存在、name 不等于当前 entry 的子包,并附带压缩前体积。
|
|
13281
|
+
*
|
|
13282
|
+
* query: `entry`(缺省按主包 `__GAME__` 处理)。
|
|
13283
|
+
*/
|
|
13284
|
+
const gamePreloadSubpackageCandidatesRoute = {
|
|
13285
|
+
method: 'get',
|
|
13286
|
+
path: '/game/preload-subpackage-candidates',
|
|
13287
|
+
handler: async (req, res) => {
|
|
13288
|
+
const entry = req.query.entry || GAME_ENTRY;
|
|
13289
|
+
try {
|
|
13290
|
+
const data = listPreloadSubpackageCandidates(entry);
|
|
13291
|
+
res.send({ code: successCode, data });
|
|
13292
|
+
}
|
|
13293
|
+
catch (err) {
|
|
13294
|
+
res.send({
|
|
13295
|
+
code: errorCode,
|
|
13296
|
+
error: { message: err.message },
|
|
13297
|
+
});
|
|
13298
|
+
}
|
|
13299
|
+
},
|
|
13300
|
+
};
|
|
13301
|
+
|
|
13302
|
+
/**
|
|
13303
|
+
* 收集快照写入:按 entry 校验 + 补体积 + 覆盖写入源码 game.json 对应位置,
|
|
13304
|
+
* 失败回滚。对应设计方案的 `collectPreloadSubpackages` /
|
|
13305
|
+
* `updatePreloadSubpackagesConfig`(HTTP 请求-响应实现,start 态由 UI 自身的
|
|
13306
|
+
* 处理中状态承载,CLI 只回 success / failed)。
|
|
13307
|
+
*
|
|
13308
|
+
* body: `{ entry, subpackages: [{ subPkgName, isStartup }] }`。
|
|
13309
|
+
*/
|
|
13310
|
+
const gameCollectPreloadSubpackagesRoute = {
|
|
13311
|
+
method: 'post',
|
|
13312
|
+
path: '/game/collect-preload-subpackages',
|
|
13313
|
+
handler: async (req, res) => {
|
|
13314
|
+
const entry = req.body?.entry || GAME_ENTRY;
|
|
13315
|
+
const subpackages = req.body?.subpackages || [];
|
|
13316
|
+
console.log('collect-preload-subpackages', { entry, count: subpackages.length });
|
|
13317
|
+
try {
|
|
13318
|
+
const data = collectPreloadSubpackages(entry, subpackages);
|
|
13319
|
+
res.send({ code: successCode, data });
|
|
13320
|
+
}
|
|
13321
|
+
catch (err) {
|
|
13322
|
+
res.send({
|
|
13323
|
+
code: errorCode,
|
|
13324
|
+
error: { message: err.message },
|
|
13325
|
+
});
|
|
13326
|
+
}
|
|
13327
|
+
},
|
|
13328
|
+
};
|
|
13329
|
+
|
|
12829
13330
|
const routes = [
|
|
12830
13331
|
gameAssetPreviewUrlRoute,
|
|
12831
13332
|
gameAssetsRoute,
|
|
@@ -12838,6 +13339,7 @@ const routes = [
|
|
|
12838
13339
|
gameDirectFeedCardCreateRoute,
|
|
12839
13340
|
gameDirectFeedCardListRoute,
|
|
12840
13341
|
gameDirectFeedCardScenariosRoute,
|
|
13342
|
+
gameDirectFeedCardStatusRoute,
|
|
12841
13343
|
gameUploadRoute,
|
|
12842
13344
|
gameWasmSplitConfigRoute,
|
|
12843
13345
|
gameWasmSplitOptionsRoute,
|
|
@@ -12858,6 +13360,9 @@ const routes = [
|
|
|
12858
13360
|
gamePipelineModeRoute,
|
|
12859
13361
|
gamePipelineModeGetRoute,
|
|
12860
13362
|
gameLanguageRoute,
|
|
13363
|
+
gamePreloadSubpackagesConfigRoute,
|
|
13364
|
+
gamePreloadSubpackageCandidatesRoute,
|
|
13365
|
+
gameCollectPreloadSubpackagesRoute,
|
|
12861
13366
|
];
|
|
12862
13367
|
/**
|
|
12863
13368
|
* Express 4 does not catch rejections from async route handlers — an
|
|
@@ -13358,7 +13863,7 @@ async function upload({ clientKey, note = '--', dir, }) {
|
|
|
13358
13863
|
}
|
|
13359
13864
|
}
|
|
13360
13865
|
|
|
13361
|
-
var version = "0.4.
|
|
13866
|
+
var version = "0.4.1";
|
|
13362
13867
|
var pkg = {
|
|
13363
13868
|
version: version};
|
|
13364
13869
|
|