@akanjs/cli 2.4.1-rc.3 → 2.4.1-rc.4
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/.build-stamp +1 -0
- package/{agent.command-h4afc69n.js → agent.command-t4346579.js} +6 -6
- package/{application.command-jkmbsd1x.js → application.command-zkmeemdt.js} +5 -4
- package/{applicationBuildRunner-ny84tjhc.js → applicationBuildRunner-5w8j9yp7.js} +2 -1
- package/buildBatch.proc.js +3 -2
- package/{capacitorApp-y0h6cgft.js → capacitorApp-wnxzrppx.js} +2 -2
- package/{cloud.command-53j76grx.js → cloud.command-5ztqxppm.js} +8 -7
- package/{context.command-ds3mka9p.js → context.command-smwxwgwf.js} +13 -13
- package/{guideline.command-ya0dh44f.js → guideline.command-1r0esdjs.js} +3 -3
- package/incrementalBuilder.proc.js +36 -13
- package/index-0fn1r7gg.js +466 -0
- package/{index-1fpjvqhs.js → index-0jwvs8vp.js} +11 -11
- package/{index-a6sbyy0b.js → index-0t7pwff2.js} +3 -0
- package/{index-h6ca6qg0.js → index-2v10yr8g.js} +1 -1
- package/{index-m9pca6jv.js → index-2v6a4wd5.js} +4 -4
- package/{index-45aj5ry0.js → index-3sd2vape.js} +3 -3
- package/{index-gb26e7z0.js → index-ahpcr9ss.js} +3 -3
- package/{index-8rc0bm04.js → index-eay4t1te.js} +2 -2
- package/{index-qaq13qk3.js → index-eqxmxan6.js} +1 -1
- package/{index-pmm9e2jf.js → index-fh990y67.js} +2 -2
- package/{index-dynknvzd.js → index-gr4cjv99.js} +3 -3
- package/{index-1xdrsbry.js → index-hk58kr1m.js} +1 -1
- package/{index-85msc0wg.js → index-jbtn8h1y.js} +3 -3
- package/{index-q7zcac6n.js → index-khzzttv1.js} +108 -191
- package/{index-rgfxj6qc.js → index-n5y2gbf2.js} +307 -15
- package/{index-n6h3482q.js → index-nv7et4t3.js} +2 -2
- package/{index-hwzpw9c1.js → index-pya1h7wx.js} +3 -3
- package/{index-hdqztm58.js → index-qpz4csbs.js} +1 -1
- package/{index-0hjg9qs5.js → index-s9yajzkj.js} +5 -5
- package/{index-fmky5k3p.js → index-wh201a76.js} +5 -5
- package/{index-swf4bmbg.js → index-y4kg682s.js} +1 -1
- package/index.js +18 -18
- package/{library.command-r15zdqvp.js → library.command-jrew04m6.js} +3 -3
- package/{localRegistry.command-25sv5hmt.js → localRegistry.command-m08dkm1b.js} +7 -6
- package/{module.command-tnj2bzst.js → module.command-p98t7522.js} +6 -6
- package/{package.command-5x5m0ej1.js → package.command-57dyrfmn.js} +3 -3
- package/package.json +2 -2
- package/{page.command-c6xdx0xm.js → page.command-tas6f3na.js} +3 -3
- package/{primitive.command-q1ycj5vr.js → primitive.command-bjrbsakw.js} +7 -7
- package/{quality.command-es67wvdp.js → quality.command-zavaafbw.js} +2 -2
- package/{repair.command-677675vw.js → repair.command-je57wx67.js} +5 -5
- package/{scalar.command-bmrmp8n4.js → scalar.command-gqqyy1kr.js} +5 -5
- package/{workflow.command-64550hka.js → workflow.command-d6mrc47s.js} +10 -10
- package/{workspace.command-pmfxxfew.js → workspace.command-bbg96z8k.js} +20 -19
|
@@ -4,17 +4,21 @@ import {
|
|
|
4
4
|
} from "./index-76rn3g2c.js";
|
|
5
5
|
import {
|
|
6
6
|
LibraryScript
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-fh990y67.js";
|
|
8
8
|
import {
|
|
9
9
|
runner,
|
|
10
10
|
script
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-qpz4csbs.js";
|
|
12
12
|
import {
|
|
13
13
|
AppExecutor,
|
|
14
14
|
LibExecutor,
|
|
15
15
|
PkgExecutor,
|
|
16
16
|
WorkspaceExecutor
|
|
17
|
-
} from "./index-
|
|
17
|
+
} from "./index-0t7pwff2.js";
|
|
18
|
+
import {
|
|
19
|
+
HmrWatcher,
|
|
20
|
+
WatchRootResolver
|
|
21
|
+
} from "./index-0fn1r7gg.js";
|
|
18
22
|
import {
|
|
19
23
|
__require
|
|
20
24
|
} from "./index-r24hmh0q.js";
|
|
@@ -69,6 +73,7 @@ import { confirm } from "@inquirer/prompts";
|
|
|
69
73
|
import { Logger as Logger5 } from "akanjs/common";
|
|
70
74
|
|
|
71
75
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
76
|
+
import { stat } from "fs/promises";
|
|
72
77
|
import path2 from "path";
|
|
73
78
|
import { Logger as Logger3 } from "akanjs/common";
|
|
74
79
|
|
|
@@ -240,6 +245,7 @@ class IncrementalBuilderHost {
|
|
|
240
245
|
#recycleRequested = false;
|
|
241
246
|
#spawnAfterRecycle = false;
|
|
242
247
|
#manualStop = false;
|
|
248
|
+
#inFlight = new Map;
|
|
243
249
|
#startOptions = {};
|
|
244
250
|
constructor({ app, entry, env, onMessage }) {
|
|
245
251
|
this.app = app;
|
|
@@ -250,11 +256,15 @@ class IncrementalBuilderHost {
|
|
|
250
256
|
get status() {
|
|
251
257
|
return this.#status;
|
|
252
258
|
}
|
|
259
|
+
get pid() {
|
|
260
|
+
return this.#proc?.pid ?? null;
|
|
261
|
+
}
|
|
253
262
|
start(options = {}) {
|
|
254
263
|
if (this.#proc)
|
|
255
264
|
this.stop();
|
|
256
265
|
this.#manualStop = false;
|
|
257
266
|
this.#startOptions = options;
|
|
267
|
+
this.#spawnAfterRecycle = options.announceBootState ?? false;
|
|
258
268
|
this.#spawn(false);
|
|
259
269
|
return this;
|
|
260
270
|
}
|
|
@@ -266,13 +276,15 @@ class IncrementalBuilderHost {
|
|
|
266
276
|
let proc;
|
|
267
277
|
proc = Bun.spawn(["bun", this.entry], {
|
|
268
278
|
cwd: this.app.cwdPath,
|
|
269
|
-
env: { ...this.env, AKAN_WATCH: "1", ...afterRecycle ? {
|
|
279
|
+
env: { ...this.env, AKAN_WATCH: "1", ...afterRecycle ? { AKAN_BUILDER_ANNOUNCE_BOOT: "1" } : {} },
|
|
270
280
|
stdio: ["ignore", "inherit", "inherit"],
|
|
271
281
|
ipc: (msg) => {
|
|
272
282
|
if (this.#proc !== proc)
|
|
273
283
|
return;
|
|
274
284
|
if (!msg || typeof msg !== "object")
|
|
275
285
|
return;
|
|
286
|
+
if (msg.type === "build-route-res" || msg.type === "build-csr-res")
|
|
287
|
+
this.#inFlight.delete(msg.id);
|
|
276
288
|
if (builderMsgTypeSet.has(msg.type))
|
|
277
289
|
this.#onMessage(msg);
|
|
278
290
|
if (msg.type === "builder-ready" && !this.ready) {
|
|
@@ -294,6 +306,7 @@ class IncrementalBuilderHost {
|
|
|
294
306
|
const wasRecycle = this.#recycleRequested;
|
|
295
307
|
this.#clearRecycle();
|
|
296
308
|
this.ready = false;
|
|
309
|
+
this.#failInFlight(wasRecycle ? "builder exited to release bundler memory before answering; reload to retry" : "builder exited unexpectedly before answering; reload once it is back");
|
|
297
310
|
if (this.#manualStop || this.#status === "stopped")
|
|
298
311
|
return;
|
|
299
312
|
if (!wasReady) {
|
|
@@ -369,15 +382,31 @@ class IncrementalBuilderHost {
|
|
|
369
382
|
}
|
|
370
383
|
try {
|
|
371
384
|
this.#proc.send(message);
|
|
385
|
+
if (message.type === "build-route" || message.type === "build-csr")
|
|
386
|
+
this.#inFlight.set(message.id, message.type);
|
|
372
387
|
return true;
|
|
373
388
|
} catch (error) {
|
|
374
389
|
this.logger.warn(`failed to send ${message.type} to builder: ${error instanceof Error ? error.message : String(error)}`);
|
|
375
390
|
return false;
|
|
376
391
|
}
|
|
377
392
|
}
|
|
393
|
+
#failInFlight(reason) {
|
|
394
|
+
if (!this.#inFlight.size)
|
|
395
|
+
return;
|
|
396
|
+
const lost = [...this.#inFlight];
|
|
397
|
+
this.#inFlight.clear();
|
|
398
|
+
this.logger.warn(`failing ${lost.length} unanswered builder request(s): ${reason}`);
|
|
399
|
+
for (const [id, type] of lost) {
|
|
400
|
+
if (type === "build-route")
|
|
401
|
+
this.#onMessage({ type: "build-route-res", id, ok: false, error: reason });
|
|
402
|
+
else
|
|
403
|
+
this.#onMessage({ type: "build-csr-res", id, ok: false, error: reason });
|
|
404
|
+
}
|
|
405
|
+
}
|
|
378
406
|
stop() {
|
|
379
407
|
this.#manualStop = true;
|
|
380
408
|
this.#clearRecycle();
|
|
409
|
+
this.#failInFlight("builder was stopped before answering");
|
|
381
410
|
if (this.#restartTimer) {
|
|
382
411
|
clearTimeout(this.#restartTimer);
|
|
383
412
|
this.#restartTimer = null;
|
|
@@ -414,6 +443,10 @@ var BUILDER_START_MAX_ATTEMPTS = 3;
|
|
|
414
443
|
var BUILDER_RSS_RECYCLE_QUIET_MS = 750;
|
|
415
444
|
var BUILDER_MIN_RSS_RECYCLE_INTERVAL_MS = 30000;
|
|
416
445
|
var BUILDER_INEFFECTIVE_RSS_RECYCLE_LIMIT = 2;
|
|
446
|
+
var BUILDER_RSS_SETTLE_MS = 20000;
|
|
447
|
+
var BUILDER_RSS_HARD_MULTIPLE = 1.5;
|
|
448
|
+
var DEV_IDLE_SUSPEND_MS = 300000;
|
|
449
|
+
var DEV_IDLE_MIN_UPTIME_MS = 30000;
|
|
417
450
|
var BUILDER_RECOVERY_BASE_DELAY_MS = 2000;
|
|
418
451
|
var BUILDER_RECOVERY_MAX_DELAY_MS = 60000;
|
|
419
452
|
var SOURCE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
@@ -518,6 +551,47 @@ var decideBuilderRssRecycle = ({
|
|
|
518
551
|
return "too-soon";
|
|
519
552
|
return "recycle";
|
|
520
553
|
};
|
|
554
|
+
var decideBuilderRssSettle = ({
|
|
555
|
+
rssBytes,
|
|
556
|
+
ceilingBytes,
|
|
557
|
+
hardMultiple = BUILDER_RSS_HARD_MULTIPLE
|
|
558
|
+
}) => rssBytes >= ceilingBytes * hardMultiple ? "recycle-now" : "wait-and-recheck";
|
|
559
|
+
var decideIdleSuspend = ({
|
|
560
|
+
enabled,
|
|
561
|
+
suspended,
|
|
562
|
+
builderReady,
|
|
563
|
+
backendReady,
|
|
564
|
+
buildFailed,
|
|
565
|
+
restartPending,
|
|
566
|
+
msSinceWake,
|
|
567
|
+
minUptimeMs = DEV_IDLE_MIN_UPTIME_MS
|
|
568
|
+
}) => {
|
|
569
|
+
if (!enabled)
|
|
570
|
+
return "disabled";
|
|
571
|
+
if (suspended)
|
|
572
|
+
return "already-suspended";
|
|
573
|
+
if (!builderReady)
|
|
574
|
+
return "builder-not-ready";
|
|
575
|
+
if (!backendReady)
|
|
576
|
+
return "backend-not-ready";
|
|
577
|
+
if (buildFailed)
|
|
578
|
+
return "build-failed";
|
|
579
|
+
if (restartPending)
|
|
580
|
+
return "restart-pending";
|
|
581
|
+
if (msSinceWake !== null && msSinceWake < minUptimeMs)
|
|
582
|
+
return "too-soon";
|
|
583
|
+
return "suspend";
|
|
584
|
+
};
|
|
585
|
+
var resolveIdleSuspendMs = (raw) => {
|
|
586
|
+
if (raw === undefined || raw === "")
|
|
587
|
+
return DEV_IDLE_SUSPEND_MS;
|
|
588
|
+
const parsed = Number(raw);
|
|
589
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
590
|
+
return null;
|
|
591
|
+
return Math.round(parsed);
|
|
592
|
+
};
|
|
593
|
+
var hasAnyBuildFailure = (statusByPhase) => [...statusByPhase.values()].some((status) => !status.ok);
|
|
594
|
+
var shouldRefreshConfigOnIdleWake = (batch) => !!batch && batch.kinds.has("config");
|
|
521
595
|
var shouldAbandonBuilderRssCeiling = (ineffectiveRecycles, limit = BUILDER_INEFFECTIVE_RSS_RECYCLE_LIMIT) => ineffectiveRecycles >= limit;
|
|
522
596
|
var shouldRelayRecycledFrontendState = (current, next) => {
|
|
523
597
|
if (!current || current.type !== next.type)
|
|
@@ -566,6 +640,7 @@ class BackendImportGraph {
|
|
|
566
640
|
#jsTranspiler = new Bun.Transpiler({ loader: "js" });
|
|
567
641
|
#jsxTranspiler = new Bun.Transpiler({ loader: "jsx" });
|
|
568
642
|
#files = new Set;
|
|
643
|
+
#scanCache = new Map;
|
|
569
644
|
#ready = false;
|
|
570
645
|
#lastRefreshSucceeded = false;
|
|
571
646
|
constructor(app, logger) {
|
|
@@ -607,11 +682,10 @@ class BackendImportGraph {
|
|
|
607
682
|
continue;
|
|
608
683
|
if (!this.#isWorkspaceSource(current, workspaceRoot))
|
|
609
684
|
continue;
|
|
610
|
-
|
|
685
|
+
const imports = await this.#importsOf(current);
|
|
686
|
+
if (!imports)
|
|
611
687
|
continue;
|
|
612
688
|
files.add(current);
|
|
613
|
-
const source = await Bun.file(current).text();
|
|
614
|
-
const imports = this.#scanImports(current, source);
|
|
615
689
|
const importerDir = path2.dirname(current);
|
|
616
690
|
for (const imp of imports) {
|
|
617
691
|
if (!GRAPH_IMPORT_KINDS.has(imp.kind) || !imp.path || NON_SOURCE_EXT_RE.test(imp.path))
|
|
@@ -622,8 +696,23 @@ class BackendImportGraph {
|
|
|
622
696
|
queue.push(resolved);
|
|
623
697
|
}
|
|
624
698
|
}
|
|
699
|
+
for (const cached of this.#scanCache.keys())
|
|
700
|
+
if (!files.has(cached))
|
|
701
|
+
this.#scanCache.delete(cached);
|
|
625
702
|
return files;
|
|
626
703
|
}
|
|
704
|
+
async#importsOf(file) {
|
|
705
|
+
const stats = await stat(file).catch(() => null);
|
|
706
|
+
if (!stats?.isFile())
|
|
707
|
+
return null;
|
|
708
|
+
const mtimeMs = Math.round(stats.mtimeMs);
|
|
709
|
+
const cached = this.#scanCache.get(file);
|
|
710
|
+
if (cached && cached.mtimeMs === mtimeMs && cached.size === stats.size)
|
|
711
|
+
return cached.specifiers;
|
|
712
|
+
const specifiers = this.#scanImports(file, await Bun.file(file).text());
|
|
713
|
+
this.#scanCache.set(file, { mtimeMs, size: stats.size, specifiers });
|
|
714
|
+
return specifiers;
|
|
715
|
+
}
|
|
627
716
|
async#entrypoints() {
|
|
628
717
|
const roots = [`${this.#app.cwdPath}/main.ts`, `${this.#app.cwdPath}/server.ts`];
|
|
629
718
|
const existing = [];
|
|
@@ -692,11 +781,20 @@ class AkanAppHost {
|
|
|
692
781
|
#rssRecycleReason = null;
|
|
693
782
|
#lastRssRecycleAtMono = null;
|
|
694
783
|
#rssCeilingIneffective = 0;
|
|
784
|
+
#rssSettleToken = 0;
|
|
785
|
+
#rssRecycleOver = null;
|
|
695
786
|
#rssCeilingAbandoned = false;
|
|
696
787
|
#buildStatusByPhase = new Map;
|
|
697
788
|
#pendingBuildStatusReplay = [];
|
|
698
789
|
#builderMessageQueue = Promise.resolve();
|
|
699
790
|
#backendGraph;
|
|
791
|
+
#idleSuspendTimer = null;
|
|
792
|
+
#suspended = false;
|
|
793
|
+
#waking = false;
|
|
794
|
+
#wokeAtMono = null;
|
|
795
|
+
#idleWatcher = null;
|
|
796
|
+
#suspendedChanges = null;
|
|
797
|
+
#pendingBuilderMessages = [];
|
|
700
798
|
constructor(app, { env, withInk = false }) {
|
|
701
799
|
this.app = app;
|
|
702
800
|
this.env = env;
|
|
@@ -715,9 +813,12 @@ class AkanAppHost {
|
|
|
715
813
|
]);
|
|
716
814
|
Object.assign(this.env, { REDIS_HOST: redisHost });
|
|
717
815
|
this.#startBackend();
|
|
816
|
+
this.#armIdleSuspend();
|
|
718
817
|
return this;
|
|
719
818
|
}
|
|
720
819
|
async stop() {
|
|
820
|
+
this.#cancelIdleSuspend();
|
|
821
|
+
this.#stopIdleWatcher();
|
|
721
822
|
if (this.#restartTimer) {
|
|
722
823
|
clearTimeout(this.#restartTimer);
|
|
723
824
|
this.#restartTimer = null;
|
|
@@ -982,6 +1083,7 @@ ${this.#backendStderrTail.join(`
|
|
|
982
1083
|
});
|
|
983
1084
|
}
|
|
984
1085
|
async#handleBuilderMessage(message) {
|
|
1086
|
+
this.#markDevActivity();
|
|
985
1087
|
if (message.type === "build-status") {
|
|
986
1088
|
this.#recordBuildStatus(message.data);
|
|
987
1089
|
this.#sendOrQueueBuildStatus(message.data);
|
|
@@ -1042,29 +1144,210 @@ ${this.#backendStderrTail.join(`
|
|
|
1042
1144
|
this.logger.error(`[builder-recycle] the builder is still at ${asMib(metrics.rssBytes)}MiB right after being recycled, so the ${asMib(ceilingBytes ?? 0)}MiB ceiling cannot be met for this app; no longer enforcing it this session. Raise AKAN_BUILDER_MAX_RSS_MB, or set it to 0 to leave the builder unbounded.`);
|
|
1043
1145
|
return;
|
|
1044
1146
|
}
|
|
1045
|
-
this.#armRssRecycle(`rss=${asMib(metrics.rssBytes)}MiB>=${asMib(ceilingBytes ?? 0)}MiB after ${metrics.workCount} build(s)
|
|
1147
|
+
this.#armRssRecycle(`rss=${asMib(metrics.rssBytes)}MiB>=${asMib(ceilingBytes ?? 0)}MiB after ${metrics.workCount} build(s)`, { rssBytes: metrics.rssBytes, ceilingBytes: ceilingBytes ?? 0 });
|
|
1046
1148
|
}
|
|
1047
|
-
#armRssRecycle(reason) {
|
|
1149
|
+
#armRssRecycle(reason, over) {
|
|
1048
1150
|
if (this.#rssRecycleReason !== reason)
|
|
1049
1151
|
this.logger.verbose(`[builder-recycle] armed (${reason}); replacing the builder once it stays quiet`);
|
|
1050
1152
|
this.#rssRecycleReason = reason;
|
|
1153
|
+
if (over)
|
|
1154
|
+
this.#rssRecycleOver = over;
|
|
1051
1155
|
if (this.#rssRecycleTimer)
|
|
1052
1156
|
clearTimeout(this.#rssRecycleTimer);
|
|
1053
1157
|
this.#rssRecycleTimer = setTimeout(() => {
|
|
1054
1158
|
this.#rssRecycleTimer = null;
|
|
1055
1159
|
const pendingReason = this.#rssRecycleReason;
|
|
1160
|
+
const pendingOver = this.#rssRecycleOver;
|
|
1056
1161
|
this.#rssRecycleReason = null;
|
|
1057
|
-
|
|
1162
|
+
this.#rssRecycleOver = null;
|
|
1163
|
+
if (!pendingReason)
|
|
1164
|
+
return;
|
|
1165
|
+
if (!pendingOver) {
|
|
1058
1166
|
this.#recycleBuilderForRss(pendingReason);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
this.#recycleBuilderForRssWhenStillOver(pendingReason, pendingOver);
|
|
1059
1170
|
}, BUILDER_RSS_RECYCLE_QUIET_MS);
|
|
1060
1171
|
}
|
|
1172
|
+
async#recycleBuilderForRssWhenStillOver(reason, { rssBytes, ceilingBytes }) {
|
|
1173
|
+
const asMib = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
1174
|
+
if (decideBuilderRssSettle({ rssBytes, ceilingBytes }) === "recycle-now") {
|
|
1175
|
+
this.#recycleBuilderForRss(reason);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
const pid = this.#builder?.pid;
|
|
1179
|
+
if (!pid) {
|
|
1180
|
+
this.#recycleBuilderForRss(reason);
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
this.#rssSettleToken += 1;
|
|
1184
|
+
const token = this.#rssSettleToken;
|
|
1185
|
+
this.logger.verbose(`[builder-recycle] holding ${Math.round(BUILDER_RSS_SETTLE_MS / 1000)}s to see whether the allocator returns it (${reason})`);
|
|
1186
|
+
await Bun.sleep(BUILDER_RSS_SETTLE_MS);
|
|
1187
|
+
if (token !== this.#rssSettleToken || this.#builder?.pid !== pid || this.#suspended || this.#waking) {
|
|
1188
|
+
this.logger.verbose("[builder-recycle] settle check abandoned; the builder moved on");
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
const settledBytes = await AkanAppHost.readProcessRssBytes(pid);
|
|
1192
|
+
if (settledBytes !== null && settledBytes < ceilingBytes) {
|
|
1193
|
+
this.logger.info(`[builder-recycle] skipped: the builder fell to ${asMib(settledBytes)}MiB (ceiling ${asMib(ceilingBytes)}MiB) on its own, so a recycle would have cost a boot build for nothing`);
|
|
1194
|
+
return;
|
|
1195
|
+
}
|
|
1196
|
+
this.#recycleBuilderForRss(settledBytes === null ? reason : `${reason}; still ${asMib(settledBytes)}MiB after settling`);
|
|
1197
|
+
}
|
|
1198
|
+
static async readProcessRssBytes(pid) {
|
|
1199
|
+
const status = await Bun.file(`/proc/${pid}/status`).text().catch(() => null);
|
|
1200
|
+
const vmRssKb = status === null ? null : /VmRSS:\s+(\d+) kB/.exec(status)?.[1];
|
|
1201
|
+
if (vmRssKb)
|
|
1202
|
+
return Number(vmRssKb) * 1024;
|
|
1203
|
+
const psOutput = await Bun.$`ps -o rss= -p ${pid}`.quiet().text().catch(() => "");
|
|
1204
|
+
const rssKb = Number(psOutput.trim());
|
|
1205
|
+
return Number.isFinite(rssKb) && rssKb > 0 ? rssKb * 1024 : null;
|
|
1206
|
+
}
|
|
1061
1207
|
#cancelRssRecycle() {
|
|
1062
1208
|
this.#rssRecycleReason = null;
|
|
1209
|
+
this.#rssRecycleOver = null;
|
|
1210
|
+
this.#rssSettleToken += 1;
|
|
1063
1211
|
if (!this.#rssRecycleTimer)
|
|
1064
1212
|
return;
|
|
1065
1213
|
clearTimeout(this.#rssRecycleTimer);
|
|
1066
1214
|
this.#rssRecycleTimer = null;
|
|
1067
1215
|
}
|
|
1216
|
+
static idleSuspendMs() {
|
|
1217
|
+
return resolveIdleSuspendMs(process.env.AKAN_DEV_IDLE_SUSPEND_MS);
|
|
1218
|
+
}
|
|
1219
|
+
#markDevActivity() {
|
|
1220
|
+
if (this.#suspended || this.#waking)
|
|
1221
|
+
return;
|
|
1222
|
+
this.#armIdleSuspend();
|
|
1223
|
+
}
|
|
1224
|
+
#armIdleSuspend() {
|
|
1225
|
+
const idleMs = AkanAppHost.idleSuspendMs();
|
|
1226
|
+
this.#cancelIdleSuspend();
|
|
1227
|
+
if (idleMs === null)
|
|
1228
|
+
return;
|
|
1229
|
+
this.#idleSuspendTimer = setTimeout(() => {
|
|
1230
|
+
this.#idleSuspendTimer = null;
|
|
1231
|
+
this.#suspendWhenIdle(idleMs);
|
|
1232
|
+
}, idleMs);
|
|
1233
|
+
}
|
|
1234
|
+
#cancelIdleSuspend() {
|
|
1235
|
+
if (!this.#idleSuspendTimer)
|
|
1236
|
+
return;
|
|
1237
|
+
clearTimeout(this.#idleSuspendTimer);
|
|
1238
|
+
this.#idleSuspendTimer = null;
|
|
1239
|
+
}
|
|
1240
|
+
async#suspendWhenIdle(idleMs) {
|
|
1241
|
+
const decision = decideIdleSuspend({
|
|
1242
|
+
enabled: true,
|
|
1243
|
+
suspended: this.#suspended,
|
|
1244
|
+
builderReady: this.#builder?.status === "ready",
|
|
1245
|
+
backendReady: this.#backendReady,
|
|
1246
|
+
buildFailed: hasAnyBuildFailure(this.#buildStatusByPhase),
|
|
1247
|
+
restartPending: this.#restartPending,
|
|
1248
|
+
msSinceWake: this.#wokeAtMono === null ? null : performance.now() - this.#wokeAtMono
|
|
1249
|
+
});
|
|
1250
|
+
if (decision !== "suspend") {
|
|
1251
|
+
this.logger.verbose(`[idle-suspend] skipped (${decision}); re-arming`);
|
|
1252
|
+
this.#armIdleSuspend();
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
if (!await this.#startIdleWatcher()) {
|
|
1256
|
+
this.#armIdleSuspend();
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
this.#suspended = true;
|
|
1260
|
+
this.#stopBuilder();
|
|
1261
|
+
this.logger.info(`[idle-suspend] no build activity for ${Math.round(idleMs / 1000)}s; released the builder \u2014 the next edit or route request brings it back`);
|
|
1262
|
+
}
|
|
1263
|
+
get #restartPending() {
|
|
1264
|
+
return !!(this.#pendingRecycle || this.#restartTimer || this.#backendRecoveryTimer || this.#builderRecoveryTimer || this.#rssRecycleReason);
|
|
1265
|
+
}
|
|
1266
|
+
async#startIdleWatcher() {
|
|
1267
|
+
try {
|
|
1268
|
+
const roots = await new WatchRootResolver(this.app).resolve();
|
|
1269
|
+
const watcher = new HmrWatcher({
|
|
1270
|
+
roots,
|
|
1271
|
+
logger: this.logger,
|
|
1272
|
+
onBatch: (batch) => {
|
|
1273
|
+
this.#recordSuspendedChange(batch);
|
|
1274
|
+
this.#wakeFromIdle(`${batch.files.length} file(s) changed`);
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
await watcher.start();
|
|
1278
|
+
this.#idleWatcher = watcher;
|
|
1279
|
+
return true;
|
|
1280
|
+
} catch (err) {
|
|
1281
|
+
this.logger.warn(`[idle-suspend] could not install the idle watcher; staying awake: ${err instanceof Error ? err.message : String(err)}`);
|
|
1282
|
+
this.#stopIdleWatcher();
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
#stopIdleWatcher() {
|
|
1287
|
+
this.#idleWatcher?.stop();
|
|
1288
|
+
this.#idleWatcher = null;
|
|
1289
|
+
}
|
|
1290
|
+
#recordSuspendedChange(batch) {
|
|
1291
|
+
const current = this.#suspendedChanges;
|
|
1292
|
+
if (!current) {
|
|
1293
|
+
this.#suspendedChanges = { files: [...batch.files], kinds: new Set(batch.kinds) };
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
this.#suspendedChanges = {
|
|
1297
|
+
files: [...new Set([...current.files, ...batch.files])],
|
|
1298
|
+
kinds: new Set([...current.kinds, ...batch.kinds])
|
|
1299
|
+
};
|
|
1300
|
+
}
|
|
1301
|
+
async#wakeFromIdle(reason) {
|
|
1302
|
+
if (!this.#suspended || this.#waking)
|
|
1303
|
+
return;
|
|
1304
|
+
this.#waking = true;
|
|
1305
|
+
this.#cancelIdleSuspend();
|
|
1306
|
+
this.#stopIdleWatcher();
|
|
1307
|
+
const batch = this.#suspendedChanges;
|
|
1308
|
+
this.#suspendedChanges = null;
|
|
1309
|
+
const startedAtMono = performance.now();
|
|
1310
|
+
this.logger.info(`[idle-suspend] waking (${reason})`);
|
|
1311
|
+
try {
|
|
1312
|
+
await this.#applyIdleWake(batch);
|
|
1313
|
+
this.logger.info(`[idle-suspend] awake in ${Math.round(performance.now() - startedAtMono)}ms`);
|
|
1314
|
+
} catch (err) {
|
|
1315
|
+
this.logger.error(`[idle-suspend] wake failed; recovering the builder: ${err instanceof Error ? err.message : String(err)}`);
|
|
1316
|
+
this.#scheduleBuilderRecovery({ files: batch?.files ?? [] });
|
|
1317
|
+
} finally {
|
|
1318
|
+
this.#suspended = false;
|
|
1319
|
+
this.#waking = false;
|
|
1320
|
+
this.#wokeAtMono = performance.now();
|
|
1321
|
+
this.#flushPendingBuilderMessages();
|
|
1322
|
+
this.#armIdleSuspend();
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
async#applyIdleWake(batch) {
|
|
1326
|
+
const files = batch?.files ?? [];
|
|
1327
|
+
if (shouldRefreshConfigOnIdleWake(batch)) {
|
|
1328
|
+
this.logger.verbose("[idle-suspend] config changed while suspended; restarting the dev host");
|
|
1329
|
+
await this.#recycleDevChildren({ type: "invalidate", kinds: [...batch?.kinds ?? []], files }, {
|
|
1330
|
+
refreshConfig: true
|
|
1331
|
+
});
|
|
1332
|
+
return;
|
|
1333
|
+
}
|
|
1334
|
+
if (files.length > 0)
|
|
1335
|
+
await this.#backendGraph.refresh();
|
|
1336
|
+
await this.#startBuilder({ announceBootState: true });
|
|
1337
|
+
const backendFiles = files.filter((file) => this.#isBackendFile(file));
|
|
1338
|
+
if (backendFiles.length === 0)
|
|
1339
|
+
return;
|
|
1340
|
+
this.logger.verbose(`[idle-suspend] ${backendFiles.length} backend file(s) changed while suspended`);
|
|
1341
|
+
this.#scheduleBackendRestart({ files: backendFiles, roles: [] });
|
|
1342
|
+
}
|
|
1343
|
+
#flushPendingBuilderMessages() {
|
|
1344
|
+
const pending = this.#pendingBuilderMessages.splice(0);
|
|
1345
|
+
if (pending.length === 0)
|
|
1346
|
+
return;
|
|
1347
|
+
this.logger.verbose(`[idle-suspend] replaying ${pending.length} request(s) held during the wake`);
|
|
1348
|
+
for (const message of pending)
|
|
1349
|
+
this.#sendToBuilder(message);
|
|
1350
|
+
}
|
|
1068
1351
|
#recycleBuilderForRss(reason) {
|
|
1069
1352
|
if (this.#pendingRecycle || this.#restartTimer) {
|
|
1070
1353
|
this.logger.verbose(`[builder-recycle] skipped (${reason}); a dev restart is already pending`);
|
|
@@ -1297,7 +1580,9 @@ ${this.#backendStderrTail.join(`
|
|
|
1297
1580
|
#isBackendFile(file) {
|
|
1298
1581
|
return this.#backendGraph.has(file);
|
|
1299
1582
|
}
|
|
1300
|
-
async#startBuilder(
|
|
1583
|
+
async#startBuilder({
|
|
1584
|
+
announceBootState = false
|
|
1585
|
+
} = {}) {
|
|
1301
1586
|
const startTime = Date.now();
|
|
1302
1587
|
this.app.verbose(`[cli] waiting for builder to complete initial base build\u2026`);
|
|
1303
1588
|
let lastError;
|
|
@@ -1306,7 +1591,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1306
1591
|
this.#enqueueBuilderMessage(msg);
|
|
1307
1592
|
});
|
|
1308
1593
|
try {
|
|
1309
|
-
await this.#waitForBuilderReady(attempt);
|
|
1594
|
+
await this.#waitForBuilderReady(attempt, { announceBootState });
|
|
1310
1595
|
this.app.verbose(`[cli] base build ready in ${Date.now() - startTime}ms \u2014 starting backend`);
|
|
1311
1596
|
return this.#builder;
|
|
1312
1597
|
} catch (err) {
|
|
@@ -1319,7 +1604,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1319
1604
|
}
|
|
1320
1605
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
1321
1606
|
}
|
|
1322
|
-
#waitForBuilderReady(attempt) {
|
|
1607
|
+
#waitForBuilderReady(attempt, { announceBootState = false } = {}) {
|
|
1323
1608
|
return new Promise((resolve, reject) => {
|
|
1324
1609
|
if (!this.#builder)
|
|
1325
1610
|
throw new Error("Builder Not Found");
|
|
@@ -1335,6 +1620,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1335
1620
|
settle(() => reject(new Error("[cli] builder timed out before emitting builder-ready")));
|
|
1336
1621
|
}, BUILDER_READY_TIMEOUT_MS);
|
|
1337
1622
|
this.#builder.start({
|
|
1623
|
+
announceBootState,
|
|
1338
1624
|
onExit: () => {
|
|
1339
1625
|
settle(() => reject(new Error(`[cli] builder exited before emitting builder-ready (attempt ${attempt})`)));
|
|
1340
1626
|
},
|
|
@@ -1349,6 +1635,12 @@ ${this.#backendStderrTail.join(`
|
|
|
1349
1635
|
});
|
|
1350
1636
|
}
|
|
1351
1637
|
#sendToBuilder(message) {
|
|
1638
|
+
this.#markDevActivity();
|
|
1639
|
+
if (this.#suspended || this.#waking) {
|
|
1640
|
+
this.#pendingBuilderMessages.push(message);
|
|
1641
|
+
this.#wakeFromIdle(`${message.type} arrived while suspended`);
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1352
1644
|
if (message.type === "build-csr" && this.env.AKAN_DEV_CSR_REBUILD !== "1") {
|
|
1353
1645
|
Object.assign(this.env, { AKAN_DEV_CSR_REBUILD: "1" });
|
|
1354
1646
|
this.logger.verbose(`[csr] armed dev CSR rebuilds (${message.reason})`);
|
|
@@ -1425,9 +1717,9 @@ function openBrowser(url) {
|
|
|
1425
1717
|
}
|
|
1426
1718
|
|
|
1427
1719
|
// pkgs/@akanjs/cli/application/application.runner.ts
|
|
1428
|
-
var loadBuildRunner = async () => (await import("./applicationBuildRunner-
|
|
1720
|
+
var loadBuildRunner = async () => (await import("./applicationBuildRunner-5w8j9yp7.js")).ApplicationBuildRunner;
|
|
1429
1721
|
var loadReleasePackager = async () => (await import("./applicationReleasePackager-brvth6rs.js")).ApplicationReleasePackager;
|
|
1430
|
-
var loadCapacitorApp = async () => (await import("./capacitorApp-
|
|
1722
|
+
var loadCapacitorApp = async () => (await import("./capacitorApp-wnxzrppx.js")).CapacitorApp;
|
|
1431
1723
|
var loadPrompts = async () => await import("@inquirer/prompts");
|
|
1432
1724
|
|
|
1433
1725
|
class ApplicationRunner extends runner("application") {
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runner,
|
|
4
4
|
script
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-qpz4csbs.js";
|
|
6
6
|
import {
|
|
7
7
|
PkgExecutor
|
|
8
|
-
} from "./index-
|
|
8
|
+
} from "./index-0t7pwff2.js";
|
|
9
9
|
import {
|
|
10
10
|
TypeScriptDependencyScanner
|
|
11
11
|
} from "./index-x53a5nya.js";
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
AkanContextAnalyzer
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-eay4t1te.js";
|
|
5
5
|
import {
|
|
6
6
|
Prompter
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-eqxmxan6.js";
|
|
8
8
|
import {
|
|
9
9
|
runner,
|
|
10
10
|
script
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-qpz4csbs.js";
|
|
12
12
|
|
|
13
13
|
// pkgs/@akanjs/cli/agent/agent.script.ts
|
|
14
14
|
import { Logger } from "akanjs/common";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
CloudRunner
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-gr4cjv99.js";
|
|
5
5
|
import {
|
|
6
6
|
AiSession,
|
|
7
7
|
CloudApi,
|
|
@@ -9,16 +9,16 @@ import {
|
|
|
9
9
|
} from "./index-5vvwc0cz.js";
|
|
10
10
|
import {
|
|
11
11
|
ApplicationScript
|
|
12
|
-
} from "./index-
|
|
12
|
+
} from "./index-n5y2gbf2.js";
|
|
13
13
|
import {
|
|
14
14
|
PackageScript
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-nv7et4t3.js";
|
|
16
16
|
import {
|
|
17
17
|
script
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-qpz4csbs.js";
|
|
19
19
|
import {
|
|
20
20
|
PkgExecutor
|
|
21
|
-
} from "./index-
|
|
21
|
+
} from "./index-0t7pwff2.js";
|
|
22
22
|
|
|
23
23
|
// pkgs/@akanjs/cli/cloud/cloud.script.ts
|
|
24
24
|
import { Logger } from "akanjs/common";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
3
|
PageScript
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-y4kg682s.js";
|
|
5
5
|
import {
|
|
6
6
|
pluralizeName
|
|
7
7
|
} from "./index-ss469dec.js";
|
|
@@ -12,20 +12,20 @@ import {
|
|
|
12
12
|
generatedFilesForSync,
|
|
13
13
|
moduleSourcePaths,
|
|
14
14
|
sourceFile
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-2v10yr8g.js";
|
|
16
16
|
import {
|
|
17
17
|
Prompter
|
|
18
|
-
} from "./index-
|
|
18
|
+
} from "./index-eqxmxan6.js";
|
|
19
19
|
import {
|
|
20
20
|
AiSession
|
|
21
21
|
} from "./index-5vvwc0cz.js";
|
|
22
22
|
import {
|
|
23
23
|
runner,
|
|
24
24
|
script
|
|
25
|
-
} from "./index-
|
|
25
|
+
} from "./index-qpz4csbs.js";
|
|
26
26
|
import {
|
|
27
27
|
ModuleExecutor
|
|
28
|
-
} from "./index-
|
|
28
|
+
} from "./index-0t7pwff2.js";
|
|
29
29
|
import {
|
|
30
30
|
FileSys
|
|
31
31
|
} from "./index-61keag0s.js";
|