@akanjs/cli 3.0.0-beta.24 → 3.0.0-beta.25
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 -1
- package/{CodeAgent-0dh9fzzg.js → CodeAgent-p65jja35.js} +2 -2
- package/{CodeTui-drwdvwg9.js → CodeTui-5xr8kxx4.js} +1 -1
- package/TunnelShare-cwh9ezsm.js +9 -0
- package/{agent.command-ttn09rx3.js → agent.command-rp505zxa.js} +7 -7
- package/akanCode.js +2 -2
- package/{application.command-z7a4bdrq.js → application.command-2x9jqy0k.js} +7 -7
- package/{applicationBuildRunner-n6xfexaa.js → applicationBuildRunner-tm0vba5t.js} +4 -4
- package/buildBatch.proc.js +77 -23
- package/{cloud.command-j0fcz44z.js → cloud.command-j6m607bp.js} +12 -12
- package/cloud.script-r5t7nhee.js +20 -0
- package/{code.command-1jgk14tc.js → code.command-jhrjttm1.js} +7 -7
- package/{context.command-4vpjmg22.js → context.command-xncdvv7n.js} +14 -14
- package/context.runner-pjy89k2s.js +21 -0
- package/{dependencyScanner-ye2e6pdz.js → dependencyScanner-06z2dmrr.js} +2 -2
- package/{fileSys-5ngz93ga.js → fileSys-wzzrntkw.js} +1 -1
- package/{guideline.command-7jk47fgq.js → guideline.command-d5qmxx29.js} +4 -4
- package/guidelines/conventions/conventions.instruction.md +7 -5
- package/incrementalBuilder.proc.js +544 -72
- package/{index-ga6hygxr.js → index-0n9q1f23.js} +3 -3
- package/{index-3t7g9e4r.js → index-2ahj896a.js} +1 -1
- package/{index-zabxte74.js → index-2wc41k5w.js} +1 -1
- package/{index-khpfpcsd.js → index-48k96p55.js} +27 -7
- package/{index-ne50hc2m.js → index-4mgbzgb5.js} +456 -1623
- package/{index-2gh9sndr.js → index-50rq2qms.js} +4 -4
- package/{index-5b6tavv3.js → index-52w0fvh8.js} +15 -1
- package/{index-zbc2q6a6.js → index-56xwsgyn.js} +3 -3
- package/{index-rmcw8yc8.js → index-58cyna2s.js} +2 -2
- package/{index-1db2cw0y.js → index-5mm3g24t.js} +2 -2
- package/{index-wp43xrjv.js → index-6dt6zft6.js} +4 -4
- package/{index-vj1cwwy5.js → index-7yxndzea.js} +7 -7
- package/{index-q44jbw48.js → index-860zjnr1.js} +1 -1
- package/{index-d4mfjgm0.js → index-8k93esfr.js} +6 -6
- package/{index-fzd22vjh.js → index-94s080qm.js} +3 -17
- package/{index-yy4s1j72.js → index-d65fzxvd.js} +1 -1
- package/{index-m0ms3n1c.js → index-da72pfdv.js} +7 -7
- package/index-dt1x62ec.js +4108 -0
- package/{index-4dn1p115.js → index-ggfhz9ab.js} +4 -4
- package/{index-9629w7ns.js → index-hzy4pe24.js} +3 -3
- package/{index-ymgtbmf1.js → index-pb10njz7.js} +191 -58
- package/{index-mgfx3mfs.js → index-per2kqy9.js} +4 -4
- package/{index-aaz7n8s2.js → index-qm4fppfk.js} +4 -4
- package/{index-ya5qbmyg.js → index-t02g6dpn.js} +3 -3
- package/{index-hb8sgepf.js → index-tpdn30ya.js} +1 -1
- package/{index-rnt66vym.js → index-v4hnkqz9.js} +1 -1
- package/{index-pp54p5zk.js → index-xfgdmm5d.js} +2 -2
- package/{index-tgjy8xf3.js → index-zazvn45w.js} +4 -4
- package/{index-0bq14vr1.js → index-zzvvc845.js} +1 -1
- package/index.js +22 -22
- package/{library.command-ccb1cp92.js → library.command-sv6g1h2g.js} +4 -4
- package/{localRegistry.command-gmj1k6q8.js → localRegistry.command-4kj56531.js} +11 -11
- package/{module.command-5k6h407d.js → module.command-ccdeder9.js} +6 -6
- package/{package.command-e6ntvpez.js → package.command-xmw605b1.js} +5 -5
- package/package.json +3 -3
- package/{page.command-v4f90wb8.js → page.command-ky7vafsn.js} +4 -4
- package/{primitive.command-g6km57q5.js → primitive.command-bh6j8nfs.js} +7 -7
- package/{prompter-ptaa3nk0.js → prompter-76k56b37.js} +2 -2
- package/{quality.command-de3p36pz.js → quality.command-rqqrpe98.js} +3 -3
- package/{repair.command-d3x1javq.js → repair.command-n9nsm9qe.js} +6 -6
- package/{scalar.command-7ezrac2b.js → scalar.command-t09gmck8.js} +5 -5
- package/{subspace.command-ksenprez.js → subspace.command-ygfdqpbj.js} +6 -6
- package/{tunnel.command-54947jck.js → tunnel.command-4g95vhsn.js} +5 -5
- package/{workflow.command-z2p32nmf.js → workflow.command-b91ph8sx.js} +11 -11
- package/{workspace.command-tkn8g0bn.js → workspace.command-p44d21qf.js} +24 -24
- package/README.ko.md +0 -72
- package/README.md +0 -88
- package/TunnelShare-pemaknmd.js +0 -9
- package/cloud.script-g2dcnr8c.js +0 -20
- package/context.runner-ehjs419v.js +0 -21
- package/index-fgq75haa.js +0 -2084
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runner,
|
|
4
4
|
script
|
|
5
|
-
} from "./index-
|
|
5
|
+
} from "./index-58cyna2s.js";
|
|
6
6
|
import {
|
|
7
7
|
AGENT_BLOCK_START,
|
|
8
8
|
AGENT_BLOCK_END,
|
|
@@ -13,17 +13,17 @@ import {
|
|
|
13
13
|
CLAUDE_COMMENT_RULE,
|
|
14
14
|
AppExecutor,
|
|
15
15
|
LibExecutor
|
|
16
|
-
} from "./index-
|
|
16
|
+
} from "./index-0n9q1f23.js";
|
|
17
17
|
import {
|
|
18
18
|
collectRecipeSources2,
|
|
19
19
|
scanRecipes2
|
|
20
20
|
} from "./index-hj6w4ka1.js";
|
|
21
21
|
import {
|
|
22
22
|
AkanContextAnalyzer
|
|
23
|
-
} from "./index-
|
|
23
|
+
} from "./index-hzy4pe24.js";
|
|
24
24
|
import {
|
|
25
25
|
Prompter2
|
|
26
|
-
} from "./index-
|
|
26
|
+
} from "./index-zzvvc845.js";
|
|
27
27
|
|
|
28
28
|
// pkgs/@akanjs/cli/agent/agent.script.ts
|
|
29
29
|
import { Logger as Logger2 } from "akanjs/common";
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
isScannedRootEntry,
|
|
9
9
|
AppExecutor,
|
|
10
10
|
LibExecutor
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-0n9q1f23.js";
|
|
12
12
|
import {
|
|
13
13
|
FileSys2
|
|
14
|
-
} from "./index-
|
|
14
|
+
} from "./index-52w0fvh8.js";
|
|
15
15
|
import {
|
|
16
16
|
collectRecipeSources2,
|
|
17
17
|
scanRecipes2,
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
workflowPathsForPlan,
|
|
22
22
|
workflowRunArtifactPath,
|
|
23
23
|
workflowSyncDir
|
|
24
|
-
} from "./index-
|
|
24
|
+
} from "./index-d65fzxvd.js";
|
|
25
25
|
|
|
26
26
|
// pkgs/@akanjs/devkit/akanContext.ts
|
|
27
27
|
import { readdir } from "fs/promises";
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
devHostStateOf,
|
|
12
12
|
isLegacyBackendFallbackFile,
|
|
13
13
|
shouldMarkBuildPhaseRecovered,
|
|
14
|
+
shouldKeepBuildFailure,
|
|
14
15
|
createBackendBuildStatus,
|
|
15
16
|
backendRestartReasonFromMessage,
|
|
16
17
|
mergeBackendRestartReasons,
|
|
@@ -32,20 +33,20 @@ import {
|
|
|
32
33
|
filesChangedSince,
|
|
33
34
|
HmrWatcher,
|
|
34
35
|
WatchRootResolver
|
|
35
|
-
} from "./index-
|
|
36
|
+
} from "./index-48k96p55.js";
|
|
36
37
|
import {
|
|
37
38
|
runner,
|
|
38
39
|
script
|
|
39
|
-
} from "./index-
|
|
40
|
+
} from "./index-58cyna2s.js";
|
|
40
41
|
import {
|
|
41
42
|
WorkspaceExecutor,
|
|
42
43
|
AppExecutor,
|
|
43
44
|
LibExecutor,
|
|
44
45
|
PkgExecutor
|
|
45
|
-
} from "./index-
|
|
46
|
+
} from "./index-0n9q1f23.js";
|
|
46
47
|
import {
|
|
47
48
|
LibraryScript
|
|
48
|
-
} from "./index-
|
|
49
|
+
} from "./index-5mm3g24t.js";
|
|
49
50
|
import {
|
|
50
51
|
resolveMobileTargets,
|
|
51
52
|
NativeApp
|
|
@@ -56,7 +57,7 @@ import {
|
|
|
56
57
|
import {
|
|
57
58
|
SlicePlanner,
|
|
58
59
|
formatSlicePlan
|
|
59
|
-
} from "./index-
|
|
60
|
+
} from "./index-860zjnr1.js";
|
|
60
61
|
import {
|
|
61
62
|
openBrowser
|
|
62
63
|
} from "./index-9dp031by.js";
|
|
@@ -66,9 +67,10 @@ import { confirm } from "@inquirer/prompts";
|
|
|
66
67
|
import { Logger as Logger6 } from "akanjs/common";
|
|
67
68
|
|
|
68
69
|
// pkgs/@akanjs/cli/application/application.runner.ts
|
|
69
|
-
import
|
|
70
|
+
import path5 from "path";
|
|
70
71
|
|
|
71
72
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
73
|
+
import path3 from "path";
|
|
72
74
|
import { Logger as Logger2 } from "akanjs/common";
|
|
73
75
|
|
|
74
76
|
// pkgs/@akanjs/devkit/sshTunnel.ts
|
|
@@ -199,8 +201,10 @@ var createTunnel = async (service, { app, environment, port = service === "postg
|
|
|
199
201
|
return `localhost:${port}`;
|
|
200
202
|
};
|
|
201
203
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.host.ts
|
|
204
|
+
import fs from "fs";
|
|
202
205
|
import path from "path";
|
|
203
206
|
import { Logger } from "akanjs/common";
|
|
207
|
+
import { CSR_DEV_DIRNAME, CSR_DEV_PATCHING_MARKER, SSR_DEV_DIRNAME } from "akanjs/server/hmr/csrDevManifest";
|
|
204
208
|
import { MemoryLimit } from "akanjs/server/memoryLimit";
|
|
205
209
|
var builderMsgTypeSet = new Set([
|
|
206
210
|
"build-route-res",
|
|
@@ -210,15 +214,17 @@ var builderMsgTypeSet = new Set([
|
|
|
210
214
|
"css-updated",
|
|
211
215
|
"pages-updated",
|
|
212
216
|
"csr-updated",
|
|
217
|
+
"ssr-updated",
|
|
213
218
|
"build-status",
|
|
214
|
-
"builder-metrics"
|
|
219
|
+
"builder-metrics",
|
|
220
|
+
"boot-armed"
|
|
215
221
|
]);
|
|
216
222
|
|
|
217
223
|
class IncrementalBuilderHost {
|
|
218
224
|
static #restartBaseDelayMs = 1000;
|
|
219
225
|
static #restartMaxDelayMs = 30000;
|
|
220
226
|
static #recycleDrainTimeoutMs = 30000;
|
|
221
|
-
static #devMaxRssBytes =
|
|
227
|
+
static #devMaxRssBytes = 2048 * 1024 * 1024;
|
|
222
228
|
logger = new Logger("IncrementalBuilderHost");
|
|
223
229
|
entry;
|
|
224
230
|
env;
|
|
@@ -234,6 +240,7 @@ class IncrementalBuilderHost {
|
|
|
234
240
|
#recycleTimer = null;
|
|
235
241
|
#recycleRequested = false;
|
|
236
242
|
#spawnAfterRecycle = false;
|
|
243
|
+
#patcherOff = false;
|
|
237
244
|
#manualStop = false;
|
|
238
245
|
#inFlight = new Map;
|
|
239
246
|
#startOptions = {};
|
|
@@ -265,10 +272,16 @@ class IncrementalBuilderHost {
|
|
|
265
272
|
this.ready = false;
|
|
266
273
|
const afterRecycle = this.#spawnAfterRecycle;
|
|
267
274
|
this.#spawnAfterRecycle = false;
|
|
275
|
+
this.#checkPatchingMarker(isRestart && !afterRecycle);
|
|
268
276
|
let proc;
|
|
269
277
|
proc = Bun.spawn(["bun", this.entry], {
|
|
270
278
|
cwd: this.app.cwdPath,
|
|
271
|
-
env: {
|
|
279
|
+
env: {
|
|
280
|
+
...this.env,
|
|
281
|
+
AKAN_WATCH: "1",
|
|
282
|
+
...afterRecycle ? { AKAN_BUILDER_ANNOUNCE_BOOT: "1" } : {},
|
|
283
|
+
...this.#patcherOff ? { AKAN_DEV_CSR_PATCHER: "off" } : {}
|
|
284
|
+
},
|
|
272
285
|
stdio: ["ignore", this.#stdio, this.#stdio],
|
|
273
286
|
ipc: (msg) => {
|
|
274
287
|
if (this.#proc !== proc)
|
|
@@ -323,6 +336,17 @@ class IncrementalBuilderHost {
|
|
|
323
336
|
}
|
|
324
337
|
this.logger.verbose(`builder spawned pid=${proc.pid} entry=${this.entry}${isRestart ? " restart=1" : ""}`);
|
|
325
338
|
}
|
|
339
|
+
#checkPatchingMarker(afterCrash) {
|
|
340
|
+
const markers = [CSR_DEV_DIRNAME, SSR_DEV_DIRNAME].map((dirName) => path.join(this.app.cwdPath, ".akan/artifact", dirName, CSR_DEV_PATCHING_MARKER)).filter((marker) => fs.existsSync(marker));
|
|
341
|
+
if (markers.length === 0)
|
|
342
|
+
return;
|
|
343
|
+
if (afterCrash && !this.#patcherOff) {
|
|
344
|
+
this.#patcherOff = true;
|
|
345
|
+
this.logger.warn("the builder died while patching a dev module registry; registry saves go to a build worker until the next config or metadata restart (AKAN_DEV_CSR_PATCHER=off)");
|
|
346
|
+
}
|
|
347
|
+
for (const marker of markers)
|
|
348
|
+
fs.rmSync(marker, { force: true });
|
|
349
|
+
}
|
|
326
350
|
async#drain(stream, kind) {
|
|
327
351
|
if (!stream)
|
|
328
352
|
return;
|
|
@@ -618,6 +642,41 @@ class BackendImportGraph {
|
|
|
618
642
|
}
|
|
619
643
|
}
|
|
620
644
|
|
|
645
|
+
// pkgs/@akanjs/devkit/akanApp/devBootLatch.ts
|
|
646
|
+
class DevBootLatch {
|
|
647
|
+
#onBooted;
|
|
648
|
+
#waitMs;
|
|
649
|
+
#ready = false;
|
|
650
|
+
#armed = false;
|
|
651
|
+
#booted = false;
|
|
652
|
+
#timer = null;
|
|
653
|
+
constructor(onBooted, { waitMs = 30000 } = {}) {
|
|
654
|
+
this.#onBooted = onBooted;
|
|
655
|
+
this.#waitMs = waitMs;
|
|
656
|
+
}
|
|
657
|
+
ready() {
|
|
658
|
+
this.#ready = true;
|
|
659
|
+
if (!this.#armed && !this.#timer) {
|
|
660
|
+
this.#timer = setTimeout(() => this.armed(), this.#waitMs);
|
|
661
|
+
this.#timer.unref?.();
|
|
662
|
+
}
|
|
663
|
+
this.#release();
|
|
664
|
+
}
|
|
665
|
+
armed() {
|
|
666
|
+
this.#armed = true;
|
|
667
|
+
this.#release();
|
|
668
|
+
}
|
|
669
|
+
#release() {
|
|
670
|
+
if (this.#booted || !this.#ready || !this.#armed)
|
|
671
|
+
return;
|
|
672
|
+
this.#booted = true;
|
|
673
|
+
if (this.#timer)
|
|
674
|
+
clearTimeout(this.#timer);
|
|
675
|
+
this.#timer = null;
|
|
676
|
+
this.#onBooted();
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
621
680
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
622
681
|
var backendMsgTypeSet = new Set(["build-route", "build-csr"]);
|
|
623
682
|
var asMib = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
@@ -641,6 +700,7 @@ class AkanAppHost {
|
|
|
641
700
|
env;
|
|
642
701
|
#onDevEvent;
|
|
643
702
|
#lastDevState = null;
|
|
703
|
+
#bootLatch = new DevBootLatch(() => this.#onDevEvent?.({ app: this.app.name, booted: true }));
|
|
644
704
|
#backend = null;
|
|
645
705
|
#builder = null;
|
|
646
706
|
#backendReady = false;
|
|
@@ -652,6 +712,8 @@ class AkanAppHost {
|
|
|
652
712
|
#backendLifecycleState = "stopped";
|
|
653
713
|
#pendingRestartReason = null;
|
|
654
714
|
#pendingRecycle = null;
|
|
715
|
+
#restartFailedAfterApply = null;
|
|
716
|
+
#stopping = false;
|
|
655
717
|
#builderRecoveryTimer = null;
|
|
656
718
|
#builderRecoveryAttempts = 0;
|
|
657
719
|
#backendStartStatus = null;
|
|
@@ -679,6 +741,7 @@ class AkanAppHost {
|
|
|
679
741
|
#builderGapStamp = null;
|
|
680
742
|
#pendingBuilderMessages = [];
|
|
681
743
|
#builderRequests = new BuilderRequestRouter;
|
|
744
|
+
#builderGeneration = 0;
|
|
682
745
|
constructor(app, {
|
|
683
746
|
env,
|
|
684
747
|
stdio = "inherit",
|
|
@@ -695,6 +758,8 @@ class AkanAppHost {
|
|
|
695
758
|
return;
|
|
696
759
|
this.#lastDevState = state;
|
|
697
760
|
this.#onDevEvent({ app: this.app.name, state, ...detail ? { detail } : {} });
|
|
761
|
+
if (state === "ready")
|
|
762
|
+
this.#bootLatch.ready();
|
|
698
763
|
}
|
|
699
764
|
async start() {
|
|
700
765
|
if (this.#backend)
|
|
@@ -712,6 +777,8 @@ class AkanAppHost {
|
|
|
712
777
|
return this;
|
|
713
778
|
}
|
|
714
779
|
async stop() {
|
|
780
|
+
this.#stopping = true;
|
|
781
|
+
this.#pendingRecycle = null;
|
|
715
782
|
this.#cancelIdleSuspend();
|
|
716
783
|
this.#stopIdleWatcher();
|
|
717
784
|
this.#clearRestartTimers();
|
|
@@ -756,6 +823,7 @@ class AkanAppHost {
|
|
|
756
823
|
this.#setBackendLifecycleState("ready", `pid=${msg.pid}`);
|
|
757
824
|
this.#recordBackendReadyStatus();
|
|
758
825
|
this.logger.verbose(`backend ready pid=${msg.pid}`);
|
|
826
|
+
this.#forgetRouteBuildStatus();
|
|
759
827
|
this.#replayBuilderState();
|
|
760
828
|
return;
|
|
761
829
|
}
|
|
@@ -976,17 +1044,26 @@ ${this.#backendStderrTail.join(`
|
|
|
976
1044
|
});
|
|
977
1045
|
}
|
|
978
1046
|
async#handleBuilderMessage(message) {
|
|
1047
|
+
if (this.#stopping)
|
|
1048
|
+
return;
|
|
979
1049
|
this.#markDevActivity();
|
|
1050
|
+
this.#trackBuilderGeneration(message);
|
|
980
1051
|
if (message.type === "build-status") {
|
|
981
1052
|
this.#recordBuildStatus(message.data);
|
|
982
1053
|
this.#sendOrQueueBuildStatus(message.data);
|
|
983
1054
|
this.#reviveBackendAfterGreenBuild(message.data);
|
|
1055
|
+
await this.#resumeFailedRecycle(message.data);
|
|
984
1056
|
return;
|
|
985
1057
|
}
|
|
986
1058
|
if (message.type === "builder-metrics") {
|
|
987
1059
|
this.#handleBuilderMetrics(message.data);
|
|
988
1060
|
return;
|
|
989
1061
|
}
|
|
1062
|
+
if (message.type === "boot-armed") {
|
|
1063
|
+
this.logger.verbose("[builder] boot builds settled");
|
|
1064
|
+
this.#bootLatch.armed();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
990
1067
|
if (message.type === "pages-updated" || message.type === "css-updated") {
|
|
991
1068
|
const recycled = message.data.reason === "builder-recycle";
|
|
992
1069
|
if (recycled && !this.#shouldRelayRecycledState(message))
|
|
@@ -1008,6 +1085,22 @@ ${this.#backendStderrTail.join(`
|
|
|
1008
1085
|
}
|
|
1009
1086
|
this.#sendToBackend(message);
|
|
1010
1087
|
}
|
|
1088
|
+
#trackBuilderGeneration(message) {
|
|
1089
|
+
const generation = AkanAppHost.#builderGenerationOf(message);
|
|
1090
|
+
if (generation === undefined || generation <= this.#builderGeneration)
|
|
1091
|
+
return;
|
|
1092
|
+
this.#builderGeneration = generation;
|
|
1093
|
+
Object.assign(this.env, { AKAN_BUILDER_INITIAL_GENERATION: String(generation) });
|
|
1094
|
+
}
|
|
1095
|
+
static #builderGenerationOf(message) {
|
|
1096
|
+
if (message.type === "invalidate")
|
|
1097
|
+
return message.generation;
|
|
1098
|
+
if (message.type === "build-status" || message.type === "builder-metrics")
|
|
1099
|
+
return message.data.generation;
|
|
1100
|
+
if (message.type === "pages-updated" || message.type === "css-updated")
|
|
1101
|
+
return message.data.generation;
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1011
1104
|
#shouldRelayRecycledState(message) {
|
|
1012
1105
|
const current = message.type === "pages-updated" ? this.#lastGoodFrontend.pages : this.#lastGoodFrontend.css;
|
|
1013
1106
|
if (shouldRelayRecycledFrontendState(current, message)) {
|
|
@@ -1266,9 +1359,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1266
1359
|
if (shouldRefreshConfigOnIdleWake(batch)) {
|
|
1267
1360
|
this.logger.verbose("[idle-suspend] config changed while suspended; restarting the dev host");
|
|
1268
1361
|
this.#discardBuilderGap("config change replaces the backend anyway");
|
|
1269
|
-
await this.#
|
|
1270
|
-
refreshConfig: true
|
|
1271
|
-
});
|
|
1362
|
+
await this.#applyRecycle({ type: "invalidate", kinds: [...batch?.kinds ?? []], files }, true);
|
|
1272
1363
|
return;
|
|
1273
1364
|
}
|
|
1274
1365
|
if (files.length > 0)
|
|
@@ -1369,7 +1460,8 @@ ${this.#backendStderrTail.join(`
|
|
|
1369
1460
|
this.#armRssRecycle(this.#rssRecycleReason);
|
|
1370
1461
|
const wantsDevHostRestart = shouldRestartDevHostByDevPlan(message);
|
|
1371
1462
|
const pending = this.#pendingRecycle;
|
|
1372
|
-
|
|
1463
|
+
const resumes = !!pending && message.kinds.includes("code") && this.#touchesFailedRecycle(message.files);
|
|
1464
|
+
if (wantsDevHostRestart || shouldRestartBuilderByDevPlan(message) || resumes) {
|
|
1373
1465
|
const refreshConfig = wantsDevHostRestart || (pending?.refreshConfig ?? false);
|
|
1374
1466
|
const merged = pending ? mergeInvalidateMessages(pending.message, message) : message;
|
|
1375
1467
|
const generation = message.devPlan?.generation ?? message.generation;
|
|
@@ -1377,16 +1469,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1377
1469
|
this.#deferRecycle(merged, { refreshConfig, generation });
|
|
1378
1470
|
return;
|
|
1379
1471
|
}
|
|
1380
|
-
this.#
|
|
1381
|
-
try {
|
|
1382
|
-
if (refreshConfig)
|
|
1383
|
-
await this.#restartDevHost(merged);
|
|
1384
|
-
else
|
|
1385
|
-
await this.#restartDevChildren(merged);
|
|
1386
|
-
} catch (err) {
|
|
1387
|
-
this.#recordDevHostRestartFailure(merged, err, refreshConfig ? "Config" : "Runtime metadata");
|
|
1388
|
-
this.#resurrectDevChildren(merged);
|
|
1389
|
-
}
|
|
1472
|
+
await this.#applyRecycle(merged, refreshConfig);
|
|
1390
1473
|
return;
|
|
1391
1474
|
}
|
|
1392
1475
|
if (await this.#shouldRestartBackend(message)) {
|
|
@@ -1395,6 +1478,41 @@ ${this.#backendStderrTail.join(`
|
|
|
1395
1478
|
}
|
|
1396
1479
|
this.#sendToBackend(message);
|
|
1397
1480
|
}
|
|
1481
|
+
async#applyRecycle(message, refreshConfig) {
|
|
1482
|
+
if (this.#stopping)
|
|
1483
|
+
return;
|
|
1484
|
+
this.#pendingRecycle = null;
|
|
1485
|
+
const progress = { applied: !refreshConfig };
|
|
1486
|
+
try {
|
|
1487
|
+
if (refreshConfig)
|
|
1488
|
+
await this.#restartDevHost(message, progress);
|
|
1489
|
+
else
|
|
1490
|
+
await this.#restartDevChildren(message);
|
|
1491
|
+
} catch (err) {
|
|
1492
|
+
const kind = refreshConfig ? "Config" : "Runtime metadata";
|
|
1493
|
+
const generation = this.#recordDevHostRestartFailure(message, err, kind, { applied: progress.applied });
|
|
1494
|
+
if (progress.applied)
|
|
1495
|
+
this.#restartFailedAfterApply = generation;
|
|
1496
|
+
else
|
|
1497
|
+
this.#pendingRecycle = { message, refreshConfig, failed: true, failedGeneration: generation };
|
|
1498
|
+
this.#resurrectDevChildren(message);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
async#resumeFailedRecycle(status) {
|
|
1502
|
+
const pending = this.#pendingRecycle;
|
|
1503
|
+
if (!pending?.failed || !status.ok || status.phase !== "pages" || !this.#touchesFailedRecycle(status.files))
|
|
1504
|
+
return;
|
|
1505
|
+
if (status.generation <= (pending.failedGeneration ?? -1))
|
|
1506
|
+
return;
|
|
1507
|
+
await this.#applyRecycle(pending.message, pending.refreshConfig);
|
|
1508
|
+
}
|
|
1509
|
+
#touchesFailedRecycle(files) {
|
|
1510
|
+
const pending = this.#pendingRecycle;
|
|
1511
|
+
if (!pending?.failed)
|
|
1512
|
+
return true;
|
|
1513
|
+
const changed = new Set(pending.message.files);
|
|
1514
|
+
return files.some((file) => changed.has(file));
|
|
1515
|
+
}
|
|
1398
1516
|
#deferRecycle(message, { refreshConfig, generation }) {
|
|
1399
1517
|
this.#pendingRecycle = { message, refreshConfig };
|
|
1400
1518
|
const kind = refreshConfig ? "Config" : "Runtime metadata";
|
|
@@ -1440,8 +1558,10 @@ ${this.#backendStderrTail.join(`
|
|
|
1440
1558
|
this.#builderRecoveryAttempts = 0;
|
|
1441
1559
|
this.logger.info("[builder-recovery] builder recovered");
|
|
1442
1560
|
this.#restartBackendForGapChanges();
|
|
1561
|
+
const failedAfterApply = this.#restartFailedAfterApply;
|
|
1562
|
+
this.#restartFailedAfterApply = null;
|
|
1443
1563
|
const status = {
|
|
1444
|
-
generation: reason.generation ?? this.#nextBackendBuildStatusGeneration(),
|
|
1564
|
+
generation: failedAfterApply !== null ? this.#nextBackendBuildStatusGeneration(failedAfterApply + 1) : reason.generation ?? this.#nextBackendBuildStatusGeneration(),
|
|
1445
1565
|
phase: "scan",
|
|
1446
1566
|
ok: true,
|
|
1447
1567
|
files: reason.files,
|
|
@@ -1465,12 +1585,12 @@ ${this.#backendStderrTail.join(`
|
|
|
1465
1585
|
this.logger.warn(`[dev-host] recycling builder/backend for runtime metadata generation=${generation ?? "(unknown)"} files=${message.files.length}`);
|
|
1466
1586
|
await this.#recycleDevChildren(message);
|
|
1467
1587
|
}
|
|
1468
|
-
async#restartDevHost(message) {
|
|
1588
|
+
async#restartDevHost(message, progress) {
|
|
1469
1589
|
const generation = message.devPlan?.generation ?? message.generation;
|
|
1470
1590
|
this.logger.warn(`[dev-host] config change detected; restarting dev host generation=${generation ?? "(unknown)"} files=${message.files.length}`);
|
|
1471
|
-
await this.#recycleDevChildren(message, { refreshConfig: true });
|
|
1591
|
+
await this.#recycleDevChildren(message, { refreshConfig: true, progress });
|
|
1472
1592
|
}
|
|
1473
|
-
async#recycleDevChildren(message, { refreshConfig = false } = {}) {
|
|
1593
|
+
async#recycleDevChildren(message, { refreshConfig = false, progress } = {}) {
|
|
1474
1594
|
const generation = message.devPlan?.generation ?? message.generation;
|
|
1475
1595
|
this.#clearRestartTimers();
|
|
1476
1596
|
this.#builderRecoveryAttempts = 0;
|
|
@@ -1484,6 +1604,8 @@ ${this.#backendStderrTail.join(`
|
|
|
1484
1604
|
await this.app.getConfig({ refresh: true });
|
|
1485
1605
|
const { env } = await this.app.prepareCommand("start");
|
|
1486
1606
|
Object.assign(this.env, env);
|
|
1607
|
+
if (progress)
|
|
1608
|
+
progress.applied = true;
|
|
1487
1609
|
}
|
|
1488
1610
|
await this.#backendGraph.refresh();
|
|
1489
1611
|
await this.#startBuilder();
|
|
@@ -1502,22 +1624,27 @@ ${this.#backendStderrTail.join(`
|
|
|
1502
1624
|
this.#lastGoodFrontend.css = message;
|
|
1503
1625
|
this.logger.verbose(`[last-good] css generation=${message.data.generation ?? "(unknown)"} assets=${Object.keys(message.data.cssAssets).length}`);
|
|
1504
1626
|
}
|
|
1505
|
-
#recordDevHostRestartFailure(message, err, kind) {
|
|
1627
|
+
#recordDevHostRestartFailure(message, err, kind, { applied }) {
|
|
1506
1628
|
const generation = message.devPlan?.generation ?? message.generation ?? this.#nextBackendBuildStatusGeneration();
|
|
1507
1629
|
const detail = err instanceof Error ? err.message : String(err);
|
|
1508
1630
|
this.logger.warn(`[dev-host] ${kind.toLowerCase()} restart failed generation=${generation}: ${detail}`);
|
|
1631
|
+
const names = message.files.slice(0, 3).map((file) => path3.basename(file));
|
|
1632
|
+
const saved = names.length > 0 ? names.join(", ") : "the changed file";
|
|
1509
1633
|
const status = {
|
|
1510
1634
|
generation,
|
|
1511
1635
|
phase: "scan",
|
|
1512
1636
|
ok: false,
|
|
1513
1637
|
files: message.files,
|
|
1514
|
-
message: `${kind} change failed to
|
|
1638
|
+
message: applied ? `${kind} change applied, but the dev server failed to restart: ${detail}. It is recovering on its own.` : `${kind} change failed to apply: ${detail}. The dev server keeps running on the previous ${kind.toLowerCase()}; once it is fixed, save ${saved} again or restart the dev server to apply it.`
|
|
1515
1639
|
};
|
|
1516
1640
|
this.#recordBuildStatus(status);
|
|
1517
1641
|
this.#sendOrQueueBuildStatus(status);
|
|
1642
|
+
return generation;
|
|
1518
1643
|
}
|
|
1519
1644
|
#recordBuildStatus(status) {
|
|
1520
1645
|
const recovered = shouldMarkBuildPhaseRecovered(this.#buildStatusByPhase, status);
|
|
1646
|
+
if (shouldKeepBuildFailure(this.#buildStatusByPhase, status))
|
|
1647
|
+
return;
|
|
1521
1648
|
this.#buildStatusByPhase.set(status.phase, status);
|
|
1522
1649
|
const label = `[build-status] generation=${status.generation} phase=${status.phase} ok=${status.ok} files=${status.files.length}`;
|
|
1523
1650
|
if (status.ok)
|
|
@@ -1533,6 +1660,10 @@ ${this.#backendStderrTail.join(`
|
|
|
1533
1660
|
}
|
|
1534
1661
|
this.#sendToBackend({ type: "build-status", data: status });
|
|
1535
1662
|
}
|
|
1663
|
+
#forgetRouteBuildStatus() {
|
|
1664
|
+
this.#buildStatusByPhase.delete("route");
|
|
1665
|
+
this.#pendingBuildStatusReplay = this.#pendingBuildStatusReplay.filter((status) => status.phase !== "route");
|
|
1666
|
+
}
|
|
1536
1667
|
#replayBuilderState() {
|
|
1537
1668
|
if (!this.#backendReady)
|
|
1538
1669
|
return;
|
|
@@ -1675,16 +1806,16 @@ ${this.#backendStderrTail.join(`
|
|
|
1675
1806
|
}
|
|
1676
1807
|
}
|
|
1677
1808
|
// pkgs/@akanjs/devkit/applicationTestPreload.ts
|
|
1678
|
-
import
|
|
1809
|
+
import path4 from "path";
|
|
1679
1810
|
var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
|
|
1680
1811
|
async function resolveSignalTestPreloadPath(target) {
|
|
1681
1812
|
const candidates = [];
|
|
1682
|
-
for (const basePath of [target.cwdPath, process.cwd(),
|
|
1813
|
+
for (const basePath of [target.cwdPath, process.cwd(), path4.dirname(Bun.main), import.meta.dir]) {
|
|
1683
1814
|
try {
|
|
1684
|
-
candidates.push(
|
|
1815
|
+
candidates.push(path4.join(path4.dirname(Bun.resolveSync("akanjs/package.json", basePath)), SIGNAL_TEST_PRELOAD_PATH));
|
|
1685
1816
|
} catch {}
|
|
1686
1817
|
}
|
|
1687
|
-
candidates.push(
|
|
1818
|
+
candidates.push(path4.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path4.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path4.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path4.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path4.join(path4.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path4.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
|
|
1688
1819
|
const uniqueCandidates = [...new Set(candidates)];
|
|
1689
1820
|
for (const candidate of uniqueCandidates)
|
|
1690
1821
|
if (await Bun.file(candidate).exists())
|
|
@@ -1697,7 +1828,7 @@ ${uniqueCandidates.map((candidate) => ` - ${candidate}`).join(`
|
|
|
1697
1828
|
|
|
1698
1829
|
// pkgs/@akanjs/cli/application/application.runner.ts
|
|
1699
1830
|
import { Logger as Logger3 } from "akanjs/common";
|
|
1700
|
-
var loadBuildRunner = async () => (await import("./applicationBuildRunner-
|
|
1831
|
+
var loadBuildRunner = async () => (await import("./applicationBuildRunner-tm0vba5t.js")).ApplicationBuildRunner;
|
|
1701
1832
|
var loadPrompts = async () => await import("@inquirer/prompts");
|
|
1702
1833
|
|
|
1703
1834
|
class ApplicationRunner extends runner("application") {
|
|
@@ -1742,7 +1873,7 @@ class ApplicationRunner extends runner("application") {
|
|
|
1742
1873
|
});
|
|
1743
1874
|
}
|
|
1744
1875
|
#runtimeDirOf(app, override) {
|
|
1745
|
-
return
|
|
1876
|
+
return path5.resolve(override ?? process.env.AKAN_RUNTIME_DIR ?? path5.join(app.workspace.workspaceRoot, "local", "apps", app.name, "runtime"));
|
|
1746
1877
|
}
|
|
1747
1878
|
async runLogs(app, options) {
|
|
1748
1879
|
const { LogControlUnavailableError, LogTailClient } = await import("akanjs/server/logging/logTailClient");
|
|
@@ -1768,7 +1899,7 @@ class ApplicationRunner extends runner("application") {
|
|
|
1768
1899
|
});
|
|
1769
1900
|
} catch (error) {
|
|
1770
1901
|
if (error instanceof LogControlUnavailableError)
|
|
1771
|
-
throw new Error(`${app.name} is not running (no ${
|
|
1902
|
+
throw new Error(`${app.name} is not running (no ${path5.basename(socketPath)} in ${runtimeDir}). Start it with \`akan start ${app.name}\`, or pass --runtime-dir for a built app.`);
|
|
1772
1903
|
throw error;
|
|
1773
1904
|
}
|
|
1774
1905
|
const sinceMs = since ? LogQueryMatcher.parseSince(since) : undefined;
|
|
@@ -1833,7 +1964,7 @@ try {
|
|
|
1833
1964
|
const serverPath = `${app.cwdPath}/server.ts`;
|
|
1834
1965
|
if (!await app.exists("server.ts"))
|
|
1835
1966
|
throw new Error(`Server file not found: apps/${app.name}/server.ts`);
|
|
1836
|
-
const target =
|
|
1967
|
+
const target = path5.resolve(app.workspace.workspaceRoot, dir);
|
|
1837
1968
|
const code = `
|
|
1838
1969
|
const { server } = await import(${JSON.stringify(serverPath)});
|
|
1839
1970
|
const { DatabaseAdaptorRole, DocumentTransfer } = await import("akanjs/service");
|
|
@@ -2406,7 +2537,7 @@ import os from "os";
|
|
|
2406
2537
|
import { MemoryLimit as MemoryLimit2 } from "akanjs/server/memoryLimit";
|
|
2407
2538
|
|
|
2408
2539
|
class DevBootConcurrency {
|
|
2409
|
-
static perAppBytes =
|
|
2540
|
+
static perAppBytes = 1800 * 1024 * 1024;
|
|
2410
2541
|
static memoryShare = 0.5;
|
|
2411
2542
|
static coresPerApp = 4;
|
|
2412
2543
|
static budget() {
|
|
@@ -2440,7 +2571,7 @@ class DevBootConcurrency {
|
|
|
2440
2571
|
|
|
2441
2572
|
// pkgs/@akanjs/cli/application/devSessionLog.ts
|
|
2442
2573
|
import { mkdir, rename } from "fs/promises";
|
|
2443
|
-
import
|
|
2574
|
+
import path6 from "path";
|
|
2444
2575
|
import dayjs from "dayjs";
|
|
2445
2576
|
|
|
2446
2577
|
class DevSessionLog {
|
|
@@ -2464,19 +2595,19 @@ class DevSessionLog {
|
|
|
2464
2595
|
return [...this.#appNames];
|
|
2465
2596
|
}
|
|
2466
2597
|
pathOf(app) {
|
|
2467
|
-
return
|
|
2598
|
+
return path6.join(this.#runtimeDirOf(app), DevSessionLog.fileName);
|
|
2468
2599
|
}
|
|
2469
2600
|
previousPathOf(app) {
|
|
2470
|
-
return
|
|
2601
|
+
return path6.join(this.#runtimeDirOf(app), DevSessionLog.previousFileName);
|
|
2471
2602
|
}
|
|
2472
2603
|
relativePathOf(app) {
|
|
2473
|
-
return
|
|
2604
|
+
return path6.relative(this.#workspaceRoot, this.pathOf(app));
|
|
2474
2605
|
}
|
|
2475
2606
|
async open() {
|
|
2476
2607
|
const stamp = dayjs(this.#now()).format("YYYY-MM-DD HH:mm:ss");
|
|
2477
2608
|
for (const app of this.#appNames) {
|
|
2478
2609
|
const file = this.pathOf(app);
|
|
2479
|
-
await mkdir(
|
|
2610
|
+
await mkdir(path6.dirname(file), { recursive: true });
|
|
2480
2611
|
await rename(file, this.previousPathOf(app)).catch(() => {
|
|
2481
2612
|
return;
|
|
2482
2613
|
});
|
|
@@ -2520,7 +2651,7 @@ class DevSessionLog {
|
|
|
2520
2651
|
this.#writers.clear();
|
|
2521
2652
|
}
|
|
2522
2653
|
#runtimeDirOf(app) {
|
|
2523
|
-
return
|
|
2654
|
+
return path6.join(this.#workspaceRoot, "local", "apps", app, "runtime");
|
|
2524
2655
|
}
|
|
2525
2656
|
#writeLine(app, text) {
|
|
2526
2657
|
const writer = this.#writers.get(app);
|
|
@@ -2567,7 +2698,7 @@ class DevSupervisor {
|
|
|
2567
2698
|
}
|
|
2568
2699
|
};
|
|
2569
2700
|
}
|
|
2570
|
-
static
|
|
2701
|
+
static bootTimeoutMs = 180000;
|
|
2571
2702
|
static shutdownGraceMs = 12000;
|
|
2572
2703
|
#options;
|
|
2573
2704
|
#children = new Map;
|
|
@@ -2616,7 +2747,7 @@ class DevSupervisor {
|
|
|
2616
2747
|
return ports;
|
|
2617
2748
|
}
|
|
2618
2749
|
#makeChild(app, port) {
|
|
2619
|
-
const { promise:
|
|
2750
|
+
const { promise: booted, resolve: markBooted } = Promise.withResolvers();
|
|
2620
2751
|
return {
|
|
2621
2752
|
status: {
|
|
2622
2753
|
app,
|
|
@@ -2629,8 +2760,8 @@ class DevSupervisor {
|
|
|
2629
2760
|
exitCode: null
|
|
2630
2761
|
},
|
|
2631
2762
|
proc: null,
|
|
2632
|
-
|
|
2633
|
-
|
|
2763
|
+
booted,
|
|
2764
|
+
markBooted,
|
|
2634
2765
|
opened: false
|
|
2635
2766
|
};
|
|
2636
2767
|
}
|
|
@@ -2649,12 +2780,12 @@ class DevSupervisor {
|
|
|
2649
2780
|
for (const child of wave)
|
|
2650
2781
|
this.#spawnChild(child);
|
|
2651
2782
|
this.#publishStatus();
|
|
2652
|
-
await Promise.all(wave.map(async (child) => await this.#
|
|
2783
|
+
await Promise.all(wave.map(async (child) => await this.#waitForBoot(child)));
|
|
2653
2784
|
}
|
|
2654
2785
|
}
|
|
2655
|
-
async#
|
|
2656
|
-
if (await DevSupervisor.timesOut(child.
|
|
2657
|
-
this.#note(`${child.status.name} has not
|
|
2786
|
+
async#waitForBoot(child) {
|
|
2787
|
+
if (await DevSupervisor.timesOut(child.booted, DevSupervisor.bootTimeoutMs))
|
|
2788
|
+
this.#note(`${child.status.name} has not finished booting after ${Math.round(DevSupervisor.bootTimeoutMs / 1000)}s; starting the next app anyway`, "warn");
|
|
2658
2789
|
}
|
|
2659
2790
|
static async timesOut(work, ms) {
|
|
2660
2791
|
let timer;
|
|
@@ -2691,9 +2822,12 @@ class DevSupervisor {
|
|
|
2691
2822
|
},
|
|
2692
2823
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2693
2824
|
ipc: (message) => {
|
|
2694
|
-
if (!message || typeof message !== "object"
|
|
2825
|
+
if (!message || typeof message !== "object")
|
|
2695
2826
|
return;
|
|
2696
|
-
|
|
2827
|
+
if ("booted" in message)
|
|
2828
|
+
child.markBooted();
|
|
2829
|
+
else if (typeof message.state === "string")
|
|
2830
|
+
this.#applyChildEvent(child, message);
|
|
2697
2831
|
},
|
|
2698
2832
|
serialization: "advanced",
|
|
2699
2833
|
onExit: (_proc, exitCode, signalCode) => {
|
|
@@ -2703,7 +2837,7 @@ class DevSupervisor {
|
|
|
2703
2837
|
return;
|
|
2704
2838
|
child.status.state = "stopped";
|
|
2705
2839
|
child.status.detail = signalCode ? `killed by ${signalCode}` : `exited with ${exitCode}`;
|
|
2706
|
-
child.
|
|
2840
|
+
child.markBooted();
|
|
2707
2841
|
this.#publishStatus();
|
|
2708
2842
|
}
|
|
2709
2843
|
});
|
|
@@ -2717,14 +2851,13 @@ class DevSupervisor {
|
|
|
2717
2851
|
child.status.state = event.state;
|
|
2718
2852
|
child.status.detail = event.detail ?? "";
|
|
2719
2853
|
if (event.state === "ready") {
|
|
2720
|
-
child.markReady();
|
|
2721
2854
|
if (this.#options.open && !child.opened) {
|
|
2722
2855
|
child.opened = true;
|
|
2723
2856
|
openBrowser(child.status.url);
|
|
2724
2857
|
}
|
|
2725
2858
|
}
|
|
2726
2859
|
if (event.state === "failed")
|
|
2727
|
-
child.
|
|
2860
|
+
child.markBooted();
|
|
2728
2861
|
this.#publishStatus();
|
|
2729
2862
|
}
|
|
2730
2863
|
async#drain(stream, name, kind) {
|
|
@@ -3125,7 +3258,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3125
3258
|
await Promise.all(shares.map(async (share) => await share.close()));
|
|
3126
3259
|
}, "Abandoning the tunnel release; the shares expire on their own.");
|
|
3127
3260
|
await Promise.all(apps.map(async (app) => {
|
|
3128
|
-
const { TunnelShare } = await import("./TunnelShare-
|
|
3261
|
+
const { TunnelShare } = await import("./TunnelShare-cwh9ezsm.js");
|
|
3129
3262
|
const share = await TunnelShare.open(app, { workspace: app.workspace });
|
|
3130
3263
|
shares.push(share);
|
|
3131
3264
|
urls.set(app.name, share.url);
|