@ttmg/cli 0.4.0 → 0.4.1-beta.wasm1
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 +10 -0
- package/dist/index.js +501 -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 {
|
|
@@ -9912,7 +9921,7 @@ const gameDirectFeedCardAssetPreviewRoute = {
|
|
|
9912
9921
|
},
|
|
9913
9922
|
};
|
|
9914
9923
|
|
|
9915
|
-
async function resolveAppIdentity$
|
|
9924
|
+
async function resolveAppIdentity$3(body) {
|
|
9916
9925
|
const clientKey = body?.clientKey?.trim() ||
|
|
9917
9926
|
body?.client_key?.trim() ||
|
|
9918
9927
|
getClientKey().clientKey?.trim();
|
|
@@ -9979,7 +9988,7 @@ const gameDirectFeedCardCreateRoute = {
|
|
|
9979
9988
|
method: 'post',
|
|
9980
9989
|
path: '/game/direct-feed-card/create',
|
|
9981
9990
|
handler: async (req, res) => {
|
|
9982
|
-
const identity = await resolveAppIdentity$
|
|
9991
|
+
const identity = await resolveAppIdentity$3(req.body);
|
|
9983
9992
|
if (identity.error) {
|
|
9984
9993
|
res.send({
|
|
9985
9994
|
code: errorCode,
|
|
@@ -10035,7 +10044,7 @@ function isUsableDirectFeedCard(card) {
|
|
|
10035
10044
|
}
|
|
10036
10045
|
return true;
|
|
10037
10046
|
}
|
|
10038
|
-
async function resolveAppIdentity$
|
|
10047
|
+
async function resolveAppIdentity$2(body) {
|
|
10039
10048
|
const clientKey = body?.clientKey?.trim() ||
|
|
10040
10049
|
body?.client_key?.trim() ||
|
|
10041
10050
|
getClientKey().clientKey?.trim();
|
|
@@ -10080,7 +10089,7 @@ const gameDirectFeedCardListRoute = {
|
|
|
10080
10089
|
method: 'post',
|
|
10081
10090
|
path: '/game/direct-feed-card/list',
|
|
10082
10091
|
handler: async (req, res) => {
|
|
10083
|
-
const identity = await resolveAppIdentity$
|
|
10092
|
+
const identity = await resolveAppIdentity$2(req.body);
|
|
10084
10093
|
if (identity.error) {
|
|
10085
10094
|
res.send({
|
|
10086
10095
|
code: errorCode,
|
|
@@ -10185,7 +10194,7 @@ function extractScenarioOptions(data) {
|
|
|
10185
10194
|
}
|
|
10186
10195
|
return [];
|
|
10187
10196
|
}
|
|
10188
|
-
async function resolveAppIdentity(body) {
|
|
10197
|
+
async function resolveAppIdentity$1(body) {
|
|
10189
10198
|
const clientKey = body?.clientKey?.trim() ||
|
|
10190
10199
|
body?.client_key?.trim() ||
|
|
10191
10200
|
getClientKey().clientKey?.trim();
|
|
@@ -10230,7 +10239,7 @@ const gameDirectFeedCardScenariosRoute = {
|
|
|
10230
10239
|
method: 'post',
|
|
10231
10240
|
path: '/game/direct-feed-card/scenarios',
|
|
10232
10241
|
handler: async (req, res) => {
|
|
10233
|
-
const identity = await resolveAppIdentity(req.body);
|
|
10242
|
+
const identity = await resolveAppIdentity$1(req.body);
|
|
10234
10243
|
if (identity.error) {
|
|
10235
10244
|
res.send({
|
|
10236
10245
|
code: errorCode,
|
|
@@ -10270,6 +10279,112 @@ const gameDirectFeedCardScenariosRoute = {
|
|
|
10270
10279
|
},
|
|
10271
10280
|
};
|
|
10272
10281
|
|
|
10282
|
+
async function resolveAppIdentity(body) {
|
|
10283
|
+
const clientKey = body?.clientKey?.trim() ||
|
|
10284
|
+
body?.client_key?.trim() ||
|
|
10285
|
+
getClientKey().clientKey?.trim();
|
|
10286
|
+
let appId = body?.appId?.trim() ||
|
|
10287
|
+
body?.app_id?.trim() ||
|
|
10288
|
+
store.getState().appId?.trim();
|
|
10289
|
+
if (!clientKey) {
|
|
10290
|
+
return {
|
|
10291
|
+
clientKey: '',
|
|
10292
|
+
appId,
|
|
10293
|
+
error: 'Missing client key. Please run `ttmg init` or pass `clientKey` from IDE.',
|
|
10294
|
+
};
|
|
10295
|
+
}
|
|
10296
|
+
if (!appId) {
|
|
10297
|
+
const gameInfoResponse = await fetchGameInfo(clientKey);
|
|
10298
|
+
if (gameInfoResponse.error) {
|
|
10299
|
+
return {
|
|
10300
|
+
clientKey,
|
|
10301
|
+
appId: '',
|
|
10302
|
+
error: gameInfoResponse.error,
|
|
10303
|
+
};
|
|
10304
|
+
}
|
|
10305
|
+
appId = gameInfoResponse.data?.app_id?.trim() || '';
|
|
10306
|
+
if (appId) {
|
|
10307
|
+
store.setState({ appId });
|
|
10308
|
+
}
|
|
10309
|
+
}
|
|
10310
|
+
if (!appId) {
|
|
10311
|
+
return {
|
|
10312
|
+
clientKey,
|
|
10313
|
+
appId: '',
|
|
10314
|
+
error: 'Missing app id. Please open project detail first or pass `appId` from IDE.',
|
|
10315
|
+
};
|
|
10316
|
+
}
|
|
10317
|
+
return {
|
|
10318
|
+
clientKey,
|
|
10319
|
+
appId,
|
|
10320
|
+
error: null,
|
|
10321
|
+
};
|
|
10322
|
+
}
|
|
10323
|
+
function normalizeContentStatus(body) {
|
|
10324
|
+
const source = body?.contentStatus || body?.content_status || {};
|
|
10325
|
+
const contentStatus = Object.entries(source).reduce((result, [contentId, status]) => {
|
|
10326
|
+
const normalizedContentId = String(contentId || '').trim();
|
|
10327
|
+
const normalizedStatus = Number(status);
|
|
10328
|
+
if (normalizedContentId && Number.isFinite(normalizedStatus)) {
|
|
10329
|
+
result[normalizedContentId] = normalizedStatus;
|
|
10330
|
+
}
|
|
10331
|
+
return result;
|
|
10332
|
+
}, {});
|
|
10333
|
+
if (Object.keys(contentStatus).length === 0) {
|
|
10334
|
+
return {
|
|
10335
|
+
contentStatus,
|
|
10336
|
+
error: 'Missing direct-play card status. Please pass a non-empty `contentStatus` from IDE.',
|
|
10337
|
+
};
|
|
10338
|
+
}
|
|
10339
|
+
return {
|
|
10340
|
+
contentStatus,
|
|
10341
|
+
error: null,
|
|
10342
|
+
};
|
|
10343
|
+
}
|
|
10344
|
+
const gameDirectFeedCardStatusRoute = {
|
|
10345
|
+
method: 'post',
|
|
10346
|
+
path: '/game/direct-feed-card/status',
|
|
10347
|
+
handler: async (req, res) => {
|
|
10348
|
+
const body = req.body;
|
|
10349
|
+
const identity = await resolveAppIdentity(body);
|
|
10350
|
+
if (identity.error) {
|
|
10351
|
+
res.send({
|
|
10352
|
+
code: errorCode,
|
|
10353
|
+
error: identity.error,
|
|
10354
|
+
data: null,
|
|
10355
|
+
});
|
|
10356
|
+
return;
|
|
10357
|
+
}
|
|
10358
|
+
const normalized = normalizeContentStatus(body);
|
|
10359
|
+
if (normalized.error) {
|
|
10360
|
+
res.send({
|
|
10361
|
+
code: errorCode,
|
|
10362
|
+
error: normalized.error,
|
|
10363
|
+
data: null,
|
|
10364
|
+
});
|
|
10365
|
+
return;
|
|
10366
|
+
}
|
|
10367
|
+
const response = await changeDirectFeedCardStatus({
|
|
10368
|
+
appId: identity.appId,
|
|
10369
|
+
clientKey: identity.clientKey,
|
|
10370
|
+
contentStatus: normalized.contentStatus,
|
|
10371
|
+
});
|
|
10372
|
+
if (response.error) {
|
|
10373
|
+
res.send({
|
|
10374
|
+
code: errorCode,
|
|
10375
|
+
error: response.error,
|
|
10376
|
+
ctx: response.ctx,
|
|
10377
|
+
});
|
|
10378
|
+
return;
|
|
10379
|
+
}
|
|
10380
|
+
res.send({
|
|
10381
|
+
code: successCode,
|
|
10382
|
+
data: response.data,
|
|
10383
|
+
ctx: response.ctx,
|
|
10384
|
+
});
|
|
10385
|
+
},
|
|
10386
|
+
};
|
|
10387
|
+
|
|
10273
10388
|
const gameDetailRoute = {
|
|
10274
10389
|
method: 'get',
|
|
10275
10390
|
path: '/game/detail',
|
|
@@ -11203,36 +11318,79 @@ async function startSplit$1({ client_key, wasm_md5, }) {
|
|
|
11203
11318
|
ctx: { logid: 'local', httpStatusCode: 400 },
|
|
11204
11319
|
};
|
|
11205
11320
|
}
|
|
11321
|
+
// ── Local boot-miss feedback (no server change) ──────────────────────
|
|
11322
|
+
// The runtime logs every sub-package function it had to pull in BEFORE the
|
|
11323
|
+
// first frame as `[wasmcode2] ... firstFrame=BEFORE` — each one is a
|
|
11324
|
+
// first-screen hitch. Drop those func ids into
|
|
11325
|
+
// `__TTMG_TEMP__/boot_extra_func_ids.txt` (one per line) and they get
|
|
11326
|
+
// merged into `alwaysInclude` so the next split pins them into the main
|
|
11327
|
+
// package, closing the gap WITHOUT re-enabling the indirect-closure flood.
|
|
11328
|
+
// No-op when the file is absent.
|
|
11329
|
+
const bootExtraPath = path$1.join(tempDir, 'boot_extra_func_ids.txt');
|
|
11330
|
+
let bootExtra = [];
|
|
11331
|
+
if (fs$1.existsSync(bootExtraPath)) {
|
|
11332
|
+
bootExtra = fs$1
|
|
11333
|
+
.readFileSync(bootExtraPath, 'utf-8')
|
|
11334
|
+
.split('\n')
|
|
11335
|
+
.map(l => parseInt(l.trim(), 10))
|
|
11336
|
+
.filter(n => Number.isInteger(n) && n > 0);
|
|
11337
|
+
}
|
|
11338
|
+
const alwaysIncludeIds = Array.from(new Set([...bootFuncIds, ...bootExtra]));
|
|
11339
|
+
// ── Collect coverage signal ──────────────────────────────────────────
|
|
11340
|
+
// With the indirect-call closure off, the main package ≈ the collected
|
|
11341
|
+
// set, so collect coverage now directly determines first-screen safety.
|
|
11342
|
+
// Surface it, and warn (never block) when it looks too thin to cover boot.
|
|
11343
|
+
const totalFuncs = Number(getGameJson()?.wasmFuncCount) || getLocalState().totalWasmFuncCount || 0;
|
|
11344
|
+
const coveragePct = totalFuncs ? (funcIds.length / totalFuncs) * 100 : 0;
|
|
11206
11345
|
verboseLog(`[wasmtool] splitting with ${funcIds.length} func IDs` +
|
|
11346
|
+
` (coverage ${coveragePct.toFixed(1)}% of ${totalFuncs})` +
|
|
11207
11347
|
(bootFuncIds.length > 0
|
|
11208
|
-
? `, ${bootFuncIds.length} boot-phase func IDs
|
|
11209
|
-
: ', no boot-phase info (legacy server,
|
|
11210
|
-
`,
|
|
11348
|
+
? `, ${bootFuncIds.length} boot-phase func IDs`
|
|
11349
|
+
: ', no boot-phase info (legacy server, relying on collect + callClosure)') +
|
|
11350
|
+
(bootExtra.length > 0 ? `, +${bootExtra.length} boot-extra(local)` : '') +
|
|
11351
|
+
`, ${alwaysIncludeIds.length} → alwaysInclude, archive=${archive}`);
|
|
11352
|
+
// THIN_COLLECT_FLOOR is a heuristic, not a hard rule — tune from real
|
|
11353
|
+
// first-screen sizes. Warn-only: a thin collect with no boot hints means
|
|
11354
|
+
// first-screen functions may be served from the sub package before the
|
|
11355
|
+
// first frame (a visible hitch).
|
|
11356
|
+
const THIN_COLLECT_FLOOR = 8000;
|
|
11357
|
+
if (alwaysIncludeIds.length === 0 && funcIds.length < THIN_COLLECT_FLOOR) {
|
|
11358
|
+
verboseWarn(`[wasmtool] collect looks thin (${funcIds.length} funcs, no boot hints). ` +
|
|
11359
|
+
`First package may miss first-screen functions → pre-first-frame sub-package loads. ` +
|
|
11360
|
+
`Collect more scenarios (resume) before splitting, or add ids to ${bootExtraPath}.`);
|
|
11361
|
+
}
|
|
11211
11362
|
try {
|
|
11212
11363
|
const result = ttmgWasmtool.split({
|
|
11213
11364
|
input: rawWasmPath,
|
|
11214
11365
|
funcIds,
|
|
11215
|
-
// Boot-phase func ids
|
|
11216
|
-
// `
|
|
11217
|
-
//
|
|
11218
|
-
//
|
|
11219
|
-
// observability signal
|
|
11220
|
-
alwaysInclude:
|
|
11366
|
+
// Boot-phase func ids (server) ∪ local boot-extra feedback →
|
|
11367
|
+
// `alwaysInclude`. These seed the direct-call closure BFS with the
|
|
11368
|
+
// exact set needed for the first frame and force first-screen
|
|
11369
|
+
// functions into main; the split tool's `alwaysIncludeAdded` counter
|
|
11370
|
+
// is the observability signal (0 = no boot hints from any source).
|
|
11371
|
+
alwaysInclude: alwaysIncludeIds.length > 0 ? alwaysIncludeIds : undefined,
|
|
11221
11372
|
// Always-on direct-call closure over (collect ∪ alwaysInclude ∪
|
|
11222
11373
|
// start_func). Folds in func ids that collect missed (untaken
|
|
11223
11374
|
// branches, race conditions during collect) so first-screen code
|
|
11224
11375
|
// paths don't trap on archive trampolines. See the split tool's
|
|
11225
11376
|
// `closure_added` counter for the per-build size impact.
|
|
11226
11377
|
callClosure: true,
|
|
11227
|
-
//
|
|
11228
|
-
//
|
|
11229
|
-
//
|
|
11230
|
-
//
|
|
11231
|
-
//
|
|
11232
|
-
//
|
|
11233
|
-
//
|
|
11234
|
-
//
|
|
11235
|
-
|
|
11378
|
+
// OFF: the indirect-call type-closure is a static over-approximation
|
|
11379
|
+
// that pulls EVERY table function whose signature matches one used by
|
|
11380
|
+
// a boot root. In IL2CPP builds ~90% of functions live in the indirect
|
|
11381
|
+
// table and share only a few hundred signatures, so any non-trivial
|
|
11382
|
+
// boot set touches them all and this broadcasts almost the entire
|
|
11383
|
+
// module into main — measured ~135k/136k funcs (~50MB) main, defeating
|
|
11384
|
+
// the split. First-screen indirect-call targets are instead covered
|
|
11385
|
+
// precisely by the runtime collect (logCall is injected into every
|
|
11386
|
+
// function body, so any function that actually runs before first frame
|
|
11387
|
+
// — including call_indirect targets — is already in `funcIds`) plus the
|
|
11388
|
+
// direct-call closure and `alwaysInclude`. Any residual first-frame
|
|
11389
|
+
// indirect hit is served on-demand by the archive loader at runtime;
|
|
11390
|
+
// keep those rare by improving collect-session coverage, not by
|
|
11391
|
+
// re-enabling this flood. Set explicitly so the wasmtool default
|
|
11392
|
+
// (currently `true`) can't silently turn it back on.
|
|
11393
|
+
callIndirectClosure: false,
|
|
11236
11394
|
outputDir: splitOutputDir,
|
|
11237
11395
|
archive,
|
|
11238
11396
|
compress: true,
|
|
@@ -12826,6 +12984,306 @@ const gamePipelineModeGetRoute = {
|
|
|
12826
12984
|
},
|
|
12827
12985
|
};
|
|
12828
12986
|
|
|
12987
|
+
/**
|
|
12988
|
+
* 启动链路子包收集 —— DevTool CLI 侧契约类型。
|
|
12989
|
+
*
|
|
12990
|
+
* 协议字段与设计方案保持一致(见
|
|
12991
|
+
* topics/performance/startup-subpackage-collection-technical-design.md)。
|
|
12992
|
+
* `entry` 在协议层用单字段 name 表示:主包入口为内部约定 `__GAME__`,
|
|
12993
|
+
* 独立分包入口为该独立分包在源码 `subpackages[]` 中的 name。
|
|
12994
|
+
*/
|
|
12995
|
+
/** 平台内部主包标识,只活在 DevTool / 客户端 / 编译服务等平台组件之间。 */
|
|
12996
|
+
const GAME_ENTRY = '__GAME__';
|
|
12997
|
+
/** 启动子包预下载配置字段名(源码侧 + 编译产物侧同名)。 */
|
|
12998
|
+
const PRELOAD_FIELD_NAME = 'parallelPreloadSubpackages';
|
|
12999
|
+
|
|
13000
|
+
/**
|
|
13001
|
+
* 从磁盘**新鲜读取**源码 game.json(不走 getGameJson 缓存,因为本流程要写)。
|
|
13002
|
+
* game.json 不存在或解析失败时抛错,由上层转成 failed 状态。
|
|
13003
|
+
*/
|
|
13004
|
+
function readRawGameJson() {
|
|
13005
|
+
const filePath = path__namespace.join(process.cwd(), SUBPACKAGE_CONFIG_FILE_NAME);
|
|
13006
|
+
if (!fs__namespace.existsSync(filePath)) {
|
|
13007
|
+
throw new Error('game.json 不存在');
|
|
13008
|
+
}
|
|
13009
|
+
const raw = fs__namespace.readFileSync(filePath, 'utf-8');
|
|
13010
|
+
let json;
|
|
13011
|
+
try {
|
|
13012
|
+
json = JSON.parse(raw);
|
|
13013
|
+
}
|
|
13014
|
+
catch {
|
|
13015
|
+
throw new Error('game.json 解析失败');
|
|
13016
|
+
}
|
|
13017
|
+
const subpackagesField = SUBPACKAGE_FIELD_NAMES.find(k => Array.isArray(json[k])) ??
|
|
13018
|
+
SUBPACKAGE_FIELD_NAMES[0];
|
|
13019
|
+
return { filePath, raw, json, subpackagesField };
|
|
13020
|
+
}
|
|
13021
|
+
/** 只读读取一份 game.json;不存在 / 解析失败时返回 null,不抛错。 */
|
|
13022
|
+
function tryReadGameJson() {
|
|
13023
|
+
try {
|
|
13024
|
+
return readRawGameJson();
|
|
13025
|
+
}
|
|
13026
|
+
catch {
|
|
13027
|
+
return null;
|
|
13028
|
+
}
|
|
13029
|
+
}
|
|
13030
|
+
/** 取 subpackages 数组(保证返回数组)。 */
|
|
13031
|
+
function getSubpackages(game) {
|
|
13032
|
+
const list = game.json[game.subpackagesField];
|
|
13033
|
+
return Array.isArray(list) ? list : [];
|
|
13034
|
+
}
|
|
13035
|
+
/** 按 name 在 subpackages[] 中定位 entry。 */
|
|
13036
|
+
function findSubpackage(game, name) {
|
|
13037
|
+
return getSubpackages(game).find(s => s?.name === name);
|
|
13038
|
+
}
|
|
13039
|
+
/**
|
|
13040
|
+
* 把对象就地写回磁盘,沿用仓库 JSON 缩进 / 换行约定。
|
|
13041
|
+
*/
|
|
13042
|
+
function writeGameJson(game) {
|
|
13043
|
+
fs__namespace.writeFileSync(game.filePath, JSON.stringify(game.json, null, JSON_INDENT) + JSON_EOL);
|
|
13044
|
+
}
|
|
13045
|
+
/** 失败回滚:把原始文本原样写回。 */
|
|
13046
|
+
function restoreGameJson(game) {
|
|
13047
|
+
fs__namespace.writeFileSync(game.filePath, game.raw);
|
|
13048
|
+
}
|
|
13049
|
+
/** entry 是否为主包入口。 */
|
|
13050
|
+
function isGameEntry(entry) {
|
|
13051
|
+
return entry === GAME_ENTRY;
|
|
13052
|
+
}
|
|
13053
|
+
|
|
13054
|
+
/**
|
|
13055
|
+
* 写入前校验:通用规则 + 独立分包入口增量规则。
|
|
13056
|
+
* 任意一条不满足直接抛 Error(上层转 failed,不写 game.json)。
|
|
13057
|
+
* 返回过滤掉防御性条目后的待写入列表(含 root)。
|
|
13058
|
+
*/
|
|
13059
|
+
function validateCollect(game, entry, reported) {
|
|
13060
|
+
const subpackages = getSubpackages(game);
|
|
13061
|
+
if (!Array.isArray(game.json[game.subpackagesField])) {
|
|
13062
|
+
throw new Error('game.json.subpackages 缺失或不是数组');
|
|
13063
|
+
}
|
|
13064
|
+
// 独立分包入口增量校验
|
|
13065
|
+
if (!isGameEntry(entry)) {
|
|
13066
|
+
const entryPkg = findSubpackage(game, entry);
|
|
13067
|
+
if (!entryPkg) {
|
|
13068
|
+
throw new Error(`独立分包入口 ${entry} 不存在于 game.json.subpackages`);
|
|
13069
|
+
}
|
|
13070
|
+
if (entryPkg.independent !== true) {
|
|
13071
|
+
throw new Error(`入口 ${entry} 不是独立分包(independent !== true),不可作为启动入口采集`);
|
|
13072
|
+
}
|
|
13073
|
+
}
|
|
13074
|
+
const nameToPkg = new Map(subpackages.map(s => [s?.name, s]));
|
|
13075
|
+
const result = [];
|
|
13076
|
+
const seen = new Set();
|
|
13077
|
+
for (const item of reported) {
|
|
13078
|
+
const subPkgName = item?.subPkgName;
|
|
13079
|
+
// 客户端不应上报 __GAME__ 作为子包名:防御性忽略,不入库、不阻塞
|
|
13080
|
+
if (!subPkgName || subPkgName === GAME_ENTRY) {
|
|
13081
|
+
continue;
|
|
13082
|
+
}
|
|
13083
|
+
// 独立分包不预下载自己
|
|
13084
|
+
if (!isGameEntry(entry) && subPkgName === entry) {
|
|
13085
|
+
throw new Error(`独立分包入口 ${entry} 不能预下载自身`);
|
|
13086
|
+
}
|
|
13087
|
+
if (seen.has(subPkgName)) {
|
|
13088
|
+
continue;
|
|
13089
|
+
}
|
|
13090
|
+
seen.add(subPkgName);
|
|
13091
|
+
const pkg = nameToPkg.get(subPkgName);
|
|
13092
|
+
if (!pkg) {
|
|
13093
|
+
throw new Error(`子包 ${subPkgName} 未定义在 game.json.subpackages`);
|
|
13094
|
+
}
|
|
13095
|
+
if (!pkg.root || typeof pkg.root !== 'string') {
|
|
13096
|
+
throw new Error(`子包 ${subPkgName} 缺少 root 字段`);
|
|
13097
|
+
}
|
|
13098
|
+
const absDir = path__namespace.isAbsolute(pkg.root)
|
|
13099
|
+
? pkg.root
|
|
13100
|
+
: path__namespace.join(process.cwd(), pkg.root);
|
|
13101
|
+
if (!fs__namespace.existsSync(absDir) || !fs__namespace.statSync(absDir).isDirectory()) {
|
|
13102
|
+
throw new Error(`子包 ${subPkgName} 的目录不存在:${pkg.root}`);
|
|
13103
|
+
}
|
|
13104
|
+
result.push({
|
|
13105
|
+
subPkgName,
|
|
13106
|
+
isStartup: !!item.isStartup,
|
|
13107
|
+
root: pkg.root,
|
|
13108
|
+
});
|
|
13109
|
+
}
|
|
13110
|
+
return result;
|
|
13111
|
+
}
|
|
13112
|
+
|
|
13113
|
+
/** 读出某个 entry 当前的 parallelPreloadSubpackages(保证返回数组)。 */
|
|
13114
|
+
function readEntryConfig(game, entry) {
|
|
13115
|
+
const container = isGameEntry(entry)
|
|
13116
|
+
? game.json
|
|
13117
|
+
: findSubpackage(game, entry);
|
|
13118
|
+
const list = container?.[PRELOAD_FIELD_NAME];
|
|
13119
|
+
return Array.isArray(list) ? list : [];
|
|
13120
|
+
}
|
|
13121
|
+
/**
|
|
13122
|
+
* 按 entry 只读读取源码 game.json 中已有的 parallelPreloadSubpackages,
|
|
13123
|
+
* 用于 UI 初始化配置预览。game.json 不存在 / 字段缺失时返回空数组,不阻塞。
|
|
13124
|
+
*/
|
|
13125
|
+
function readPreloadSubpackagesConfig(entry) {
|
|
13126
|
+
const game = tryReadGameJson();
|
|
13127
|
+
if (!game) {
|
|
13128
|
+
return { entry, parallelPreloadSubpackages: [] };
|
|
13129
|
+
}
|
|
13130
|
+
return { entry, parallelPreloadSubpackages: readEntryConfig(game, entry) };
|
|
13131
|
+
}
|
|
13132
|
+
/**
|
|
13133
|
+
* 列出某个 entry 下可被采集(可预下载)的候选子包,供 UI 基于真实可用包选择。
|
|
13134
|
+
*
|
|
13135
|
+
* 候选 = game.json.subpackages 中 root 目录真实存在、且 name 不等于当前 entry
|
|
13136
|
+
* 的子包(独立分包入口不可预下载自身)。仅供 mock 上报基于真实可用包选择,
|
|
13137
|
+
* 不含 size —— size 在编译期由 ttmg-compile 按压缩加密后体积回填。
|
|
13138
|
+
* game.json 不存在 / 字段缺失时返回空数组,不阻塞。
|
|
13139
|
+
*/
|
|
13140
|
+
function listPreloadSubpackageCandidates(entry) {
|
|
13141
|
+
const game = tryReadGameJson();
|
|
13142
|
+
if (!game) {
|
|
13143
|
+
return { entry, candidates: [], totalSubpackages: 0 };
|
|
13144
|
+
}
|
|
13145
|
+
const subpackages = getSubpackages(game);
|
|
13146
|
+
const candidates = [];
|
|
13147
|
+
for (const sub of subpackages) {
|
|
13148
|
+
const subPkgName = sub?.name;
|
|
13149
|
+
const root = sub?.root;
|
|
13150
|
+
if (typeof subPkgName !== 'string' || !subPkgName)
|
|
13151
|
+
continue;
|
|
13152
|
+
if (subPkgName === entry)
|
|
13153
|
+
continue;
|
|
13154
|
+
if (typeof root !== 'string' || !root)
|
|
13155
|
+
continue;
|
|
13156
|
+
const absDir = path__namespace.isAbsolute(root)
|
|
13157
|
+
? root
|
|
13158
|
+
: path__namespace.join(process.cwd(), root);
|
|
13159
|
+
if (!fs__namespace.existsSync(absDir) || !fs__namespace.statSync(absDir).isDirectory())
|
|
13160
|
+
continue;
|
|
13161
|
+
candidates.push({
|
|
13162
|
+
subPkgName,
|
|
13163
|
+
root,
|
|
13164
|
+
independent: sub.independent === true,
|
|
13165
|
+
});
|
|
13166
|
+
}
|
|
13167
|
+
return { entry, candidates, totalSubpackages: subpackages.length };
|
|
13168
|
+
}
|
|
13169
|
+
/**
|
|
13170
|
+
* 收集快照写入:校验 → 按 entry 覆盖写入对应位置 → 失败回滚。
|
|
13171
|
+
*
|
|
13172
|
+
* 只写入 `subPkgName` + `isStartup`,**不写 size**:size 是压缩+加密后的真实
|
|
13173
|
+
* 下发体积,由编译服务(ttmg-compile)在产出 STTPKG 时回填到编译产物,收集期
|
|
13174
|
+
* 拿不到也不写(见技术方案 3.4 / 3.5)。
|
|
13175
|
+
*
|
|
13176
|
+
* - 写入前校验失败:抛错,不修改 game.json。
|
|
13177
|
+
* - 进入写入阶段后异常:回滚原始 game.json 再抛错。
|
|
13178
|
+
* 返回本次 entry 下最终写入的 parallelPreloadSubpackages。
|
|
13179
|
+
*/
|
|
13180
|
+
function collectPreloadSubpackages(entry, reported) {
|
|
13181
|
+
// readRawGameJson 抛错即「game.json 不存在 / 解析失败」,写入前阶段,不动文件
|
|
13182
|
+
const game = readRawGameJson();
|
|
13183
|
+
const validated = validateCollect(game, entry, reported);
|
|
13184
|
+
const parallelPreloadSubpackages = validated.map(item => ({
|
|
13185
|
+
subPkgName: item.subPkgName,
|
|
13186
|
+
isStartup: item.isStartup,
|
|
13187
|
+
}));
|
|
13188
|
+
let started = false;
|
|
13189
|
+
try {
|
|
13190
|
+
const container = isGameEntry(entry)
|
|
13191
|
+
? game.json
|
|
13192
|
+
: findSubpackage(game, entry);
|
|
13193
|
+
if (!container) {
|
|
13194
|
+
// 独立分包入口已在 validate 阶段确认存在,这里是防御
|
|
13195
|
+
throw new Error(`入口 ${entry} 不存在`);
|
|
13196
|
+
}
|
|
13197
|
+
started = true;
|
|
13198
|
+
container[PRELOAD_FIELD_NAME] = parallelPreloadSubpackages;
|
|
13199
|
+
writeGameJson(game);
|
|
13200
|
+
}
|
|
13201
|
+
catch (err) {
|
|
13202
|
+
if (started) {
|
|
13203
|
+
restoreGameJson(game);
|
|
13204
|
+
}
|
|
13205
|
+
throw err;
|
|
13206
|
+
}
|
|
13207
|
+
return { entry, parallelPreloadSubpackages };
|
|
13208
|
+
}
|
|
13209
|
+
|
|
13210
|
+
/**
|
|
13211
|
+
* 读取某个启动入口已有的 parallelPreloadSubpackages(只读),用于 UI 初始化
|
|
13212
|
+
* 配置预览。对应设计方案的 `getPreloadSubpackagesConfig`。
|
|
13213
|
+
*
|
|
13214
|
+
* query: `entry`(缺省按主包 `__GAME__` 处理)。
|
|
13215
|
+
*/
|
|
13216
|
+
const gamePreloadSubpackagesConfigRoute = {
|
|
13217
|
+
method: 'get',
|
|
13218
|
+
path: '/game/preload-subpackages-config',
|
|
13219
|
+
handler: async (req, res) => {
|
|
13220
|
+
const entry = req.query.entry || GAME_ENTRY;
|
|
13221
|
+
try {
|
|
13222
|
+
const data = readPreloadSubpackagesConfig(entry);
|
|
13223
|
+
res.send({ code: successCode, data });
|
|
13224
|
+
}
|
|
13225
|
+
catch (err) {
|
|
13226
|
+
res.send({
|
|
13227
|
+
code: errorCode,
|
|
13228
|
+
error: { message: err.message },
|
|
13229
|
+
});
|
|
13230
|
+
}
|
|
13231
|
+
},
|
|
13232
|
+
};
|
|
13233
|
+
|
|
13234
|
+
/**
|
|
13235
|
+
* 列出某个启动入口下可被采集(可预下载)的候选子包,供 UI mock 上报时
|
|
13236
|
+
* 基于真实可用包选择,而不是手填名字。候选直接来自 game.json.subpackages
|
|
13237
|
+
* 中 root 真实存在、name 不等于当前 entry 的子包,并附带压缩前体积。
|
|
13238
|
+
*
|
|
13239
|
+
* query: `entry`(缺省按主包 `__GAME__` 处理)。
|
|
13240
|
+
*/
|
|
13241
|
+
const gamePreloadSubpackageCandidatesRoute = {
|
|
13242
|
+
method: 'get',
|
|
13243
|
+
path: '/game/preload-subpackage-candidates',
|
|
13244
|
+
handler: async (req, res) => {
|
|
13245
|
+
const entry = req.query.entry || GAME_ENTRY;
|
|
13246
|
+
try {
|
|
13247
|
+
const data = listPreloadSubpackageCandidates(entry);
|
|
13248
|
+
res.send({ code: successCode, data });
|
|
13249
|
+
}
|
|
13250
|
+
catch (err) {
|
|
13251
|
+
res.send({
|
|
13252
|
+
code: errorCode,
|
|
13253
|
+
error: { message: err.message },
|
|
13254
|
+
});
|
|
13255
|
+
}
|
|
13256
|
+
},
|
|
13257
|
+
};
|
|
13258
|
+
|
|
13259
|
+
/**
|
|
13260
|
+
* 收集快照写入:按 entry 校验 + 补体积 + 覆盖写入源码 game.json 对应位置,
|
|
13261
|
+
* 失败回滚。对应设计方案的 `collectPreloadSubpackages` /
|
|
13262
|
+
* `updatePreloadSubpackagesConfig`(HTTP 请求-响应实现,start 态由 UI 自身的
|
|
13263
|
+
* 处理中状态承载,CLI 只回 success / failed)。
|
|
13264
|
+
*
|
|
13265
|
+
* body: `{ entry, subpackages: [{ subPkgName, isStartup }] }`。
|
|
13266
|
+
*/
|
|
13267
|
+
const gameCollectPreloadSubpackagesRoute = {
|
|
13268
|
+
method: 'post',
|
|
13269
|
+
path: '/game/collect-preload-subpackages',
|
|
13270
|
+
handler: async (req, res) => {
|
|
13271
|
+
const entry = req.body?.entry || GAME_ENTRY;
|
|
13272
|
+
const subpackages = req.body?.subpackages || [];
|
|
13273
|
+
console.log('collect-preload-subpackages', { entry, count: subpackages.length });
|
|
13274
|
+
try {
|
|
13275
|
+
const data = collectPreloadSubpackages(entry, subpackages);
|
|
13276
|
+
res.send({ code: successCode, data });
|
|
13277
|
+
}
|
|
13278
|
+
catch (err) {
|
|
13279
|
+
res.send({
|
|
13280
|
+
code: errorCode,
|
|
13281
|
+
error: { message: err.message },
|
|
13282
|
+
});
|
|
13283
|
+
}
|
|
13284
|
+
},
|
|
13285
|
+
};
|
|
13286
|
+
|
|
12829
13287
|
const routes = [
|
|
12830
13288
|
gameAssetPreviewUrlRoute,
|
|
12831
13289
|
gameAssetsRoute,
|
|
@@ -12838,6 +13296,7 @@ const routes = [
|
|
|
12838
13296
|
gameDirectFeedCardCreateRoute,
|
|
12839
13297
|
gameDirectFeedCardListRoute,
|
|
12840
13298
|
gameDirectFeedCardScenariosRoute,
|
|
13299
|
+
gameDirectFeedCardStatusRoute,
|
|
12841
13300
|
gameUploadRoute,
|
|
12842
13301
|
gameWasmSplitConfigRoute,
|
|
12843
13302
|
gameWasmSplitOptionsRoute,
|
|
@@ -12858,6 +13317,9 @@ const routes = [
|
|
|
12858
13317
|
gamePipelineModeRoute,
|
|
12859
13318
|
gamePipelineModeGetRoute,
|
|
12860
13319
|
gameLanguageRoute,
|
|
13320
|
+
gamePreloadSubpackagesConfigRoute,
|
|
13321
|
+
gamePreloadSubpackageCandidatesRoute,
|
|
13322
|
+
gameCollectPreloadSubpackagesRoute,
|
|
12861
13323
|
];
|
|
12862
13324
|
/**
|
|
12863
13325
|
* Express 4 does not catch rejections from async route handlers — an
|
|
@@ -13358,7 +13820,7 @@ async function upload({ clientKey, note = '--', dir, }) {
|
|
|
13358
13820
|
}
|
|
13359
13821
|
}
|
|
13360
13822
|
|
|
13361
|
-
var version = "0.4.
|
|
13823
|
+
var version = "0.4.1-beta.wasm1";
|
|
13362
13824
|
var pkg = {
|
|
13363
13825
|
version: version};
|
|
13364
13826
|
|