@cimplify/cli 0.15.2 → 0.16.0
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/LICENSE +21 -0
- package/README.md +2 -2
- package/dist/add-SXC6RUPQ.mjs +140 -0
- package/dist/{add-KRIIKAS4.mjs → add-XQVQLD3F.mjs} +4 -4
- package/dist/ai-NQH5L4I6.mjs +17 -0
- package/dist/{assets-CWB72PZG.mjs → assets-FZGVOQXH.mjs} +12 -7
- package/dist/{auth-step-up-Z6KOTTV7.mjs → auth-step-up-67LCPDNL.mjs} +5 -5
- package/dist/build-J4ZUIOPX.mjs +625 -0
- package/dist/{cancel-ZHVVILOU.mjs → cancel-VGJHZJ4J.mjs} +4 -4
- package/dist/capture-RAJ2B3BH.mjs +69 -0
- package/dist/chunk-25ZPFHLF.mjs +51 -0
- package/dist/chunk-47ZXSY47.mjs +52 -0
- package/dist/{chunk-NK6VC264.mjs → chunk-54J3XR4T.mjs} +2 -2
- package/dist/chunk-5HP2INMY.mjs +10631 -0
- package/dist/chunk-7JR6JCFF.mjs +32 -0
- package/dist/chunk-7PQL7E7K.mjs +68 -0
- package/dist/chunk-AKACUJ3W.mjs +81 -0
- package/dist/chunk-BYNE553A.mjs +2945 -0
- package/dist/{chunk-NYXA6X56.mjs → chunk-CLQYPTJ2.mjs} +31 -11
- package/dist/chunk-CNZAOVSK.mjs +104 -0
- package/dist/chunk-CPBESPWK.mjs +5576 -0
- package/dist/{chunk-GRR5U2M5.mjs → chunk-DYWUPHV3.mjs} +11 -7
- package/dist/chunk-EAV2P4N3.mjs +1941 -0
- package/dist/{chunk-KN2YSKXZ.mjs → chunk-EDZSOL5O.mjs} +14 -6
- package/dist/chunk-EGQIPYUN.mjs +696 -0
- package/dist/{chunk-DMLQBAGC.mjs → chunk-EOO4UOQD.mjs} +1 -1
- package/dist/chunk-EYEMPIMP.mjs +74 -0
- package/dist/chunk-G2ZEFSQJ.mjs +46 -0
- package/dist/{chunk-TH4XENKH.mjs → chunk-I4HNHPEM.mjs} +1 -1
- package/dist/chunk-JEVSFNPT.mjs +363 -0
- package/dist/chunk-JKECUAAV.mjs +2169 -0
- package/dist/chunk-KMHORI4Y.mjs +638 -0
- package/dist/chunk-KNPYBZ6B.mjs +502 -0
- package/dist/{chunk-3QUK5BPY.mjs → chunk-KOD4GBQ5.mjs} +8 -1
- package/dist/chunk-KQNZKV6E.mjs +535 -0
- package/dist/{chunk-MXYUAJEW.mjs → chunk-LLRUOIMU.mjs} +5 -2
- package/dist/{chunk-F2UI225N.mjs → chunk-LNMITJ4Z.mjs} +6 -6
- package/dist/chunk-MEM3MDV4.mjs +6463 -0
- package/dist/chunk-NY4YT7O6.mjs +84 -0
- package/dist/{chunk-A6WHBSXZ.mjs → chunk-Q6CF4C3E.mjs} +15 -13
- package/dist/chunk-RP66A52P.mjs +86 -0
- package/dist/{chunk-I2UP7YTG.mjs → chunk-SAXGWS2K.mjs} +1 -1
- package/dist/chunk-SCKQ4XSD.mjs +41 -0
- package/dist/chunk-SF44L2OD.mjs +1152 -0
- package/dist/chunk-SGT5IJVW.mjs +28 -0
- package/dist/{chunk-WEAB6HHQ.mjs → chunk-SS4JTMIV.mjs} +8 -8
- package/dist/chunk-T4MW3PD4.mjs +1456 -0
- package/dist/chunk-THATLMEX.mjs +1227 -0
- package/dist/{chunk-MLE4MVYC.mjs → chunk-TT4YI33G.mjs} +2 -2
- package/dist/chunk-TYV2SVFN.mjs +101 -0
- package/dist/chunk-VHK7WBO3.mjs +189 -0
- package/dist/chunk-VSA44UKR.mjs +391 -0
- package/dist/chunk-XL54SQJ3.mjs +42 -0
- package/dist/{chunk-5KYR6WJB.mjs → chunk-XQAHREKO.mjs} +17 -1
- package/dist/chunk-ZUCDXRFI.mjs +2011 -0
- package/dist/create-4S2EAUR3.mjs +10 -0
- package/dist/credits-NJOAOPPW.mjs +33 -0
- package/dist/{deploy-JE6TUYCL.mjs → deploy-26Y4UGZZ.mjs} +14 -14
- package/dist/dev-MOYURRWQ.mjs +401 -0
- package/dist/dev-WL43Q7VD.mjs +8 -0
- package/dist/dispatcher.mjs +131 -30
- package/dist/{doctor-HFB4IBYK.mjs → doctor-E6ETD2GJ.mjs} +12 -19
- package/dist/{domains-BZKGIDF4.mjs → domains-LOTQMVOR.mjs} +6 -6
- package/dist/engine-runtime-fetch-CIE7S7PB.mjs +10 -0
- package/dist/{env-FZNXKLGL.mjs → env-3PDFC5XF.mjs} +7 -7
- package/dist/env-MOTYOPM5.mjs +19 -0
- package/dist/explain-K5QEVWCD.mjs +251 -0
- package/dist/{inspect-X3M7WSTL.mjs → inspect-Q3E2DHYY.mjs} +3 -3
- package/dist/inspect-SJ4WBAXO.mjs +86 -0
- package/dist/introspect-7F34YN5V.mjs +9 -0
- package/dist/kernel-DEGWMHFL.mjs +662 -0
- package/dist/kernel-plugin.js +6862 -0
- package/dist/keys-W5SN3DPK.mjs +71 -0
- package/dist/lines-XGJYTQ7P.mjs +56 -0
- package/dist/{link-4NM2QMI5.mjs → link-NKUAVNM2.mjs} +6 -5
- package/dist/{lint-DDZ7HUQN.mjs → lint-G4YPQSSO.mjs} +2 -2
- package/dist/{list-2MFOQKKQ.mjs → list-TDR7K2JB.mjs} +3 -3
- package/dist/lock-EJPW2LM3.mjs +57 -0
- package/dist/{login-RLMDEWAL.mjs → login-AYLU7JU7.mjs} +10 -8
- package/dist/{logout-Z57X6HPE.mjs → logout-3M6VMHLH.mjs} +2 -2
- package/dist/{logs-36BLYJHJ.mjs → logs-4EL5SOXN.mjs} +5 -5
- package/dist/logs-4QQGG2XY.mjs +44 -0
- package/dist/mcp-4R2R6E5M.mjs +1077 -0
- package/dist/{mock-EVER34IX.mjs → mock-AK24GSP2.mjs} +2 -2
- package/dist/outdated-JEKCVLTG.mjs +58 -0
- package/dist/port-SBZ7X3EH.mjs +2408 -0
- package/dist/{projects-4XRNHJAH.mjs → projects-7JKTC5WJ.mjs} +9 -7
- package/dist/publish-QQCX47HK.mjs +20 -0
- package/dist/remix-NLLYD3BI.mjs +125 -0
- package/dist/repo-Z4G4EW3N.mjs +9 -0
- package/dist/report-M7F4ZYP7.mjs +2 -0
- package/dist/rollback-2WQ2CW7T.mjs +43 -0
- package/dist/{rollback-WCRWXSE3.mjs → rollback-ZGU32HGT.mjs} +6 -6
- package/dist/scene-HXARHUM7.mjs +324 -0
- package/dist/{status-VA326DWD.mjs → status-C3ZSLZ7Q.mjs} +5 -5
- package/dist/status-WEQOC25K.mjs +18 -0
- package/dist/test-ZPBWV4VX.mjs +233 -0
- package/dist/testers-A3LMXOJJ.mjs +49 -0
- package/dist/{unlink-H3BO3DOQ.mjs → unlink-4G4FE4RJ.mjs} +2 -2
- package/dist/{update-YYOTN7MG.mjs → update-GHKKM5FS.mjs} +4 -4
- package/dist/{whoami-CQPPARRS.mjs → whoami-3QQN6G2A.mjs} +3 -3
- package/dist/workshop-CD74D2LK.mjs +1703 -0
- package/package.json +11 -7
- package/templates/storefront-auto/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-auto/package.json +1 -1
- package/templates/storefront-bakery/package.json +1 -1
- package/templates/storefront-fashion/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-fashion/package.json +1 -1
- package/templates/storefront-grocery/package.json +1 -1
- package/templates/storefront-pharmacy/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-pharmacy/package.json +1 -1
- package/templates/storefront-restaurant/package.json +1 -1
- package/templates/storefront-retail/app/products/[slug]/page.tsx +1 -1
- package/templates/storefront-retail/package.json +1 -1
- package/templates/storefront-services/package.json +1 -1
- package/dist/chunk-ARIDYKLO.mjs +0 -6463
- package/dist/dev-V7JSUVOI.mjs +0 -8
- package/dist/explain-O5AC73EE.mjs +0 -251
- package/dist/introspect-2NGESQZE.mjs +0 -8
- package/dist/repo-X3KZ5R64.mjs +0 -9
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { frameGates, browserRequired, economyGates } from './chunk-KMHORI4Y.mjs';
|
|
3
|
+
import { writeBuildOutput, buildReport, locateChromium, startDevServer, HARDWARE_GPU_ARGS, BROWSER_ARGS, codeLogArgs, warmBrowser, measureCanvas, gateMisses, gateNotices, canvasMeasurements, softwareRenderer, measureLaunches, HOLD_PARAM, SILENT_PARAM } from './chunk-BYNE553A.mjs';
|
|
4
|
+
import './chunk-NY4YT7O6.mjs';
|
|
5
|
+
import './chunk-SF44L2OD.mjs';
|
|
6
|
+
import './chunk-VSA44UKR.mjs';
|
|
7
|
+
import './chunk-EGQIPYUN.mjs';
|
|
8
|
+
import './chunk-SCKQ4XSD.mjs';
|
|
9
|
+
import { readSourceSet, bakerFor, locateAudioPreviewer, buildInteractive, progressiveFromEnv, partsFromEnv, directoryContent, pinnedHermes, formatBytes, withBoot, BOOT_FORMAT_VERSION, MODULE_INDEX_PATH, splitModuleIndex, MODULE_MORE_PATH, requireInteractiveProject, workPath, PART_PATH_SEPARATOR } from './chunk-CPBESPWK.mjs';
|
|
10
|
+
import { sceneCompilerFromFlag } from './chunk-VHK7WBO3.mjs';
|
|
11
|
+
import './chunk-5HP2INMY.mjs';
|
|
12
|
+
import './chunk-JKECUAAV.mjs';
|
|
13
|
+
import { isEngineGame } from './chunk-T4MW3PD4.mjs';
|
|
14
|
+
import './chunk-KQNZKV6E.mjs';
|
|
15
|
+
import { refreshEngineRuntime } from './chunk-G2ZEFSQJ.mjs';
|
|
16
|
+
import { locateToolchain } from './chunk-ZUCDXRFI.mjs';
|
|
17
|
+
import './chunk-EYEMPIMP.mjs';
|
|
18
|
+
import { approvedWasm, EMPTY_WASM_REGISTRY } from './chunk-AKACUJ3W.mjs';
|
|
19
|
+
import './chunk-25ZPFHLF.mjs';
|
|
20
|
+
import { formatDiagnostic } from './chunk-SGT5IJVW.mjs';
|
|
21
|
+
import { parseArgs, flagString, flagBool } from './chunk-XQAHREKO.mjs';
|
|
22
|
+
import './chunk-CLQYPTJ2.mjs';
|
|
23
|
+
import './chunk-TT4YI33G.mjs';
|
|
24
|
+
import { info, dim, failure, CliError, CLI_ERROR_CODE, success, bold, result } from './chunk-KOD4GBQ5.mjs';
|
|
25
|
+
import fs from 'fs/promises';
|
|
26
|
+
import path2 from 'path';
|
|
27
|
+
import { LUAU_SCRIPTS_GROUP, isEngineRestGroup } from '@cimplify/sdk/interactives';
|
|
28
|
+
import { declareEngineHeap, localSession, WebHost, workerEngines } from '@cimplify/sdk/interactives/web';
|
|
29
|
+
import { mkdtempSync, rmSync } from 'fs';
|
|
30
|
+
import { tmpdir } from 'os';
|
|
31
|
+
|
|
32
|
+
var FIRST_RUN_GATE = "first run";
|
|
33
|
+
var FIRST_RUN_BUDGET_MS = 1e4;
|
|
34
|
+
var GUEST_SPECIFIER = "@cimplify/sdk/interactives/guest";
|
|
35
|
+
var CODE_GROUP_ORDINAL = 0;
|
|
36
|
+
var ADMISSION_LOCALE = {
|
|
37
|
+
currency: "USD",
|
|
38
|
+
decimal_separator: ".",
|
|
39
|
+
direction: "ltr",
|
|
40
|
+
first_day_of_week: "monday",
|
|
41
|
+
grouping_separator: ",",
|
|
42
|
+
hour_cycle: "h23",
|
|
43
|
+
language: "en",
|
|
44
|
+
measurement: "metric",
|
|
45
|
+
region: null,
|
|
46
|
+
time_zone: "UTC"
|
|
47
|
+
};
|
|
48
|
+
var ADMISSION_PERSONA = {
|
|
49
|
+
age_range: "adult",
|
|
50
|
+
avatar: null,
|
|
51
|
+
display_name: "Reviewer",
|
|
52
|
+
handle: "reviewer",
|
|
53
|
+
participant_id: "participant_admission"
|
|
54
|
+
};
|
|
55
|
+
function gate(status, detail) {
|
|
56
|
+
return { gates: [{ name: FIRST_RUN_GATE, status, detail }], measurements: null };
|
|
57
|
+
}
|
|
58
|
+
function describe(fault) {
|
|
59
|
+
return fault.source === "guest" ? `${fault.phase}: ${fault.message}` : `${fault.source}: ${fault.message}`;
|
|
60
|
+
}
|
|
61
|
+
function buildBodies(result4) {
|
|
62
|
+
const bodies = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const chunk of result4.chunks) bodies.set(chunk.digest, Buffer.concat(chunk.parts));
|
|
64
|
+
return bodies;
|
|
65
|
+
}
|
|
66
|
+
function packageRefs(pkg) {
|
|
67
|
+
const seen = /* @__PURE__ */ new Set();
|
|
68
|
+
const chunks = [];
|
|
69
|
+
pkg.groups.forEach((group, ordinal) => {
|
|
70
|
+
for (const file of group.files) {
|
|
71
|
+
if (seen.has(file.chunk)) continue;
|
|
72
|
+
seen.add(file.chunk);
|
|
73
|
+
chunks.push({ digest: file.chunk, before_play: group.before_play, asset_group: group.name, size: file.bytes, lazy: isEngineRestGroup(group.name) });
|
|
74
|
+
}
|
|
75
|
+
if (ordinal !== CODE_GROUP_ORDINAL) return;
|
|
76
|
+
for (const module of pkg.runtime) {
|
|
77
|
+
if (seen.has(module.chunk)) continue;
|
|
78
|
+
seen.add(module.chunk);
|
|
79
|
+
chunks.push({ digest: module.chunk, before_play: group.before_play, asset_group: group.name, runtime: { name: module.name, version: module.version } });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return chunks;
|
|
83
|
+
}
|
|
84
|
+
async function firstRunGate(result4) {
|
|
85
|
+
const manifest = result4.manifest;
|
|
86
|
+
const pkg = result4.package;
|
|
87
|
+
if (manifest === null || pkg === null || manifest.profile.kind !== "native") return { gates: [], measurements: null };
|
|
88
|
+
if (!("Worker" in globalThis)) return gate("skipped", "the first-run gate needs a runtime with Web Workers");
|
|
89
|
+
const bodies = buildBodies(result4);
|
|
90
|
+
const chunks = packageRefs(pkg);
|
|
91
|
+
const session = localSession({
|
|
92
|
+
interactive_id: manifest.identity.id ?? "itv_admission",
|
|
93
|
+
profile: "native",
|
|
94
|
+
persona: ADMISSION_PERSONA
|
|
95
|
+
});
|
|
96
|
+
const faults = [];
|
|
97
|
+
const host = new WebHost({
|
|
98
|
+
engines: workerEngines(import.meta.resolve(GUEST_SPECIFIER)),
|
|
99
|
+
transport: session.transport,
|
|
100
|
+
hello: session.hello,
|
|
101
|
+
package: { chunks, ...pkg.engine_heap === void 0 ? {} : { engine_heap: pkg.engine_heap } },
|
|
102
|
+
chunks: {
|
|
103
|
+
async fetch(digest) {
|
|
104
|
+
const body = bodies.get(digest);
|
|
105
|
+
if (body === void 0) throw new Error(`chunk ${digest} is not in the build`);
|
|
106
|
+
return body;
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
locale: ADMISSION_LOCALE,
|
|
110
|
+
limits: { boot_ms: FIRST_RUN_BUDGET_MS },
|
|
111
|
+
on_fault: (fault) => faults.push(fault)
|
|
112
|
+
});
|
|
113
|
+
const rendered = new Promise((resolve) => {
|
|
114
|
+
const stop = host.tree.subscribe(() => {
|
|
115
|
+
if (host.tree.snapshot() === null) return;
|
|
116
|
+
stop();
|
|
117
|
+
resolve();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
let timer = null;
|
|
121
|
+
const deadline = new Promise((_, reject) => {
|
|
122
|
+
timer = setTimeout(() => reject(new Error(`nothing rendered within ${FIRST_RUN_BUDGET_MS} ms`)), FIRST_RUN_BUDGET_MS);
|
|
123
|
+
});
|
|
124
|
+
try {
|
|
125
|
+
await Promise.race([Promise.all([host.start(), rendered]), deadline]);
|
|
126
|
+
return gate("passed", "the client entry started and rendered once");
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const fault = faults[0];
|
|
129
|
+
return gate("failed", fault === void 0 ? String(error instanceof Error ? error.message : error) : describe(fault));
|
|
130
|
+
} finally {
|
|
131
|
+
if (timer !== null) clearTimeout(timer);
|
|
132
|
+
host.dispose();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
var RECORD_GUEST = "@cimplify/sdk/interactives/record";
|
|
136
|
+
var LOOKAHEAD_FRAMES = 180;
|
|
137
|
+
var SETTLE_FRAMES = 30;
|
|
138
|
+
var MAX_RECORD_FRAMES = 60 * 60;
|
|
139
|
+
var RECORD_WALL_BUDGET_MS = 18e4;
|
|
140
|
+
var RECORD_TIERS = ["low", "mid", "high"];
|
|
141
|
+
var READY_MILESTONE = "ready_for_input";
|
|
142
|
+
var FIRST_FRAME_MILESTONE = "first_frame_scheduled";
|
|
143
|
+
var STACK_FRAMES = 6;
|
|
144
|
+
var URGENT_PRIORITY = 0;
|
|
145
|
+
var PREFETCH_KIND = 2;
|
|
146
|
+
var PIPELINE_KIND = 3;
|
|
147
|
+
var DEFERRED_KIND = 5;
|
|
148
|
+
var DEFERRED_PREFIX = "deferred:";
|
|
149
|
+
var PIPELINE_PREFIX = "pipeline:";
|
|
150
|
+
var CHUNK_KIND = 4;
|
|
151
|
+
var CHUNK_PREFIX = "chunk:";
|
|
152
|
+
var BYTECODE_GROUP_PREFIX = "engine:hermes";
|
|
153
|
+
var PART_SEPARATOR = "#part/";
|
|
154
|
+
var CODE_GROUP_ORDINAL2 = 0;
|
|
155
|
+
var SOURCE_SUFFIX = ".js";
|
|
156
|
+
var RECORD_LOCALE = {
|
|
157
|
+
currency: "USD",
|
|
158
|
+
decimal_separator: ".",
|
|
159
|
+
direction: "ltr",
|
|
160
|
+
first_day_of_week: "monday",
|
|
161
|
+
grouping_separator: ",",
|
|
162
|
+
hour_cycle: "h23",
|
|
163
|
+
language: "en",
|
|
164
|
+
measurement: "metric",
|
|
165
|
+
region: null,
|
|
166
|
+
time_zone: "UTC"
|
|
167
|
+
};
|
|
168
|
+
var RECORD_PERSONA = {
|
|
169
|
+
age_range: "adult",
|
|
170
|
+
avatar: null,
|
|
171
|
+
display_name: "Recorder",
|
|
172
|
+
handle: "recorder",
|
|
173
|
+
participant_id: "participant_recorder"
|
|
174
|
+
};
|
|
175
|
+
function filesByPath(pkg) {
|
|
176
|
+
const out = /* @__PURE__ */ new Map();
|
|
177
|
+
for (const group of pkg.groups) for (const file of group.files) if (!out.has(file.path)) out.set(file.path, { path: file.path, digest: file.chunk, group: group.name, bytes: file.bytes });
|
|
178
|
+
return out;
|
|
179
|
+
}
|
|
180
|
+
function isSource(part) {
|
|
181
|
+
return part.path.endsWith(SOURCE_SUFFIX);
|
|
182
|
+
}
|
|
183
|
+
function codeParts(pkg) {
|
|
184
|
+
const code = pkg.groups[CODE_GROUP_ORDINAL2];
|
|
185
|
+
return code === void 0 ? [] : code.files.map((file) => ({ path: file.path, digest: file.chunk, group: code.name, bytes: file.bytes }));
|
|
186
|
+
}
|
|
187
|
+
function describe2(fault) {
|
|
188
|
+
return fault.source === "guest" ? `${fault.phase}: ${fault.message}${fault.stack ? ` (${fault.stack.split("\n").slice(0, STACK_FRAMES).join(" < ")})` : ""}` : `${fault.source}: ${fault.message}`;
|
|
189
|
+
}
|
|
190
|
+
var HOST_ENGINE_PREFIXES = ["engine:", "threads:"];
|
|
191
|
+
var RECORD_ORDER = ["mid", "low", "high"];
|
|
192
|
+
function hostSpecific(group) {
|
|
193
|
+
return isEngineRestGroup(group) || HOST_ENGINE_PREFIXES.some((prefix) => group.startsWith(prefix));
|
|
194
|
+
}
|
|
195
|
+
function touched(touch) {
|
|
196
|
+
if (touch.digest === null || touch.group === null || hostSpecific(touch.group)) return null;
|
|
197
|
+
return { path: touch.path, digest: touch.digest, group: touch.group, bytes: touch.bytes };
|
|
198
|
+
}
|
|
199
|
+
function eagerParts(pkg) {
|
|
200
|
+
const out = /* @__PURE__ */ new Map();
|
|
201
|
+
for (const group of pkg.groups) {
|
|
202
|
+
if (hostSpecific(group.name)) continue;
|
|
203
|
+
for (const file of group.files) {
|
|
204
|
+
const at = file.path.indexOf(PART_SEPARATOR);
|
|
205
|
+
if (at < 0 || file.refinement === true) continue;
|
|
206
|
+
const asset = file.path.slice(0, at);
|
|
207
|
+
const list = out.get(asset) ?? [];
|
|
208
|
+
list.push({ path: file.path, digest: file.chunk, group: group.name, bytes: file.bytes });
|
|
209
|
+
out.set(asset, list);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
function bootSet(record, pkg) {
|
|
215
|
+
if (record.boot_frame === null) return { boot: [], follow: [] };
|
|
216
|
+
const eager = eagerParts(pkg);
|
|
217
|
+
const boot = record.code.filter((part) => !isSource(part));
|
|
218
|
+
const follow = [];
|
|
219
|
+
const seen = new Set(boot.map((part) => part.digest));
|
|
220
|
+
const add = (into, part) => {
|
|
221
|
+
if (seen.has(part.digest)) return;
|
|
222
|
+
seen.add(part.digest);
|
|
223
|
+
into.push(part);
|
|
224
|
+
};
|
|
225
|
+
for (const touch of record.touches) {
|
|
226
|
+
const part = touched(touch);
|
|
227
|
+
if (part === null || touch.frame > record.boot_frame + LOOKAHEAD_FRAMES) continue;
|
|
228
|
+
const into = touch.frame <= record.boot_frame && touch.priority === URGENT_PRIORITY ? boot : follow;
|
|
229
|
+
add(into, part);
|
|
230
|
+
for (const base of eager.get(touch.path) ?? []) add(into, base);
|
|
231
|
+
}
|
|
232
|
+
return { boot, follow };
|
|
233
|
+
}
|
|
234
|
+
function bootPlan(records, pkg) {
|
|
235
|
+
const usable = RECORD_ORDER.flatMap((tier) => records.filter((record) => record.tier === tier && record.boot_frame !== null));
|
|
236
|
+
if (usable.length === 0) return null;
|
|
237
|
+
const boot = [];
|
|
238
|
+
const follow = [];
|
|
239
|
+
const seen = /* @__PURE__ */ new Set();
|
|
240
|
+
const add = (into, part) => {
|
|
241
|
+
if (seen.has(part.digest)) return;
|
|
242
|
+
seen.add(part.digest);
|
|
243
|
+
into.push(part);
|
|
244
|
+
};
|
|
245
|
+
for (const record of usable) for (const part of bootSet(record, pkg).boot) add(boot, part);
|
|
246
|
+
for (const record of usable) for (const part of record.code.filter(isSource)) add(boot, part);
|
|
247
|
+
for (const group of pkg.groups) {
|
|
248
|
+
if (!group.name.startsWith(BYTECODE_GROUP_PREFIX)) continue;
|
|
249
|
+
for (const file of group.files) add(boot, { path: file.path, digest: file.chunk, group: group.name, bytes: file.bytes });
|
|
250
|
+
}
|
|
251
|
+
for (const group of pkg.groups) {
|
|
252
|
+
if (group.name !== LUAU_SCRIPTS_GROUP) continue;
|
|
253
|
+
for (const file of group.files) add(follow, { path: file.path, digest: file.chunk, group: group.name, bytes: file.bytes });
|
|
254
|
+
}
|
|
255
|
+
for (const record of usable) for (const part of bootSet(record, pkg).follow) add(follow, part);
|
|
256
|
+
const pipelines = [...new Set(usable.flatMap((record) => record.pipelines.filter((p) => p.frame <= (record.boot_frame ?? 0) + LOOKAHEAD_FRAMES).map((p) => p.key)))].sort();
|
|
257
|
+
return { boot, follow, pipelines };
|
|
258
|
+
}
|
|
259
|
+
function urgent(mark) {
|
|
260
|
+
return mark.touches.some((touch) => touch.kind !== PREFETCH_KIND && touch.kind !== PIPELINE_KIND && touch.priority === URGENT_PRIORITY);
|
|
261
|
+
}
|
|
262
|
+
async function recordBoot(result4, tier = "mid", guest = import.meta.resolve(RECORD_GUEST)) {
|
|
263
|
+
const pkg = result4.package;
|
|
264
|
+
const manifest = result4.manifest;
|
|
265
|
+
if (pkg === null || manifest === null) throw new Error("the build produced no package to record");
|
|
266
|
+
if (!("Worker" in globalThis)) throw new Error("recording a boot needs a runtime with Web Workers");
|
|
267
|
+
const bodies = buildBodies(result4);
|
|
268
|
+
const paths = filesByPath(pkg);
|
|
269
|
+
const digests = new Map([...paths.values()].map((part) => [part.digest, part]));
|
|
270
|
+
const touches = [];
|
|
271
|
+
const pipelines = [];
|
|
272
|
+
const seen = /* @__PURE__ */ new Set();
|
|
273
|
+
let frames = 0;
|
|
274
|
+
let heapPeak = 0;
|
|
275
|
+
const deferredHits = [];
|
|
276
|
+
let started = false;
|
|
277
|
+
let drawn = false;
|
|
278
|
+
let readyFrame = null;
|
|
279
|
+
let bootFrame = null;
|
|
280
|
+
let busyFrame = 0;
|
|
281
|
+
let end = null;
|
|
282
|
+
let fault = null;
|
|
283
|
+
let finish = () => {
|
|
284
|
+
};
|
|
285
|
+
const finished = new Promise((resolve) => {
|
|
286
|
+
finish = resolve;
|
|
287
|
+
});
|
|
288
|
+
const stop = (why) => {
|
|
289
|
+
if (end !== null) return;
|
|
290
|
+
end = why;
|
|
291
|
+
finish();
|
|
292
|
+
};
|
|
293
|
+
const body = (digest) => {
|
|
294
|
+
const found = bodies.get(digest);
|
|
295
|
+
if (found === void 0) throw new Error(`chunk ${digest} is not in the build`);
|
|
296
|
+
return found;
|
|
297
|
+
};
|
|
298
|
+
const chunks = {
|
|
299
|
+
fetch: async (digest) => body(digest),
|
|
300
|
+
fetchRun: async (digests2) => digests2.map(body)
|
|
301
|
+
};
|
|
302
|
+
const session = localSession({ interactive_id: manifest.identity.id ?? "itv_recorder", profile: "native", persona: RECORD_PERSONA });
|
|
303
|
+
const host = new WebHost({
|
|
304
|
+
engines: workerEngines(guest),
|
|
305
|
+
transport: session.transport,
|
|
306
|
+
hello: session.hello,
|
|
307
|
+
package: { chunks: packageRefs(pkg) },
|
|
308
|
+
chunks,
|
|
309
|
+
locale: RECORD_LOCALE,
|
|
310
|
+
device_tier: tier,
|
|
311
|
+
limits: { boot_ms: RECORD_WALL_BUDGET_MS },
|
|
312
|
+
on_fault: (raised) => {
|
|
313
|
+
fault ??= describe2(raised);
|
|
314
|
+
stop("fault");
|
|
315
|
+
},
|
|
316
|
+
on_milestone: (name) => {
|
|
317
|
+
if (name === READY_MILESTONE && readyFrame === null) readyFrame = frames;
|
|
318
|
+
if (name === FIRST_FRAME_MILESTONE) drawn = true;
|
|
319
|
+
},
|
|
320
|
+
on_frame_mark: (mark) => {
|
|
321
|
+
for (const touch of mark.touches) {
|
|
322
|
+
if (touch.kind === PIPELINE_KIND) {
|
|
323
|
+
pipelines.push({ key: touch.path.slice(PIPELINE_PREFIX.length), frame: mark.frame });
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (touch.kind === DEFERRED_KIND) {
|
|
327
|
+
deferredHits.push({ frame: mark.frame, total: Number(touch.path.slice(DEFERRED_PREFIX.length)) });
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
const part = touch.kind === CHUNK_KIND ? digests.get(touch.path.slice(CHUNK_PREFIX.length)) : paths.get(touch.path);
|
|
331
|
+
const key = part?.digest ?? touch.path;
|
|
332
|
+
if (seen.has(key)) continue;
|
|
333
|
+
seen.add(key);
|
|
334
|
+
touches.push({ path: part?.path ?? touch.path, frame: mark.frame, kind: touch.kind, priority: touch.priority, digest: part?.digest ?? null, group: part?.group ?? null, bytes: part?.bytes ?? 0 });
|
|
335
|
+
}
|
|
336
|
+
frames = mark.frame + 1;
|
|
337
|
+
heapPeak = Math.max(heapPeak, mark.heap ?? 0);
|
|
338
|
+
if (bootFrame === null && readyFrame !== null) bootFrame = readyFrame;
|
|
339
|
+
if (mark.outstanding > 0 || urgent(mark)) busyFrame = mark.frame;
|
|
340
|
+
if (bootFrame === null && started && drawn && mark.frame - busyFrame >= SETTLE_FRAMES) bootFrame = mark.frame;
|
|
341
|
+
if (bootFrame !== null && mark.frame >= bootFrame + LOOKAHEAD_FRAMES) stop(readyFrame !== null ? "ready" : "quiet");
|
|
342
|
+
else if (mark.frame >= MAX_RECORD_FRAMES) stop("limit");
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
const wall = setTimeout(() => stop("limit"), RECORD_WALL_BUDGET_MS);
|
|
346
|
+
try {
|
|
347
|
+
await Promise.race([
|
|
348
|
+
host.start().then(() => {
|
|
349
|
+
started = true;
|
|
350
|
+
}),
|
|
351
|
+
finished
|
|
352
|
+
]);
|
|
353
|
+
await finished;
|
|
354
|
+
} catch (error) {
|
|
355
|
+
fault ??= error instanceof Error ? error.message : String(error);
|
|
356
|
+
stop("fault");
|
|
357
|
+
} finally {
|
|
358
|
+
clearTimeout(wall);
|
|
359
|
+
host.dispose();
|
|
360
|
+
}
|
|
361
|
+
const ended = end ?? "fault";
|
|
362
|
+
const boot_frame = ended === "fault" || ended === "limit" ? null : bootFrame;
|
|
363
|
+
const reached = (until) => deferredHits.filter((h) => h.frame <= until).reduce((most, h) => Math.max(most, h.total), 0);
|
|
364
|
+
const deferred = { before_input: reached(boot_frame ?? Number.MAX_SAFE_INTEGER), total: reached(Number.MAX_SAFE_INTEGER) };
|
|
365
|
+
return { tier, end: ended, boot_frame, frames, code: codeParts(pkg), touches, pipelines, fault, heap_peak: heapPeak, deferred };
|
|
366
|
+
}
|
|
367
|
+
var GATE = "canvas";
|
|
368
|
+
var LOCAL_HOST = "127.0.0.1";
|
|
369
|
+
var ANY_PORT = 0;
|
|
370
|
+
var MISS_SEPARATOR = "; ";
|
|
371
|
+
var THREADED = "isolated";
|
|
372
|
+
var CODE_LOG_PREFIX = "cimplify-code-log-";
|
|
373
|
+
var HARDWARE_LABEL = "hardware GPU";
|
|
374
|
+
var SOFTWARE_LABEL = "SwiftShader";
|
|
375
|
+
async function launchesIn(chromium, url, args, hardware) {
|
|
376
|
+
const browser = await chromium.launch({ headless: true, args });
|
|
377
|
+
try {
|
|
378
|
+
await warmBrowser(browser);
|
|
379
|
+
const page = await browser.newPage();
|
|
380
|
+
if (hardware && await softwareRenderer(page)) return null;
|
|
381
|
+
const launches = await measureLaunches(page, url);
|
|
382
|
+
await page.close();
|
|
383
|
+
return launches;
|
|
384
|
+
} finally {
|
|
385
|
+
await browser.close();
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
function launchSummary(label, launches) {
|
|
389
|
+
return `${label}: cold first frame ${launches.first_frame_ms} ms, warm first frame ${launches.warm.first_frame_ms} ms`;
|
|
390
|
+
}
|
|
391
|
+
function result2(status, detail) {
|
|
392
|
+
return { name: GATE, status, detail };
|
|
393
|
+
}
|
|
394
|
+
async function canvasGates(sourceDir, set, built, toolchain) {
|
|
395
|
+
if (toolchain === null) return { gates: [result2("failed", "the pinned SDK is not installed where the canvas harness can serve it")], measurements: null };
|
|
396
|
+
const chromium = locateChromium([toolchain.base, toolchain.sdkDir]);
|
|
397
|
+
if (typeof chromium === "string") return { gates: [result2("failed", chromium)], measurements: null };
|
|
398
|
+
const server = await startDevServer({
|
|
399
|
+
root: sourceDir,
|
|
400
|
+
host: LOCAL_HOST,
|
|
401
|
+
port: ANY_PORT,
|
|
402
|
+
watch: false,
|
|
403
|
+
build: { set, result: built, lockUpdated: false },
|
|
404
|
+
packagesRoot: toolchain.base
|
|
405
|
+
});
|
|
406
|
+
const threaded = built.package?.engine?.threads === THREADED;
|
|
407
|
+
const codeLogs = threaded ? mkdtempSync(path2.join(tmpdir(), CODE_LOG_PREFIX)) : null;
|
|
408
|
+
let browser = null;
|
|
409
|
+
try {
|
|
410
|
+
const hardware = await launchesIn(chromium, server.url, HARDWARE_GPU_ARGS, true);
|
|
411
|
+
const software = codeLogs === null ? null : await launchesIn(chromium, server.url, BROWSER_ARGS, false);
|
|
412
|
+
browser = await chromium.launch({ headless: true, args: [...BROWSER_ARGS, ...codeLogs === null ? [] : codeLogArgs(codeLogs)] });
|
|
413
|
+
await warmBrowser(browser);
|
|
414
|
+
const page = await browser.newPage();
|
|
415
|
+
const report = await measureCanvas(page, server.url, built.manifest?.identity.name ?? GATE, codeLogs, hardware ?? software);
|
|
416
|
+
await page.close();
|
|
417
|
+
const misses = gateMisses(report);
|
|
418
|
+
const notices = gateNotices(report);
|
|
419
|
+
const measured = canvasMeasurements(report);
|
|
420
|
+
const softwareLaunches = software ?? report.software_launches;
|
|
421
|
+
const launches = hardware === null ? [] : [launchSummary(HARDWARE_LABEL, hardware)];
|
|
422
|
+
if (softwareLaunches !== null) launches.push(launchSummary(SOFTWARE_LABEL, softwareLaunches));
|
|
423
|
+
const summary = [
|
|
424
|
+
`cold first frame ${report.first_frame_ms} ms, warm first frame ${report.warm.first_frame_ms} ms (${hardware === null ? SOFTWARE_LABEL : HARDWARE_LABEL}), frame work p99 ${measured.frame_micros_p99} \xB5s, longest collection ${measured.gc_pause_micros_p99} \xB5s`,
|
|
425
|
+
...launches
|
|
426
|
+
].join(MISS_SEPARATOR);
|
|
427
|
+
if (misses.length > 0) return { gates: [result2("failed", [summary, ...misses].join(MISS_SEPARATOR))], measurements: null };
|
|
428
|
+
return { gates: [result2("passed", [summary, ...notices].join(MISS_SEPARATOR))], measurements: measured };
|
|
429
|
+
} catch (error) {
|
|
430
|
+
return { gates: [result2("failed", `the canvas harness could not run the game: ${error instanceof Error ? error.message : String(error)}`)], measurements: null };
|
|
431
|
+
} finally {
|
|
432
|
+
await browser?.close();
|
|
433
|
+
await server.close();
|
|
434
|
+
if (codeLogs !== null) rmSync(codeLogs, { recursive: true, force: true });
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// src/interactives/harness/offline.ts
|
|
439
|
+
var OFFLINE_GATE = "offline launch";
|
|
440
|
+
var OFFLINE_FIRST_FRAME_MS = 4e3;
|
|
441
|
+
var LOCAL_HOST2 = "127.0.0.1";
|
|
442
|
+
var ANY_PORT2 = 0;
|
|
443
|
+
var LAUNCH_HOOK = "cimplifyLaunch";
|
|
444
|
+
var HOST_START_MARK = "cimplify:host-start";
|
|
445
|
+
var FIRST_FRAME_MARK = "cimplify:first_frame_scheduled";
|
|
446
|
+
var POLL_MS = 20;
|
|
447
|
+
var STEP_TIMEOUT_MS = 3e4;
|
|
448
|
+
var ERRORS = "#errors";
|
|
449
|
+
var NATIVE_ENGINE_FAULT = "needs WebAssembly, which this host does not provide";
|
|
450
|
+
function result3(status, detail) {
|
|
451
|
+
return { name: OFFLINE_GATE, status, detail };
|
|
452
|
+
}
|
|
453
|
+
async function offlineFirstFrame(page, url, budgetMs) {
|
|
454
|
+
await page.goto(`${url}?${HOLD_PARAM}&${SILENT_PARAM}`);
|
|
455
|
+
await page.waitForFunction((hook) => typeof globalThis[hook] === "function", LAUNCH_HOOK, { polling: POLL_MS, timeout: STEP_TIMEOUT_MS });
|
|
456
|
+
await page.evaluate((hook) => {
|
|
457
|
+
void globalThis[hook]();
|
|
458
|
+
}, LAUNCH_HOOK);
|
|
459
|
+
const deadline = Date.now() + budgetMs + STEP_TIMEOUT_MS;
|
|
460
|
+
for (; ; ) {
|
|
461
|
+
const elapsed = await page.evaluate(
|
|
462
|
+
([start, first]) => {
|
|
463
|
+
const begun = performance.getEntriesByName(start)[0];
|
|
464
|
+
const drawn = performance.getEntriesByName(first)[0];
|
|
465
|
+
return begun === void 0 || drawn === void 0 ? null : drawn.startTime - begun.startTime;
|
|
466
|
+
},
|
|
467
|
+
[HOST_START_MARK, FIRST_FRAME_MARK]
|
|
468
|
+
);
|
|
469
|
+
if (elapsed !== null) return Math.round(elapsed);
|
|
470
|
+
const fault = (await page.locator(ERRORS).textContent() ?? "").trim();
|
|
471
|
+
if (fault.length > 0) return fault;
|
|
472
|
+
if (Date.now() > deadline) return null;
|
|
473
|
+
await page.waitForTimeout(POLL_MS);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
async function offlineLaunchGate(sourceDir, set, built, toolchain) {
|
|
477
|
+
if (built.manifest?.profile.game === null || built.manifest?.profile.game === void 0) return result3("skipped", "only games are launched offline");
|
|
478
|
+
if (toolchain === null) return result3("failed", "the pinned SDK is not installed where the offline launch can serve it");
|
|
479
|
+
const chromium = locateChromium([toolchain.base, toolchain.sdkDir]);
|
|
480
|
+
if (typeof chromium === "string") return result3("skipped", chromium);
|
|
481
|
+
const server = await startDevServer({ root: sourceDir, host: LOCAL_HOST2, port: ANY_PORT2, watch: false, build: { set, result: built, lockUpdated: false }, packagesRoot: toolchain.base });
|
|
482
|
+
const browser = await chromium.launch({ headless: true, args: [...BROWSER_ARGS] });
|
|
483
|
+
try {
|
|
484
|
+
await warmBrowser(browser);
|
|
485
|
+
const page = await browser.newPage();
|
|
486
|
+
const ms = await offlineFirstFrame(page, server.url, OFFLINE_FIRST_FRAME_MS);
|
|
487
|
+
await page.close();
|
|
488
|
+
const rule = `with a ctx host that never answers, the first frame is scheduled within ${OFFLINE_FIRST_FRAME_MS} ms of launch, so no service call stands between a player and play`;
|
|
489
|
+
if (typeof ms === "string" && ms.includes(NATIVE_ENGINE_FAULT)) return result3("skipped", "native engine games launch only in the device hosts, which answer every ctx request with unavailable within its deadline");
|
|
490
|
+
if (typeof ms === "string") return result3("failed", `the game faulted with no ctx answers: ${ms}`);
|
|
491
|
+
if (ms === null) return result3("failed", `no frame was ever scheduled; ${rule}: start the scene first and let services attach when they settle`);
|
|
492
|
+
if (ms > OFFLINE_FIRST_FRAME_MS) return result3("failed", `first frame after ${ms} ms; ${rule}: start the scene first and let services attach when they settle`);
|
|
493
|
+
return result3("passed", `first frame after ${ms} ms with no ctx answers (budget ${OFFLINE_FIRST_FRAME_MS} ms)`);
|
|
494
|
+
} catch (error) {
|
|
495
|
+
return result3("failed", `the offline launch could not run the game: ${error instanceof Error ? error.message : String(error)}`);
|
|
496
|
+
} finally {
|
|
497
|
+
await browser.close();
|
|
498
|
+
await server.close();
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/interactives/commands/build.ts
|
|
503
|
+
var BUILD_OUT_DIR = "build";
|
|
504
|
+
var GATES_DIR = "gates";
|
|
505
|
+
var BOOT_FILE = "boot.json";
|
|
506
|
+
async function buildSourceDirectory(options) {
|
|
507
|
+
const set = await readSourceSet(options.sourceDir);
|
|
508
|
+
const wasm = options.approvedWasm.length > 0 ? approvedWasm(options.approvedWasm) : EMPTY_WASM_REGISTRY;
|
|
509
|
+
const toolchain = locateToolchain(null);
|
|
510
|
+
const bakeMode = options.publish ? "required" : "auto";
|
|
511
|
+
const { baker } = bakerFor(bakeMode);
|
|
512
|
+
const { previewer } = locateAudioPreviewer();
|
|
513
|
+
const built = buildInteractive(set, {
|
|
514
|
+
baker,
|
|
515
|
+
bakeMode,
|
|
516
|
+
audioPreviewer: previewer,
|
|
517
|
+
wasm,
|
|
518
|
+
toolchain,
|
|
519
|
+
bytecode: pinnedHermes(toolchain?.base ?? null),
|
|
520
|
+
content: directoryContent(options.sourceDir),
|
|
521
|
+
scenes: await sceneCompilerFromFlag(),
|
|
522
|
+
parts: partsFromEnv() ?? (options.publish === true || options.boot === true ? true : void 0),
|
|
523
|
+
progressive: progressiveFromEnv() ?? (options.publish === true || options.boot === true ? true : void 0)
|
|
524
|
+
});
|
|
525
|
+
let frames = null;
|
|
526
|
+
if (options.gates && built.ok) {
|
|
527
|
+
const first = await firstRunGate(built);
|
|
528
|
+
const runs = isEngineGame(built.manifest?.profile.game ?? null) ? await canvasGates(options.sourceDir, set, built, toolchain) : await frameGates(path2.join(options.outDir, GATES_DIR), built, toolchain, options.publish === true || browserRequired());
|
|
529
|
+
const offline = options.publish ? [await offlineLaunchGate(options.sourceDir, set, built, toolchain)] : [];
|
|
530
|
+
frames = { gates: [...economyGates(built.manifest), ...first.gates, ...runs.gates, ...offline], measurements: runs.measurements };
|
|
531
|
+
}
|
|
532
|
+
const final = options.boot === true && built.ok ? await recordAndPackBoot(options.outDir, built) : built;
|
|
533
|
+
await writeBuildOutput(options.outDir, final, frames);
|
|
534
|
+
return buildReport(final, frames);
|
|
535
|
+
}
|
|
536
|
+
var sum = (parts) => parts.reduce((total, part) => total + part.bytes, 0);
|
|
537
|
+
async function recordAndPackBoot(outDir, built) {
|
|
538
|
+
const records = [];
|
|
539
|
+
for (const tier of RECORD_TIERS) {
|
|
540
|
+
const record = await recordBoot(built, tier);
|
|
541
|
+
const ranges = built.package === null ? { boot: [], follow: [] } : bootSet(record, built.package);
|
|
542
|
+
info(
|
|
543
|
+
dim(
|
|
544
|
+
` boot ${tier.padEnd(4)} ${record.end.padEnd(5)} frame ${record.boot_frame ?? "none"}, ${ranges.boot.length} parts ${formatBytes(sum(ranges.boot))} then ${ranges.follow.length} parts ${formatBytes(sum(ranges.follow))}, ${record.pipelines.length} pipelines, engine heap ${formatBytes(record.heap_peak)}${record.deferred.total > 0 ? `, deferred engine code ran ${record.deferred.before_input} times before input and ${record.deferred.total} in all` : ""}${record.fault ? `, ${record.fault}` : ""}`
|
|
545
|
+
)
|
|
546
|
+
);
|
|
547
|
+
records.push(record);
|
|
548
|
+
}
|
|
549
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
550
|
+
await fs.writeFile(path2.join(outDir, BOOT_FILE), `${JSON.stringify({ records }, null, 2)}
|
|
551
|
+
`);
|
|
552
|
+
const recorded = built.package === null ? null : bootPlan(records, built.package);
|
|
553
|
+
const split = recorded === null ? null : withBootIndex(built, recorded);
|
|
554
|
+
const pkg = split?.pkg ?? built.package;
|
|
555
|
+
const plan = split?.plan ?? recorded;
|
|
556
|
+
const chunks = split?.chunks ?? built.chunks;
|
|
557
|
+
if (pkg === null || plan === null) return built;
|
|
558
|
+
const boot = { version: BOOT_FORMAT_VERSION, parts: [...plan.boot, ...plan.follow].map((part) => part.digest), follow: plan.boot.length, pipelines: plan.pipelines };
|
|
559
|
+
const peaks = Object.fromEntries(records.filter((record) => record.end !== "fault" && record.heap_peak > 0).map((record) => [record.tier, record.heap_peak]));
|
|
560
|
+
const packed = withBoot(pkg, boot, declareEngineHeap(peaks));
|
|
561
|
+
return { ...built, chunks, package: packed.manifest, packageBytes: packed.bytes, packageDigest: packed.digest };
|
|
562
|
+
}
|
|
563
|
+
function assetOf(part) {
|
|
564
|
+
const at = part.path.indexOf(PART_PATH_SEPARATOR);
|
|
565
|
+
return at < 0 ? part.path : part.path.slice(0, at);
|
|
566
|
+
}
|
|
567
|
+
function withBootIndex(built, plan) {
|
|
568
|
+
const pkg = built.package;
|
|
569
|
+
const file = pkg?.groups.flatMap((g) => g.files).find((f) => f.path === MODULE_INDEX_PATH);
|
|
570
|
+
const index = file === void 0 ? void 0 : built.chunks.find((c) => c.digest === file.chunk);
|
|
571
|
+
if (pkg === null || file === void 0 || index === void 0) return null;
|
|
572
|
+
const split = splitModuleIndex(index, new Set(plan.boot.map(assetOf)));
|
|
573
|
+
if (split === null) return null;
|
|
574
|
+
const [boot, more] = [split.boot, split.more];
|
|
575
|
+
const grown = boot.payloadBytes + more.payloadBytes - index.payloadBytes;
|
|
576
|
+
const groups = pkg.groups.map(
|
|
577
|
+
(g) => g.files.some((f) => f === file) ? { ...g, files: g.files.flatMap((f) => f === file ? [{ ...f, chunk: boot.digest, bytes: boot.payloadBytes }, { ...f, path: MODULE_MORE_PATH, chunk: more.digest, bytes: more.payloadBytes }] : [f]) } : g
|
|
578
|
+
);
|
|
579
|
+
const delivery = { ...pkg.delivery, total_bytes: pkg.delivery.total_bytes + grown, before_play_bytes: pkg.delivery.before_play_bytes + grown };
|
|
580
|
+
const swapped = (p) => p.digest === index.digest ? { ...p, digest: boot.digest, bytes: boot.payloadBytes } : p;
|
|
581
|
+
const follow = { path: MODULE_MORE_PATH, digest: more.digest, group: plan.boot.find((p) => p.digest === index.digest)?.group ?? "code", bytes: more.payloadBytes };
|
|
582
|
+
return {
|
|
583
|
+
pkg: { ...pkg, groups, delivery },
|
|
584
|
+
plan: { ...plan, boot: plan.boot.map(swapped), follow: [follow, ...plan.follow.map(swapped)] },
|
|
585
|
+
chunks: [...built.chunks.filter((c) => c !== index), boot, more]
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
async function run(argv) {
|
|
589
|
+
const args = parseArgs(argv);
|
|
590
|
+
const sourceDir = path2.resolve(args.positional[0] ?? process.cwd());
|
|
591
|
+
requireInteractiveProject(sourceDir, "build");
|
|
592
|
+
const outDir = path2.resolve(flagString(args, "out") ?? workPath(sourceDir, BUILD_OUT_DIR));
|
|
593
|
+
const wasmFlag = flagString(args, "approved-wasm");
|
|
594
|
+
await refreshEngineRuntime(sourceDir);
|
|
595
|
+
const report = await buildSourceDirectory({
|
|
596
|
+
sourceDir,
|
|
597
|
+
outDir,
|
|
598
|
+
approvedWasm: wasmFlag ? wasmFlag.split(",").filter((d) => d.length > 0) : [],
|
|
599
|
+
gates: flagBool(args, "gates"),
|
|
600
|
+
publish: flagBool(args, "publish"),
|
|
601
|
+
boot: flagBool(args, "boot")
|
|
602
|
+
});
|
|
603
|
+
for (const d of report.diagnostics) (d.severity === "error" ? failure : info)(formatDiagnostic(d));
|
|
604
|
+
for (const g of report.gates ?? []) (g.status === "failed" ? failure : info)(`${g.name}: ${g.detail}`);
|
|
605
|
+
if (!report.ok) {
|
|
606
|
+
throw new CliError(CLI_ERROR_CODE.BUILD_FAILED, `admission refused the source set; details in ${path2.join(outDir, "result.json")}`, { details: report });
|
|
607
|
+
}
|
|
608
|
+
success(`Built ${bold(report.package_digest ?? "")}`);
|
|
609
|
+
info(dim(` ${report.stats.modules} modules, ${report.stats.assets} assets, ${report.stats.chunks} chunks, ${report.stats.total_bytes} bytes -> ${outDir}`));
|
|
610
|
+
for (const line of tierLines(report)) info(line);
|
|
611
|
+
result(report);
|
|
612
|
+
}
|
|
613
|
+
function tierLines(report) {
|
|
614
|
+
if (report.stats.tiers.length === 0) return [];
|
|
615
|
+
const lines = [dim(" tier download first play on request unique")];
|
|
616
|
+
for (const t of report.stats.tiers) {
|
|
617
|
+
const download = `${formatBytes(t.download_bytes)} / ${formatBytes(t.download_budget)}`;
|
|
618
|
+
const first = `${formatBytes(t.first_play_bytes)} / ${formatBytes(t.first_play_budget)}`;
|
|
619
|
+
const mark = t.fits ? "" : " over budget";
|
|
620
|
+
lines.push(` ${t.tier.padEnd(6)} ${download.padEnd(21)} ${first.padEnd(20)} ${formatBytes(t.lazy_bytes).padEnd(12)} ${formatBytes(t.unique_bytes)}${mark}`);
|
|
621
|
+
}
|
|
622
|
+
return lines;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export { BOOT_FILE, BUILD_OUT_DIR, GATES_DIR, buildSourceDirectory, run as default, tierLines };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { parseArgs } from './chunk-
|
|
3
|
-
import { ApiClient } from './chunk-
|
|
4
|
-
import { readAuth, readProjectLink } from './chunk-
|
|
5
|
-
import { CliError, CLI_ERROR_CODE, success, result } from './chunk-
|
|
2
|
+
import { parseArgs } from './chunk-XQAHREKO.mjs';
|
|
3
|
+
import { ApiClient } from './chunk-CLQYPTJ2.mjs';
|
|
4
|
+
import { readAuth, readProjectLink } from './chunk-TT4YI33G.mjs';
|
|
5
|
+
import { CliError, CLI_ERROR_CODE, success, result } from './chunk-KOD4GBQ5.mjs';
|
|
6
6
|
|
|
7
7
|
// src/commands/cancel.ts
|
|
8
8
|
function cancelEndpoint(businessId, projectId, deploymentId) {
|