@akanjs/cli 3.0.0-beta.23 → 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-b6rck8d6.js → application.command-2x9jqy0k.js} +7 -7
- package/{applicationBuildRunner-vh74vrhg.js → applicationBuildRunner-tm0vba5t.js} +4 -4
- package/buildBatch.proc.js +99 -12
- package/{cloud.command-gwdypy7k.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/agentRule/agentRule.instruction.md +7 -0
- 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-smme4r47.js → index-2wc41k5w.js} +1 -1
- package/{index-khpfpcsd.js → index-48k96p55.js} +27 -7
- package/{index-776sswhv.js → index-4mgbzgb5.js} +281 -651
- package/{index-2gh9sndr.js → index-50rq2qms.js} +4 -4
- package/{index-5b6tavv3.js → index-52w0fvh8.js} +15 -1
- package/{index-yjkte9aj.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-jn07hxrr.js → index-8k93esfr.js} +6 -6
- package/{index-22d57v4c.js → index-94s080qm.js} +3 -3
- 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-jg5e15db.js → index-pb10njz7.js} +192 -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-cxvgn7fr.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 +4 -4
- 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-0cep72ce.js → subspace.command-ygfdqpbj.js} +6 -6
- package/templates/workspaceRoot/package.json.template +1 -3
- package/{tunnel.command-hsrv2mny.js → tunnel.command-4g95vhsn.js} +5 -5
- package/{workflow.command-z2p32nmf.js → workflow.command-b91ph8sx.js} +11 -11
- package/{workspace.command-axwscbnn.js → workspace.command-p44d21qf.js} +24 -24
- package/README.ko.md +0 -72
- package/README.md +0 -88
- package/TunnelShare-scf4e4pd.js +0 -9
- package/cloud.script-at2beffs.js +0 -20
- package/context.runner-ehjs419v.js +0 -21
- package/index-fgq75haa.js +0 -2084
- package/templates/workspaceRoot/patches/react-dom@19.2.7.patch.template +0 -62
|
@@ -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",
|
|
@@ -209,15 +213,18 @@ var builderMsgTypeSet = new Set([
|
|
|
209
213
|
"invalidate",
|
|
210
214
|
"css-updated",
|
|
211
215
|
"pages-updated",
|
|
216
|
+
"csr-updated",
|
|
217
|
+
"ssr-updated",
|
|
212
218
|
"build-status",
|
|
213
|
-
"builder-metrics"
|
|
219
|
+
"builder-metrics",
|
|
220
|
+
"boot-armed"
|
|
214
221
|
]);
|
|
215
222
|
|
|
216
223
|
class IncrementalBuilderHost {
|
|
217
224
|
static #restartBaseDelayMs = 1000;
|
|
218
225
|
static #restartMaxDelayMs = 30000;
|
|
219
226
|
static #recycleDrainTimeoutMs = 30000;
|
|
220
|
-
static #devMaxRssBytes =
|
|
227
|
+
static #devMaxRssBytes = 2048 * 1024 * 1024;
|
|
221
228
|
logger = new Logger("IncrementalBuilderHost");
|
|
222
229
|
entry;
|
|
223
230
|
env;
|
|
@@ -233,6 +240,7 @@ class IncrementalBuilderHost {
|
|
|
233
240
|
#recycleTimer = null;
|
|
234
241
|
#recycleRequested = false;
|
|
235
242
|
#spawnAfterRecycle = false;
|
|
243
|
+
#patcherOff = false;
|
|
236
244
|
#manualStop = false;
|
|
237
245
|
#inFlight = new Map;
|
|
238
246
|
#startOptions = {};
|
|
@@ -264,10 +272,16 @@ class IncrementalBuilderHost {
|
|
|
264
272
|
this.ready = false;
|
|
265
273
|
const afterRecycle = this.#spawnAfterRecycle;
|
|
266
274
|
this.#spawnAfterRecycle = false;
|
|
275
|
+
this.#checkPatchingMarker(isRestart && !afterRecycle);
|
|
267
276
|
let proc;
|
|
268
277
|
proc = Bun.spawn(["bun", this.entry], {
|
|
269
278
|
cwd: this.app.cwdPath,
|
|
270
|
-
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
|
+
},
|
|
271
285
|
stdio: ["ignore", this.#stdio, this.#stdio],
|
|
272
286
|
ipc: (msg) => {
|
|
273
287
|
if (this.#proc !== proc)
|
|
@@ -322,6 +336,17 @@ class IncrementalBuilderHost {
|
|
|
322
336
|
}
|
|
323
337
|
this.logger.verbose(`builder spawned pid=${proc.pid} entry=${this.entry}${isRestart ? " restart=1" : ""}`);
|
|
324
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
|
+
}
|
|
325
350
|
async#drain(stream, kind) {
|
|
326
351
|
if (!stream)
|
|
327
352
|
return;
|
|
@@ -617,6 +642,41 @@ class BackendImportGraph {
|
|
|
617
642
|
}
|
|
618
643
|
}
|
|
619
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
|
+
|
|
620
680
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
621
681
|
var backendMsgTypeSet = new Set(["build-route", "build-csr"]);
|
|
622
682
|
var asMib = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
@@ -640,6 +700,7 @@ class AkanAppHost {
|
|
|
640
700
|
env;
|
|
641
701
|
#onDevEvent;
|
|
642
702
|
#lastDevState = null;
|
|
703
|
+
#bootLatch = new DevBootLatch(() => this.#onDevEvent?.({ app: this.app.name, booted: true }));
|
|
643
704
|
#backend = null;
|
|
644
705
|
#builder = null;
|
|
645
706
|
#backendReady = false;
|
|
@@ -651,6 +712,8 @@ class AkanAppHost {
|
|
|
651
712
|
#backendLifecycleState = "stopped";
|
|
652
713
|
#pendingRestartReason = null;
|
|
653
714
|
#pendingRecycle = null;
|
|
715
|
+
#restartFailedAfterApply = null;
|
|
716
|
+
#stopping = false;
|
|
654
717
|
#builderRecoveryTimer = null;
|
|
655
718
|
#builderRecoveryAttempts = 0;
|
|
656
719
|
#backendStartStatus = null;
|
|
@@ -678,6 +741,7 @@ class AkanAppHost {
|
|
|
678
741
|
#builderGapStamp = null;
|
|
679
742
|
#pendingBuilderMessages = [];
|
|
680
743
|
#builderRequests = new BuilderRequestRouter;
|
|
744
|
+
#builderGeneration = 0;
|
|
681
745
|
constructor(app, {
|
|
682
746
|
env,
|
|
683
747
|
stdio = "inherit",
|
|
@@ -694,6 +758,8 @@ class AkanAppHost {
|
|
|
694
758
|
return;
|
|
695
759
|
this.#lastDevState = state;
|
|
696
760
|
this.#onDevEvent({ app: this.app.name, state, ...detail ? { detail } : {} });
|
|
761
|
+
if (state === "ready")
|
|
762
|
+
this.#bootLatch.ready();
|
|
697
763
|
}
|
|
698
764
|
async start() {
|
|
699
765
|
if (this.#backend)
|
|
@@ -711,6 +777,8 @@ class AkanAppHost {
|
|
|
711
777
|
return this;
|
|
712
778
|
}
|
|
713
779
|
async stop() {
|
|
780
|
+
this.#stopping = true;
|
|
781
|
+
this.#pendingRecycle = null;
|
|
714
782
|
this.#cancelIdleSuspend();
|
|
715
783
|
this.#stopIdleWatcher();
|
|
716
784
|
this.#clearRestartTimers();
|
|
@@ -755,6 +823,7 @@ class AkanAppHost {
|
|
|
755
823
|
this.#setBackendLifecycleState("ready", `pid=${msg.pid}`);
|
|
756
824
|
this.#recordBackendReadyStatus();
|
|
757
825
|
this.logger.verbose(`backend ready pid=${msg.pid}`);
|
|
826
|
+
this.#forgetRouteBuildStatus();
|
|
758
827
|
this.#replayBuilderState();
|
|
759
828
|
return;
|
|
760
829
|
}
|
|
@@ -975,17 +1044,26 @@ ${this.#backendStderrTail.join(`
|
|
|
975
1044
|
});
|
|
976
1045
|
}
|
|
977
1046
|
async#handleBuilderMessage(message) {
|
|
1047
|
+
if (this.#stopping)
|
|
1048
|
+
return;
|
|
978
1049
|
this.#markDevActivity();
|
|
1050
|
+
this.#trackBuilderGeneration(message);
|
|
979
1051
|
if (message.type === "build-status") {
|
|
980
1052
|
this.#recordBuildStatus(message.data);
|
|
981
1053
|
this.#sendOrQueueBuildStatus(message.data);
|
|
982
1054
|
this.#reviveBackendAfterGreenBuild(message.data);
|
|
1055
|
+
await this.#resumeFailedRecycle(message.data);
|
|
983
1056
|
return;
|
|
984
1057
|
}
|
|
985
1058
|
if (message.type === "builder-metrics") {
|
|
986
1059
|
this.#handleBuilderMetrics(message.data);
|
|
987
1060
|
return;
|
|
988
1061
|
}
|
|
1062
|
+
if (message.type === "boot-armed") {
|
|
1063
|
+
this.logger.verbose("[builder] boot builds settled");
|
|
1064
|
+
this.#bootLatch.armed();
|
|
1065
|
+
return;
|
|
1066
|
+
}
|
|
989
1067
|
if (message.type === "pages-updated" || message.type === "css-updated") {
|
|
990
1068
|
const recycled = message.data.reason === "builder-recycle";
|
|
991
1069
|
if (recycled && !this.#shouldRelayRecycledState(message))
|
|
@@ -1007,6 +1085,22 @@ ${this.#backendStderrTail.join(`
|
|
|
1007
1085
|
}
|
|
1008
1086
|
this.#sendToBackend(message);
|
|
1009
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
|
+
}
|
|
1010
1104
|
#shouldRelayRecycledState(message) {
|
|
1011
1105
|
const current = message.type === "pages-updated" ? this.#lastGoodFrontend.pages : this.#lastGoodFrontend.css;
|
|
1012
1106
|
if (shouldRelayRecycledFrontendState(current, message)) {
|
|
@@ -1265,9 +1359,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1265
1359
|
if (shouldRefreshConfigOnIdleWake(batch)) {
|
|
1266
1360
|
this.logger.verbose("[idle-suspend] config changed while suspended; restarting the dev host");
|
|
1267
1361
|
this.#discardBuilderGap("config change replaces the backend anyway");
|
|
1268
|
-
await this.#
|
|
1269
|
-
refreshConfig: true
|
|
1270
|
-
});
|
|
1362
|
+
await this.#applyRecycle({ type: "invalidate", kinds: [...batch?.kinds ?? []], files }, true);
|
|
1271
1363
|
return;
|
|
1272
1364
|
}
|
|
1273
1365
|
if (files.length > 0)
|
|
@@ -1368,7 +1460,8 @@ ${this.#backendStderrTail.join(`
|
|
|
1368
1460
|
this.#armRssRecycle(this.#rssRecycleReason);
|
|
1369
1461
|
const wantsDevHostRestart = shouldRestartDevHostByDevPlan(message);
|
|
1370
1462
|
const pending = this.#pendingRecycle;
|
|
1371
|
-
|
|
1463
|
+
const resumes = !!pending && message.kinds.includes("code") && this.#touchesFailedRecycle(message.files);
|
|
1464
|
+
if (wantsDevHostRestart || shouldRestartBuilderByDevPlan(message) || resumes) {
|
|
1372
1465
|
const refreshConfig = wantsDevHostRestart || (pending?.refreshConfig ?? false);
|
|
1373
1466
|
const merged = pending ? mergeInvalidateMessages(pending.message, message) : message;
|
|
1374
1467
|
const generation = message.devPlan?.generation ?? message.generation;
|
|
@@ -1376,16 +1469,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1376
1469
|
this.#deferRecycle(merged, { refreshConfig, generation });
|
|
1377
1470
|
return;
|
|
1378
1471
|
}
|
|
1379
|
-
this.#
|
|
1380
|
-
try {
|
|
1381
|
-
if (refreshConfig)
|
|
1382
|
-
await this.#restartDevHost(merged);
|
|
1383
|
-
else
|
|
1384
|
-
await this.#restartDevChildren(merged);
|
|
1385
|
-
} catch (err) {
|
|
1386
|
-
this.#recordDevHostRestartFailure(merged, err, refreshConfig ? "Config" : "Runtime metadata");
|
|
1387
|
-
this.#resurrectDevChildren(merged);
|
|
1388
|
-
}
|
|
1472
|
+
await this.#applyRecycle(merged, refreshConfig);
|
|
1389
1473
|
return;
|
|
1390
1474
|
}
|
|
1391
1475
|
if (await this.#shouldRestartBackend(message)) {
|
|
@@ -1394,6 +1478,41 @@ ${this.#backendStderrTail.join(`
|
|
|
1394
1478
|
}
|
|
1395
1479
|
this.#sendToBackend(message);
|
|
1396
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
|
+
}
|
|
1397
1516
|
#deferRecycle(message, { refreshConfig, generation }) {
|
|
1398
1517
|
this.#pendingRecycle = { message, refreshConfig };
|
|
1399
1518
|
const kind = refreshConfig ? "Config" : "Runtime metadata";
|
|
@@ -1439,8 +1558,10 @@ ${this.#backendStderrTail.join(`
|
|
|
1439
1558
|
this.#builderRecoveryAttempts = 0;
|
|
1440
1559
|
this.logger.info("[builder-recovery] builder recovered");
|
|
1441
1560
|
this.#restartBackendForGapChanges();
|
|
1561
|
+
const failedAfterApply = this.#restartFailedAfterApply;
|
|
1562
|
+
this.#restartFailedAfterApply = null;
|
|
1442
1563
|
const status = {
|
|
1443
|
-
generation: reason.generation ?? this.#nextBackendBuildStatusGeneration(),
|
|
1564
|
+
generation: failedAfterApply !== null ? this.#nextBackendBuildStatusGeneration(failedAfterApply + 1) : reason.generation ?? this.#nextBackendBuildStatusGeneration(),
|
|
1444
1565
|
phase: "scan",
|
|
1445
1566
|
ok: true,
|
|
1446
1567
|
files: reason.files,
|
|
@@ -1464,12 +1585,12 @@ ${this.#backendStderrTail.join(`
|
|
|
1464
1585
|
this.logger.warn(`[dev-host] recycling builder/backend for runtime metadata generation=${generation ?? "(unknown)"} files=${message.files.length}`);
|
|
1465
1586
|
await this.#recycleDevChildren(message);
|
|
1466
1587
|
}
|
|
1467
|
-
async#restartDevHost(message) {
|
|
1588
|
+
async#restartDevHost(message, progress) {
|
|
1468
1589
|
const generation = message.devPlan?.generation ?? message.generation;
|
|
1469
1590
|
this.logger.warn(`[dev-host] config change detected; restarting dev host generation=${generation ?? "(unknown)"} files=${message.files.length}`);
|
|
1470
|
-
await this.#recycleDevChildren(message, { refreshConfig: true });
|
|
1591
|
+
await this.#recycleDevChildren(message, { refreshConfig: true, progress });
|
|
1471
1592
|
}
|
|
1472
|
-
async#recycleDevChildren(message, { refreshConfig = false } = {}) {
|
|
1593
|
+
async#recycleDevChildren(message, { refreshConfig = false, progress } = {}) {
|
|
1473
1594
|
const generation = message.devPlan?.generation ?? message.generation;
|
|
1474
1595
|
this.#clearRestartTimers();
|
|
1475
1596
|
this.#builderRecoveryAttempts = 0;
|
|
@@ -1483,6 +1604,8 @@ ${this.#backendStderrTail.join(`
|
|
|
1483
1604
|
await this.app.getConfig({ refresh: true });
|
|
1484
1605
|
const { env } = await this.app.prepareCommand("start");
|
|
1485
1606
|
Object.assign(this.env, env);
|
|
1607
|
+
if (progress)
|
|
1608
|
+
progress.applied = true;
|
|
1486
1609
|
}
|
|
1487
1610
|
await this.#backendGraph.refresh();
|
|
1488
1611
|
await this.#startBuilder();
|
|
@@ -1501,22 +1624,27 @@ ${this.#backendStderrTail.join(`
|
|
|
1501
1624
|
this.#lastGoodFrontend.css = message;
|
|
1502
1625
|
this.logger.verbose(`[last-good] css generation=${message.data.generation ?? "(unknown)"} assets=${Object.keys(message.data.cssAssets).length}`);
|
|
1503
1626
|
}
|
|
1504
|
-
#recordDevHostRestartFailure(message, err, kind) {
|
|
1627
|
+
#recordDevHostRestartFailure(message, err, kind, { applied }) {
|
|
1505
1628
|
const generation = message.devPlan?.generation ?? message.generation ?? this.#nextBackendBuildStatusGeneration();
|
|
1506
1629
|
const detail = err instanceof Error ? err.message : String(err);
|
|
1507
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";
|
|
1508
1633
|
const status = {
|
|
1509
1634
|
generation,
|
|
1510
1635
|
phase: "scan",
|
|
1511
1636
|
ok: false,
|
|
1512
1637
|
files: message.files,
|
|
1513
|
-
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.`
|
|
1514
1639
|
};
|
|
1515
1640
|
this.#recordBuildStatus(status);
|
|
1516
1641
|
this.#sendOrQueueBuildStatus(status);
|
|
1642
|
+
return generation;
|
|
1517
1643
|
}
|
|
1518
1644
|
#recordBuildStatus(status) {
|
|
1519
1645
|
const recovered = shouldMarkBuildPhaseRecovered(this.#buildStatusByPhase, status);
|
|
1646
|
+
if (shouldKeepBuildFailure(this.#buildStatusByPhase, status))
|
|
1647
|
+
return;
|
|
1520
1648
|
this.#buildStatusByPhase.set(status.phase, status);
|
|
1521
1649
|
const label = `[build-status] generation=${status.generation} phase=${status.phase} ok=${status.ok} files=${status.files.length}`;
|
|
1522
1650
|
if (status.ok)
|
|
@@ -1532,6 +1660,10 @@ ${this.#backendStderrTail.join(`
|
|
|
1532
1660
|
}
|
|
1533
1661
|
this.#sendToBackend({ type: "build-status", data: status });
|
|
1534
1662
|
}
|
|
1663
|
+
#forgetRouteBuildStatus() {
|
|
1664
|
+
this.#buildStatusByPhase.delete("route");
|
|
1665
|
+
this.#pendingBuildStatusReplay = this.#pendingBuildStatusReplay.filter((status) => status.phase !== "route");
|
|
1666
|
+
}
|
|
1535
1667
|
#replayBuilderState() {
|
|
1536
1668
|
if (!this.#backendReady)
|
|
1537
1669
|
return;
|
|
@@ -1674,16 +1806,16 @@ ${this.#backendStderrTail.join(`
|
|
|
1674
1806
|
}
|
|
1675
1807
|
}
|
|
1676
1808
|
// pkgs/@akanjs/devkit/applicationTestPreload.ts
|
|
1677
|
-
import
|
|
1809
|
+
import path4 from "path";
|
|
1678
1810
|
var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
|
|
1679
1811
|
async function resolveSignalTestPreloadPath(target) {
|
|
1680
1812
|
const candidates = [];
|
|
1681
|
-
for (const basePath of [target.cwdPath, process.cwd(),
|
|
1813
|
+
for (const basePath of [target.cwdPath, process.cwd(), path4.dirname(Bun.main), import.meta.dir]) {
|
|
1682
1814
|
try {
|
|
1683
|
-
candidates.push(
|
|
1815
|
+
candidates.push(path4.join(path4.dirname(Bun.resolveSync("akanjs/package.json", basePath)), SIGNAL_TEST_PRELOAD_PATH));
|
|
1684
1816
|
} catch {}
|
|
1685
1817
|
}
|
|
1686
|
-
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));
|
|
1687
1819
|
const uniqueCandidates = [...new Set(candidates)];
|
|
1688
1820
|
for (const candidate of uniqueCandidates)
|
|
1689
1821
|
if (await Bun.file(candidate).exists())
|
|
@@ -1696,7 +1828,7 @@ ${uniqueCandidates.map((candidate) => ` - ${candidate}`).join(`
|
|
|
1696
1828
|
|
|
1697
1829
|
// pkgs/@akanjs/cli/application/application.runner.ts
|
|
1698
1830
|
import { Logger as Logger3 } from "akanjs/common";
|
|
1699
|
-
var loadBuildRunner = async () => (await import("./applicationBuildRunner-
|
|
1831
|
+
var loadBuildRunner = async () => (await import("./applicationBuildRunner-tm0vba5t.js")).ApplicationBuildRunner;
|
|
1700
1832
|
var loadPrompts = async () => await import("@inquirer/prompts");
|
|
1701
1833
|
|
|
1702
1834
|
class ApplicationRunner extends runner("application") {
|
|
@@ -1741,7 +1873,7 @@ class ApplicationRunner extends runner("application") {
|
|
|
1741
1873
|
});
|
|
1742
1874
|
}
|
|
1743
1875
|
#runtimeDirOf(app, override) {
|
|
1744
|
-
return
|
|
1876
|
+
return path5.resolve(override ?? process.env.AKAN_RUNTIME_DIR ?? path5.join(app.workspace.workspaceRoot, "local", "apps", app.name, "runtime"));
|
|
1745
1877
|
}
|
|
1746
1878
|
async runLogs(app, options) {
|
|
1747
1879
|
const { LogControlUnavailableError, LogTailClient } = await import("akanjs/server/logging/logTailClient");
|
|
@@ -1767,7 +1899,7 @@ class ApplicationRunner extends runner("application") {
|
|
|
1767
1899
|
});
|
|
1768
1900
|
} catch (error) {
|
|
1769
1901
|
if (error instanceof LogControlUnavailableError)
|
|
1770
|
-
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.`);
|
|
1771
1903
|
throw error;
|
|
1772
1904
|
}
|
|
1773
1905
|
const sinceMs = since ? LogQueryMatcher.parseSince(since) : undefined;
|
|
@@ -1832,7 +1964,7 @@ try {
|
|
|
1832
1964
|
const serverPath = `${app.cwdPath}/server.ts`;
|
|
1833
1965
|
if (!await app.exists("server.ts"))
|
|
1834
1966
|
throw new Error(`Server file not found: apps/${app.name}/server.ts`);
|
|
1835
|
-
const target =
|
|
1967
|
+
const target = path5.resolve(app.workspace.workspaceRoot, dir);
|
|
1836
1968
|
const code = `
|
|
1837
1969
|
const { server } = await import(${JSON.stringify(serverPath)});
|
|
1838
1970
|
const { DatabaseAdaptorRole, DocumentTransfer } = await import("akanjs/service");
|
|
@@ -2405,7 +2537,7 @@ import os from "os";
|
|
|
2405
2537
|
import { MemoryLimit as MemoryLimit2 } from "akanjs/server/memoryLimit";
|
|
2406
2538
|
|
|
2407
2539
|
class DevBootConcurrency {
|
|
2408
|
-
static perAppBytes =
|
|
2540
|
+
static perAppBytes = 1800 * 1024 * 1024;
|
|
2409
2541
|
static memoryShare = 0.5;
|
|
2410
2542
|
static coresPerApp = 4;
|
|
2411
2543
|
static budget() {
|
|
@@ -2439,7 +2571,7 @@ class DevBootConcurrency {
|
|
|
2439
2571
|
|
|
2440
2572
|
// pkgs/@akanjs/cli/application/devSessionLog.ts
|
|
2441
2573
|
import { mkdir, rename } from "fs/promises";
|
|
2442
|
-
import
|
|
2574
|
+
import path6 from "path";
|
|
2443
2575
|
import dayjs from "dayjs";
|
|
2444
2576
|
|
|
2445
2577
|
class DevSessionLog {
|
|
@@ -2463,19 +2595,19 @@ class DevSessionLog {
|
|
|
2463
2595
|
return [...this.#appNames];
|
|
2464
2596
|
}
|
|
2465
2597
|
pathOf(app) {
|
|
2466
|
-
return
|
|
2598
|
+
return path6.join(this.#runtimeDirOf(app), DevSessionLog.fileName);
|
|
2467
2599
|
}
|
|
2468
2600
|
previousPathOf(app) {
|
|
2469
|
-
return
|
|
2601
|
+
return path6.join(this.#runtimeDirOf(app), DevSessionLog.previousFileName);
|
|
2470
2602
|
}
|
|
2471
2603
|
relativePathOf(app) {
|
|
2472
|
-
return
|
|
2604
|
+
return path6.relative(this.#workspaceRoot, this.pathOf(app));
|
|
2473
2605
|
}
|
|
2474
2606
|
async open() {
|
|
2475
2607
|
const stamp = dayjs(this.#now()).format("YYYY-MM-DD HH:mm:ss");
|
|
2476
2608
|
for (const app of this.#appNames) {
|
|
2477
2609
|
const file = this.pathOf(app);
|
|
2478
|
-
await mkdir(
|
|
2610
|
+
await mkdir(path6.dirname(file), { recursive: true });
|
|
2479
2611
|
await rename(file, this.previousPathOf(app)).catch(() => {
|
|
2480
2612
|
return;
|
|
2481
2613
|
});
|
|
@@ -2519,7 +2651,7 @@ class DevSessionLog {
|
|
|
2519
2651
|
this.#writers.clear();
|
|
2520
2652
|
}
|
|
2521
2653
|
#runtimeDirOf(app) {
|
|
2522
|
-
return
|
|
2654
|
+
return path6.join(this.#workspaceRoot, "local", "apps", app, "runtime");
|
|
2523
2655
|
}
|
|
2524
2656
|
#writeLine(app, text) {
|
|
2525
2657
|
const writer = this.#writers.get(app);
|
|
@@ -2566,7 +2698,7 @@ class DevSupervisor {
|
|
|
2566
2698
|
}
|
|
2567
2699
|
};
|
|
2568
2700
|
}
|
|
2569
|
-
static
|
|
2701
|
+
static bootTimeoutMs = 180000;
|
|
2570
2702
|
static shutdownGraceMs = 12000;
|
|
2571
2703
|
#options;
|
|
2572
2704
|
#children = new Map;
|
|
@@ -2615,7 +2747,7 @@ class DevSupervisor {
|
|
|
2615
2747
|
return ports;
|
|
2616
2748
|
}
|
|
2617
2749
|
#makeChild(app, port) {
|
|
2618
|
-
const { promise:
|
|
2750
|
+
const { promise: booted, resolve: markBooted } = Promise.withResolvers();
|
|
2619
2751
|
return {
|
|
2620
2752
|
status: {
|
|
2621
2753
|
app,
|
|
@@ -2628,8 +2760,8 @@ class DevSupervisor {
|
|
|
2628
2760
|
exitCode: null
|
|
2629
2761
|
},
|
|
2630
2762
|
proc: null,
|
|
2631
|
-
|
|
2632
|
-
|
|
2763
|
+
booted,
|
|
2764
|
+
markBooted,
|
|
2633
2765
|
opened: false
|
|
2634
2766
|
};
|
|
2635
2767
|
}
|
|
@@ -2648,12 +2780,12 @@ class DevSupervisor {
|
|
|
2648
2780
|
for (const child of wave)
|
|
2649
2781
|
this.#spawnChild(child);
|
|
2650
2782
|
this.#publishStatus();
|
|
2651
|
-
await Promise.all(wave.map(async (child) => await this.#
|
|
2783
|
+
await Promise.all(wave.map(async (child) => await this.#waitForBoot(child)));
|
|
2652
2784
|
}
|
|
2653
2785
|
}
|
|
2654
|
-
async#
|
|
2655
|
-
if (await DevSupervisor.timesOut(child.
|
|
2656
|
-
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");
|
|
2657
2789
|
}
|
|
2658
2790
|
static async timesOut(work, ms) {
|
|
2659
2791
|
let timer;
|
|
@@ -2690,9 +2822,12 @@ class DevSupervisor {
|
|
|
2690
2822
|
},
|
|
2691
2823
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2692
2824
|
ipc: (message) => {
|
|
2693
|
-
if (!message || typeof message !== "object"
|
|
2825
|
+
if (!message || typeof message !== "object")
|
|
2694
2826
|
return;
|
|
2695
|
-
|
|
2827
|
+
if ("booted" in message)
|
|
2828
|
+
child.markBooted();
|
|
2829
|
+
else if (typeof message.state === "string")
|
|
2830
|
+
this.#applyChildEvent(child, message);
|
|
2696
2831
|
},
|
|
2697
2832
|
serialization: "advanced",
|
|
2698
2833
|
onExit: (_proc, exitCode, signalCode) => {
|
|
@@ -2702,7 +2837,7 @@ class DevSupervisor {
|
|
|
2702
2837
|
return;
|
|
2703
2838
|
child.status.state = "stopped";
|
|
2704
2839
|
child.status.detail = signalCode ? `killed by ${signalCode}` : `exited with ${exitCode}`;
|
|
2705
|
-
child.
|
|
2840
|
+
child.markBooted();
|
|
2706
2841
|
this.#publishStatus();
|
|
2707
2842
|
}
|
|
2708
2843
|
});
|
|
@@ -2716,14 +2851,13 @@ class DevSupervisor {
|
|
|
2716
2851
|
child.status.state = event.state;
|
|
2717
2852
|
child.status.detail = event.detail ?? "";
|
|
2718
2853
|
if (event.state === "ready") {
|
|
2719
|
-
child.markReady();
|
|
2720
2854
|
if (this.#options.open && !child.opened) {
|
|
2721
2855
|
child.opened = true;
|
|
2722
2856
|
openBrowser(child.status.url);
|
|
2723
2857
|
}
|
|
2724
2858
|
}
|
|
2725
2859
|
if (event.state === "failed")
|
|
2726
|
-
child.
|
|
2860
|
+
child.markBooted();
|
|
2727
2861
|
this.#publishStatus();
|
|
2728
2862
|
}
|
|
2729
2863
|
async#drain(stream, name, kind) {
|
|
@@ -3124,7 +3258,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3124
3258
|
await Promise.all(shares.map(async (share) => await share.close()));
|
|
3125
3259
|
}, "Abandoning the tunnel release; the shares expire on their own.");
|
|
3126
3260
|
await Promise.all(apps.map(async (app) => {
|
|
3127
|
-
const { TunnelShare } = await import("./TunnelShare-
|
|
3261
|
+
const { TunnelShare } = await import("./TunnelShare-cwh9ezsm.js");
|
|
3128
3262
|
const share = await TunnelShare.open(app, { workspace: app.workspace });
|
|
3129
3263
|
shares.push(share);
|
|
3130
3264
|
urls.set(app.name, share.url);
|