@akanjs/cli 2.4.1-rc.2 → 2.4.1-rc.3
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/agent.command-h4afc69n.js +1 -1
- package/{application.command-47mj9qsy.js → application.command-jkmbsd1x.js} +1 -1
- package/{applicationBuildRunner-yz144508.js → applicationBuildRunner-ny84tjhc.js} +1 -1
- package/buildBatch.proc.js +175 -0
- package/{cloud.command-hxcgsf80.js → cloud.command-53j76grx.js} +6 -6
- package/{context.command-nqbtak4f.js → context.command-ds3mka9p.js} +9 -9
- package/incrementalBuilder.proc.js +249 -151
- package/{index-mq6ns0f9.js → index-0hjg9qs5.js} +7 -7
- package/{index-qhtr07v8.js → index-1fpjvqhs.js} +13 -13
- package/{index-sgmas1fc.js → index-dynknvzd.js} +3 -3
- package/{index-73pr2cmy.js → index-m9pca6jv.js} +1 -1
- package/{index-a5rmdgy4.js → index-q7zcac6n.js} +1403 -1405
- package/{index-77crfweb.js → index-rgfxj6qc.js} +176 -10
- package/index.js +9 -9
- package/{localRegistry.command-6z4s13mj.js → localRegistry.command-25sv5hmt.js} +3 -3
- package/{module.command-qrj3kmyz.js → module.command-tnj2bzst.js} +2 -2
- package/package.json +2 -2
- package/{primitive.command-pv9ssmtf.js → primitive.command-q1ycj5vr.js} +3 -3
- package/{scalar.command-kabkd6wd.js → scalar.command-bmrmp8n4.js} +2 -2
- package/{workflow.command-64r6cw0w.js → workflow.command-64550hka.js} +5 -5
- package/{workspace.command-875aj35r.js → workspace.command-pmfxxfew.js} +15 -15
- package/{index-8pkbzj26.js → index-fmky5k3p.js} +3 -3
- package/{index-y3hdhy4p.js → index-gb26e7z0.js} +3 -3
|
@@ -6,40 +6,108 @@ import {
|
|
|
6
6
|
AppExecutor,
|
|
7
7
|
WorkspaceExecutor
|
|
8
8
|
} from "./index-a6sbyy0b.js";
|
|
9
|
+
import"./index-61keag0s.js";
|
|
9
10
|
import {
|
|
10
11
|
AutoImportSync,
|
|
11
|
-
CsrArtifactBuilder,
|
|
12
12
|
DevChangePlanner,
|
|
13
13
|
DevGeneratedIndexSync,
|
|
14
|
-
FontOptimizer,
|
|
15
14
|
GraphClientEntryDiscovery,
|
|
16
15
|
HmrWatcher,
|
|
17
|
-
PagesBundleBuilder,
|
|
18
16
|
RouteClientBuilder,
|
|
19
17
|
SsrBaseArtifactBuilder,
|
|
20
18
|
WatchRootResolver
|
|
21
|
-
} from "./index-
|
|
22
|
-
import"./index-61keag0s.js";
|
|
19
|
+
} from "./index-q7zcac6n.js";
|
|
23
20
|
import"./index-6pz1j0zj.js";
|
|
24
21
|
import"./index-r24hmh0q.js";
|
|
25
22
|
|
|
26
23
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
24
|
+
import path2 from "path";
|
|
25
|
+
import { Logger as Logger2 } from "akanjs/common";
|
|
26
|
+
|
|
27
|
+
// pkgs/@akanjs/devkit/incrementalBuilder/buildBatchRunner.ts
|
|
27
28
|
import path from "path";
|
|
28
29
|
import { Logger } from "akanjs/common";
|
|
30
|
+
|
|
31
|
+
class BuildBatchRunner {
|
|
32
|
+
static #entryCandidates = (workspaceRoot) => [
|
|
33
|
+
path.join(workspaceRoot, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
|
|
34
|
+
path.join(workspaceRoot, "node_modules/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
|
|
35
|
+
path.join(import.meta.dir, "buildBatch.proc.js"),
|
|
36
|
+
path.join(import.meta.dir, "buildBatch.proc.ts")
|
|
37
|
+
];
|
|
38
|
+
#logger = new Logger("BuildBatchRunner");
|
|
39
|
+
#entry = null;
|
|
40
|
+
#workspaceRoot;
|
|
41
|
+
#cwd;
|
|
42
|
+
constructor({ workspaceRoot, cwd }) {
|
|
43
|
+
this.#workspaceRoot = workspaceRoot;
|
|
44
|
+
this.#cwd = cwd;
|
|
45
|
+
}
|
|
46
|
+
async#resolveEntry() {
|
|
47
|
+
if (this.#entry)
|
|
48
|
+
return this.#entry;
|
|
49
|
+
const candidates = BuildBatchRunner.#entryCandidates(this.#workspaceRoot);
|
|
50
|
+
for (const candidate of candidates) {
|
|
51
|
+
if (!await Bun.file(candidate).exists())
|
|
52
|
+
continue;
|
|
53
|
+
this.#entry = candidate;
|
|
54
|
+
return candidate;
|
|
55
|
+
}
|
|
56
|
+
throw new Error(`[build-batch] worker entry not found; looked in: ${candidates.join(", ")}`);
|
|
57
|
+
}
|
|
58
|
+
async run(request, onMessage = () => {
|
|
59
|
+
return;
|
|
60
|
+
}) {
|
|
61
|
+
const started = Date.now();
|
|
62
|
+
const entry = await this.#resolveEntry();
|
|
63
|
+
let result = null;
|
|
64
|
+
const proc = Bun.spawn(["bun", entry, JSON.stringify(request)], {
|
|
65
|
+
cwd: this.#cwd,
|
|
66
|
+
env: process.env,
|
|
67
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
68
|
+
serialization: "advanced",
|
|
69
|
+
ipc: (message2) => {
|
|
70
|
+
if (!message2 || typeof message2 !== "object")
|
|
71
|
+
return;
|
|
72
|
+
if (message2.type === "build-batch-result")
|
|
73
|
+
result = message2.data;
|
|
74
|
+
else
|
|
75
|
+
onMessage(message2);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
const exitCode = await proc.exited;
|
|
79
|
+
if (result) {
|
|
80
|
+
this.#logger.verbose(`[build-batch] generation=${request.generation} needs=${request.needs.join(",")} done in ${Date.now() - started}ms`);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
const message = `build worker exited with code ${exitCode} before reporting a result`;
|
|
84
|
+
this.#logger.error(`[build-batch] generation=${request.generation} ${message}`);
|
|
85
|
+
return {
|
|
86
|
+
generation: request.generation,
|
|
87
|
+
errors: Object.fromEntries(request.needs.map((need) => [need, message])),
|
|
88
|
+
crashed: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
|
|
29
94
|
class IncrementalBuilder {
|
|
30
|
-
#logger = new
|
|
95
|
+
#logger = new Logger2("IncrementalBuilder");
|
|
31
96
|
#app;
|
|
32
97
|
#artifact;
|
|
33
98
|
#watch;
|
|
34
|
-
#cssCompiler;
|
|
35
99
|
#optimizedFonts;
|
|
36
100
|
#discovery;
|
|
101
|
+
#batchRunner;
|
|
37
102
|
#changePlanner;
|
|
38
103
|
#generatedIndexSync;
|
|
39
104
|
#autoImportSync;
|
|
40
105
|
#generation = 0;
|
|
41
106
|
#csrActive = IncrementalBuilder.#csrArmedByEnv();
|
|
42
107
|
#workQueue = Promise.resolve();
|
|
108
|
+
#inFlight = 0;
|
|
109
|
+
#workCount = 0;
|
|
110
|
+
#shuttingDown = false;
|
|
43
111
|
#cssRebuildQueue = Promise.resolve();
|
|
44
112
|
#cssRebuildTimer = null;
|
|
45
113
|
#pendingCssRebuild = null;
|
|
@@ -47,10 +115,13 @@ class IncrementalBuilder {
|
|
|
47
115
|
this.#app = options.app;
|
|
48
116
|
this.#artifact = options.artifact;
|
|
49
117
|
this.#watch = options.watch;
|
|
50
|
-
this.#cssCompiler = options.cssCompiler;
|
|
51
118
|
this.#optimizedFonts = options.optimizedFonts;
|
|
52
119
|
this.#discovery = options.discovery;
|
|
53
120
|
this.#generation = options.initialGeneration ?? 0;
|
|
121
|
+
this.#batchRunner = new BuildBatchRunner({
|
|
122
|
+
workspaceRoot: options.app.workspace.workspaceRoot,
|
|
123
|
+
cwd: options.app.cwdPath
|
|
124
|
+
});
|
|
54
125
|
this.#changePlanner = new DevChangePlanner({ workspaceRoot: options.app.workspace.workspaceRoot });
|
|
55
126
|
this.#generatedIndexSync = new DevGeneratedIndexSync({ workspaceRoot: options.app.workspace.workspaceRoot });
|
|
56
127
|
this.#autoImportSync = new AutoImportSync({ workspaceRoot: options.app.workspace.workspaceRoot });
|
|
@@ -111,6 +182,7 @@ class IncrementalBuilder {
|
|
|
111
182
|
}
|
|
112
183
|
async#enqueueWork(label, fn) {
|
|
113
184
|
const started = Date.now();
|
|
185
|
+
this.#inFlight += 1;
|
|
114
186
|
const run = this.#workQueue.then(fn, fn);
|
|
115
187
|
this.#workQueue = run.then(() => {
|
|
116
188
|
return;
|
|
@@ -120,14 +192,51 @@ class IncrementalBuilder {
|
|
|
120
192
|
try {
|
|
121
193
|
return await run;
|
|
122
194
|
} finally {
|
|
195
|
+
this.#inFlight -= 1;
|
|
196
|
+
this.#workCount += 1;
|
|
123
197
|
this.#logger.verbose(`[work-queue] ${label} finished in ${Date.now() - started}ms`);
|
|
198
|
+
this.#reportMetrics();
|
|
124
199
|
}
|
|
125
200
|
}
|
|
201
|
+
get #idle() {
|
|
202
|
+
return this.#inFlight === 0 && this.#cssRebuildTimer === null;
|
|
203
|
+
}
|
|
204
|
+
#reportMetrics() {
|
|
205
|
+
if (!this.#idle || this.#shuttingDown)
|
|
206
|
+
return;
|
|
207
|
+
process.send?.({
|
|
208
|
+
type: "builder-metrics",
|
|
209
|
+
data: { rssBytes: process.memoryUsage.rss(), generation: this.#generation, workCount: this.#workCount }
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async shutdown(reason) {
|
|
213
|
+
if (this.#shuttingDown)
|
|
214
|
+
return;
|
|
215
|
+
this.#shuttingDown = true;
|
|
216
|
+
const started = Date.now();
|
|
217
|
+
this.#logger.info(`shutdown requested (${reason}); draining ${this.#inFlight} work item(s)`);
|
|
218
|
+
if (this.#cssRebuildTimer) {
|
|
219
|
+
clearTimeout(this.#cssRebuildTimer);
|
|
220
|
+
this.#cssRebuildTimer = null;
|
|
221
|
+
this.#pendingCssRebuild = null;
|
|
222
|
+
}
|
|
223
|
+
await this.#workQueue.catch(() => {
|
|
224
|
+
return;
|
|
225
|
+
});
|
|
226
|
+
await this.#cssRebuildQueue.catch(() => {
|
|
227
|
+
return;
|
|
228
|
+
});
|
|
229
|
+
this.#logger.info(`drained in ${Date.now() - started}ms; exiting for recycle`);
|
|
230
|
+
process.exit(0);
|
|
231
|
+
}
|
|
232
|
+
get shuttingDown() {
|
|
233
|
+
return this.#shuttingDown;
|
|
234
|
+
}
|
|
126
235
|
batchTouchesPagesTree(appDir, batch) {
|
|
127
|
-
const absAppDir =
|
|
236
|
+
const absAppDir = path2.resolve(appDir);
|
|
128
237
|
for (const f of batch.files) {
|
|
129
|
-
const abs =
|
|
130
|
-
if (!abs.startsWith(`${absAppDir}${
|
|
238
|
+
const abs = path2.resolve(f);
|
|
239
|
+
if (!abs.startsWith(`${absAppDir}${path2.sep}`) && abs !== absAppDir)
|
|
131
240
|
continue;
|
|
132
241
|
if (/\.(tsx|ts|jsx|js)$/.test(abs))
|
|
133
242
|
return true;
|
|
@@ -135,67 +244,22 @@ class IncrementalBuilder {
|
|
|
135
244
|
return false;
|
|
136
245
|
}
|
|
137
246
|
async batchMayChangePageKeys(appDir, batch) {
|
|
138
|
-
const absAppDir =
|
|
139
|
-
const pageKeys = new Set((await this.#app.getPageKeys()).map((key) =>
|
|
247
|
+
const absAppDir = path2.resolve(appDir);
|
|
248
|
+
const pageKeys = new Set((await this.#app.getPageKeys()).map((key) => path2.normalize(key)));
|
|
140
249
|
for (const f of batch.files) {
|
|
141
|
-
const abs =
|
|
142
|
-
if (!abs.startsWith(`${absAppDir}${
|
|
250
|
+
const abs = path2.resolve(f);
|
|
251
|
+
if (!abs.startsWith(`${absAppDir}${path2.sep}`) && abs !== absAppDir)
|
|
143
252
|
continue;
|
|
144
253
|
if (!/\.(tsx|ts|jsx|js)$/.test(abs))
|
|
145
254
|
continue;
|
|
146
|
-
const rel =
|
|
255
|
+
const rel = path2.normalize(path2.relative(absAppDir, abs));
|
|
147
256
|
if (!await Bun.file(abs).exists() || !pageKeys.has(rel))
|
|
148
257
|
return true;
|
|
149
258
|
}
|
|
150
259
|
return false;
|
|
151
260
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const cssByBasePathStarted = Date.now();
|
|
155
|
-
const cssByBasePath = await this.#cssCompiler.getCssByBasePath({ refresh });
|
|
156
|
-
this.#logger.verbose(`css-get-by-base-path ok (${Date.now() - cssByBasePathStarted}ms)`);
|
|
157
|
-
const fontStarted = Date.now();
|
|
158
|
-
const optimizedFonts = await this.#getOptimizedFonts(changedFiles ?? []);
|
|
159
|
-
this.#logger.verbose(`font-assets ready (${Date.now() - fontStarted}ms)`);
|
|
160
|
-
const cssAssetEntries = [];
|
|
161
|
-
const cssBase64ByUrl = {};
|
|
162
|
-
await Promise.all(Object.entries(cssByBasePath).flatMap(([basePath, baseCssText]) => {
|
|
163
|
-
const cssText = [baseCssText, optimizedFonts.css].filter(Boolean).join(`
|
|
164
|
-
`);
|
|
165
|
-
if (!cssText)
|
|
166
|
-
return [];
|
|
167
|
-
return [
|
|
168
|
-
(async () => {
|
|
169
|
-
const cssAssetName = basePath || "root";
|
|
170
|
-
const cssHash = Bun.hash(`${basePath}
|
|
171
|
-
${cssText}`).toString(36);
|
|
172
|
-
const cssRelPath = `styles/${cssAssetName}-${cssHash}.css`;
|
|
173
|
-
const cssUrl = `/_akan/styles/${cssAssetName}-${cssHash}.css`;
|
|
174
|
-
await Bun.write(path.join(artifactDir, cssRelPath), cssText);
|
|
175
|
-
cssAssetEntries.push([basePath, { cssUrl, cssRelPath }]);
|
|
176
|
-
cssBase64ByUrl[cssUrl] = Buffer.from(new TextEncoder().encode(cssText)).toString("base64");
|
|
177
|
-
})()
|
|
178
|
-
];
|
|
179
|
-
}));
|
|
180
|
-
const cssAssets = Object.fromEntries(cssAssetEntries);
|
|
181
|
-
if (JSON.stringify(this.#artifact.cssAssets ?? {}) === JSON.stringify(cssAssets)) {
|
|
182
|
-
this.#logger.verbose("css-rebuild unchanged assets; broadcast skipped");
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
this.#artifact = { ...this.#artifact, cssAssets };
|
|
186
|
-
this.#logger.verbose(`css-compile ok assets=${Object.keys(cssAssets).length} (${Date.now() - cssStarted}ms)`);
|
|
187
|
-
process.send?.({
|
|
188
|
-
type: "css-updated",
|
|
189
|
-
data: {
|
|
190
|
-
cssAssets,
|
|
191
|
-
cssBase64ByUrl,
|
|
192
|
-
generation,
|
|
193
|
-
changedFiles
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
scheduleCssRebuild(artifactDir, { refresh, generation, changedFiles }) {
|
|
198
|
-
this.#pendingCssRebuild = { artifactDir, refresh, generation, changedFiles };
|
|
261
|
+
scheduleCssRebuild({ generation, changedFiles }) {
|
|
262
|
+
this.#pendingCssRebuild = { generation, changedFiles };
|
|
199
263
|
if (this.#cssRebuildTimer)
|
|
200
264
|
clearTimeout(this.#cssRebuildTimer);
|
|
201
265
|
this.#cssRebuildTimer = setTimeout(() => {
|
|
@@ -204,15 +268,13 @@ ${cssText}`).toString(36);
|
|
|
204
268
|
this.#pendingCssRebuild = null;
|
|
205
269
|
if (!next)
|
|
206
270
|
return;
|
|
271
|
+
this.#inFlight += 1;
|
|
207
272
|
this.#cssRebuildQueue = this.#cssRebuildQueue.then(async () => {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
changedFiles: next.changedFiles
|
|
273
|
+
await this.#runBatch({
|
|
274
|
+
generation: next.generation ?? this.#generation,
|
|
275
|
+
needs: ["css"],
|
|
276
|
+
changedFiles: next.changedFiles ?? []
|
|
213
277
|
});
|
|
214
|
-
this.#sendBuildStatus("css", { generation: next.generation, ok: true, files: next.changedFiles });
|
|
215
|
-
this.#logger.verbose(`css-rebuild checked (${Date.now() - started}ms)`);
|
|
216
278
|
}).catch((err) => {
|
|
217
279
|
const message = err instanceof Error ? err.message : String(err);
|
|
218
280
|
this.#logger.error(`css-rebuild failed: ${message}`);
|
|
@@ -222,29 +284,12 @@ ${cssText}`).toString(36);
|
|
|
222
284
|
files: next.changedFiles,
|
|
223
285
|
message
|
|
224
286
|
});
|
|
287
|
+
}).finally(() => {
|
|
288
|
+
this.#inFlight -= 1;
|
|
289
|
+
this.#reportMetrics();
|
|
225
290
|
});
|
|
226
291
|
}, 150);
|
|
227
292
|
}
|
|
228
|
-
async#getOptimizedFonts(changedFiles) {
|
|
229
|
-
if (!this.#shouldReoptimizeFonts(changedFiles)) {
|
|
230
|
-
this.#logger.verbose(`font-optimize cached files=${this.#optimizedFonts.files.length}`);
|
|
231
|
-
return this.#optimizedFonts;
|
|
232
|
-
}
|
|
233
|
-
const started = Date.now();
|
|
234
|
-
this.#optimizedFonts = await new FontOptimizer(this.#app, "start").optimize();
|
|
235
|
-
this.#logger.verbose(`font-optimize ok files=${this.#optimizedFonts.files.length} (${Date.now() - started}ms)`);
|
|
236
|
-
return this.#optimizedFonts;
|
|
237
|
-
}
|
|
238
|
-
#shouldReoptimizeFonts(changedFiles) {
|
|
239
|
-
if (changedFiles.length === 0)
|
|
240
|
-
return false;
|
|
241
|
-
return changedFiles.some((file) => {
|
|
242
|
-
const normalized = path.resolve(file);
|
|
243
|
-
if (/\.(woff2?|ttf|otf)$/i.test(normalized))
|
|
244
|
-
return true;
|
|
245
|
-
return this.#optimizedFonts.files.some((fontFile) => path.resolve(fontFile) === normalized);
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
293
|
async installWatcher() {
|
|
249
294
|
const [appDir, artifactDir] = [`${this.#app.cwdPath}/page`, this.#artifactDir];
|
|
250
295
|
const roots = await new WatchRootResolver(this.#app).resolve();
|
|
@@ -306,42 +351,60 @@ ${cssText}`).toString(36);
|
|
|
306
351
|
} else if (kinds.includes("code") && rebuildClient && this.batchTouchesPagesTree(appDir, expandedBatch)) {
|
|
307
352
|
this.#logger.verbose("pageKeys refresh skipped; changed page source cannot add/remove a route key");
|
|
308
353
|
}
|
|
309
|
-
|
|
310
|
-
try {
|
|
311
|
-
const started = Date.now();
|
|
312
|
-
await new CsrArtifactBuilder(this.#app).build();
|
|
313
|
-
this.#sendBuildStatus("csr", { generation, ok: true, files });
|
|
314
|
-
this.#logger.verbose(`csr-rebundle ok (${Date.now() - started}ms)`);
|
|
315
|
-
} catch (err) {
|
|
316
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
317
|
-
this.#logger.error(`csr-rebundle failed: ${message}`);
|
|
318
|
-
this.#sendBuildStatus("csr", { generation, ok: false, files, message });
|
|
319
|
-
}
|
|
320
|
-
} else if (kinds.includes("code") && rebuildClient) {
|
|
321
|
-
this.#logger.verbose(`csr-rebundle skipped; request /__csr or ?csr=true (or set AKAN_DEV_CSR_REBUILD=1) to enable per-save CSR rebuilds`);
|
|
322
|
-
}
|
|
323
|
-
process.send?.(event);
|
|
354
|
+
const needs = [];
|
|
324
355
|
if (kinds.includes("code") && rebuildClient) {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
});
|
|
332
|
-
this.#sendBuildStatus("pages", { generation, ok: true, files });
|
|
333
|
-
this.#logger.verbose(`pages-rebundle ok buildId=${next.buildId} (${Date.now() - started}ms)`);
|
|
334
|
-
} catch (err) {
|
|
335
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
336
|
-
this.#logger.error(`pages-rebundle failed: ${message}`);
|
|
337
|
-
this.#sendBuildStatus("pages", { generation, ok: false, files, message });
|
|
338
|
-
}
|
|
356
|
+
if (this.#shouldRebuildCsr())
|
|
357
|
+
needs.push("csr");
|
|
358
|
+
else
|
|
359
|
+
this.#logger.verbose(`csr-rebundle skipped; request /__csr or ?csr=true (or set AKAN_DEV_CSR_REBUILD=1) to enable per-save CSR rebuilds`);
|
|
360
|
+
needs.push("pages");
|
|
361
|
+
needs.push("css");
|
|
339
362
|
}
|
|
340
|
-
|
|
341
|
-
|
|
363
|
+
process.send?.(event);
|
|
364
|
+
if (needs.length > 0)
|
|
365
|
+
await this.#runBatch({ generation, needs, changedFiles: files });
|
|
366
|
+
else if (kinds.includes("css")) {
|
|
367
|
+
this.scheduleCssRebuild({ generation, changedFiles: files });
|
|
342
368
|
this.#logger.verbose(`css-rebuild scheduled generation=${generation}`);
|
|
343
369
|
}
|
|
344
370
|
}
|
|
371
|
+
async#runBatch({
|
|
372
|
+
generation,
|
|
373
|
+
needs,
|
|
374
|
+
changedFiles
|
|
375
|
+
}) {
|
|
376
|
+
const started = Date.now();
|
|
377
|
+
const result = await this.#batchRunner.run(await this.#batchRequest({ generation, needs, changedFiles }), (msg) => process.send?.(msg));
|
|
378
|
+
if (result.optimizedFonts)
|
|
379
|
+
this.#optimizedFonts = result.optimizedFonts;
|
|
380
|
+
if (result.cssAssets)
|
|
381
|
+
this.#artifact = { ...this.#artifact, cssAssets: result.cssAssets };
|
|
382
|
+
if (result.crashed) {
|
|
383
|
+
for (const need of needs)
|
|
384
|
+
this.#sendBuildStatus(need, { generation, ok: false, files: changedFiles, message: result.errors[need] });
|
|
385
|
+
}
|
|
386
|
+
if (needs.includes("css"))
|
|
387
|
+
this.#logger.verbose(`css-rebuild checked (${Date.now() - started}ms)`);
|
|
388
|
+
return result;
|
|
389
|
+
}
|
|
390
|
+
async#batchRequest({
|
|
391
|
+
generation,
|
|
392
|
+
needs,
|
|
393
|
+
changedFiles
|
|
394
|
+
}) {
|
|
395
|
+
return {
|
|
396
|
+
appName: this.#app.name,
|
|
397
|
+
workspaceRoot: this.#app.workspace.workspaceRoot,
|
|
398
|
+
repoName: this.#app.workspace.repoName,
|
|
399
|
+
generation,
|
|
400
|
+
needs,
|
|
401
|
+
changedFiles,
|
|
402
|
+
pageKeys: await this.#app.getPageKeys(),
|
|
403
|
+
optimizedFonts: this.#optimizedFonts,
|
|
404
|
+
cssAssets: this.#artifact.cssAssets ?? null,
|
|
405
|
+
artifactDir: path2.resolve(this.#artifactDir)
|
|
406
|
+
};
|
|
407
|
+
}
|
|
345
408
|
async boot() {
|
|
346
409
|
if (this.#watch)
|
|
347
410
|
await this.installWatcher();
|
|
@@ -351,34 +414,45 @@ ${cssText}`).toString(36);
|
|
|
351
414
|
async announceRecoveredState(changedFiles) {
|
|
352
415
|
const generation = ++this.#generation;
|
|
353
416
|
await this.#enqueueWork("boot-recovered", async () => {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
this.#
|
|
364
|
-
this.#
|
|
417
|
+
await this.#runBatch({ generation, needs: ["pages", "css"], changedFiles });
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
async announceBootState() {
|
|
421
|
+
const generation = ++this.#generation;
|
|
422
|
+
const reason = "builder-recycle";
|
|
423
|
+
process.send?.({
|
|
424
|
+
type: "pages-updated",
|
|
425
|
+
data: {
|
|
426
|
+
bundlePath: this.#artifact.pagesBundlePath,
|
|
427
|
+
buildId: this.#artifact.pagesBundleBuildId,
|
|
428
|
+
generation,
|
|
429
|
+
changedFiles: [],
|
|
430
|
+
reason
|
|
365
431
|
}
|
|
366
|
-
this.scheduleCssRebuild(this.#artifactDir, { refresh: true, generation, changedFiles });
|
|
367
432
|
});
|
|
433
|
+
const cssAssets = this.#artifact.cssAssets ?? {};
|
|
434
|
+
const cssBase64ByUrl = Object.fromEntries(await Promise.all(Object.values(cssAssets).map(async ({ cssUrl, cssRelPath }) => [
|
|
435
|
+
cssUrl,
|
|
436
|
+
Buffer.from(await Bun.file(path2.join(this.#artifactDir, cssRelPath)).arrayBuffer()).toString("base64")
|
|
437
|
+
])));
|
|
438
|
+
process.send?.({
|
|
439
|
+
type: "css-updated",
|
|
440
|
+
data: { cssAssets, cssBase64ByUrl, generation, changedFiles: [], reason }
|
|
441
|
+
});
|
|
442
|
+
this.#logger.verbose(`announced boot state after recycle generation=${generation}`);
|
|
368
443
|
}
|
|
369
444
|
async handleBuildCsr(msg) {
|
|
370
445
|
return this.#enqueueWork("build-csr", async () => {
|
|
371
446
|
const started = Date.now();
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
this.#logger.
|
|
376
|
-
return { type: "build-csr-res", id: msg.id, ok:
|
|
377
|
-
} catch (err) {
|
|
378
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
379
|
-
this.#logger.error(`csr-build failed: ${message}`);
|
|
380
|
-
return { type: "build-csr-res", id: msg.id, ok: false, error: message };
|
|
447
|
+
const result = await this.#batchRunner.run(await this.#batchRequest({ generation: this.#generation, needs: ["csr"], changedFiles: [] }));
|
|
448
|
+
const error = result.errors.csr;
|
|
449
|
+
if (error) {
|
|
450
|
+
this.#logger.error(`csr-build failed: ${error}`);
|
|
451
|
+
return { type: "build-csr-res", id: msg.id, ok: false, error };
|
|
381
452
|
}
|
|
453
|
+
this.#csrActive = true;
|
|
454
|
+
this.#logger.info(`csr-build ok on demand (${Date.now() - started}ms); rebuilding CSR on every save now`);
|
|
455
|
+
return { type: "build-csr-res", id: msg.id, ok: true };
|
|
382
456
|
});
|
|
383
457
|
}
|
|
384
458
|
#shouldRebuildCsr() {
|
|
@@ -388,11 +462,21 @@ ${cssText}`).toString(36);
|
|
|
388
462
|
return process.env.AKAN_DEV_CSR_REBUILD === "1";
|
|
389
463
|
}
|
|
390
464
|
static async#buildBootDeps(app) {
|
|
391
|
-
const { artifact,
|
|
392
|
-
if (IncrementalBuilder.#csrArmedByEnv())
|
|
393
|
-
await new CsrArtifactBuilder(app).build();
|
|
465
|
+
const { artifact, optimizedFonts } = await new SsrBaseArtifactBuilder(app).build();
|
|
394
466
|
const discovery = await GraphClientEntryDiscovery.create(app);
|
|
395
|
-
return { artifact,
|
|
467
|
+
return { artifact, optimizedFonts, discovery };
|
|
468
|
+
}
|
|
469
|
+
async rearmCsrFromEnv() {
|
|
470
|
+
if (!IncrementalBuilder.#csrArmedByEnv())
|
|
471
|
+
return;
|
|
472
|
+
this.#csrActive = true;
|
|
473
|
+
await this.#enqueueWork("build-csr-rearm", async () => {
|
|
474
|
+
const result = await this.#batchRunner.run(await this.#batchRequest({ generation: this.#generation, needs: ["csr"], changedFiles: [] }));
|
|
475
|
+
if (result.errors.csr)
|
|
476
|
+
this.#logger.error(`csr-rearm failed: ${result.errors.csr}`);
|
|
477
|
+
else
|
|
478
|
+
this.#logger.verbose("csr-rearm ok; this session had CSR armed before the builder restarted");
|
|
479
|
+
});
|
|
396
480
|
}
|
|
397
481
|
static #recoverBoot(app, bootError, logger) {
|
|
398
482
|
const firstMessage = bootError instanceof Error ? bootError.message : String(bootError);
|
|
@@ -436,7 +520,7 @@ ${cssText}`).toString(36);
|
|
|
436
520
|
});
|
|
437
521
|
}
|
|
438
522
|
static async main() {
|
|
439
|
-
const logger = new
|
|
523
|
+
const logger = new Logger2("IncrementalBuilder");
|
|
440
524
|
const { appName, repoName, workspaceRoot } = WorkspaceExecutor.getBaseDevEnv();
|
|
441
525
|
if (!workspaceRoot || !appName)
|
|
442
526
|
throw new Error("AKAN_WORKSPACE_ROOT or AKAN_PUBLIC_APP_NAME is not set");
|
|
@@ -445,20 +529,31 @@ ${cssText}`).toString(36);
|
|
|
445
529
|
const watch = process.env.AKAN_WATCH !== "0";
|
|
446
530
|
let builder = null;
|
|
447
531
|
const bootingError = "builder is recovering from a failed boot build; retry after the build error is fixed";
|
|
532
|
+
const recyclingError = "builder is recycling to release bundler memory; retry after it restarts";
|
|
448
533
|
process.on("message", (msg) => {
|
|
449
534
|
if (!msg || typeof msg !== "object")
|
|
450
535
|
return;
|
|
451
|
-
if (msg.type === "
|
|
536
|
+
if (msg.type === "builder-shutdown") {
|
|
452
537
|
if (!builder) {
|
|
453
|
-
|
|
538
|
+
logger.warn(`ignoring shutdown request (${msg.reason}); builder is still recovering from a failed boot`);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
builder.shutdown(msg.reason);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (msg.type === "build-route") {
|
|
545
|
+
const error = builder?.shuttingDown ? recyclingError : bootingError;
|
|
546
|
+
if (!builder || builder.shuttingDown) {
|
|
547
|
+
process.send?.({ type: "build-route-res", id: msg.id, ok: false, error });
|
|
454
548
|
return;
|
|
455
549
|
}
|
|
456
550
|
builder.handleBuildRoute(msg).then((res) => process.send?.(res));
|
|
457
551
|
return;
|
|
458
552
|
}
|
|
459
553
|
if (msg.type === "build-csr") {
|
|
460
|
-
|
|
461
|
-
|
|
554
|
+
const error = builder?.shuttingDown ? recyclingError : bootingError;
|
|
555
|
+
if (!builder || builder.shuttingDown) {
|
|
556
|
+
process.send?.({ type: "build-csr-res", id: msg.id, ok: false, error });
|
|
462
557
|
return;
|
|
463
558
|
}
|
|
464
559
|
builder.handleBuildCsr(msg).then((res) => process.send?.(res));
|
|
@@ -481,6 +576,9 @@ ${cssText}`).toString(36);
|
|
|
481
576
|
await builder.boot();
|
|
482
577
|
if (recoveredFiles)
|
|
483
578
|
await builder.announceRecoveredState(recoveredFiles);
|
|
579
|
+
else if (process.env.AKAN_BUILDER_RECYCLED === "1")
|
|
580
|
+
await builder.announceBootState();
|
|
581
|
+
await builder.rearmCsrFromEnv();
|
|
484
582
|
}
|
|
485
583
|
}
|
|
486
584
|
IncrementalBuilder.main().catch((err) => {
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
CloudRunner
|
|
4
|
-
} from "./index-
|
|
5
|
-
import {
|
|
6
|
-
ApplicationScript
|
|
7
|
-
} from "./index-77crfweb.js";
|
|
8
|
-
import {
|
|
9
|
-
PackageScript
|
|
10
|
-
} from "./index-n6h3482q.js";
|
|
4
|
+
} from "./index-dynknvzd.js";
|
|
11
5
|
import {
|
|
12
6
|
AiSession,
|
|
13
7
|
CloudApi,
|
|
14
8
|
GlobalConfig
|
|
15
9
|
} from "./index-5vvwc0cz.js";
|
|
10
|
+
import {
|
|
11
|
+
ApplicationScript
|
|
12
|
+
} from "./index-rgfxj6qc.js";
|
|
13
|
+
import {
|
|
14
|
+
PackageScript
|
|
15
|
+
} from "./index-n6h3482q.js";
|
|
16
16
|
import {
|
|
17
17
|
script
|
|
18
18
|
} from "./index-hdqztm58.js";
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
RepairRunner
|
|
4
|
+
} from "./index-85msc0wg.js";
|
|
2
5
|
import {
|
|
3
6
|
WorkflowRunner
|
|
4
7
|
} from "./index-45aj5ry0.js";
|
|
5
8
|
import {
|
|
6
9
|
ScalarScript
|
|
7
|
-
} from "./index-
|
|
10
|
+
} from "./index-gb26e7z0.js";
|
|
8
11
|
import {
|
|
9
12
|
PrimitiveScript
|
|
10
|
-
} from "./index-
|
|
13
|
+
} from "./index-m9pca6jv.js";
|
|
11
14
|
import {
|
|
12
|
-
|
|
13
|
-
} from "./index-
|
|
15
|
+
ModuleScript
|
|
16
|
+
} from "./index-fmky5k3p.js";
|
|
14
17
|
import {
|
|
15
18
|
isPlaceholderAppId
|
|
16
19
|
} from "./index-1xdrsbry.js";
|
|
@@ -25,15 +28,6 @@ import {
|
|
|
25
28
|
resourceList,
|
|
26
29
|
upsertCodexMcpServerBlock
|
|
27
30
|
} from "./index-8rc0bm04.js";
|
|
28
|
-
import {
|
|
29
|
-
getMobileTargets
|
|
30
|
-
} from "./index-76rn3g2c.js";
|
|
31
|
-
import {
|
|
32
|
-
ModuleScript
|
|
33
|
-
} from "./index-8pkbzj26.js";
|
|
34
|
-
import {
|
|
35
|
-
Prompter
|
|
36
|
-
} from "./index-qaq13qk3.js";
|
|
37
31
|
import {
|
|
38
32
|
buildAkanModuleContextIndex,
|
|
39
33
|
createWorkflowBaselineSummary,
|
|
@@ -41,6 +35,12 @@ import {
|
|
|
41
35
|
jsonText,
|
|
42
36
|
toolingRolloutGate
|
|
43
37
|
} from "./index-h6ca6qg0.js";
|
|
38
|
+
import {
|
|
39
|
+
Prompter
|
|
40
|
+
} from "./index-qaq13qk3.js";
|
|
41
|
+
import {
|
|
42
|
+
getMobileTargets
|
|
43
|
+
} from "./index-76rn3g2c.js";
|
|
44
44
|
import {
|
|
45
45
|
CommandContainer,
|
|
46
46
|
runner,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import {
|
|
3
|
-
openBrowser
|
|
4
|
-
} from "./index-77crfweb.js";
|
|
5
2
|
import {
|
|
6
3
|
AiSession,
|
|
7
4
|
CloudApi,
|
|
8
5
|
GlobalConfig,
|
|
9
6
|
getDefaultHostConfig
|
|
10
7
|
} from "./index-5vvwc0cz.js";
|
|
8
|
+
import {
|
|
9
|
+
openBrowser
|
|
10
|
+
} from "./index-rgfxj6qc.js";
|
|
11
11
|
import {
|
|
12
12
|
runner
|
|
13
13
|
} from "./index-hdqztm58.js";
|