@akanjs/cli 3.0.0-beta.19 → 3.0.0-beta.20
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-6cm97kay.js → CodeAgent-awv258wb.js} +2 -2
- package/{CodeTui-za4jsm41.js → CodeTui-1nqh35ez.js} +20 -22
- package/TunnelShare-scf4e4pd.js +9 -0
- package/{agent.command-e0wpbbyg.js → agent.command-1s9ag7sb.js} +8 -9
- package/akanCode.js +4 -4
- package/{application.command-8wyxqc8q.js → application.command-49hwy6va.js} +25 -56
- package/{applicationBuildRunner-n9gzaxd0.js → applicationBuildRunner-ybyp97pv.js} +33 -59
- package/{applicationReleasePackager-vpggfqgq.js → applicationReleasePackager-esnrphw8.js} +40 -60
- package/buildBatch.proc.js +19 -24
- package/{capacitorApp-q3z0qht0.js → capacitorApp-1synz4en.js} +3 -4
- package/{cloud.command-4zwa5dsx.js → cloud.command-d7rqtzt9.js} +16 -26
- package/cloud.script-bkbpcr96.js +19 -0
- package/{code.command-468c9d7s.js → code.command-nn48xzej.js} +22 -37
- package/context.command-s3mcmvpa.js +48 -0
- package/context.runner-k99nb5pk.js +21 -0
- package/{dependencyScanner-0z2h2tks.js → dependencyScanner-ye2e6pdz.js} +2 -2
- package/{devTui-ffjsarqa.js → devTui-q7k7bakd.js} +5 -9
- package/fileSys-5ngz93ga.js +9 -0
- package/{guideline.command-vhw13pqr.js → guideline.command-sct4qnt5.js} +4 -5
- package/guidelines/conventions/conventions.instruction.md +5 -1
- package/guidelines/mcpRule/mcpRule.instruction.md +7 -5
- package/incrementalBuilder.proc.js +30 -48
- package/{index-s5rhm04y.js → index-0bq14vr1.js} +1 -1
- package/{index-10dkrrww.js → index-13ffe9wp.js} +4 -4
- package/{index-srmay5jw.js → index-22d57v4c.js} +84 -67
- package/{index-53176s7j.js → index-31yr441d.js} +10 -17
- package/index-3hx1z96c.js +87 -0
- package/{index-4m929vvn.js → index-3sq321ak.js} +10 -18
- package/{index-bhtmj233.js → index-3t7g9e4r.js} +41 -78
- package/{index-q0y2jq9j.js → index-5b6tavv3.js} +6 -3
- package/{index-47pj3aee.js → index-5e0n4hqx.js} +11 -12
- package/{index-7ep8qtvp.js → index-5pc1qrbv.js} +5 -8
- package/{index-esk82rvk.js → index-8mna0947.js} +34 -71
- package/{index-sq2dzkzm.js → index-8sf7wmdr.js} +6 -6
- package/{index-crmeqnmf.js → index-9d9bgcx4.js} +283 -722
- package/{index-bwf965h6.js → index-ahemdr5n.js} +6 -6
- package/{index-ab0c38ea.js → index-as7xmyz8.js} +20 -20
- package/{index-bc88j1ry.js → index-axzjaqve.js} +94 -142
- package/{index-3d9y066c.js → index-bjwxs904.js} +9 -8
- package/{index-667w0yah.js → index-c6eggbee.js} +2 -5
- package/{index-a1w6934z.js → index-ddxvdd1p.js} +169 -305
- package/{index-92zztqy1.js → index-dy0jx9ay.js} +2 -17
- package/{index-22xtgmhn.js → index-dzf3r49a.js} +1 -1
- package/{index-89hy31kf.js → index-fgq75haa.js} +172 -287
- package/{index-by5gpp8x.js → index-hj6w4ka1.js} +2 -1
- package/{index-hwkygn9s.js → index-khpfpcsd.js} +302 -33
- package/{index-vqx8bdxz.js → index-mxav6j3c.js} +78 -231
- package/{index-2446k7b4.js → index-myhc3c1j.js} +512 -588
- package/{index-3jrp517k.js → index-nv5976x3.js} +45 -128
- package/{index-zqgj2rt2.js → index-przybvy5.js} +69 -147
- package/{index-n3cv0tm4.js → index-smme4r47.js} +1 -1
- package/{index-qdgzsnhg.js → index-w2zdcw3k.js} +110 -231
- package/{index-14zrtgfa.js → index-wa5jbxj1.js} +6 -39
- package/{index-bftkz6cp.js → index-ww4z397j.js} +198 -555
- package/{index-kwaq3n9n.js → index-xqjwd384.js} +4 -7
- package/{index-txr7dj2g.js → index-xqpzbab5.js} +106 -240
- package/{index-e4v6vhxq.js → index-xsjh6nzh.js} +178 -165
- package/{index-18zybgkc.js → index-ygn77a5w.js} +47 -99
- package/{index-9d6nvkz2.js → index-ztp3e73t.js} +79 -35
- package/index.js +23 -24
- package/{library.command-cqr175dy.js → library.command-1gx04jgn.js} +4 -5
- package/{localRegistry.command-49z55qqz.js → localRegistry.command-zndxj1q6.js} +21 -42
- package/{module.command-9p8mm3b0.js → module.command-3waddk95.js} +6 -7
- package/{package.command-wxyz909t.js → package.command-zrc7br1y.js} +5 -6
- package/package.json +2 -2
- package/{page.command-gsrsfqqs.js → page.command-vpjgardh.js} +4 -5
- package/{primitive.command-ns1mptje.js → primitive.command-sz84tk4t.js} +9 -20
- package/{prompter-1f8d9pcc.js → prompter-ptaa3nk0.js} +2 -2
- package/{quality.command-e76k2q9k.js → quality.command-g869hafs.js} +129 -363
- package/{recipeScanner-fpagtgc1.js → recipeScanner-4b69ybe0.js} +1 -1
- package/{repair.command-pkrsm3cw.js → repair.command-chsnjvvv.js} +11 -35
- package/{scalar.command-0cpt3fc8.js → scalar.command-2v21newx.js} +5 -6
- package/skills/akan-code/SKILL.md +12 -4
- package/{subspace.command-fktpwp0g.js → subspace.command-ckcbwfp1.js} +39 -58
- package/templates/appSample/srvkit/{AuthGuard.ts → guards.ts} +3 -3
- package/templates/appSample/srvkit/{SessionInternalArg.ts → internalArgs.ts} +3 -3
- package/templates/client.ts +7 -30
- package/templates/lib/__lib/lib.dictionary.ts +0 -2
- package/templates/lib/__lib/lib.document.ts +0 -2
- package/templates/lib/sig.ts +8 -30
- package/templates/server.ts +0 -2
- package/{tunnel.command-d5jy9gk8.js → tunnel.command-kd2yr4fx.js} +5 -6
- package/{typeChecker-3xgrzc2v.js → typeChecker-px49mezc.js} +1 -1
- package/typecheck.proc.js +1 -1
- package/workflow.command-44252cqv.js +79 -0
- package/{workspace.command-gv84eh1p.js → workspace.command-fqcztt5p.js} +72 -107
- package/TunnelShare-2j6s511d.js +0 -9
- package/cloud.script-fwx3vb34.js +0 -20
- package/context.command-dsnw0jby.js +0 -80
- package/context.runner-rs4rj15d.js +0 -22
- package/devTuiView-1q1mdgrv.js +0 -9
- package/getDirname-dr3jz1xg.js +0 -7
- package/index-6wwzs1bc.js +0 -7
- package/workflow.command-6tkcysmx.js +0 -105
|
@@ -1,89 +1,74 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
import {
|
|
3
|
+
ApplicationBuildReporter,
|
|
4
|
+
BUILDER_MIN_RSS_RECYCLE_INTERVAL_MS,
|
|
5
|
+
SOURCE_EXTS,
|
|
6
|
+
shouldRestartBackendByDevPlan,
|
|
7
|
+
shouldRestartBuilderByDevPlan,
|
|
8
|
+
shouldAbandonBackendRecovery,
|
|
9
|
+
normalizeBackendReportedGeneration,
|
|
10
|
+
shouldRestartDevHostByDevPlan,
|
|
11
|
+
devHostStateOf,
|
|
12
|
+
isLegacyBackendFallbackFile,
|
|
13
|
+
shouldMarkBuildPhaseRecovered,
|
|
14
|
+
createBackendBuildStatus,
|
|
15
|
+
backendRestartReasonFromMessage,
|
|
16
|
+
mergeBackendRestartReasons,
|
|
17
|
+
shouldReplaceLastGoodMessage,
|
|
18
|
+
shouldQueueBuildStatusReplay,
|
|
19
|
+
hasBuildFailureForGeneration,
|
|
20
|
+
decideBuilderRssRecycle,
|
|
21
|
+
decideBuilderRssSettle,
|
|
22
|
+
decideIdleSuspend,
|
|
23
|
+
resolveIdleSuspendMs,
|
|
24
|
+
hasAnyBuildFailure,
|
|
25
|
+
shouldRefreshConfigOnIdleWake,
|
|
26
|
+
shouldHoldForReturningBuilder,
|
|
27
|
+
shouldWarnBuilderRssCeilingTight,
|
|
28
|
+
isRssCeilingUnreachable,
|
|
29
|
+
shouldRelayRecycledFrontendState,
|
|
30
|
+
mergeInvalidateMessages,
|
|
31
|
+
buildStatusReplaySequence,
|
|
32
|
+
filesChangedSince,
|
|
33
|
+
HmrWatcher,
|
|
34
|
+
WatchRootResolver
|
|
35
|
+
} from "./index-khpfpcsd.js";
|
|
2
36
|
import {
|
|
3
37
|
runner,
|
|
4
38
|
script
|
|
5
|
-
} from "./index-
|
|
39
|
+
} from "./index-w2zdcw3k.js";
|
|
6
40
|
import {
|
|
7
41
|
WorkspaceExecutor,
|
|
8
42
|
AppExecutor,
|
|
9
43
|
LibExecutor,
|
|
10
44
|
PkgExecutor
|
|
11
|
-
} from "./index-
|
|
45
|
+
} from "./index-9d9bgcx4.js";
|
|
12
46
|
import {
|
|
13
47
|
LibraryScript
|
|
14
|
-
} from "./index-
|
|
15
|
-
import {
|
|
16
|
-
HmrWatcher,
|
|
17
|
-
WatchRootResolver
|
|
18
|
-
} from "./index-hwkygn9s.js";
|
|
48
|
+
} from "./index-5pc1qrbv.js";
|
|
19
49
|
import {
|
|
20
50
|
stripAnsi
|
|
21
51
|
} from "./index-7md2g9vw.js";
|
|
22
52
|
import {
|
|
23
53
|
SlicePlanner,
|
|
24
54
|
formatSlicePlan
|
|
25
|
-
} from "./index-
|
|
55
|
+
} from "./index-xqjwd384.js";
|
|
26
56
|
import {
|
|
27
57
|
resolveMobileTargets
|
|
28
|
-
} from "./index-
|
|
58
|
+
} from "./index-5e0n4hqx.js";
|
|
29
59
|
import {
|
|
30
60
|
openBrowser
|
|
31
61
|
} from "./index-9dp031by.js";
|
|
32
62
|
|
|
33
|
-
// pkgs/@akanjs/devkit/applicationBuildReporter.ts
|
|
34
|
-
import { Logger } from "akanjs/common";
|
|
35
|
-
|
|
36
|
-
class ApplicationBuildReporter {
|
|
37
|
-
static create() {
|
|
38
|
-
return {
|
|
39
|
-
phaseDone: (phase) => Logger.rawLog(ApplicationBuildReporter.formatPhaseLine(phase))
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
static printSummary(result) {
|
|
43
|
-
Logger.rawLog("");
|
|
44
|
-
Logger.rawLog(`Route artifacts: ${result.artifactDir}`);
|
|
45
|
-
Logger.rawLog(`Server output: ${result.outputDir}`);
|
|
46
|
-
Logger.rawLog(`Done in ${ApplicationBuildReporter.formatDuration(result.durationMs)}`);
|
|
47
|
-
}
|
|
48
|
-
static formatError(error) {
|
|
49
|
-
if (error instanceof AggregateError) {
|
|
50
|
-
const nestedMessages = error.errors.map((nestedError, index) => ApplicationBuildReporter.formatError(nestedError).trim() || `Unknown error ${index + 1}`).map((message) => message.replace(/^/gm, " ")).join(`
|
|
51
|
-
`);
|
|
52
|
-
return nestedMessages ? `${error.message}
|
|
53
|
-
${nestedMessages}` : error.message;
|
|
54
|
-
}
|
|
55
|
-
if (error instanceof Error) {
|
|
56
|
-
const causeMessage = error.cause ? `
|
|
57
|
-
Caused by: ${ApplicationBuildReporter.formatError(error.cause)}` : "";
|
|
58
|
-
return `${error.message}${causeMessage}`;
|
|
59
|
-
}
|
|
60
|
-
if (typeof error === "object" && error !== null && "message" in error)
|
|
61
|
-
return String(error.message);
|
|
62
|
-
return String(error);
|
|
63
|
-
}
|
|
64
|
-
static formatDuration(ms) {
|
|
65
|
-
if (ms < 1000)
|
|
66
|
-
return `${ms}ms`;
|
|
67
|
-
if (ms < 60000)
|
|
68
|
-
return `${Math.round(ms / 100) / 10}s`;
|
|
69
|
-
const seconds = Math.floor(ms / 1000);
|
|
70
|
-
return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
71
|
-
}
|
|
72
|
-
static formatPhaseLine(phase) {
|
|
73
|
-
const summary = phase.summary ? `: ${phase.summary}` : "";
|
|
74
|
-
return `\u2713 ${phase.label}${summary} (${ApplicationBuildReporter.formatDuration(phase.durationMs)})`;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
63
|
// pkgs/@akanjs/cli/application/application.script.ts
|
|
79
64
|
import { confirm } from "@inquirer/prompts";
|
|
80
|
-
import { Logger as
|
|
65
|
+
import { Logger as Logger6 } from "akanjs/common";
|
|
81
66
|
|
|
82
67
|
// pkgs/@akanjs/cli/application/application.runner.ts
|
|
83
|
-
import
|
|
68
|
+
import path4 from "path";
|
|
84
69
|
|
|
85
70
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
86
|
-
import { Logger as
|
|
71
|
+
import { Logger as Logger2 } from "akanjs/common";
|
|
87
72
|
|
|
88
73
|
// pkgs/@akanjs/devkit/sshTunnel.ts
|
|
89
74
|
import { Client } from "ssh2";
|
|
@@ -172,7 +157,7 @@ var createSshTunnel = async (options) => {
|
|
|
172
157
|
for (const socket of sockets)
|
|
173
158
|
closeSocket(socket, error);
|
|
174
159
|
closeQuietly(() => client.end());
|
|
175
|
-
|
|
160
|
+
server.stop();
|
|
176
161
|
};
|
|
177
162
|
client.once("error", (error) => closeTunnel(error));
|
|
178
163
|
client.once("close", () => closeTunnel());
|
|
@@ -192,22 +177,10 @@ var closeBunSocket = (socket, error) => {
|
|
|
192
177
|
socket.data.stream?.end();
|
|
193
178
|
closeQuietly(() => socket.end());
|
|
194
179
|
};
|
|
195
|
-
var closeBunServer = (server) => {
|
|
196
|
-
server.stop();
|
|
197
|
-
};
|
|
198
180
|
|
|
199
181
|
// pkgs/@akanjs/devkit/createTunnel.ts
|
|
200
|
-
var getSshTunnelOptions = (app, environment) => {
|
|
201
|
-
const { serveDomain, repoName } = WorkspaceExecutor.getBaseDevEnv();
|
|
202
|
-
return {
|
|
203
|
-
host: `${app.name}-${environment}.${serveDomain}`,
|
|
204
|
-
port: process.env.SSH_TUNNEL_PORT ? parseInt(process.env.SSH_TUNNEL_PORT) : 32767,
|
|
205
|
-
username: process.env.SSH_TUNNEL_USERNAME ?? "root",
|
|
206
|
-
password: process.env.SSH_TUNNEL_PASSWORD ?? repoName
|
|
207
|
-
};
|
|
208
|
-
};
|
|
209
182
|
var createTunnel = async (service, { app, environment, port = service === "postgres" ? 5432 : 6379 }) => {
|
|
210
|
-
const
|
|
183
|
+
const { serveDomain, repoName } = WorkspaceExecutor.getBaseDevEnv();
|
|
211
184
|
await createSshTunnel({
|
|
212
185
|
localHost: "0.0.0.0",
|
|
213
186
|
localPort: port,
|
|
@@ -215,13 +188,18 @@ var createTunnel = async (service, { app, environment, port = service === "postg
|
|
|
215
188
|
srcPort: port,
|
|
216
189
|
dstHost: `${service}-0.${service}-svc.${app.name}-${environment}.svc.cluster.local`,
|
|
217
190
|
dstPort: service === "postgres" ? 5432 : 6379,
|
|
218
|
-
sshOptions
|
|
191
|
+
sshOptions: {
|
|
192
|
+
host: `${app.name}-${environment}.${serveDomain}`,
|
|
193
|
+
port: process.env.SSH_TUNNEL_PORT ? parseInt(process.env.SSH_TUNNEL_PORT) : 32767,
|
|
194
|
+
username: process.env.SSH_TUNNEL_USERNAME ?? "root",
|
|
195
|
+
password: process.env.SSH_TUNNEL_PASSWORD ?? repoName
|
|
196
|
+
}
|
|
219
197
|
});
|
|
220
198
|
return `localhost:${port}`;
|
|
221
199
|
};
|
|
222
200
|
// pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.host.ts
|
|
223
201
|
import path from "path";
|
|
224
|
-
import { Logger
|
|
202
|
+
import { Logger } from "akanjs/common";
|
|
225
203
|
import { MemoryLimit } from "akanjs/server/memoryLimit";
|
|
226
204
|
var builderMsgTypeSet = new Set([
|
|
227
205
|
"build-route-res",
|
|
@@ -239,7 +217,7 @@ class IncrementalBuilderHost {
|
|
|
239
217
|
static #restartMaxDelayMs = 30000;
|
|
240
218
|
static #recycleDrainTimeoutMs = 30000;
|
|
241
219
|
static #devMaxRssBytes = 1200 * 1024 * 1024;
|
|
242
|
-
logger = new
|
|
220
|
+
logger = new Logger("IncrementalBuilderHost");
|
|
243
221
|
entry;
|
|
244
222
|
env;
|
|
245
223
|
app;
|
|
@@ -429,12 +407,8 @@ class IncrementalBuilderHost {
|
|
|
429
407
|
const lost = [...this.#inFlight];
|
|
430
408
|
this.#inFlight.clear();
|
|
431
409
|
this.logger.warn(`failing ${lost.length} unanswered builder request(s): ${reason}`);
|
|
432
|
-
for (const [id, type] of lost)
|
|
433
|
-
|
|
434
|
-
this.#onMessage({ type: "build-route-res", id, ok: false, error: reason });
|
|
435
|
-
else
|
|
436
|
-
this.#onMessage({ type: "build-csr-res", id, ok: false, error: reason });
|
|
437
|
-
}
|
|
410
|
+
for (const [id, type] of lost)
|
|
411
|
+
this.#onMessage({ type: `${type}-res`, id, ok: false, error: reason });
|
|
438
412
|
}
|
|
439
413
|
stop() {
|
|
440
414
|
this.#manualStop = true;
|
|
@@ -503,211 +477,7 @@ class BuilderRequestRouter {
|
|
|
503
477
|
|
|
504
478
|
// pkgs/@akanjs/devkit/akanApp/BackendImportGraph.ts
|
|
505
479
|
import { stat } from "fs/promises";
|
|
506
|
-
import path3 from "path";
|
|
507
|
-
|
|
508
|
-
// pkgs/@akanjs/devkit/akanApp/devHostPolicy.ts
|
|
509
480
|
import path2 from "path";
|
|
510
|
-
var BACKEND_RECOVERY_MAX_ATTEMPTS = 5;
|
|
511
|
-
var HELD_BUILDER_REQUEST_LIMIT = 64;
|
|
512
|
-
var BUILDER_MIN_RSS_RECYCLE_INTERVAL_MS = 30000;
|
|
513
|
-
var BUILDER_TIGHT_RSS_REPORT_LIMIT = 2;
|
|
514
|
-
var BUILDER_RSS_HARD_MULTIPLE = 1.5;
|
|
515
|
-
var DEV_IDLE_SUSPEND_MS = 300000;
|
|
516
|
-
var DEV_IDLE_MIN_UPTIME_MS = 30000;
|
|
517
|
-
var SOURCE_EXTS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
518
|
-
var SERVER_SUFFIXES = [".service.ts", ".document.ts"];
|
|
519
|
-
var SHARED_SUFFIXES = [".constant.ts", ".dictionary.ts", ".signal.ts"];
|
|
520
|
-
var RUNTIME_METADATA_BASENAMES = new Set(["dict.ts", "sig.ts", "useClient.ts", "useServer.ts"]);
|
|
521
|
-
var shouldRestartBackendByDevPlan = (message) => {
|
|
522
|
-
if (!message.devPlan)
|
|
523
|
-
return null;
|
|
524
|
-
if (message.devPlan.actions.includes("report-error"))
|
|
525
|
-
return false;
|
|
526
|
-
if (message.devPlan.actions.includes("restart-builder"))
|
|
527
|
-
return false;
|
|
528
|
-
return message.devPlan.actions.includes("restart-backend");
|
|
529
|
-
};
|
|
530
|
-
var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
|
|
531
|
-
var shouldAbandonBackendRecovery = (attempts, maxAttempts = BACKEND_RECOVERY_MAX_ATTEMPTS) => attempts >= maxAttempts;
|
|
532
|
-
var normalizeBackendReportedGeneration = (generation) => generation >= 0 ? generation : undefined;
|
|
533
|
-
var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
|
|
534
|
-
var devHostStateByBackendState = {
|
|
535
|
-
starting: "starting",
|
|
536
|
-
ready: "ready",
|
|
537
|
-
"restart-pending": "restarting",
|
|
538
|
-
recovering: "recovering",
|
|
539
|
-
stopping: "stopped",
|
|
540
|
-
stopped: "stopped"
|
|
541
|
-
};
|
|
542
|
-
var devHostStateOf = (state, gaveUp) => state === "stopped" && gaveUp ? "failed" : devHostStateByBackendState[state];
|
|
543
|
-
var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
|
|
544
|
-
var generationValue = (generation) => generation ?? -1;
|
|
545
|
-
var isLegacyBackendFallbackFile = (file, workspaceRoot) => {
|
|
546
|
-
const abs = path2.resolve(file);
|
|
547
|
-
const ext = path2.extname(abs).toLowerCase();
|
|
548
|
-
if (!SOURCE_EXTS.has(ext))
|
|
549
|
-
return false;
|
|
550
|
-
const rel = path2.relative(path2.resolve(workspaceRoot), abs);
|
|
551
|
-
if (!rel || rel.startsWith("..") || path2.isAbsolute(rel))
|
|
552
|
-
return false;
|
|
553
|
-
const parts = rel.split(path2.sep).filter(Boolean);
|
|
554
|
-
const [scope] = parts;
|
|
555
|
-
if (scope !== "apps" && scope !== "libs" && scope !== "pkgs")
|
|
556
|
-
return false;
|
|
557
|
-
const base = path2.basename(abs);
|
|
558
|
-
return parts.includes("srvkit") || parts.includes("common") || SERVER_SUFFIXES.some((suffix) => base.endsWith(suffix)) || SHARED_SUFFIXES.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES.has(base) || base === "main.ts" || base === "server.ts";
|
|
559
|
-
};
|
|
560
|
-
var shouldMarkBuildPhaseRecovered = (previousByPhase, status) => {
|
|
561
|
-
const previous = previousByPhase.get(status.phase);
|
|
562
|
-
return Boolean(previous && status.ok && !previous.ok && generationValue(status.generation) >= previous.generation);
|
|
563
|
-
};
|
|
564
|
-
var createBackendBuildStatus = ({
|
|
565
|
-
generation,
|
|
566
|
-
ok,
|
|
567
|
-
files = [],
|
|
568
|
-
message
|
|
569
|
-
}) => ({
|
|
570
|
-
generation,
|
|
571
|
-
phase: "backend",
|
|
572
|
-
ok,
|
|
573
|
-
files,
|
|
574
|
-
message
|
|
575
|
-
});
|
|
576
|
-
var backendRestartReasonFromMessage = (message) => {
|
|
577
|
-
const roleSet = new Set;
|
|
578
|
-
for (const role of message.devPlan?.roles ?? []) {
|
|
579
|
-
if (role === "server" || role === "shared" || role === "barrel" || role === "config")
|
|
580
|
-
roleSet.add(role);
|
|
581
|
-
}
|
|
582
|
-
return {
|
|
583
|
-
generation: message.devPlan?.generation ?? message.generation,
|
|
584
|
-
files: [...new Set(message.files)].sort(),
|
|
585
|
-
roles: RESTART_ROLE_ORDER.filter((role) => roleSet.has(role))
|
|
586
|
-
};
|
|
587
|
-
};
|
|
588
|
-
var mergeBackendRestartReasons = (current, next) => ({
|
|
589
|
-
generation: generationValue(next.generation) >= generationValue(current?.generation) ? next.generation : current?.generation,
|
|
590
|
-
files: [...new Set([...current?.files ?? [], ...next.files])].sort(),
|
|
591
|
-
roles: RESTART_ROLE_ORDER.filter((role) => current?.roles.includes(role) || next.roles.includes(role))
|
|
592
|
-
});
|
|
593
|
-
var shouldReplaceLastGoodMessage = (current, next) => !current || generationValue(next.data.generation) >= generationValue(current.data.generation);
|
|
594
|
-
var shouldQueueBuildStatusReplay = (backendReady, pendingReplayCount) => !backendReady || pendingReplayCount > 0;
|
|
595
|
-
var hasBuildFailureForGeneration = (statusByPhase, generation) => {
|
|
596
|
-
if (typeof generation !== "number")
|
|
597
|
-
return false;
|
|
598
|
-
for (const status of statusByPhase.values()) {
|
|
599
|
-
if (!status.ok && status.generation === generation)
|
|
600
|
-
return true;
|
|
601
|
-
}
|
|
602
|
-
return false;
|
|
603
|
-
};
|
|
604
|
-
var decideBuilderRssRecycle = ({
|
|
605
|
-
rssBytes,
|
|
606
|
-
ceilingBytes,
|
|
607
|
-
buildFailed,
|
|
608
|
-
msSinceLastRecycle,
|
|
609
|
-
minIntervalMs = BUILDER_MIN_RSS_RECYCLE_INTERVAL_MS
|
|
610
|
-
}) => {
|
|
611
|
-
if (!ceilingBytes)
|
|
612
|
-
return "unbounded";
|
|
613
|
-
if (rssBytes < ceilingBytes)
|
|
614
|
-
return "below-ceiling";
|
|
615
|
-
if (buildFailed)
|
|
616
|
-
return "build-failed";
|
|
617
|
-
if (msSinceLastRecycle !== null && msSinceLastRecycle < minIntervalMs)
|
|
618
|
-
return "too-soon";
|
|
619
|
-
return "recycle";
|
|
620
|
-
};
|
|
621
|
-
var decideBuilderRssSettle = ({
|
|
622
|
-
rssBytes,
|
|
623
|
-
ceilingBytes,
|
|
624
|
-
hardMultiple = BUILDER_RSS_HARD_MULTIPLE
|
|
625
|
-
}) => rssBytes >= ceilingBytes * hardMultiple ? "recycle-now" : "wait-and-recheck";
|
|
626
|
-
var decideIdleSuspend = ({
|
|
627
|
-
enabled,
|
|
628
|
-
suspended,
|
|
629
|
-
builderReady,
|
|
630
|
-
backendReady,
|
|
631
|
-
buildFailed,
|
|
632
|
-
restartPending,
|
|
633
|
-
msSinceWake,
|
|
634
|
-
minUptimeMs = DEV_IDLE_MIN_UPTIME_MS
|
|
635
|
-
}) => {
|
|
636
|
-
if (!enabled)
|
|
637
|
-
return "disabled";
|
|
638
|
-
if (suspended)
|
|
639
|
-
return "already-suspended";
|
|
640
|
-
if (!builderReady)
|
|
641
|
-
return "builder-not-ready";
|
|
642
|
-
if (!backendReady)
|
|
643
|
-
return "backend-not-ready";
|
|
644
|
-
if (buildFailed)
|
|
645
|
-
return "build-failed";
|
|
646
|
-
if (restartPending)
|
|
647
|
-
return "restart-pending";
|
|
648
|
-
if (msSinceWake !== null && msSinceWake < minUptimeMs)
|
|
649
|
-
return "too-soon";
|
|
650
|
-
return "suspend";
|
|
651
|
-
};
|
|
652
|
-
var resolveIdleSuspendMs = (raw) => {
|
|
653
|
-
if (raw === undefined || raw === "")
|
|
654
|
-
return DEV_IDLE_SUSPEND_MS;
|
|
655
|
-
const parsed = Number(raw);
|
|
656
|
-
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
657
|
-
return null;
|
|
658
|
-
return Math.round(parsed);
|
|
659
|
-
};
|
|
660
|
-
var hasAnyBuildFailure = (statusByPhase) => [...statusByPhase.values()].some((status) => !status.ok);
|
|
661
|
-
var shouldRefreshConfigOnIdleWake = (batch) => !!batch && batch.kinds.has("config");
|
|
662
|
-
var RETURNING_BUILDER_STATUSES = new Set(["starting", "recycling", "restarting"]);
|
|
663
|
-
var shouldHoldForReturningBuilder = ({
|
|
664
|
-
status,
|
|
665
|
-
heldCount,
|
|
666
|
-
limit = HELD_BUILDER_REQUEST_LIMIT
|
|
667
|
-
}) => RETURNING_BUILDER_STATUSES.has(status) && heldCount < limit;
|
|
668
|
-
var shouldWarnBuilderRssCeilingTight = (reportsSinceRecycle, limit = BUILDER_TIGHT_RSS_REPORT_LIMIT) => reportsSinceRecycle >= limit;
|
|
669
|
-
var isRssCeilingUnreachable = (freshRssBytes, ceilingBytes) => freshRssBytes !== null && ceilingBytes !== null && freshRssBytes >= ceilingBytes;
|
|
670
|
-
var shouldRelayRecycledFrontendState = (current, next) => {
|
|
671
|
-
if (!current || current.type !== next.type)
|
|
672
|
-
return true;
|
|
673
|
-
if (current.type === "pages-updated" && next.type === "pages-updated")
|
|
674
|
-
return current.data.bundlePath !== next.data.bundlePath;
|
|
675
|
-
if (current.type === "css-updated" && next.type === "css-updated")
|
|
676
|
-
return JSON.stringify(current.data.cssAssets) !== JSON.stringify(next.data.cssAssets);
|
|
677
|
-
return true;
|
|
678
|
-
};
|
|
679
|
-
var mergeDevPlans = (current, next) => {
|
|
680
|
-
if (!current)
|
|
681
|
-
return next;
|
|
682
|
-
if (!next)
|
|
683
|
-
return current;
|
|
684
|
-
const reasonByFile = { ...current.reasonByFile };
|
|
685
|
-
for (const [file, reasons] of Object.entries(next.reasonByFile)) {
|
|
686
|
-
reasonByFile[file] = [...new Set([...reasonByFile[file] ?? [], ...reasons])].sort();
|
|
687
|
-
}
|
|
688
|
-
return {
|
|
689
|
-
generation: Math.max(current.generation, next.generation),
|
|
690
|
-
files: [...new Set([...current.files, ...next.files])].sort(),
|
|
691
|
-
generatedFiles: [...new Set([...current.generatedFiles, ...next.generatedFiles])].sort(),
|
|
692
|
-
roles: [...new Set([...current.roles, ...next.roles])].sort(),
|
|
693
|
-
actions: [...new Set([...current.actions, ...next.actions])].sort(),
|
|
694
|
-
reasonByFile
|
|
695
|
-
};
|
|
696
|
-
};
|
|
697
|
-
var mergeInvalidateMessages = (current, next) => {
|
|
698
|
-
const generation = Math.max(generationValue(current.generation), generationValue(next.generation));
|
|
699
|
-
return {
|
|
700
|
-
type: "invalidate",
|
|
701
|
-
kinds: [...new Set([...current.kinds, ...next.kinds])].sort(),
|
|
702
|
-
files: [...new Set([...current.files, ...next.files])].sort(),
|
|
703
|
-
generation: generation >= 0 ? generation : undefined,
|
|
704
|
-
devPlan: mergeDevPlans(current.devPlan, next.devPlan)
|
|
705
|
-
};
|
|
706
|
-
};
|
|
707
|
-
var buildStatusReplaySequence = (pendingReplay, latestByPhase) => [...pendingReplay, ...latestByPhase.values()];
|
|
708
|
-
var filesChangedSince = (before, after) => [...before].filter(([file, stamp]) => after.get(file) !== stamp).map(([file]) => file);
|
|
709
|
-
|
|
710
|
-
// pkgs/@akanjs/devkit/akanApp/BackendImportGraph.ts
|
|
711
481
|
var NON_SOURCE_EXT_RE = /\.(css|scss|sass|less|json|svg|png|jpe?g|webp|gif|avif|ico|woff2?|ttf|otf|mp3|mp4|wav|html)$/i;
|
|
712
482
|
var GRAPH_IMPORT_KINDS = new Set([
|
|
713
483
|
"import-statement",
|
|
@@ -738,7 +508,7 @@ class BackendImportGraph {
|
|
|
738
508
|
return this.#lastRefreshSucceeded;
|
|
739
509
|
}
|
|
740
510
|
has(file) {
|
|
741
|
-
return this.#files.has(
|
|
511
|
+
return this.#files.has(path2.resolve(file));
|
|
742
512
|
}
|
|
743
513
|
async fingerprint() {
|
|
744
514
|
const stamps = await Promise.all([...this.#files].map(async (file) => {
|
|
@@ -766,9 +536,9 @@ class BackendImportGraph {
|
|
|
766
536
|
const roots = await this.#entrypoints();
|
|
767
537
|
const files = new Set;
|
|
768
538
|
const queue = [...roots];
|
|
769
|
-
const workspaceRoot =
|
|
539
|
+
const workspaceRoot = path2.resolve(this.#app.workspace.workspaceRoot);
|
|
770
540
|
while (queue.length > 0) {
|
|
771
|
-
const current =
|
|
541
|
+
const current = path2.resolve(queue.pop());
|
|
772
542
|
if (files.has(current))
|
|
773
543
|
continue;
|
|
774
544
|
if (!this.#isWorkspaceSource(current, workspaceRoot))
|
|
@@ -777,7 +547,7 @@ class BackendImportGraph {
|
|
|
777
547
|
if (!imports)
|
|
778
548
|
continue;
|
|
779
549
|
files.add(current);
|
|
780
|
-
const importerDir =
|
|
550
|
+
const importerDir = path2.dirname(current);
|
|
781
551
|
for (const imp of imports) {
|
|
782
552
|
if (!GRAPH_IMPORT_KINDS.has(imp.kind) || !imp.path || NON_SOURCE_EXT_RE.test(imp.path))
|
|
783
553
|
continue;
|
|
@@ -808,7 +578,7 @@ class BackendImportGraph {
|
|
|
808
578
|
const roots = [`${this.#app.cwdPath}/main.ts`, `${this.#app.cwdPath}/server.ts`];
|
|
809
579
|
const existing = [];
|
|
810
580
|
for (const root of roots) {
|
|
811
|
-
const abs =
|
|
581
|
+
const abs = path2.resolve(root);
|
|
812
582
|
if (await Bun.file(abs).exists())
|
|
813
583
|
existing.push(abs);
|
|
814
584
|
}
|
|
@@ -817,25 +587,25 @@ class BackendImportGraph {
|
|
|
817
587
|
#resolve(specifier, importerDir) {
|
|
818
588
|
try {
|
|
819
589
|
const resolved = Bun.resolveSync(specifier, importerDir);
|
|
820
|
-
if (!
|
|
590
|
+
if (!path2.isAbsolute(resolved))
|
|
821
591
|
return null;
|
|
822
|
-
if (!SOURCE_EXTS.has(
|
|
592
|
+
if (!SOURCE_EXTS.has(path2.extname(resolved).toLowerCase()))
|
|
823
593
|
return null;
|
|
824
|
-
return
|
|
594
|
+
return path2.resolve(resolved);
|
|
825
595
|
} catch {
|
|
826
596
|
return null;
|
|
827
597
|
}
|
|
828
598
|
}
|
|
829
599
|
#isWorkspaceSource(file, workspaceRoot) {
|
|
830
|
-
const rel =
|
|
831
|
-
if (rel.startsWith("..") ||
|
|
600
|
+
const rel = path2.relative(workspaceRoot, file);
|
|
601
|
+
if (rel.startsWith("..") || path2.isAbsolute(rel))
|
|
832
602
|
return false;
|
|
833
|
-
if (rel.includes(`${
|
|
603
|
+
if (rel.includes(`${path2.sep}node_modules${path2.sep}`) || rel.includes(`${path2.sep}.akan${path2.sep}`))
|
|
834
604
|
return false;
|
|
835
|
-
return SOURCE_EXTS.has(
|
|
605
|
+
return SOURCE_EXTS.has(path2.extname(file).toLowerCase());
|
|
836
606
|
}
|
|
837
607
|
#scanImports(file, source) {
|
|
838
|
-
const ext =
|
|
608
|
+
const ext = path2.extname(file).toLowerCase();
|
|
839
609
|
if (ext === ".tsx")
|
|
840
610
|
return this.#tsxTranspiler.scanImports(source);
|
|
841
611
|
if (ext === ".jsx")
|
|
@@ -848,6 +618,7 @@ class BackendImportGraph {
|
|
|
848
618
|
|
|
849
619
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
850
620
|
var backendMsgTypeSet = new Set(["build-route", "build-csr"]);
|
|
621
|
+
var asMib = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
851
622
|
var BACKEND_RESTART_DEBOUNCE_MS = 120;
|
|
852
623
|
var BACKEND_GRACEFUL_TIMEOUT_MS = 8000;
|
|
853
624
|
var BACKEND_RECOVERY_BASE_DELAY_MS = 1000;
|
|
@@ -863,7 +634,7 @@ var BUILDER_RECOVERY_MAX_DELAY_MS = 60000;
|
|
|
863
634
|
|
|
864
635
|
class AkanAppHost {
|
|
865
636
|
app;
|
|
866
|
-
logger = new
|
|
637
|
+
logger = new Logger2("AkanAppHost");
|
|
867
638
|
stdio;
|
|
868
639
|
env;
|
|
869
640
|
#onDevEvent;
|
|
@@ -941,23 +712,18 @@ class AkanAppHost {
|
|
|
941
712
|
async stop() {
|
|
942
713
|
this.#cancelIdleSuspend();
|
|
943
714
|
this.#stopIdleWatcher();
|
|
944
|
-
|
|
945
|
-
clearTimeout(this.#restartTimer);
|
|
946
|
-
this.#restartTimer = null;
|
|
947
|
-
}
|
|
948
|
-
if (this.#backendRecoveryTimer) {
|
|
949
|
-
clearTimeout(this.#backendRecoveryTimer);
|
|
950
|
-
this.#backendRecoveryTimer = null;
|
|
951
|
-
}
|
|
952
|
-
if (this.#builderRecoveryTimer) {
|
|
953
|
-
clearTimeout(this.#builderRecoveryTimer);
|
|
954
|
-
this.#builderRecoveryTimer = null;
|
|
955
|
-
}
|
|
715
|
+
this.#clearRestartTimers();
|
|
956
716
|
this.#failPendingBuilderMessages("dev server is shutting down");
|
|
957
717
|
await this.#stopBackend();
|
|
958
718
|
this.#stopBuilder();
|
|
959
719
|
return this;
|
|
960
720
|
}
|
|
721
|
+
#clearRestartTimers() {
|
|
722
|
+
for (const timer of [this.#restartTimer, this.#backendRecoveryTimer, this.#builderRecoveryTimer])
|
|
723
|
+
if (timer)
|
|
724
|
+
clearTimeout(timer);
|
|
725
|
+
this.#restartTimer = this.#backendRecoveryTimer = this.#builderRecoveryTimer = null;
|
|
726
|
+
}
|
|
961
727
|
kill() {
|
|
962
728
|
this.stop();
|
|
963
729
|
}
|
|
@@ -1253,7 +1019,6 @@ ${this.#backendStderrTail.join(`
|
|
|
1253
1019
|
if (this.#rssCeilingAbandoned)
|
|
1254
1020
|
return;
|
|
1255
1021
|
const ceilingBytes = IncrementalBuilderHost.maxRssBytes();
|
|
1256
|
-
const asMib = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
1257
1022
|
const decision = decideBuilderRssRecycle({
|
|
1258
1023
|
rssBytes: metrics.rssBytes,
|
|
1259
1024
|
ceilingBytes,
|
|
@@ -1291,7 +1056,6 @@ ${this.#backendStderrTail.join(`
|
|
|
1291
1056
|
if (!isRssCeilingUnreachable(freshRssBytes, ceilingBytes))
|
|
1292
1057
|
return;
|
|
1293
1058
|
this.#rssCeilingAbandoned = true;
|
|
1294
|
-
const asMib = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
1295
1059
|
this.logger.error(`[builder-recycle] a freshly recycled builder is already at ${asMib(freshRssBytes ?? 0)}MiB with nothing built on demand, so the ${asMib(ceilingBytes)}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.`);
|
|
1296
1060
|
}
|
|
1297
1061
|
#armRssRecycle(reason, over) {
|
|
@@ -1318,7 +1082,6 @@ ${this.#backendStderrTail.join(`
|
|
|
1318
1082
|
}, BUILDER_RSS_RECYCLE_QUIET_MS);
|
|
1319
1083
|
}
|
|
1320
1084
|
async#recycleBuilderForRssWhenStillOver(reason, { rssBytes, ceilingBytes }) {
|
|
1321
|
-
const asMib = (bytes) => Math.round(bytes / 1024 / 1024);
|
|
1322
1085
|
if (decideBuilderRssSettle({ rssBytes, ceilingBytes }) === "recycle-now") {
|
|
1323
1086
|
this.#recycleBuilderForRss(reason);
|
|
1324
1087
|
return;
|
|
@@ -1510,7 +1273,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1510
1273
|
await this.#backendGraph.refresh();
|
|
1511
1274
|
await this.#startBuilder({ announceBootState: true });
|
|
1512
1275
|
const missed = await this.#takeBuilderGapChanges();
|
|
1513
|
-
const backendFiles = [...new Set([...files.filter((file) => this.#
|
|
1276
|
+
const backendFiles = [...new Set([...files.filter((file) => this.#backendGraph.has(file)), ...missed])];
|
|
1514
1277
|
if (backendFiles.length === 0)
|
|
1515
1278
|
return;
|
|
1516
1279
|
this.logger.verbose(`[idle-suspend] ${backendFiles.length} backend file(s) changed while suspended`);
|
|
@@ -1565,10 +1328,6 @@ ${this.#backendStderrTail.join(`
|
|
|
1565
1328
|
for (const message of pending)
|
|
1566
1329
|
this.#sendToBuilder(message);
|
|
1567
1330
|
}
|
|
1568
|
-
#holdUntilBuilderReady(message) {
|
|
1569
|
-
this.#pendingBuilderMessages.push(message);
|
|
1570
|
-
this.logger.verbose(`[builder] holding ${message.type} until the builder is ready (${this.#pendingBuilderMessages.length} waiting)`);
|
|
1571
|
-
}
|
|
1572
1331
|
#discardPendingBuilderMessages(reason) {
|
|
1573
1332
|
const held = this.#pendingBuilderMessages.splice(0);
|
|
1574
1333
|
if (held.length === 0)
|
|
@@ -1580,12 +1339,17 @@ ${this.#backendStderrTail.join(`
|
|
|
1580
1339
|
if (held.length === 0)
|
|
1581
1340
|
return;
|
|
1582
1341
|
this.logger.warn(`failing ${held.length} held builder request(s): ${reason}`);
|
|
1583
|
-
for (const message of held)
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1342
|
+
for (const message of held)
|
|
1343
|
+
this.#failBuilderRequest(message, reason);
|
|
1344
|
+
}
|
|
1345
|
+
#failBuilderRequest(message, error) {
|
|
1346
|
+
if (message.type === "build-route")
|
|
1347
|
+
this.#sendToBackend({ type: "build-route-res", id: message.id, ok: false, error });
|
|
1348
|
+
else if (message.type === "build-csr")
|
|
1349
|
+
this.#sendToBackend({ type: "build-csr-res", id: message.id, ok: false, error });
|
|
1350
|
+
else
|
|
1351
|
+
return false;
|
|
1352
|
+
return true;
|
|
1589
1353
|
}
|
|
1590
1354
|
#recycleBuilderForRss(reason) {
|
|
1591
1355
|
if (this.#pendingRecycle || this.#restartTimer) {
|
|
@@ -1706,18 +1470,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1706
1470
|
}
|
|
1707
1471
|
async#recycleDevChildren(message, { refreshConfig = false } = {}) {
|
|
1708
1472
|
const generation = message.devPlan?.generation ?? message.generation;
|
|
1709
|
-
|
|
1710
|
-
clearTimeout(this.#restartTimer);
|
|
1711
|
-
this.#restartTimer = null;
|
|
1712
|
-
}
|
|
1713
|
-
if (this.#backendRecoveryTimer) {
|
|
1714
|
-
clearTimeout(this.#backendRecoveryTimer);
|
|
1715
|
-
this.#backendRecoveryTimer = null;
|
|
1716
|
-
}
|
|
1717
|
-
if (this.#builderRecoveryTimer) {
|
|
1718
|
-
clearTimeout(this.#builderRecoveryTimer);
|
|
1719
|
-
this.#builderRecoveryTimer = null;
|
|
1720
|
-
}
|
|
1473
|
+
this.#clearRestartTimers();
|
|
1721
1474
|
this.#builderRecoveryAttempts = 0;
|
|
1722
1475
|
this.#pendingRestartReason = null;
|
|
1723
1476
|
this.#lastGoodFrontend = {};
|
|
@@ -1807,7 +1560,7 @@ ${this.#backendStderrTail.join(`
|
|
|
1807
1560
|
}
|
|
1808
1561
|
if (message.kinds.includes("code"))
|
|
1809
1562
|
await this.#backendGraph.refresh();
|
|
1810
|
-
if (message.files.some((file) => this.#
|
|
1563
|
+
if (message.files.some((file) => this.#backendGraph.has(file)))
|
|
1811
1564
|
return true;
|
|
1812
1565
|
if (!this.#backendGraph.lastRefreshSucceeded) {
|
|
1813
1566
|
const fallbackFiles = message.files.filter((file) => isLegacyBackendFallbackFile(file, this.app.workspace.workspaceRoot));
|
|
@@ -1818,9 +1571,6 @@ ${this.#backendStderrTail.join(`
|
|
|
1818
1571
|
}
|
|
1819
1572
|
return false;
|
|
1820
1573
|
}
|
|
1821
|
-
#isBackendFile(file) {
|
|
1822
|
-
return this.#backendGraph.has(file);
|
|
1823
|
-
}
|
|
1824
1574
|
async#startBuilder({
|
|
1825
1575
|
announceBootState = false
|
|
1826
1576
|
} = {}) {
|
|
@@ -1906,27 +1656,12 @@ ${this.#backendStderrTail.join(`
|
|
|
1906
1656
|
return;
|
|
1907
1657
|
const status = this.#builder?.status ?? "stopped";
|
|
1908
1658
|
if (shouldHoldForReturningBuilder({ status, heldCount: this.#pendingBuilderMessages.length })) {
|
|
1909
|
-
this.#
|
|
1910
|
-
|
|
1911
|
-
}
|
|
1912
|
-
if (message.type === "build-route") {
|
|
1913
|
-
this.#sendToBackend({
|
|
1914
|
-
type: "build-route-res",
|
|
1915
|
-
id: message.id,
|
|
1916
|
-
ok: false,
|
|
1917
|
-
error: `builder is ${status}; reload after the builder is ready`
|
|
1918
|
-
});
|
|
1659
|
+
this.#pendingBuilderMessages.push(message);
|
|
1660
|
+
this.logger.verbose(`[builder] holding ${message.type} until the builder is ready (${this.#pendingBuilderMessages.length} waiting)`);
|
|
1919
1661
|
return;
|
|
1920
1662
|
}
|
|
1921
|
-
if (message
|
|
1922
|
-
this.#sendToBackend({
|
|
1923
|
-
type: "build-csr-res",
|
|
1924
|
-
id: message.id,
|
|
1925
|
-
ok: false,
|
|
1926
|
-
error: `builder is ${status}; reload after the builder is ready`
|
|
1927
|
-
});
|
|
1663
|
+
if (this.#failBuilderRequest(message, `builder is ${status}; reload after the builder is ready`))
|
|
1928
1664
|
return;
|
|
1929
|
-
}
|
|
1930
1665
|
this.logger.warn("akanAppHost builder is not running");
|
|
1931
1666
|
}
|
|
1932
1667
|
#stopBuilder() {
|
|
@@ -1938,25 +1673,20 @@ ${this.#backendStderrTail.join(`
|
|
|
1938
1673
|
}
|
|
1939
1674
|
}
|
|
1940
1675
|
// pkgs/@akanjs/devkit/applicationTestPreload.ts
|
|
1941
|
-
import
|
|
1676
|
+
import path3 from "path";
|
|
1942
1677
|
var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
|
|
1943
1678
|
async function resolveSignalTestPreloadPath(target) {
|
|
1944
1679
|
const candidates = [];
|
|
1945
|
-
const
|
|
1680
|
+
for (const basePath of [target.cwdPath, process.cwd(), path3.dirname(Bun.main), import.meta.dir]) {
|
|
1946
1681
|
try {
|
|
1947
|
-
candidates.push(
|
|
1682
|
+
candidates.push(path3.join(path3.dirname(Bun.resolveSync("akanjs/package.json", basePath)), SIGNAL_TEST_PRELOAD_PATH));
|
|
1948
1683
|
} catch {}
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
addResolvedPackageCandidate(process.cwd());
|
|
1952
|
-
addResolvedPackageCandidate(path4.dirname(Bun.main));
|
|
1953
|
-
addResolvedPackageCandidate(import.meta.dir);
|
|
1954
|
-
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));
|
|
1684
|
+
}
|
|
1685
|
+
candidates.push(path3.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path3.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path3.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path3.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path3.join(path3.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path3.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
|
|
1955
1686
|
const uniqueCandidates = [...new Set(candidates)];
|
|
1956
|
-
for (const candidate of uniqueCandidates)
|
|
1687
|
+
for (const candidate of uniqueCandidates)
|
|
1957
1688
|
if (await Bun.file(candidate).exists())
|
|
1958
1689
|
return candidate;
|
|
1959
|
-
}
|
|
1960
1690
|
throw new Error(`Failed to locate ${SIGNAL_TEST_PRELOAD_PATH} from ${target.cwdPath}.
|
|
1961
1691
|
Probed paths:
|
|
1962
1692
|
${uniqueCandidates.map((candidate) => ` - ${candidate}`).join(`
|
|
@@ -1964,10 +1694,10 @@ ${uniqueCandidates.map((candidate) => ` - ${candidate}`).join(`
|
|
|
1964
1694
|
}
|
|
1965
1695
|
|
|
1966
1696
|
// pkgs/@akanjs/cli/application/application.runner.ts
|
|
1967
|
-
import { Logger as
|
|
1968
|
-
var loadBuildRunner = async () => (await import("./applicationBuildRunner-
|
|
1969
|
-
var loadReleasePackager = async () => (await import("./applicationReleasePackager-
|
|
1970
|
-
var loadCapacitorApp = async () => (await import("./capacitorApp-
|
|
1697
|
+
import { Logger as Logger3 } from "akanjs/common";
|
|
1698
|
+
var loadBuildRunner = async () => (await import("./applicationBuildRunner-ybyp97pv.js")).ApplicationBuildRunner;
|
|
1699
|
+
var loadReleasePackager = async () => (await import("./applicationReleasePackager-esnrphw8.js")).ApplicationReleasePackager;
|
|
1700
|
+
var loadCapacitorApp = async () => (await import("./capacitorApp-1synz4en.js")).CapacitorApp;
|
|
1971
1701
|
var loadPrompts = async () => await import("@inquirer/prompts");
|
|
1972
1702
|
|
|
1973
1703
|
class ApplicationRunner extends runner("application") {
|
|
@@ -1986,9 +1716,6 @@ class ApplicationRunner extends runner("application") {
|
|
|
1986
1716
|
async removeApplication(app) {
|
|
1987
1717
|
await app.workspace.removeDir(`apps/${app.name}`);
|
|
1988
1718
|
}
|
|
1989
|
-
async getConfig(app) {
|
|
1990
|
-
return await app.getConfig();
|
|
1991
|
-
}
|
|
1992
1719
|
async getScriptFilename(app) {
|
|
1993
1720
|
if (!await app.exists("script")) {
|
|
1994
1721
|
await app.mkdir("script");
|
|
@@ -2015,7 +1742,7 @@ class ApplicationRunner extends runner("application") {
|
|
|
2015
1742
|
});
|
|
2016
1743
|
}
|
|
2017
1744
|
#runtimeDirOf(app, override) {
|
|
2018
|
-
return
|
|
1745
|
+
return path4.resolve(override ?? process.env.AKAN_RUNTIME_DIR ?? path4.join(app.workspace.workspaceRoot, "local", "apps", app.name, "runtime"));
|
|
2019
1746
|
}
|
|
2020
1747
|
async runLogs(app, options) {
|
|
2021
1748
|
const { LogControlUnavailableError, LogTailClient } = await import("akanjs/server/logging/logTailClient");
|
|
@@ -2025,13 +1752,10 @@ class ApplicationRunner extends runner("application") {
|
|
|
2025
1752
|
const { level, grep, endpoint, trace, child, role, origin, since } = options;
|
|
2026
1753
|
const query = Object.fromEntries(Object.entries({ level, grep, endpoint, trace, child, role, origin, since }).filter(([, value]) => value !== undefined && value !== null && value !== ""));
|
|
2027
1754
|
const print = (record) => process.stdout.write(options.json ? `${JSON.stringify(record)}
|
|
2028
|
-
` :
|
|
1755
|
+
` : Logger3.render(record));
|
|
2029
1756
|
const note = (text) => process.stderr.write(`[akan logs] ${text}
|
|
2030
1757
|
`);
|
|
2031
|
-
|
|
2032
|
-
const closed = new Promise((resolve) => {
|
|
2033
|
-
finish = resolve;
|
|
2034
|
-
});
|
|
1758
|
+
const { promise: closed, resolve: finish } = Promise.withResolvers();
|
|
2035
1759
|
let client;
|
|
2036
1760
|
try {
|
|
2037
1761
|
client = await LogTailClient.connect(socketPath, {
|
|
@@ -2040,11 +1764,11 @@ class ApplicationRunner extends runner("application") {
|
|
|
2040
1764
|
if (response.type === "dropped")
|
|
2041
1765
|
note(`${response.count} records dropped (reader too slow)`);
|
|
2042
1766
|
},
|
|
2043
|
-
onClose: () => finish
|
|
1767
|
+
onClose: () => finish()
|
|
2044
1768
|
});
|
|
2045
1769
|
} catch (error) {
|
|
2046
1770
|
if (error instanceof LogControlUnavailableError)
|
|
2047
|
-
throw new Error(`${app.name} is not running (no ${
|
|
1771
|
+
throw new Error(`${app.name} is not running (no ${path4.basename(socketPath)} in ${runtimeDir}). Start it with \`akan start ${app.name}\`, or pass --runtime-dir for a built app.`);
|
|
2048
1772
|
throw error;
|
|
2049
1773
|
}
|
|
2050
1774
|
const sinceMs = since ? LogQueryMatcher.parseSince(since) : undefined;
|
|
@@ -2109,7 +1833,7 @@ try {
|
|
|
2109
1833
|
const serverPath = `${app.cwdPath}/server.ts`;
|
|
2110
1834
|
if (!await app.exists("server.ts"))
|
|
2111
1835
|
throw new Error(`Server file not found: apps/${app.name}/server.ts`);
|
|
2112
|
-
const target =
|
|
1836
|
+
const target = path4.resolve(app.workspace.workspaceRoot, dir);
|
|
2113
1837
|
const code = `
|
|
2114
1838
|
const { server } = await import(${JSON.stringify(serverPath)});
|
|
2115
1839
|
const { DatabaseAdaptorRole, DocumentTransfer } = await import("akanjs/service");
|
|
@@ -2205,13 +1929,7 @@ try {
|
|
|
2205
1929
|
await new (await loadCapacitorApp())(app, mobileTarget.config).buildAndroid("apk", { env, regenerate });
|
|
2206
1930
|
});
|
|
2207
1931
|
}
|
|
2208
|
-
async startAndroid(app, {
|
|
2209
|
-
open = false,
|
|
2210
|
-
operation = "local",
|
|
2211
|
-
env = "local",
|
|
2212
|
-
target,
|
|
2213
|
-
regenerate = false
|
|
2214
|
-
} = {}) {
|
|
1932
|
+
async startAndroid(app, { open = false, operation = "local", env = "local", target, regenerate = false } = {}) {
|
|
2215
1933
|
const targets = await resolveMobileTargets(app, target);
|
|
2216
1934
|
if (operation === "release")
|
|
2217
1935
|
await this.#buildMobileCsr(app, env);
|
|
@@ -2268,16 +1986,20 @@ try {
|
|
|
2268
1986
|
return;
|
|
2269
1987
|
for (const failure of failures) {
|
|
2270
1988
|
const message = failure.error instanceof Error ? failure.error.message : String(failure.error);
|
|
2271
|
-
|
|
1989
|
+
Logger3.rawLog(`Mobile target ${failure.target} failed: ${message}`, undefined, "error");
|
|
2272
1990
|
}
|
|
2273
1991
|
throw new Error(`${failures.length}/${results.length} mobile targets failed`);
|
|
2274
1992
|
}
|
|
2275
|
-
async codepush(app
|
|
1993
|
+
async codepush(app) {
|
|
1994
|
+
throw new Error(`akan codepush is still in development and deploys nothing yet, so ${app.name} was not updated. To release the app's source with OTA update support, run \`akan release-source ${app.name}\`.`);
|
|
1995
|
+
}
|
|
1996
|
+
async#initCapacitorApp(app) {
|
|
2276
1997
|
const [target] = await resolveMobileTargets(app, undefined);
|
|
2277
1998
|
if (!target)
|
|
2278
1999
|
throw new Error(`No mobile target configured for ${app.name}`);
|
|
2279
2000
|
const capacitorApp = new (await loadCapacitorApp())(app, target.config);
|
|
2280
2001
|
await capacitorApp.init();
|
|
2002
|
+
return capacitorApp;
|
|
2281
2003
|
}
|
|
2282
2004
|
#getLocalDatabaseServices(mode) {
|
|
2283
2005
|
if (mode === "single")
|
|
@@ -2335,11 +2057,7 @@ ${detail}`
|
|
|
2335
2057
|
await workspace.spawn(`docker`, ["compose", "down"], { cwd: `${workspace.workspaceRoot}/local` });
|
|
2336
2058
|
}
|
|
2337
2059
|
async configureApp(app) {
|
|
2338
|
-
const
|
|
2339
|
-
if (!target)
|
|
2340
|
-
throw new Error(`No mobile target configured for ${app.name}`);
|
|
2341
|
-
const capacitorApp = new (await loadCapacitorApp())(app, target.config);
|
|
2342
|
-
await capacitorApp.init();
|
|
2060
|
+
const capacitorApp = await this.#initCapacitorApp(app);
|
|
2343
2061
|
if (await (await loadPrompts()).confirm({ message: "want to add camera permission?" }))
|
|
2344
2062
|
await capacitorApp.addCamera();
|
|
2345
2063
|
if (await (await loadPrompts()).confirm({ message: "want to add contact permission?" }))
|
|
@@ -2349,31 +2067,10 @@ ${detail}`
|
|
|
2349
2067
|
await capacitorApp.save();
|
|
2350
2068
|
}
|
|
2351
2069
|
async releaseSource(app, { rebuild, buildNum = 0, environment = "debug", local = true } = {}) {
|
|
2352
|
-
|
|
2070
|
+
const packager = new (await loadReleasePackager())(app, { build: () => this.build(app).then(() => {
|
|
2353
2071
|
return;
|
|
2354
|
-
}) })
|
|
2355
|
-
|
|
2356
|
-
buildNum,
|
|
2357
|
-
environment,
|
|
2358
|
-
local
|
|
2359
|
-
});
|
|
2360
|
-
return;
|
|
2361
|
-
}
|
|
2362
|
-
async createApplicationTemplate(workspace, appName) {
|
|
2363
|
-
await workspace.applyTemplate({ basePath: `apps/${appName}`, template: "appRoot", dict: { appName } });
|
|
2364
|
-
}
|
|
2365
|
-
async compressProjectFiles(app, { rebuild, buildNum = 0, environment = "debug", local = true } = {}) {
|
|
2366
|
-
await new (await loadReleasePackager())(app, {
|
|
2367
|
-
build: () => this.build(app).then(() => {
|
|
2368
|
-
return;
|
|
2369
|
-
})
|
|
2370
|
-
}).compressProjectFiles({
|
|
2371
|
-
rebuild,
|
|
2372
|
-
buildNum,
|
|
2373
|
-
environment,
|
|
2374
|
-
local
|
|
2375
|
-
});
|
|
2376
|
-
return;
|
|
2072
|
+
}) });
|
|
2073
|
+
await packager.releaseSource({ rebuild, buildNum, environment, local });
|
|
2377
2074
|
}
|
|
2378
2075
|
}
|
|
2379
2076
|
|
|
@@ -2525,7 +2222,8 @@ class HostProcessProbe {
|
|
|
2525
2222
|
if (!/^\d+$/.test(entry))
|
|
2526
2223
|
continue;
|
|
2527
2224
|
const holds = HostProcessProbe.#listDir(`/proc/${entry}/fd`).some((fd) => {
|
|
2528
|
-
const
|
|
2225
|
+
const link = HostProcessProbe.#orElse(() => readlinkSync(`/proc/${entry}/fd/${fd}`), "");
|
|
2226
|
+
const inode = /^socket:\[(\d+)\]$/.exec(link)?.[1];
|
|
2529
2227
|
return !!inode && inodes.has(inode);
|
|
2530
2228
|
});
|
|
2531
2229
|
if (holds)
|
|
@@ -2534,24 +2232,16 @@ class HostProcessProbe {
|
|
|
2534
2232
|
return pids;
|
|
2535
2233
|
}
|
|
2536
2234
|
static #readProc(pid, file) {
|
|
2537
|
-
|
|
2538
|
-
return readFileSync(`/proc/${pid}/${file}`, "utf8");
|
|
2539
|
-
} catch {
|
|
2540
|
-
return null;
|
|
2541
|
-
}
|
|
2235
|
+
return HostProcessProbe.#orElse(() => readFileSync(`/proc/${pid}/${file}`, "utf8"), null);
|
|
2542
2236
|
}
|
|
2543
2237
|
static #listDir(dir) {
|
|
2544
|
-
|
|
2545
|
-
return readdirSync(dir);
|
|
2546
|
-
} catch {
|
|
2547
|
-
return [];
|
|
2548
|
-
}
|
|
2238
|
+
return HostProcessProbe.#orElse(() => readdirSync(dir), []);
|
|
2549
2239
|
}
|
|
2550
|
-
static #
|
|
2240
|
+
static #orElse(read, fallback) {
|
|
2551
2241
|
try {
|
|
2552
|
-
return
|
|
2242
|
+
return read();
|
|
2553
2243
|
} catch {
|
|
2554
|
-
return
|
|
2244
|
+
return fallback;
|
|
2555
2245
|
}
|
|
2556
2246
|
}
|
|
2557
2247
|
}
|
|
@@ -2665,12 +2355,12 @@ class DevPortReclaimer {
|
|
|
2665
2355
|
}
|
|
2666
2356
|
|
|
2667
2357
|
// pkgs/@akanjs/cli/application/devStreamView.ts
|
|
2668
|
-
import { Logger as
|
|
2358
|
+
import { Logger as Logger4 } from "akanjs/common";
|
|
2669
2359
|
import chalk from "chalk";
|
|
2670
2360
|
var appColors = ["cyan", "magenta", "yellow", "green", "blue", "red"];
|
|
2671
2361
|
|
|
2672
2362
|
class DevStreamView {
|
|
2673
|
-
#logger = new
|
|
2363
|
+
#logger = new Logger4("akan start");
|
|
2674
2364
|
#partial = new Map;
|
|
2675
2365
|
#lastState = new Map;
|
|
2676
2366
|
#color = new Map;
|
|
@@ -2774,7 +2464,7 @@ class DevBootConcurrency {
|
|
|
2774
2464
|
|
|
2775
2465
|
// pkgs/@akanjs/cli/application/devSessionLog.ts
|
|
2776
2466
|
import { mkdir, rename } from "fs/promises";
|
|
2777
|
-
import
|
|
2467
|
+
import path5 from "path";
|
|
2778
2468
|
import dayjs from "dayjs";
|
|
2779
2469
|
|
|
2780
2470
|
class DevSessionLog {
|
|
@@ -2798,19 +2488,19 @@ class DevSessionLog {
|
|
|
2798
2488
|
return [...this.#appNames];
|
|
2799
2489
|
}
|
|
2800
2490
|
pathOf(app) {
|
|
2801
|
-
return
|
|
2491
|
+
return path5.join(this.#runtimeDirOf(app), DevSessionLog.fileName);
|
|
2802
2492
|
}
|
|
2803
2493
|
previousPathOf(app) {
|
|
2804
|
-
return
|
|
2494
|
+
return path5.join(this.#runtimeDirOf(app), DevSessionLog.previousFileName);
|
|
2805
2495
|
}
|
|
2806
2496
|
relativePathOf(app) {
|
|
2807
|
-
return
|
|
2497
|
+
return path5.relative(this.#workspaceRoot, this.pathOf(app));
|
|
2808
2498
|
}
|
|
2809
2499
|
async open() {
|
|
2810
2500
|
const stamp = dayjs(this.#now()).format("YYYY-MM-DD HH:mm:ss");
|
|
2811
2501
|
for (const app of this.#appNames) {
|
|
2812
2502
|
const file = this.pathOf(app);
|
|
2813
|
-
await mkdir(
|
|
2503
|
+
await mkdir(path5.dirname(file), { recursive: true });
|
|
2814
2504
|
await rename(file, this.previousPathOf(app)).catch(() => {
|
|
2815
2505
|
return;
|
|
2816
2506
|
});
|
|
@@ -2854,7 +2544,7 @@ class DevSessionLog {
|
|
|
2854
2544
|
this.#writers.clear();
|
|
2855
2545
|
}
|
|
2856
2546
|
#runtimeDirOf(app) {
|
|
2857
|
-
return
|
|
2547
|
+
return path5.join(this.#workspaceRoot, "local", "apps", app, "runtime");
|
|
2858
2548
|
}
|
|
2859
2549
|
#writeLine(app, text) {
|
|
2860
2550
|
const writer = this.#writers.get(app);
|
|
@@ -2877,6 +2567,16 @@ class DevSessionLog {
|
|
|
2877
2567
|
|
|
2878
2568
|
// pkgs/@akanjs/cli/application/devSupervisor.ts
|
|
2879
2569
|
class DevSupervisor {
|
|
2570
|
+
static resolveDevUi(plain, {
|
|
2571
|
+
isTty = !!process.stdout.isTTY,
|
|
2572
|
+
columns = process.stdout.columns ?? 0
|
|
2573
|
+
} = {}) {
|
|
2574
|
+
if (plain)
|
|
2575
|
+
return { mode: "stream", downgraded: false };
|
|
2576
|
+
if (!isTty || columns <= 0)
|
|
2577
|
+
return { mode: "stream", downgraded: true };
|
|
2578
|
+
return { mode: "tui", downgraded: false };
|
|
2579
|
+
}
|
|
2880
2580
|
static supervisedEnvKey = "AKAN_DEV_SUPERVISED";
|
|
2881
2581
|
static childHooks() {
|
|
2882
2582
|
if (process.env[DevSupervisor.supervisedEnvKey] !== "1" || !process.send)
|
|
@@ -2940,10 +2640,7 @@ class DevSupervisor {
|
|
|
2940
2640
|
return ports;
|
|
2941
2641
|
}
|
|
2942
2642
|
#makeChild(app, port) {
|
|
2943
|
-
|
|
2944
|
-
const ready = new Promise((resolve) => {
|
|
2945
|
-
markReady = resolve;
|
|
2946
|
-
});
|
|
2643
|
+
const { promise: ready, resolve: markReady } = Promise.withResolvers();
|
|
2947
2644
|
return {
|
|
2948
2645
|
status: {
|
|
2949
2646
|
app,
|
|
@@ -2980,16 +2677,18 @@ class DevSupervisor {
|
|
|
2980
2677
|
}
|
|
2981
2678
|
}
|
|
2982
2679
|
async#waitForReady(child) {
|
|
2983
|
-
|
|
2680
|
+
if (await DevSupervisor.timesOut(child.ready, DevSupervisor.readyTimeoutMs))
|
|
2681
|
+
this.#note(`${child.status.name} has not reported ready after ${Math.round(DevSupervisor.readyTimeoutMs / 1000)}s; starting the next app anyway`, "warn");
|
|
2682
|
+
}
|
|
2683
|
+
static async timesOut(work, ms) {
|
|
2684
|
+
let timer;
|
|
2984
2685
|
const expired = new Promise((resolve) => {
|
|
2985
|
-
timer = setTimeout(() => resolve(
|
|
2686
|
+
timer = setTimeout(() => resolve(true), ms);
|
|
2986
2687
|
});
|
|
2987
2688
|
try {
|
|
2988
|
-
|
|
2989
|
-
this.#note(`${child.status.name} has not reported ready after ${Math.round(DevSupervisor.readyTimeoutMs / 1000)}s; starting the next app anyway`, "warn");
|
|
2689
|
+
return await Promise.race([work.then(() => false), expired]);
|
|
2990
2690
|
} finally {
|
|
2991
|
-
|
|
2992
|
-
clearTimeout(timer);
|
|
2691
|
+
clearTimeout(timer);
|
|
2993
2692
|
}
|
|
2994
2693
|
}
|
|
2995
2694
|
static childArgs(name, { write = true } = {}) {
|
|
@@ -3111,37 +2810,19 @@ class DevSupervisor {
|
|
|
3111
2810
|
for (const child of running)
|
|
3112
2811
|
child.proc.kill("SIGTERM");
|
|
3113
2812
|
const exited = Promise.all(running.map(async (child) => await child.proc.exited));
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
if (!child.proc.killed) {
|
|
3122
|
-
this.#note(`${child.status.name} did not exit in time; killing it`, "warn");
|
|
3123
|
-
child.proc.kill("SIGKILL");
|
|
3124
|
-
}
|
|
3125
|
-
}
|
|
3126
|
-
}
|
|
3127
|
-
} finally {
|
|
3128
|
-
if (timer)
|
|
3129
|
-
clearTimeout(timer);
|
|
2813
|
+
if (!await DevSupervisor.timesOut(exited, DevSupervisor.shutdownGraceMs))
|
|
2814
|
+
return;
|
|
2815
|
+
for (const child of running) {
|
|
2816
|
+
if (child.proc.killed)
|
|
2817
|
+
continue;
|
|
2818
|
+
this.#note(`${child.status.name} did not exit in time; killing it`, "warn");
|
|
2819
|
+
child.proc.kill("SIGKILL");
|
|
3130
2820
|
}
|
|
3131
2821
|
}
|
|
3132
2822
|
}
|
|
3133
2823
|
|
|
3134
|
-
// pkgs/@akanjs/cli/application/devUiMode.ts
|
|
3135
|
-
var resolveDevUi = (plain, { isTty = !!process.stdout.isTTY, columns = process.stdout.columns ?? 0 } = {}) => {
|
|
3136
|
-
if (plain)
|
|
3137
|
-
return { mode: "stream", downgraded: false };
|
|
3138
|
-
if (!isTty || columns <= 0)
|
|
3139
|
-
return { mode: "stream", downgraded: true };
|
|
3140
|
-
return { mode: "tui", downgraded: false };
|
|
3141
|
-
};
|
|
3142
|
-
|
|
3143
2824
|
// pkgs/@akanjs/cli/application/interruptTeardown.ts
|
|
3144
|
-
import { Logger as
|
|
2825
|
+
import { Logger as Logger5 } from "akanjs/common";
|
|
3145
2826
|
|
|
3146
2827
|
class InterruptTeardown {
|
|
3147
2828
|
#teardowns = [];
|
|
@@ -3155,7 +2836,7 @@ class InterruptTeardown {
|
|
|
3155
2836
|
this.#listen = listen ?? ((onSignal) => {
|
|
3156
2837
|
process.on("SIGINT", onSignal);
|
|
3157
2838
|
});
|
|
3158
|
-
this.#report = report ?? ((message) =>
|
|
2839
|
+
this.#report = report ?? ((message) => Logger5.rawLog(message, undefined, "error"));
|
|
3159
2840
|
}
|
|
3160
2841
|
add(run, abandoned) {
|
|
3161
2842
|
this.#teardowns.push({ run, abandoned, running: false });
|
|
@@ -3190,10 +2871,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3190
2871
|
#interrupt = new InterruptTeardown;
|
|
3191
2872
|
async confirmDatabaseModeDependencyInstall(databaseMode, installSpecs) {
|
|
3192
2873
|
return await confirm({
|
|
3193
|
-
message:
|
|
3194
|
-
`Database mode '${databaseMode}' requires missing dependencies: ${installSpecs.join(", ")}.`,
|
|
3195
|
-
"Install them now?"
|
|
3196
|
-
].join(" "),
|
|
2874
|
+
message: `Database mode '${databaseMode}' requires missing dependencies: ${installSpecs.join(", ")}. Install them now?`,
|
|
3197
2875
|
default: true
|
|
3198
2876
|
});
|
|
3199
2877
|
}
|
|
@@ -3204,21 +2882,11 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3204
2882
|
const shouldInstall = await this.confirmDatabaseModeDependencyInstall(databaseMode, installSpecs);
|
|
3205
2883
|
if (!shouldInstall)
|
|
3206
2884
|
throw new Error(`Database mode '${databaseMode}' requires missing dependencies: ${installSpecs.join(", ")}.`);
|
|
3207
|
-
|
|
3208
|
-
try {
|
|
3209
|
-
await app.workspace.spawn("bun", ["add", ...installSpecs], {
|
|
3210
|
-
stdio: "inherit"
|
|
3211
|
-
});
|
|
3212
|
-
await app.workspace.getPackageJson({ refresh: true });
|
|
3213
|
-
spinner.succeed(`Installed database dependencies for ${databaseMode} mode`);
|
|
3214
|
-
} catch (error) {
|
|
3215
|
-
spinner.fail(`Failed to install database dependencies for ${databaseMode} mode`);
|
|
3216
|
-
throw error;
|
|
3217
|
-
}
|
|
2885
|
+
await this.#addDependencies(app, installSpecs, `database dependencies for ${databaseMode} mode`);
|
|
3218
2886
|
}
|
|
3219
2887
|
async confirmMobileDependencyInstall(installSpecs) {
|
|
3220
2888
|
return await confirm({
|
|
3221
|
-
message:
|
|
2889
|
+
message: `Mobile builds require missing dependencies: ${installSpecs.join(", ")}. Install them now?`,
|
|
3222
2890
|
default: true
|
|
3223
2891
|
});
|
|
3224
2892
|
}
|
|
@@ -3229,15 +2897,16 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3229
2897
|
const shouldInstall = await this.confirmMobileDependencyInstall(installSpecs);
|
|
3230
2898
|
if (!shouldInstall)
|
|
3231
2899
|
throw new Error(`Mobile builds require missing dependencies: ${installSpecs.join(", ")}.`);
|
|
3232
|
-
|
|
2900
|
+
await this.#addDependencies(app, installSpecs, "mobile dependencies");
|
|
2901
|
+
}
|
|
2902
|
+
async#addDependencies(app, installSpecs, what) {
|
|
2903
|
+
const spinner = app.workspace.spinning(`Installing ${what}...`);
|
|
3233
2904
|
try {
|
|
3234
|
-
await app.workspace.spawn("bun", ["add", ...installSpecs], {
|
|
3235
|
-
stdio: "inherit"
|
|
3236
|
-
});
|
|
2905
|
+
await app.workspace.spawn("bun", ["add", ...installSpecs], { stdio: "inherit" });
|
|
3237
2906
|
await app.workspace.getPackageJson({ refresh: true });
|
|
3238
|
-
spinner.succeed(
|
|
2907
|
+
spinner.succeed(`Installed ${what}`);
|
|
3239
2908
|
} catch (error) {
|
|
3240
|
-
spinner.fail(
|
|
2909
|
+
spinner.fail(`Failed to install ${what}`);
|
|
3241
2910
|
throw error;
|
|
3242
2911
|
}
|
|
3243
2912
|
}
|
|
@@ -3279,7 +2948,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3279
2948
|
const spinner = app.spinning("Planning distributable slice...");
|
|
3280
2949
|
const plan = await this.applicationRunner.planSlice(app);
|
|
3281
2950
|
spinner.succeed(`Slice planned (${plan.libs.length} libs, ${plan.warnings.length} warnings)`);
|
|
3282
|
-
|
|
2951
|
+
Logger6.rawLog(format === "json" ? JSON.stringify(plan, null, 2) : formatSlicePlan(plan));
|
|
3283
2952
|
}
|
|
3284
2953
|
async sync(sys) {
|
|
3285
2954
|
if (sys.type === "app")
|
|
@@ -3302,18 +2971,15 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3302
2971
|
async build(app, { write = true, fast = false, quiet = false } = {}) {
|
|
3303
2972
|
await app.scanSync({ write });
|
|
3304
2973
|
if (!quiet)
|
|
3305
|
-
|
|
2974
|
+
Logger6.rawLog(`Creating an optimized production build for ${app.name}...`);
|
|
3306
2975
|
try {
|
|
3307
|
-
const result = await this.applicationRunner.build(app, {
|
|
3308
|
-
|
|
3309
|
-
spinner: !quiet
|
|
3310
|
-
});
|
|
3311
|
-
Logger7.rawLog(`${app.name} built in dist/apps/${app.name}`);
|
|
2976
|
+
const result = await this.applicationRunner.build(app, { fast, spinner: !quiet });
|
|
2977
|
+
Logger6.rawLog(`${app.name} built in dist/apps/${app.name}`);
|
|
3312
2978
|
if (!quiet)
|
|
3313
2979
|
ApplicationBuildReporter.printSummary(result);
|
|
3314
2980
|
} catch (error) {
|
|
3315
|
-
|
|
3316
|
-
|
|
2981
|
+
Logger6.rawLog(`${app.name} build failed in dist/apps/${app.name}`, undefined, "error");
|
|
2982
|
+
Logger6.rawLog(ApplicationBuildReporter.formatError(error), undefined, "error");
|
|
3317
2983
|
throw error;
|
|
3318
2984
|
}
|
|
3319
2985
|
}
|
|
@@ -3325,15 +2991,14 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3325
2991
|
spinner.succeed(`${app.name} typechecked`);
|
|
3326
2992
|
} catch (error) {
|
|
3327
2993
|
spinner.fail(`${app.name} typecheck failed`);
|
|
3328
|
-
|
|
2994
|
+
Logger6.rawLog(ApplicationBuildReporter.formatError(error), undefined, "error");
|
|
3329
2995
|
throw error;
|
|
3330
2996
|
}
|
|
3331
2997
|
}
|
|
3332
2998
|
async test(exec, { write = true } = {}) {
|
|
3333
2999
|
if (exec instanceof LibExecutor) {
|
|
3334
3000
|
await this.libraryScript.syncLibrary(exec);
|
|
3335
|
-
|
|
3336
|
-
spinner.succeed(`${exec.name} prepared`);
|
|
3001
|
+
exec.spinning(`Preparing ${exec.name}...`).succeed(`${exec.name} prepared`);
|
|
3337
3002
|
await this.applicationRunner.test(exec);
|
|
3338
3003
|
return;
|
|
3339
3004
|
}
|
|
@@ -3362,7 +3027,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3362
3027
|
const first = apps[0];
|
|
3363
3028
|
if (!first)
|
|
3364
3029
|
throw new Error("No app selected to start");
|
|
3365
|
-
const { mode, downgraded } = resolveDevUi(plain);
|
|
3030
|
+
const { mode, downgraded } = DevSupervisor.resolveDevUi(plain);
|
|
3366
3031
|
if (downgraded)
|
|
3367
3032
|
first.workspace.log("no sized terminal to draw on; printing prefixed lines instead");
|
|
3368
3033
|
if (kill)
|
|
@@ -3384,15 +3049,12 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3384
3049
|
this.#stopDatabaseOnInterrupt(app.workspace);
|
|
3385
3050
|
}
|
|
3386
3051
|
const spinner = app.spinning("Preparing backend...");
|
|
3387
|
-
|
|
3052
|
+
return await this.applicationRunner.start(app, {
|
|
3388
3053
|
open,
|
|
3389
|
-
onStart: () => {
|
|
3390
|
-
spinner.succeed(`${app.name} prepared, ready to start`);
|
|
3391
|
-
},
|
|
3054
|
+
onStart: () => spinner.succeed(`${app.name} prepared, ready to start`),
|
|
3392
3055
|
onDevEvent,
|
|
3393
3056
|
stdio
|
|
3394
3057
|
});
|
|
3395
|
-
return akanAppHost;
|
|
3396
3058
|
}
|
|
3397
3059
|
async reclaimDevPorts(apps) {
|
|
3398
3060
|
const workspace = apps[0]?.workspace;
|
|
@@ -3403,7 +3065,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3403
3065
|
for (const holder of killed)
|
|
3404
3066
|
workspace.log(`freed port ${holder.port} \u2014 killed pid ${holder.pid} (${holder.command})`);
|
|
3405
3067
|
for (const holder of foreign)
|
|
3406
|
-
|
|
3068
|
+
Logger6.rawLog(`port ${holder.port} is held by pid ${holder.pid} and was left alone: ${holder.command}`, undefined, "error");
|
|
3407
3069
|
if (killed.length === 0 && foreign.length === 0)
|
|
3408
3070
|
workspace.log("dev ports are free; nothing to kill");
|
|
3409
3071
|
}
|
|
@@ -3420,7 +3082,7 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3420
3082
|
throw new Error("No app selected to start");
|
|
3421
3083
|
const startedDatabase = dbup ? await this.#prepareSharedDatabase(apps) : false;
|
|
3422
3084
|
const supervisor = new DevSupervisor({ apps, mode, concurrency, open, write, shares });
|
|
3423
|
-
const view = mode === "tui" ?
|
|
3085
|
+
const view = mode === "tui" ? new (await import("./devTui-q7k7bakd.js")).DevTui(supervisor) : new DevStreamView(apps.map((app) => app.name));
|
|
3424
3086
|
await supervisor.run(view);
|
|
3425
3087
|
if (startedDatabase)
|
|
3426
3088
|
await this.#stopDatabase(workspace);
|
|
@@ -3495,23 +3157,13 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3495
3157
|
const akanConfig = await app.getConfig();
|
|
3496
3158
|
await this.syncMobileDependencies(app, akanConfig);
|
|
3497
3159
|
await this.syncMobileAppCapacitorPlugins(app, akanConfig);
|
|
3498
|
-
await this.applicationRunner.startAndroid(app, {
|
|
3499
|
-
open,
|
|
3500
|
-
operation,
|
|
3501
|
-
env,
|
|
3502
|
-
target,
|
|
3503
|
-
regenerate
|
|
3504
|
-
});
|
|
3160
|
+
await this.applicationRunner.startAndroid(app, { open, operation, env, target, regenerate });
|
|
3505
3161
|
}
|
|
3506
3162
|
async releaseAndroid(app, assembleType, { write = true, target, env = "main", regenerate = false, allowLocalRelease = false } = {}) {
|
|
3507
3163
|
await app.scanSync({ write });
|
|
3508
3164
|
if (env === "local" && !allowLocalRelease)
|
|
3509
3165
|
throw new Error("releaseAndroid --env local is blocked. Pass allowLocalRelease only for explicit local release testing.");
|
|
3510
|
-
await this.applicationRunner.releaseAndroid(app, assembleType, {
|
|
3511
|
-
target,
|
|
3512
|
-
env,
|
|
3513
|
-
regenerate
|
|
3514
|
-
});
|
|
3166
|
+
await this.applicationRunner.releaseAndroid(app, assembleType, { target, env, regenerate });
|
|
3515
3167
|
}
|
|
3516
3168
|
async configureApp(app) {
|
|
3517
3169
|
await this.applicationRunner.configureApp(app);
|
|
@@ -3519,8 +3171,8 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3519
3171
|
async releaseSource(app, options) {
|
|
3520
3172
|
await this.applicationRunner.releaseSource(app, options);
|
|
3521
3173
|
}
|
|
3522
|
-
async codepush(app
|
|
3523
|
-
await this.applicationRunner.codepush(app
|
|
3174
|
+
async codepush(app) {
|
|
3175
|
+
await this.applicationRunner.codepush(app);
|
|
3524
3176
|
}
|
|
3525
3177
|
async transferDatabase(app, direction, dir) {
|
|
3526
3178
|
await this.applicationRunner.transferDatabase(app, direction, dir);
|
|
@@ -3558,13 +3210,13 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3558
3210
|
await Promise.all(shares.map(async (share) => await share.close()));
|
|
3559
3211
|
}, "Abandoning the tunnel release; the shares expire on their own.");
|
|
3560
3212
|
await Promise.all(apps.map(async (app) => {
|
|
3561
|
-
const { TunnelShare } = await import("./TunnelShare-
|
|
3213
|
+
const { TunnelShare } = await import("./TunnelShare-scf4e4pd.js");
|
|
3562
3214
|
const share = await TunnelShare.open(app, { workspace: app.workspace });
|
|
3563
3215
|
shares.push(share);
|
|
3564
3216
|
urls.set(app.name, share.url);
|
|
3565
|
-
|
|
3217
|
+
Logger6.rawLog(`${app.name} is shared at ${share.url}`);
|
|
3566
3218
|
})).catch((error) => {
|
|
3567
|
-
|
|
3219
|
+
Logger6.rawLog(`Could not open the tunnel: ${error instanceof Error ? error.message : String(error)}`, undefined, "error");
|
|
3568
3220
|
});
|
|
3569
3221
|
return urls;
|
|
3570
3222
|
}
|
|
@@ -3574,21 +3226,12 @@ class ApplicationScript extends script("application", [ApplicationRunner, Librar
|
|
|
3574
3226
|
}, "Abandoning the local database teardown; containers are left running.");
|
|
3575
3227
|
}
|
|
3576
3228
|
async#stopDatabase(workspace) {
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
timer = setTimeout(() => resolve("timeout"), ApplicationScript.dbShutdownTimeoutMs);
|
|
3229
|
+
const stopped = this.dbdown(workspace).catch(() => {
|
|
3230
|
+
return;
|
|
3580
3231
|
});
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
}), timeout]);
|
|
3585
|
-
if (result !== "timeout")
|
|
3586
|
-
return;
|
|
3587
|
-
Logger7.rawLog(`Local database did not stop within ${ApplicationScript.dbShutdownTimeoutMs}ms; run \`akan dbdown\` once Docker responds.`, undefined, "error");
|
|
3588
|
-
} finally {
|
|
3589
|
-
if (timer)
|
|
3590
|
-
clearTimeout(timer);
|
|
3591
|
-
}
|
|
3232
|
+
if (!await DevSupervisor.timesOut(stopped, ApplicationScript.dbShutdownTimeoutMs))
|
|
3233
|
+
return;
|
|
3234
|
+
Logger6.rawLog(`Local database did not stop within ${ApplicationScript.dbShutdownTimeoutMs}ms; run \`akan dbdown\` once Docker responds.`, undefined, "error");
|
|
3592
3235
|
}
|
|
3593
3236
|
}
|
|
3594
3237
|
|