@akanjs/cli 3.0.0-alpha.11 → 3.0.0-alpha.111

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.
Files changed (140) hide show
  1. package/.build-stamp +1 -1
  2. package/README.ko.md +1 -1
  3. package/README.md +1 -1
  4. package/{abstractCompactor-e5yq60qz.js → abstractCompactor-mjh5ppsq.js} +3 -5
  5. package/{agent.command-a3n15c6t.js → agent.command-d2gfxfck.js} +11 -13
  6. package/{application.command-rdm3et0g.js → application.command-pj2m7584.js} +65 -38
  7. package/{applicationBuildRunner-6m8n71w4.js → applicationBuildRunner-kxwbssgn.js} +27 -23
  8. package/{applicationReleasePackager-gg8ccbeh.js → applicationReleasePackager-4yasxwv9.js} +8 -9
  9. package/buildBatch.proc.js +12 -14
  10. package/capacitorApp-36rbr858.js +12 -0
  11. package/{cloud.command-56aeekqn.js → cloud.command-gxqp8xdv.js} +15 -18
  12. package/commandManifest.json +1 -1
  13. package/{context.command-ysxawe7h.js → context.command-ez8a0zyz.js} +19 -23
  14. package/dependencyScanner-shs82ynp.js +8 -0
  15. package/devTui-40ggmd99.js +553 -0
  16. package/devTuiView-c08ccbat.js +9 -0
  17. package/getDirname-dr3jz1xg.js +7 -0
  18. package/{guideline.command-yqf9a372.js → guideline.command-xmqwx8zm.js} +24 -22
  19. package/guidelines/agentRule/agentRule.generate.json +31 -0
  20. package/guidelines/agentRule/agentRule.instruction.md +531 -0
  21. package/guidelines/conventions/conventions.instruction.md +493 -369
  22. package/guidelines/framework/framework.generate.json +3 -2
  23. package/guidelines/framework/framework.instruction.md +0 -9
  24. package/guidelines/mcpRule/mcpRule.generate.json +35 -0
  25. package/guidelines/mcpRule/mcpRule.instruction.md +460 -0
  26. package/guidelines/modelStore/modelStore.instruction.md +12 -0
  27. package/guidelines/modelTemplate/modelTemplate.instruction.md +5 -0
  28. package/guidelines/queryRule/queryRule.generate.json +31 -0
  29. package/guidelines/queryRule/queryRule.instruction.md +178 -0
  30. package/guidelines/runtimeRule/runtimeRule.generate.json +25 -0
  31. package/guidelines/runtimeRule/runtimeRule.instruction.md +220 -0
  32. package/guidelines/ssrRule/ssrRule.instruction.md +51 -5
  33. package/guidelines/transportRule/transportRule.generate.json +31 -0
  34. package/guidelines/transportRule/transportRule.instruction.md +60 -0
  35. package/guidelines/workspaceOnboarding/workspaceOnboarding.instruction.md +54 -764
  36. package/guidelines/workspaceRecipes/workspaceRecipes.generate.json +33 -0
  37. package/guidelines/workspaceRecipes/workspaceRecipes.instruction.md +467 -0
  38. package/incrementalBuilder.proc.js +26 -25
  39. package/{index-8pd02prk.js → index-259dsnz3.js} +58 -25
  40. package/{index-yzsxc30b.js → index-2tv3fdx4.js} +975 -234
  41. package/index-3d9y066c.js +76 -0
  42. package/{index-76rn3g2c.js → index-47pj3aee.js} +4 -4
  43. package/{index-m90fn8xe.js → index-4edzf1gr.js} +32 -32
  44. package/{index-3789fdpg.js → index-600zhnve.js} +94 -61
  45. package/{index-1577bej2.js → index-6wwzs1bc.js} +2 -2
  46. package/{index-cv9cxs5k.js → index-7737gxcp.js} +18 -18
  47. package/{index-a5cs7d8r.js → index-8few9nyh.js} +13 -13
  48. package/{index-mewacxye.js → index-8pm30m73.js} +57 -57
  49. package/{index-wnp7hwq7.js → index-92zztqy1.js} +7 -7
  50. package/{index-fgc8r6dj.js → index-a5vmm6x4.js} +3 -1
  51. package/{index-cav1azma.js → index-a64esmtb.js} +1106 -1369
  52. package/{index-nwqqssnb.js → index-aj7tyh5w.js} +26 -23
  53. package/{index-xys926f2.js → index-by5gpp8x.js} +4 -4
  54. package/{index-yzmqvg23.js → index-fz5q3gd7.js} +8 -8
  55. package/{index-kcsa1zff.js → index-gyd220p2.js} +4 -4
  56. package/{index-j37qq1f2.js → index-h7yw2z9n.js} +7 -7
  57. package/{index-bjpxzr6s.js → index-hwkygn9s.js} +51 -3
  58. package/index-j9yhg404.js +159 -0
  59. package/{index-46tjzh6s.js → index-kcxmv48c.js} +29 -29
  60. package/{index-0cj2zxbm.js → index-kn1h7p1x.js} +12 -11
  61. package/{index-e4b2ae6b.js → index-ktb5njgy.js} +41 -34
  62. package/{index-etwh7sd3.js → index-mak3cd28.js} +100 -32
  63. package/{index-xjd3rqzj.js → index-n68jx79w.js} +6 -15
  64. package/{index-z1s5f4e0.js → index-ne2qy0nk.js} +36 -37
  65. package/{index-65aeh789.js → index-pnqbkf5p.js} +1 -1
  66. package/{index-67546d0j.js → index-q0y2jq9j.js} +14 -2
  67. package/{index-4mgkgba3.js → index-rwg2aa4f.js} +14 -14
  68. package/{index-bzznxgcx.js → index-v0qh106m.js} +1002 -128
  69. package/{index-j9cxndc0.js → index-w8kmx1b6.js} +20 -11
  70. package/{index-h5whaj26.js → index-ws5e0j51.js} +16 -12
  71. package/{index-9va8zayb.js → index-wt7qrtd4.js} +27 -27
  72. package/index.js +22 -25
  73. package/{library.command-x6t29a30.js → library.command-nbsky672.js} +11 -10
  74. package/{localRegistry.command-649g1n7b.js → localRegistry.command-ts6t5e5a.js} +20 -23
  75. package/{module.command-8w8ys5t3.js → module.command-29vqzrh8.js} +13 -16
  76. package/{package.command-zgy7f3fz.js → package.command-axv0abmn.js} +8 -10
  77. package/package.json +5 -7
  78. package/{page.command-yjmnkyk2.js → page.command-vs9s3zrh.js} +7 -9
  79. package/{primitive.command-gfqaagsg.js → primitive.command-cbxmqbtc.js} +13 -16
  80. package/{quality.command-ec8r1dn3.js → quality.command-gmbs7nws.js} +184 -334
  81. package/recipeScanner-fpagtgc1.js +11 -0
  82. package/{repair.command-p9ey6mkm.js → repair.command-bqsjptsj.js} +12 -14
  83. package/{routeSourceValidator-e4jmbfcx.js → routeSourceValidator-apyqw7wk.js} +90 -19
  84. package/{scalar.command-9dhmqhtm.js → scalar.command-6k1vhe9s.js} +11 -14
  85. package/subspace.command-35a3t6vv.js +755 -0
  86. package/templates/app/env/env.client.debug.ts.template +1 -5
  87. package/templates/app/env/env.client.develop.ts.template +1 -5
  88. package/templates/app/env/env.client.local.ts.template +1 -5
  89. package/templates/app/env/env.client.main.ts.template +1 -5
  90. package/templates/app/env/env.client.testing.ts.template +1 -5
  91. package/templates/app/env/env.client.type.ts +2 -6
  92. package/templates/app/env/env.server.debug.ts.template +1 -5
  93. package/templates/app/env/env.server.develop.ts.template +1 -5
  94. package/templates/app/env/env.server.local.ts.template +1 -5
  95. package/templates/app/env/env.server.main.ts.template +1 -5
  96. package/templates/app/main.ts +3 -1
  97. package/templates/app/page/_index.tsx +3 -3
  98. package/templates/app/page/_layout.tsx +31 -32
  99. package/templates/appSample/lib/task/task.signal.ts +11 -6
  100. package/templates/appSample/page/task/[taskId]/_index.tsx +22 -27
  101. package/templates/appSample/page/task/[taskId]/edit.tsx +25 -30
  102. package/templates/appSample/page/task/_index.tsx +3 -7
  103. package/templates/appSample/page/task/_layout.tsx +4 -15
  104. package/templates/appSample/page/task/new.tsx +4 -10
  105. package/templates/appSample/srvkit/AuthGuard.ts +2 -2
  106. package/templates/crudPages/[__model__Id]/edit/page.tsx +27 -29
  107. package/templates/crudPages/[__model__Id]/page.tsx +24 -32
  108. package/templates/crudPages/new/page.tsx +26 -25
  109. package/templates/crudPages/page.tsx +27 -26
  110. package/templates/crudSinglePage/page.tsx +26 -21
  111. package/templates/env/_env.server.type.ts +7 -5
  112. package/templates/lib/db.ts +1 -1
  113. package/templates/lib/dict.ts +1 -1
  114. package/templates/lib/sig.ts +5 -1
  115. package/templates/lib/srv.ts +6 -2
  116. package/templates/module/__model__.dictionary.ts +4 -1
  117. package/templates/module/__model__.signal.ts +2 -3
  118. package/templates/module/__model__.store.ts +1 -2
  119. package/templates/workspaceRoot/.gitignore.template +0 -1
  120. package/templates/workspaceRoot/.husky/pre-commit.template +16 -0
  121. package/templates/workspaceRoot/.vscode/settings.json.template +1 -1
  122. package/templates/workspaceRoot/AGENTS.md.template +4 -0
  123. package/templates/workspaceRoot/CLAUDE.md.template +19 -0
  124. package/templates/workspaceRoot/biome.json.template +1 -1
  125. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +10 -14
  126. package/templates/workspaceRoot/docs/GENERATED.md.template +12 -10
  127. package/templates/workspaceRoot/package.json.template +2 -4
  128. package/typeChecker-3xgrzc2v.js +7 -0
  129. package/typecheck.proc.js +4 -5
  130. package/{workflow.command-ycwc6zr6.js → workflow.command-nwpcdc8p.js} +22 -25
  131. package/{workspace.command-xhyhajxa.js → workspace.command-aczjxzbf.js} +64 -66
  132. package/capacitorApp-mr81qdxq.js +0 -61
  133. package/dependencyScanner-f6grrep3.js +0 -9
  134. package/getDirname-m9s0cmhq.js +0 -8
  135. package/index-mxvakhsm.js +0 -66
  136. package/index-r24hmh0q.js +0 -4
  137. package/recipeScanner-tghkzrrt.js +0 -12
  138. package/typeChecker-kravn7ns.js +0 -8
  139. /package/{index-0wae5ebk.js → index-meh558jy.js} +0 -0
  140. /package/{index-ss469dec.js → index-nz8prx9g.js} +0 -0
@@ -1,16 +1,189 @@
1
1
  // @bun
2
- import {
3
- Spinner
4
- } from "./index-mxvakhsm.js";
5
- import {
6
- getDirname
7
- } from "./index-1577bej2.js";
8
2
  import {
9
3
  FileSys
10
- } from "./index-67546d0j.js";
4
+ } from "./index-q0y2jq9j.js";
11
5
  import {
12
- __require
13
- } from "./index-r24hmh0q.js";
6
+ getDirname2
7
+ } from "./index-6wwzs1bc.js";
8
+
9
+ // pkgs/@akanjs/devkit/codegenLock.ts
10
+ import { mkdir, open, readFile, rm, stat } from "fs/promises";
11
+ import path from "path";
12
+ import { Logger } from "akanjs/common";
13
+
14
+ class CodegenLock {
15
+ static fileName = "codegen.lock";
16
+ static waitTimeoutMs = 1e4;
17
+ static unknownHolderStaleMs = 60000;
18
+ static #pollMs = 25;
19
+ static #logger = new Logger("CodegenLock");
20
+ static #queue = Promise.resolve();
21
+ static pathIn(workspaceRoot) {
22
+ return path.join(workspaceRoot, "local", ".akan", CodegenLock.fileName);
23
+ }
24
+ static async run(workspaceRoot, label, fn) {
25
+ const ahead = CodegenLock.#queue;
26
+ let done;
27
+ CodegenLock.#queue = new Promise((resolve) => {
28
+ done = resolve;
29
+ });
30
+ try {
31
+ await CodegenLock.#waitForQueue(ahead, label);
32
+ return await CodegenLock.#withFileLock(workspaceRoot, label, fn);
33
+ } finally {
34
+ done();
35
+ }
36
+ }
37
+ static async#waitForQueue(ahead, label) {
38
+ let timer = null;
39
+ const expired = new Promise((resolve) => {
40
+ timer = setTimeout(() => resolve("expired"), CodegenLock.waitTimeoutMs);
41
+ });
42
+ try {
43
+ if (await Promise.race([ahead.then(() => "done"), expired]) === "expired")
44
+ CodegenLock.#logger.warn(`codegen lock queued past ${CodegenLock.waitTimeoutMs}ms in this process; continuing without waiting (${label})`);
45
+ } finally {
46
+ if (timer)
47
+ clearTimeout(timer);
48
+ }
49
+ }
50
+ static async#withFileLock(workspaceRoot, label, fn) {
51
+ const lockPath = CodegenLock.pathIn(workspaceRoot);
52
+ await mkdir(path.dirname(lockPath), { recursive: true }).catch(() => {
53
+ return;
54
+ });
55
+ const held = await CodegenLock.#acquire(lockPath, label);
56
+ try {
57
+ return await fn();
58
+ } finally {
59
+ if (held)
60
+ await rm(lockPath, { force: true }).catch(() => {
61
+ return;
62
+ });
63
+ }
64
+ }
65
+ static async#acquire(lockPath, label) {
66
+ const deadline = Date.now() + CodegenLock.waitTimeoutMs;
67
+ for (;; ) {
68
+ const handle = await open(lockPath, "wx").catch(() => null);
69
+ if (handle) {
70
+ await handle.writeFile(JSON.stringify({ pid: process.pid, at: Date.now(), label })).catch(() => {
71
+ return;
72
+ });
73
+ await handle.close().catch(() => {
74
+ return;
75
+ });
76
+ return true;
77
+ }
78
+ if (await CodegenLock.#reclaimIfAbandoned(lockPath))
79
+ continue;
80
+ if (Date.now() >= deadline) {
81
+ CodegenLock.#logger.warn(`codegen lock at ${lockPath} held past ${CodegenLock.waitTimeoutMs}ms; continuing without it (${label})`);
82
+ return false;
83
+ }
84
+ await Bun.sleep(CodegenLock.#pollMs);
85
+ }
86
+ }
87
+ static async#reclaimIfAbandoned(lockPath) {
88
+ const info = await stat(lockPath).catch(() => null);
89
+ if (!info)
90
+ return true;
91
+ const holder = CodegenLock.#parseHolder(await readFile(lockPath, "utf8").catch(() => ""));
92
+ if (holder) {
93
+ if (CodegenLock.#isAlive(holder.pid))
94
+ return false;
95
+ } else if (Date.now() - info.mtimeMs < CodegenLock.unknownHolderStaleMs)
96
+ return false;
97
+ await rm(lockPath, { force: true }).catch(() => {
98
+ return;
99
+ });
100
+ return true;
101
+ }
102
+ static #parseHolder(raw) {
103
+ try {
104
+ const parsed = JSON.parse(raw);
105
+ if (typeof parsed.pid !== "number" || typeof parsed.at !== "number")
106
+ return null;
107
+ return { pid: parsed.pid, at: parsed.at, label: typeof parsed.label === "string" ? parsed.label : "" };
108
+ } catch {
109
+ return null;
110
+ }
111
+ }
112
+ static #isAlive(pid) {
113
+ if (!Number.isInteger(pid) || pid <= 0)
114
+ return false;
115
+ try {
116
+ process.kill(pid, 0);
117
+ return true;
118
+ } catch (error) {
119
+ return error.code === "EPERM";
120
+ }
121
+ }
122
+ }
123
+
124
+ // pkgs/@akanjs/devkit/spinner.ts
125
+ import ora from "ora";
126
+
127
+ class Spinner {
128
+ static padding = 12;
129
+ static oraOptions = { discardStdin: false };
130
+ static canAnimate(stream = process.stderr) {
131
+ return !stream.isTTY || stream.columns > 0;
132
+ }
133
+ spinner;
134
+ stopWatch = null;
135
+ startAt = new Date;
136
+ prefix;
137
+ message;
138
+ enableSpin;
139
+ constructor(message, { prefix = "", indent = 0, enableSpin = true } = {}) {
140
+ Spinner.padding = Math.max(Spinner.padding, prefix.length);
141
+ this.prefix = prefix;
142
+ this.message = message;
143
+ this.spinner = ora({ ...Spinner.oraOptions, text: message });
144
+ this.spinner.prefixText = prefix.padStart(Spinner.padding, " ");
145
+ this.spinner.indent = indent;
146
+ this.enableSpin = enableSpin && Spinner.canAnimate();
147
+ }
148
+ start() {
149
+ this.startAt = new Date;
150
+ if (this.enableSpin) {
151
+ this.spinner.start();
152
+ this.stopWatch = setInterval(() => {
153
+ this.spinner.prefixText = this.prefix.padStart(Spinner.padding, " ");
154
+ this.spinner.text = `${this.message} (${this.#getElapsedTimeStr({ floor: true })})`;
155
+ }, 1000);
156
+ } else
157
+ this.spinner.info();
158
+ return this;
159
+ }
160
+ succeed(message) {
161
+ this.spinner.succeed(`${message} (${this.#getElapsedTimeStr()})`);
162
+ this.#reset();
163
+ }
164
+ fail(message) {
165
+ this.spinner.fail(`${message} (${this.#getElapsedTimeStr()})`);
166
+ this.#reset();
167
+ }
168
+ isSpinning() {
169
+ return this.spinner.isSpinning;
170
+ }
171
+ #reset() {
172
+ if (this.stopWatch)
173
+ clearInterval(this.stopWatch);
174
+ this.stopWatch = null;
175
+ }
176
+ #getElapsedTimeStr({ floor = false } = {}) {
177
+ const ms = Date.now() - this.startAt.getTime();
178
+ if (ms < 1000)
179
+ return `${ms}ms`;
180
+ const s = Math.floor(ms / 1000);
181
+ if (s < 60)
182
+ return `${floor ? Math.floor(ms / 1000) : Math.floor(ms / 100) / 10}s`;
183
+ const m = Math.floor(s / 60);
184
+ return `${m}m ${s % 60}s`;
185
+ }
186
+ }
14
187
 
15
188
  // pkgs/@akanjs/devkit/executors.ts
16
189
  import {
@@ -22,19 +195,20 @@ import { readFileSync as readFileSync2 } from "fs";
22
195
  import {
23
196
  copyFile,
24
197
  cp as cpEntry,
25
- mkdir,
198
+ mkdir as mkdir3,
26
199
  readdir as readDirEntries,
27
200
  realpath,
28
- rm as rm2,
29
- stat,
201
+ rm as rm3,
202
+ stat as stat2,
30
203
  symlink
31
204
  } from "fs/promises";
32
- import path5 from "path";
205
+ import path8 from "path";
33
206
  import { pathToFileURL } from "url";
34
207
  import {
35
208
  capitalize,
209
+ getPageSourceFileViolation,
36
210
  isRouteSourceFile,
37
- Logger,
211
+ Logger as Logger2,
38
212
  parseRouteModuleKey,
39
213
  validatePageSourceFile,
40
214
  validateSubRoutePageKey
@@ -44,7 +218,7 @@ import chalk2 from "chalk";
44
218
 
45
219
  // pkgs/@akanjs/devkit/agentsIndex.ts
46
220
  var recipeScannerLoad = null;
47
- var loadRecipeScanner = () => recipeScannerLoad ??= import("./recipeScanner-tghkzrrt.js");
221
+ var loadRecipeScanner = () => recipeScannerLoad ??= import("./recipeScanner-fpagtgc1.js");
48
222
  var AGENT_BLOCK_START = "<!-- akan:agent:start -->";
49
223
  var AGENT_BLOCK_END = "<!-- akan:agent:end -->";
50
224
  var AGENT_VERSION_PREFIX = "<!-- akan:agent:version ";
@@ -54,9 +228,9 @@ ${block}`;
54
228
  var extractBlockVersion = (content) => content.match(/<!-- akan:agent:version ([^\s]+) -->/)?.[1] ?? null;
55
229
  var readDevkitVersion = async () => {
56
230
  const { readFile } = await import("fs/promises");
57
- const { getDirname: getDirname2 } = await import("./getDirname-m9s0cmhq.js");
231
+ const { getDirname } = await import("./getDirname-dr3jz1xg.js");
58
232
  try {
59
- const raw = await readFile(`${getDirname2(import.meta.url)}/package.json`, "utf-8");
233
+ const raw = await readFile(`${getDirname(import.meta.url)}/package.json`, "utf-8");
60
234
  return JSON.parse(raw).version ?? null;
61
235
  } catch {
62
236
  return null;
@@ -155,15 +329,36 @@ ${AGENT_BLOCK_START}
155
329
  ${block}
156
330
  ${AGENT_BLOCK_END}
157
331
  `;
332
+ var CLAUDE_COMMENT_RULE = `## Comments \u2014 Overrides Your Default
333
+
334
+ Write **no comments** unless the comment passes the test below. This is the rule agents break most often here, so it
335
+ is repeated outside the guide: a diff that adds a comment the test rejects is a diff to redo.
336
+
337
+ Before typing \`//\`, \`/*\`, or a doc block, ask \u2014 **does this sentence carry a fact that is nowhere in the code?**
338
+
339
+ - Restates the identifier, the signature, or the line under it \u2192 delete it.
340
+ - Labels a section (\`// helpers\`, \`// state\`) or narrates a step (\`// fetch the user\`, \`// then save\`) \u2192 delete it.
341
+ - JSDoc on an ordinary function, or a why/how preamble on ordinary logic \u2192 delete it.
342
+ - Explains the edit you just made, for whoever reads the diff \u2192 say it in your reply, not in the file.
343
+ - Names a vendor or protocol quirk, an infrastructure constraint, a library gotcha, security reasoning, a math
344
+ derivation, a domain field's business meaning, a state transition, or why an obvious alternative was rejected \u2192
345
+ keep it, one line.
346
+
347
+ That keep-list is exact \u2014 \`Comments\` in the guide is the full version. "It aids readability" and "this logic is
348
+ subtle" are not on it: rename or split the code instead. When you edit an existing file, match its density; if the
349
+ surrounding code carries none, your diff carries none.`;
158
350
  var renderScopeClaudeMd = (scope) => `# ${scope.name} \u2014 Claude Code Guide
159
351
 
160
352
  @AGENTS.md
353
+
354
+ ${CLAUDE_COMMENT_RULE}
161
355
  `;
162
356
 
163
357
  // pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
164
358
  import fs from "fs";
165
- import path from "path";
359
+ import path2 from "path";
166
360
  import { fileURLToPath } from "url";
361
+ import { normalizeRoutePrefix } from "akanjs/base";
167
362
  import { resolveAkanI18nConfig } from "akanjs/common";
168
363
 
169
364
  // pkgs/@akanjs/devkit/akanConfig/types.ts
@@ -199,9 +394,8 @@ var DEFAULT_OPTIMIZE_IMPORTS = [
199
394
  "react-icons/*"
200
395
  ];
201
396
  var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
397
+ var DEFAULT_DOCKER_IMAGE = "oven/bun:1-slim";
202
398
  var SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"];
203
- var NATIVE_RUNTIME_PACKAGES = ["sharp"];
204
- var DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"];
205
399
  var MOBILE_RUNTIME_PACKAGES = [
206
400
  "@capacitor/cli",
207
401
  "@capacitor/core",
@@ -230,8 +424,6 @@ var DATABASE_MODE_RUNTIME_PACKAGES = {
230
424
  };
231
425
  var AKAN_RUNTIME_PACKAGES = new Set([
232
426
  ...SSR_RUNTIME_PACKAGES,
233
- ...NATIVE_RUNTIME_PACKAGES,
234
- ...DEFAULT_BACKEND_RUNTIME_PACKAGES,
235
427
  ...MOBILE_RUNTIME_PACKAGES,
236
428
  ...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat()
237
429
  ]);
@@ -246,14 +438,15 @@ var DEFAULT_AKAN_IMAGE_CONFIG = {
246
438
  dangerouslyAllowSVG: false,
247
439
  maximumRedirects: 3,
248
440
  fetchTimeoutMs: 7000,
249
- maxRemoteBytes: 25 * 1024 * 1024
441
+ maxRemoteBytes: 25 * 1024 * 1024,
442
+ maxConcurrency: 0
250
443
  };
251
444
  var normalizeIndexPath = (indexPath) => {
252
445
  const normalized = indexPath?.trim();
253
446
  if (!normalized)
254
447
  return;
255
- const path2 = `/${normalized.replace(/^\/+|\/+$/g, "")}`;
256
- return path2 === "/" ? "/" : path2;
448
+ const path = `/${normalized.replace(/^\/+|\/+$/g, "")}`;
449
+ return path === "/" ? "/" : path;
257
450
  };
258
451
  var normalizeStringList = (values) => {
259
452
  const normalized = values?.map((value) => value.trim()).filter(Boolean) ?? [];
@@ -293,21 +486,39 @@ var normalizeDeepLinks = (deepLinks) => {
293
486
  ...sha256CertFingerprints ? { android: { sha256CertFingerprints } } : {}
294
487
  };
295
488
  };
489
+ var emptyLibContributions = () => ({
490
+ externalLibs: [],
491
+ docker: { preRuns: [], postRuns: [] },
492
+ keepFonts: []
493
+ });
494
+ var normalizeKeepFonts = (keepFonts) => [
495
+ ...new Set((keepFonts ?? []).map((glob) => glob.trim().replace(/^\/+/, "")).filter(Boolean))
496
+ ];
497
+ var dedupeDockerRuns = (runs) => {
498
+ const byKey = new Map;
499
+ for (const run of runs)
500
+ byKey.set(typeof run === "string" ? run : JSON.stringify(run), run);
501
+ return [...byKey.values()];
502
+ };
296
503
 
297
504
  class AkanAppConfig {
298
505
  app;
299
506
  rootPackageJson;
300
507
  docker;
508
+ dockerfile;
301
509
  defaultDatabaseMode;
510
+ web;
302
511
  externalLibs;
303
512
  barrelImports;
304
513
  optimizeImports;
305
514
  images;
306
515
  i18n;
516
+ api;
307
517
  publicEnv;
308
518
  mobile;
309
519
  hasMobileConfig;
310
520
  secrets;
521
+ assets;
311
522
  syncPageLibs;
312
523
  baseDevEnv;
313
524
  libs;
@@ -316,7 +527,7 @@ class AkanAppConfig {
316
527
  subRoutes = new Map;
317
528
  basePaths = new Set;
318
529
  branches = new Set(["debug", "develop", "main"]);
319
- constructor(app, libs, rootPackageJson, config, baseDevEnv, plugins = []) {
530
+ constructor(app, libs, rootPackageJson, config, baseDevEnv, plugins = [], libContributions = emptyLibContributions()) {
320
531
  this.app = app;
321
532
  this.rootPackageJson = rootPackageJson;
322
533
  this.libs = libs;
@@ -324,7 +535,7 @@ class AkanAppConfig {
324
535
  this.plugins = plugins;
325
536
  this.#applyRoutes(config?.routes);
326
537
  this.defaultDatabaseMode = config?.defaultDatabaseMode ?? "single";
327
- this.externalLibs = config?.externalLibs ?? [];
538
+ this.externalLibs = [...new Set([...config?.externalLibs ?? [], ...libContributions.externalLibs])];
328
539
  this.barrelImports = [
329
540
  ...DEFAULT_BARREL_IMPORTS,
330
541
  ...WORKSPACE_BARREL_FACETS.map((facet) => `@apps/${app.name}/${facet}`),
@@ -336,12 +547,33 @@ class AkanAppConfig {
336
547
  this.i18n = resolveAkanI18nConfig(config?.i18n);
337
548
  process.env.AKAN_PUBLIC_DEFAULT_LOCALE = this.i18n.defaultLocale;
338
549
  process.env.AKAN_PUBLIC_LOCALES = this.i18n.locales.join(",");
550
+ this.api = {
551
+ prefix: normalizeRoutePrefix(config?.api?.prefix) ?? "/api",
552
+ websocketPrefix: normalizeRoutePrefix(config?.api?.websocketPrefix) ?? "/ws"
553
+ };
554
+ process.env.AKAN_PUBLIC_API_PREFIX = this.api.prefix;
555
+ process.env.AKAN_PUBLIC_WS_PREFIX = this.api.websocketPrefix;
339
556
  this.publicEnv = config?.publicEnv ?? [];
340
557
  this.secrets = config?.secrets ?? [];
558
+ this.assets = {
559
+ pruneFonts: config?.assets?.pruneFonts ?? true,
560
+ keepFonts: [
561
+ ...normalizeKeepFonts(config?.assets?.keepFonts),
562
+ ...libContributions.keepFonts ?? []
563
+ ]
564
+ };
341
565
  this.syncPageLibs = config?.syncPageLibs ?? false;
342
566
  this.hasMobileConfig = Boolean(config.mobile);
343
567
  this.mobile = this.#resolveMobileConfig(config.mobile);
344
- this.docker = this.#makeDockerContent(config?.docker ?? {});
568
+ this.web = this.#resolveWebConfig(config.web);
569
+ this.docker = AkanAppConfig.#resolveDocker(config.docker, libContributions.docker);
570
+ this.dockerfile = this.#makeDockerfile();
571
+ }
572
+ #resolveWebConfig(web) {
573
+ const resolved = typeof web === "object" ? { ssr: true, csr: web.csr } : { ssr: web ?? true, csr: web ?? true };
574
+ if (!resolved.csr && this.hasMobileConfig)
575
+ throw new Error(`apps/${this.app.name}/akan.config.ts turns the CSR bundle off but declares mobile targets; the Capacitor build ships that bundle. Drop the mobile section or leave CSR on.`);
576
+ return resolved;
345
577
  }
346
578
  #resolveMobileConfig(mobile) {
347
579
  const {
@@ -451,23 +683,31 @@ class AkanAppConfig {
451
683
  return archs.map((arch) => `FROM ${image[arch] ?? defaultImage} AS ${arch}`).join(`
452
684
  `);
453
685
  }
454
- #makeDockerContent(docker) {
455
- if (docker.content)
456
- return {
457
- content: docker.content,
458
- image: {},
459
- preRuns: [],
460
- postRuns: [],
461
- command: []
462
- };
463
- const preRunScripts = this.#getDockerRunScripts(docker.preRuns ?? []);
464
- const postRunScripts = this.#getDockerRunScripts(docker.postRuns ?? []);
465
- const imageScript = docker.image ? this.#getDockerImageScript(docker.image, "oven/bun:1-slim") : "FROM oven/bun:1-slim";
466
- const command = docker.command ?? ["bun", "main.js"];
467
- const content = `${imageScript}
686
+ static #resolveDocker(docker, libDocker) {
687
+ if (typeof docker === "string")
688
+ return docker;
689
+ return {
690
+ image: docker?.image ?? DEFAULT_DOCKER_IMAGE,
691
+ preRuns: dedupeDockerRuns([...libDocker.preRuns, ...docker?.preRuns ?? []]),
692
+ postRuns: dedupeDockerRuns([...libDocker.postRuns, ...docker?.postRuns ?? []]),
693
+ command: docker?.command ?? ["bun", "main.js"]
694
+ };
695
+ }
696
+ #makeDockerfile() {
697
+ if (typeof this.docker === "string")
698
+ return this.docker;
699
+ const { image, preRuns, postRuns, command } = this.docker;
700
+ const preRunScripts = this.#getDockerRunScripts(preRuns);
701
+ const postRunScripts = this.#getDockerRunScripts(postRuns);
702
+ const imageScript = this.#getDockerImageScript(image, DEFAULT_DOCKER_IMAGE);
703
+ const webEnvLines = [
704
+ ...this.web.ssr ? [] : ["ENV AKAN_SSR=false"],
705
+ ...this.web.csr ? [] : ["ENV AKAN_CSR=false"]
706
+ ].join(`
707
+ `);
708
+ return `${imageScript}
709
+ RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends ca-certificates tzdata && rm -rf /var/lib/apt/lists/*
468
710
  RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
469
- RUN apt-get update && apt-get upgrade -y
470
- RUN apt-get install -y --no-install-recommends git redis build-essential python3 ca-certificates fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils udev ffmpeg
471
711
  ARG TARGETARCH
472
712
  ${preRunScripts.join(`
473
713
  `)}
@@ -487,16 +727,12 @@ ENV AKAN_PUBLIC_ENV=${this.baseDevEnv.env}
487
727
  ${this.basePaths.size ? `ENV AKAN_PUBLIC_BASE_PATHS=${[...this.basePaths].join(",")}` : ""}
488
728
  ENV AKAN_PUBLIC_DEFAULT_LOCALE=${this.i18n.defaultLocale}
489
729
  ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
730
+ ENV AKAN_PUBLIC_API_PREFIX=${this.api.prefix}
731
+ ENV AKAN_PUBLIC_WS_PREFIX=${this.api.websocketPrefix}
490
732
  ENV AKAN_PUBLIC_OPERATION_MODE=cloud
491
-
733
+ ENV AKAN_LOG_TO_FILE=0
734
+ ${webEnvLines}
492
735
  CMD [${command.map((c) => `"${c}"`).join(",")}]`;
493
- return {
494
- content,
495
- image: imageScript,
496
- preRuns: docker.preRuns ?? [],
497
- postRuns: docker.postRuns ?? [],
498
- command
499
- };
500
736
  }
501
737
  static #importGeneration = 0;
502
738
  static async importConfigModule(cwdPath, { bustImportCache = false } = {}) {
@@ -507,13 +743,28 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
507
743
  static async from(app, { bustImportCache = false } = {}) {
508
744
  const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
509
745
  AkanAppConfig.importConfigModule(app.cwdPath, { bustImportCache }),
510
- WorkspaceExecutor.getBaseDevEnv(path.join(app.workspace.workspaceRoot, ".env")),
746
+ WorkspaceExecutor.getBaseDevEnv(path2.join(app.workspace.workspaceRoot, ".env")),
511
747
  app.workspace.getLibs(),
512
748
  app.workspace.getPackageJson()
513
749
  ]);
514
750
  const resolved = typeof configImp === "function" ? configImp(app) : configImp;
515
751
  const { plugins, ...config } = resolved ?? {};
516
- return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? []);
752
+ const libContributions = await AkanAppConfig.#collectLibContributions(app, libs, bustImportCache);
753
+ return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? [], libContributions);
754
+ }
755
+ static async#collectLibContributions(app, libs, bustImportCache) {
756
+ const libConfigs = await Promise.all(libs.map(async (libName) => LibExecutor.from(app, libName).getConfig({ refresh: bustImportCache }).catch((error) => {
757
+ app.logger.warn(`Skipped libs/${libName}/akan.config.ts contributions: ${String(error)}`);
758
+ return null;
759
+ })));
760
+ return {
761
+ externalLibs: libConfigs.flatMap((libConfig) => libConfig?.externalLibs ?? []),
762
+ docker: {
763
+ preRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.preRuns ?? []),
764
+ postRuns: libConfigs.flatMap((libConfig) => libConfig?.docker.postRuns ?? [])
765
+ },
766
+ keepFonts: libConfigs.flatMap((libConfig) => (libConfig?.assets.keepFonts ?? []).map((glob) => `libs/${libConfig?.lib.name}/${glob}`))
767
+ };
517
768
  }
518
769
  #resolveProductionDependencyVersion(lib) {
519
770
  const rootVersion = this.rootPackageJson.dependencies?.[lib] ?? this.rootPackageJson.devDependencies?.[lib];
@@ -523,13 +774,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
523
774
  return akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
524
775
  }
525
776
  #getProductionRuntimePackages() {
526
- return [
527
- ...this.externalLibs,
528
- ...SSR_RUNTIME_PACKAGES,
529
- ...NATIVE_RUNTIME_PACKAGES,
530
- ...DEFAULT_BACKEND_RUNTIME_PACKAGES,
531
- ...this.getDatabaseModeRuntimePackages()
532
- ];
777
+ return [...this.externalLibs, ...SSR_RUNTIME_PACKAGES, ...this.getDatabaseModeRuntimePackages()];
533
778
  }
534
779
  getDatabaseModeRuntimePackages(databaseMode = this.defaultDatabaseMode) {
535
780
  return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
@@ -578,11 +823,11 @@ var akanPackageJson = null;
578
823
  function getAkanPackageJson() {
579
824
  if (akanPackageJson)
580
825
  return akanPackageJson;
581
- const sourceDir = path.dirname(fileURLToPath(import.meta.url));
826
+ const sourceDir = path2.dirname(fileURLToPath(import.meta.url));
582
827
  const packageJsonPaths = [
583
- path.join(sourceDir, "../../../akanjs/package.json"),
584
- path.join(process.cwd(), "pkgs/akanjs/package.json"),
585
- path.join(path.dirname(Bun.main), "node_modules/akanjs/package.json")
828
+ path2.join(sourceDir, "../../../akanjs/package.json"),
829
+ path2.join(process.cwd(), "pkgs/akanjs/package.json"),
830
+ path2.join(path2.dirname(Bun.main), "node_modules/akanjs/package.json")
586
831
  ];
587
832
  try {
588
833
  packageJsonPaths.unshift(Bun.resolveSync("akanjs/package.json", sourceDir));
@@ -617,14 +862,18 @@ function mergeImageConfig(config = {}) {
617
862
  class AkanLibConfig {
618
863
  lib;
619
864
  externalLibs;
865
+ docker;
866
+ assets;
620
867
  plugins;
621
868
  constructor(lib, config, plugins = []) {
622
869
  this.lib = lib;
623
870
  this.externalLibs = config?.externalLibs ?? [];
871
+ this.docker = { preRuns: config?.docker?.preRuns ?? [], postRuns: config?.docker?.postRuns ?? [] };
872
+ this.assets = { keepFonts: normalizeKeepFonts(config?.assets?.keepFonts) };
624
873
  this.plugins = plugins;
625
874
  }
626
- static async from(lib) {
627
- const [configImp] = await Promise.all([import(`${lib.cwdPath}/akan.config.ts`).then((mod) => mod.default)]);
875
+ static async from(lib, { bustImportCache = false } = {}) {
876
+ const configImp = await AkanAppConfig.importConfigModule(lib.cwdPath, { bustImportCache });
628
877
  const resolved = typeof configImp === "function" ? configImp(lib) : configImp;
629
878
  const { plugins, ...config } = resolved ?? {};
630
879
  return new AkanLibConfig(lib, config, plugins ?? []);
@@ -633,23 +882,422 @@ class AkanLibConfig {
633
882
  //! need to refactor
634
883
  var increaseBuildNum = async (app) => {
635
884
  const appConfig = await AkanAppConfig.from(app);
636
- const akanConfigPath = path.join(app.cwdPath, "akan.config.ts");
885
+ const akanConfigPath = path2.join(app.cwdPath, "akan.config.ts");
637
886
  const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
638
887
  const akanConfigContent = akanConfig.replace(`buildNum: ${appConfig.mobile.buildNum}`, `buildNum: ${appConfig.mobile.buildNum + 1}`);
639
888
  fs.writeFileSync(akanConfigPath, akanConfigContent);
640
889
  };
641
890
  var decreaseBuildNum = async (app) => {
642
891
  const appConfig = await AkanAppConfig.from(app);
643
- const akanConfigPath = path.join(app.cwdPath, "akan.config.ts");
892
+ const akanConfigPath = path2.join(app.cwdPath, "akan.config.ts");
644
893
  const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
645
894
  const akanConfigContent = akanConfig.replace(`buildNum: ${appConfig.mobile.buildNum}`, `buildNum: ${appConfig.mobile.buildNum - 1}`);
646
895
  fs.writeFileSync(akanConfigPath, akanConfigContent);
647
896
  };
897
+ // pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
898
+ import { mkdir as mkdir2 } from "fs/promises";
899
+ import path4 from "path";
900
+
901
+ // pkgs/@akanjs/devkit/transforms/asyncDefaultExportDetector.ts
902
+ import fs2 from "fs";
903
+ import path3 from "path";
904
+
905
+ class AsyncDefaultExportDetector {
906
+ static #typescriptLoad;
907
+ static #loadTypescript() {
908
+ AsyncDefaultExportDetector.#typescriptLoad ??= import("typescript").then((mod) => mod.default ?? mod);
909
+ return AsyncDefaultExportDetector.#typescriptLoad;
910
+ }
911
+ static async detect(moduleAbsPath) {
912
+ try {
913
+ const typescript = await AsyncDefaultExportDetector.#loadTypescript();
914
+ const source = fs2.readFileSync(path3.resolve(moduleAbsPath), "utf8");
915
+ const sourceFile = typescript.createSourceFile(moduleAbsPath, source, typescript.ScriptTarget.Latest, true, AsyncDefaultExportDetector.#scriptKind(typescript, moduleAbsPath));
916
+ return new AsyncDefaultExportDetector(typescript).detectInSourceFile(sourceFile);
917
+ } catch {
918
+ return false;
919
+ }
920
+ }
921
+ #ts;
922
+ constructor(typescript) {
923
+ this.#ts = typescript;
924
+ }
925
+ detectInSourceFile(sourceFile) {
926
+ const ts = this.#ts;
927
+ const asyncBindings = new Map;
928
+ let defaultIdentifier = null;
929
+ for (const statement of sourceFile.statements) {
930
+ if (ts.isFunctionDeclaration(statement)) {
931
+ if (this.#hasModifier(statement, ts.SyntaxKind.DefaultKeyword)) {
932
+ return this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword);
933
+ }
934
+ if (statement.name) {
935
+ asyncBindings.set(statement.name.text, this.#hasModifier(statement, ts.SyntaxKind.AsyncKeyword));
936
+ }
937
+ continue;
938
+ }
939
+ if (ts.isVariableStatement(statement)) {
940
+ for (const declaration of statement.declarationList.declarations) {
941
+ if (!ts.isIdentifier(declaration.name))
942
+ continue;
943
+ asyncBindings.set(declaration.name.text, this.#isAsyncFunctionExpression(declaration.initializer));
944
+ }
945
+ continue;
946
+ }
947
+ if (ts.isExportAssignment(statement)) {
948
+ if (this.#isAsyncFunctionExpression(statement.expression))
949
+ return true;
950
+ if (ts.isCallExpression(statement.expression))
951
+ return true;
952
+ if (ts.isIdentifier(statement.expression))
953
+ defaultIdentifier = statement.expression.text;
954
+ continue;
955
+ }
956
+ if (ts.isExportDeclaration(statement) && statement.exportClause && ts.isNamedExports(statement.exportClause)) {
957
+ const exportClause = statement.exportClause;
958
+ for (const specifier of exportClause.elements) {
959
+ if (specifier.name.text !== "default")
960
+ continue;
961
+ defaultIdentifier = specifier.propertyName?.text ?? specifier.name.text;
962
+ }
963
+ }
964
+ }
965
+ return defaultIdentifier ? asyncBindings.get(defaultIdentifier) === true : false;
966
+ }
967
+ #hasModifier(node, kind) {
968
+ const ts = this.#ts;
969
+ return ts.canHaveModifiers(node) && (ts.getModifiers(node)?.some((modifier) => modifier.kind === kind) ?? false);
970
+ }
971
+ #isAsyncFunctionExpression(node) {
972
+ const ts = this.#ts;
973
+ return Boolean(node && (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && this.#hasModifier(node, ts.SyntaxKind.AsyncKeyword));
974
+ }
975
+ static #scriptKind(typescript, moduleAbsPath) {
976
+ return moduleAbsPath.endsWith(".tsx") || moduleAbsPath.endsWith(".jsx") ? typescript.ScriptKind.TSX : typescript.ScriptKind.TS;
977
+ }
978
+ }
979
+
980
+ // pkgs/@akanjs/devkit/artifact/implicitRootLayout.ts
981
+ var LAYOUT_KEY_RE = /^\.\/(.+\/)?_layout\.(tsx|ts|jsx|js)$/;
982
+ async function appHasStModule(appCwdPath) {
983
+ return Bun.file(path4.join(appCwdPath, "lib", "st.ts")).exists();
984
+ }
985
+ var IMPLICIT_LAYOUT_DIR = path4.join(".akan", "generated", "root-layouts");
986
+ var IMPLICIT_DICT_DIR = path4.join(".akan", "generated", "dict");
987
+ var IMPLICIT_OVERRIDES_DIR = path4.join(".akan", "generated", "overrides");
988
+ var OVERRIDES_KEY_RE = /^\.\/(.+\/)?_overrides\.(tsx|ts|jsx|js)$/;
989
+ async function writeGeneratedOverridesLayoutFile(opts) {
990
+ const filename = `${opts.key.replace(/^\.\//, "").replace(/[^a-zA-Z0-9]+/g, "_")}.tsx`;
991
+ const absPath = path4.join(path4.resolve(opts.appCwdPath), IMPLICIT_OVERRIDES_DIR, filename);
992
+ const userRel = path4.relative(path4.dirname(absPath), opts.userAbsPath).split(path4.sep).join("/");
993
+ const userSpecifier = userRel.startsWith(".") ? userRel : `./${userRel}`;
994
+ const source = `"use client";
995
+ import { UiOverrideProvider } from "akanjs/ui";
996
+ import { createElement, type ReactNode } from "react";
997
+ import value from ${JSON.stringify(userSpecifier)};
998
+
999
+ export default function AkanUiOverridesLayout({ children }: { children?: ReactNode }) {
1000
+ return createElement(UiOverrideProvider, { value }, children);
1001
+ }
1002
+ `;
1003
+ await Bun.write(absPath, source);
1004
+ return absPath;
1005
+ }
1006
+ function getRootBoundarySegments(key) {
1007
+ const match = LAYOUT_KEY_RE.exec(key);
1008
+ if (!match)
1009
+ return null;
1010
+ const prefix = match[1]?.replace(/\/$/, "");
1011
+ if (!prefix)
1012
+ return [];
1013
+ return prefix.split("/").filter(Boolean);
1014
+ }
1015
+ function implicitRootLayoutKey(segments) {
1016
+ return `./${[...segments, "__root_layout"].join("/")}.tsx`;
1017
+ }
1018
+ function implicitRootLayoutAbsPath(appCwdPath, segments) {
1019
+ const filename = segments.length ? `${segments.join("__")}__root_layout.tsx` : "__root_layout.tsx";
1020
+ return path4.join(path4.resolve(appCwdPath), IMPLICIT_LAYOUT_DIR, filename);
1021
+ }
1022
+ function implicitDictionaryMacroAbsPath(appCwdPath) {
1023
+ return path4.join(path4.resolve(appCwdPath), IMPLICIT_DICT_DIR, "useDict.ts");
1024
+ }
1025
+ function isRootBoundarySegments(segments, basePaths) {
1026
+ const firstVisibleIndex = segments.findIndex((segment) => !/^\(.+\)$/.test(segment));
1027
+ if (firstVisibleIndex === -1)
1028
+ return segments.length <= 1;
1029
+ if (segments.slice(firstVisibleIndex + 1).some((segment) => /^\(.+\)$/.test(segment)))
1030
+ return false;
1031
+ const visible = segments.slice(firstVisibleIndex);
1032
+ const allowedBasePaths = new Set([...basePaths].map((basePath) => basePath.trim()).filter(Boolean));
1033
+ return visible.length === 1 && (firstVisibleIndex > 0 || allowedBasePaths.has(visible[0] ?? ""));
1034
+ }
1035
+ function findRootBoundaries(pageKeys, appCwdPath, basePaths) {
1036
+ const boundaries = new Map;
1037
+ for (const key of pageKeys) {
1038
+ const segments = getRootBoundarySegments(key);
1039
+ if (!segments)
1040
+ continue;
1041
+ if (!isRootBoundarySegments(segments, basePaths))
1042
+ continue;
1043
+ const id = segments.join("/");
1044
+ boundaries.set(id, {
1045
+ sourceKey: key,
1046
+ sourceAbsPath: path4.resolve(appCwdPath, "page", key.replace(/^\.\//, "")),
1047
+ segments
1048
+ });
1049
+ }
1050
+ const hasExplicitRootBoundary = [...boundaries.values()].some((boundary) => boundary.segments.length === 0);
1051
+ if (!hasExplicitRootBoundary && boundaries.size === 0) {
1052
+ boundaries.set("", { sourceKey: null, sourceAbsPath: null, segments: [] });
1053
+ }
1054
+ return [...boundaries.values()].sort((a, b) => a.segments.join("/").localeCompare(b.segments.join("/")));
1055
+ }
1056
+ function hasAncestorRootBoundary(boundary, boundaries) {
1057
+ return boundaries.some((candidate) => candidate !== boundary && candidate.segments.length < boundary.segments.length && candidate.segments.every((segment, index) => boundary.segments[index] === segment));
1058
+ }
1059
+ function findExplicitRootLayoutAbsPath(pageKeys, appCwdPath) {
1060
+ const rootLayoutKey = pageKeys.find((key) => {
1061
+ const segments = getRootBoundarySegments(key);
1062
+ return segments !== null && segments.length === 0;
1063
+ });
1064
+ return rootLayoutKey ? path4.resolve(appCwdPath, "page", rootLayoutKey.replace(/^\.\//, "")) : null;
1065
+ }
1066
+ function routePrefixForSegments(segments) {
1067
+ const visible = segments.filter((segment) => !/^\(.+\)$/.test(segment));
1068
+ return visible[0] ?? null;
1069
+ }
1070
+ async function assertEnvClientConvention(appCwdPath, appName) {
1071
+ const envPath = path4.join(appCwdPath, "env", "env.client.ts");
1072
+ if (!await Bun.file(envPath).exists()) {
1073
+ throw new Error(`[route-convention] app "${appName}" must provide env/env.client.ts exporting "env" for generated System.Provider`);
1074
+ }
1075
+ }
1076
+ async function writeGeneratedDictionaryMacroFile(appCwdPath, appName) {
1077
+ const absPath = implicitDictionaryMacroAbsPath(appCwdPath);
1078
+ await mkdir2(path4.dirname(absPath), { recursive: true });
1079
+ await Bun.write(absPath, `import { getAllDictionary } from "@apps/${appName}/lib/dict" with { type: "macro" };
1080
+
1081
+ export const allDictionary = getAllDictionary();
1082
+ `);
1083
+ return absPath;
1084
+ }
1085
+ async function writeGeneratedRootLayoutFile(opts) {
1086
+ await assertEnvClientConvention(opts.appCwdPath, opts.appName);
1087
+ const dictMacroAbsPath = opts.includeSystemProvider ? await writeGeneratedDictionaryMacroFile(opts.appCwdPath, opts.appName) : null;
1088
+ const absPath = implicitRootLayoutAbsPath(opts.appCwdPath, opts.boundary.segments);
1089
+ await mkdir2(path4.dirname(absPath), { recursive: true });
1090
+ const dictMacroRel = dictMacroAbsPath ? path4.relative(path4.dirname(absPath), dictMacroAbsPath).split(path4.sep).join("/") : null;
1091
+ const dictMacroSpecifier = dictMacroRel ? dictMacroRel.startsWith(".") ? dictMacroRel : `./${dictMacroRel}` : null;
1092
+ const sourceRel = opts.boundary.sourceAbsPath ? path4.relative(path4.dirname(absPath), opts.boundary.sourceAbsPath).split(path4.sep).join("/") : null;
1093
+ const sourceSpecifier = sourceRel ? sourceRel.startsWith(".") ? sourceRel : `./${sourceRel}` : null;
1094
+ const inheritedSourceAbsPath = opts.rootSourceAbsPath && opts.rootSourceAbsPath !== opts.boundary.sourceAbsPath ? opts.rootSourceAbsPath : null;
1095
+ const inheritedSourceRel = inheritedSourceAbsPath ? path4.relative(path4.dirname(absPath), inheritedSourceAbsPath).split(path4.sep).join("/") : null;
1096
+ const inheritedSourceSpecifier = inheritedSourceRel ? inheritedSourceRel.startsWith(".") ? inheritedSourceRel : `./${inheritedSourceRel}` : null;
1097
+ const clientImport = opts.includeStInit ? `import { st } from "@apps/${opts.appName}/client";
1098
+ void st;
1099
+ ` : `import "@apps/${opts.appName}/client";
1100
+ `;
1101
+ const inheritedLabel = inheritedSourceAbsPath ? path4.relative(opts.appCwdPath, inheritedSourceAbsPath) : "";
1102
+ const inheritedImport = inheritedSourceSpecifier ? `import * as inheritedModule from ${JSON.stringify(inheritedSourceSpecifier)};
1103
+ const inheritedLayout = resolveRouteModule(inheritedModule as never, ${JSON.stringify(inheritedLabel)}).module as LayoutModule;
1104
+ ` : `const inheritedLayout: LayoutModule = {};
1105
+ `;
1106
+ const prefix = routePrefixForSegments(opts.boundary.segments);
1107
+ const userLabel = opts.boundary.sourceAbsPath ? path4.relative(opts.appCwdPath, opts.boundary.sourceAbsPath) : "";
1108
+ const userImport = sourceSpecifier ? `import * as userModule from ${JSON.stringify(sourceSpecifier)};
1109
+ const userLayout = resolveRouteModule(userModule as never, ${JSON.stringify(userLabel)}).module as LayoutModule;
1110
+ const UserLayout = userLayout.default as (props: LayoutProps) => ReactNode | Promise<ReactNode>;
1111
+ ` : `const UserLayout = ({ children }: LayoutProps) => children;
1112
+ const userLayout: LayoutModule = {};
1113
+ `;
1114
+ const isAsyncUserLayout = opts.boundary.sourceAbsPath ? await AsyncDefaultExportDetector.detect(opts.boundary.sourceAbsPath) : false;
1115
+ const userLayoutElement = "<UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>";
1116
+ const layoutSignature = isAsyncUserLayout ? "export default async function GeneratedLayout" : "export default function GeneratedLayout";
1117
+ const layoutBinding = isAsyncUserLayout ? ` const layout =
1118
+ process.env.AKAN_PUBLIC_RENDER_ENV === "csr"
1119
+ ? await UserLayout({ params, searchParams, children })
1120
+ : ${userLayoutElement};
1121
+ ` : "";
1122
+ const layoutChild = isAsyncUserLayout ? "{layout}" : userLayoutElement;
1123
+ const layoutReturn = isAsyncUserLayout ? "layout" : userLayoutElement;
1124
+ const source = opts.includeSystemProvider ? `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
1125
+ import { loadFonts, resolveRouteModule } from "akanjs/client";
1126
+ import type { ReactNode } from "react";
1127
+ import { System } from "akanjs/ui";
1128
+ import { env } from "@apps/${opts.appName}/env/env.client";
1129
+ import { allDictionary } from ${JSON.stringify(dictMacroSpecifier)};
1130
+ ${clientImport}${inheritedImport}${userImport}
1131
+ const userFonts = userLayout.fonts ?? inheritedLayout.fonts ?? [];
1132
+ const defaultFonts = userFonts.filter((font) => font.default);
1133
+ if (defaultFonts.length > 1) throw new Error("[route-convention] only one default font is allowed per root layout");
1134
+ const defaultFont = defaultFonts[0];
1135
+ const defaultFontClassName = defaultFont ? (defaultFont.className ?? \`font-\${defaultFont.name}\`) : undefined;
1136
+
1137
+ export async function generateHead(props: PageProps) {
1138
+ if (userLayout.generateHead) return userLayout.generateHead(props);
1139
+ if (userLayout.head !== undefined) return userLayout.head;
1140
+ if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
1141
+ return inheritedLayout.head;
1142
+ }
1143
+
1144
+ export async function generateMetadata(props: PageProps) {
1145
+ if (userLayout.generateMetadata) return userLayout.generateMetadata(props);
1146
+ if (userLayout.metadata !== undefined) return userLayout.metadata;
1147
+ if (inheritedLayout.generateMetadata) return inheritedLayout.generateMetadata(props);
1148
+ return inheritedLayout.metadata;
1149
+ }
1150
+
1151
+ export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
1152
+ export const Error = userLayout.Error ?? inheritedLayout.Error;
1153
+ export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
1154
+
1155
+ ${layoutSignature}({ children, params, searchParams }: LayoutProps) {
1156
+ ${layoutBinding} return (
1157
+ <System.Provider
1158
+ of={GeneratedLayout as never}
1159
+ appName=${JSON.stringify(opts.appName)}
1160
+ ${prefix ? `prefix=${JSON.stringify(prefix)}
1161
+ ` : ""}params={params}
1162
+ manifest={userLayout.manifest ?? inheritedLayout.manifest}
1163
+ env={env}
1164
+ theme={userLayout.theme ?? inheritedLayout.theme}
1165
+ fonts={loadFonts(userFonts)}
1166
+ className={defaultFontClassName}
1167
+ gaTrackingId={userLayout.gaTrackingId ?? inheritedLayout.gaTrackingId}
1168
+ layoutStyle={userLayout.layoutStyle ?? inheritedLayout.layoutStyle}
1169
+ reconnect={userLayout.reconnect ?? inheritedLayout.reconnect ?? false}
1170
+ wsConnect={userLayout.wsConnect ?? inheritedLayout.wsConnect ?? true}
1171
+ allDictionary={process.env.AKAN_PUBLIC_RENDER_ENV === "ssr" ? allDictionary : undefined}
1172
+ >
1173
+ ${layoutChild}
1174
+ </System.Provider>
1175
+ );
1176
+ }
1177
+ ` : `import type { LayoutModule, LayoutProps, PageProps } from "akanjs/client";
1178
+ import { resolveRouteModule } from "akanjs/client";
1179
+ import type { ReactNode } from "react";
1180
+ ${inheritedImport}${userImport}
1181
+ export async function generateHead(props: PageProps) {
1182
+ if (userLayout.generateHead) return userLayout.generateHead(props);
1183
+ if (userLayout.head !== undefined) return userLayout.head;
1184
+ if (inheritedLayout.generateHead) return inheritedLayout.generateHead(props);
1185
+ return inheritedLayout.head;
1186
+ }
1187
+
1188
+ export async function generateMetadata(props: PageProps) {
1189
+ if (userLayout.generateMetadata) return userLayout.generateMetadata(props);
1190
+ if (userLayout.metadata !== undefined) return userLayout.metadata;
1191
+ if (inheritedLayout.generateMetadata) return inheritedLayout.generateMetadata(props);
1192
+ return inheritedLayout.metadata;
1193
+ }
1194
+
1195
+ export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
1196
+ export const Error = userLayout.Error ?? inheritedLayout.Error;
1197
+ export const pageConfig = userLayout.pageConfig ?? inheritedLayout.pageConfig;
1198
+
1199
+ ${layoutSignature}({ children, params, searchParams }: LayoutProps) {
1200
+ ${layoutBinding} return ${layoutReturn};
1201
+ }
1202
+ `;
1203
+ await Bun.write(absPath, source);
1204
+ return absPath;
1205
+ }
1206
+ async function resolveSsrPageEntries(opts) {
1207
+ const absPageDir = path4.resolve(opts.appCwdPath, "page");
1208
+ const hasSt = await appHasStModule(opts.appCwdPath);
1209
+ const basePaths = opts.basePaths ?? [];
1210
+ const rootSourceAbsPath = findExplicitRootLayoutAbsPath(opts.pageKeys, opts.appCwdPath);
1211
+ const rootBoundaries = findRootBoundaries(opts.pageKeys, opts.appCwdPath, basePaths);
1212
+ const rootLayoutKeys = new Set(opts.pageKeys.filter((key) => {
1213
+ const segments = getRootBoundarySegments(key);
1214
+ return segments !== null && isRootBoundarySegments(segments, basePaths);
1215
+ }));
1216
+ const base = await Promise.all(opts.pageKeys.filter((key) => !rootLayoutKeys.has(key)).map(async (key) => {
1217
+ const userAbsPath = path4.resolve(absPageDir, key);
1218
+ if (OVERRIDES_KEY_RE.test(key)) {
1219
+ const moduleAbsPath = await writeGeneratedOverridesLayoutFile({
1220
+ appCwdPath: opts.appCwdPath,
1221
+ key,
1222
+ userAbsPath
1223
+ });
1224
+ return { key, moduleAbsPath, seedAbsPaths: [userAbsPath] };
1225
+ }
1226
+ return { key, moduleAbsPath: userAbsPath };
1227
+ }));
1228
+ const generated = await Promise.all(rootBoundaries.map(async (boundary) => ({
1229
+ key: implicitRootLayoutKey(boundary.segments),
1230
+ moduleAbsPath: await writeGeneratedRootLayoutFile({
1231
+ appCwdPath: opts.appCwdPath,
1232
+ appName: opts.appName,
1233
+ boundary,
1234
+ rootSourceAbsPath,
1235
+ includeStInit: hasSt && boundary.segments.length === 0,
1236
+ includeSystemProvider: !hasAncestorRootBoundary(boundary, rootBoundaries)
1237
+ }),
1238
+ seedAbsPaths: [...new Set([boundary.sourceAbsPath, rootSourceAbsPath].filter((absPath) => absPath !== null))]
1239
+ })));
1240
+ const entries = [...base, ...generated];
1241
+ entries.sort((a, b) => a.key.localeCompare(b.key));
1242
+ return entries;
1243
+ }
1244
+ async function resolveSsrPageEntriesForApp(app, pageKeys) {
1245
+ const config = await app.getConfig();
1246
+ return resolveSsrPageEntries({ appCwdPath: app.cwdPath, appName: app.name, pageKeys, basePaths: config.basePaths });
1247
+ }
1248
+
648
1249
  // pkgs/@akanjs/devkit/linter.ts
649
1250
  import { spawn } from "child_process";
650
1251
  import { existsSync, readFileSync } from "fs";
651
- import path2 from "path";
1252
+ import path5 from "path";
652
1253
  import chalk from "chalk";
1254
+ var parseBiomeReport = (output) => {
1255
+ for (let start = output.indexOf("{");start !== -1; start = output.indexOf("{", start + 1)) {
1256
+ const end = objectEndAt(output, start);
1257
+ if (end === -1)
1258
+ break;
1259
+ const report = asBiomeReport(output.slice(start, end + 1));
1260
+ if (report)
1261
+ return report;
1262
+ }
1263
+ throw new Error(output.trim() || "No Biome JSON output");
1264
+ };
1265
+ var objectEndAt = (output, start) => {
1266
+ let depth = 0;
1267
+ let inString = false;
1268
+ let escaped = false;
1269
+ for (let idx = start;idx < output.length; idx += 1) {
1270
+ const char = output[idx];
1271
+ if (inString) {
1272
+ if (escaped)
1273
+ escaped = false;
1274
+ else if (char === "\\")
1275
+ escaped = true;
1276
+ else if (char === '"')
1277
+ inString = false;
1278
+ continue;
1279
+ }
1280
+ if (char === '"')
1281
+ inString = true;
1282
+ else if (char === "{")
1283
+ depth += 1;
1284
+ else if (char === "}" && --depth === 0)
1285
+ return idx;
1286
+ }
1287
+ return -1;
1288
+ };
1289
+ var asBiomeReport = (candidate) => {
1290
+ try {
1291
+ const parsed = JSON.parse(candidate);
1292
+ if (!parsed || typeof parsed !== "object")
1293
+ return null;
1294
+ if (!("diagnostics" in parsed) && !("summary" in parsed))
1295
+ return null;
1296
+ return parsed;
1297
+ } catch {
1298
+ return null;
1299
+ }
1300
+ };
653
1301
  var BIOME_CONFIG_FILES = ["biome.json", "biome.jsonc"];
654
1302
 
655
1303
  class Linter {
@@ -658,13 +1306,13 @@ class Linter {
658
1306
  #biomeBin;
659
1307
  constructor(cwdPath) {
660
1308
  this.lintRoot = this.#findBiomeRootPath(cwdPath);
661
- this.configPath = Linter.#configPathIn(this.lintRoot) ?? path2.join(this.lintRoot, "biome.json");
662
- const localBiomeBin = path2.join(this.lintRoot, "node_modules/.bin/biome");
1309
+ this.configPath = Linter.#configPathIn(this.lintRoot) ?? path5.join(this.lintRoot, "biome.json");
1310
+ const localBiomeBin = path5.join(this.lintRoot, "node_modules/.bin/biome");
663
1311
  this.#biomeBin = existsSync(localBiomeBin) ? localBiomeBin : "biome";
664
1312
  }
665
1313
  static #configPathIn(dir) {
666
1314
  for (const fileName of BIOME_CONFIG_FILES) {
667
- const configPath = path2.join(dir, fileName);
1315
+ const configPath = path5.join(dir, fileName);
668
1316
  if (existsSync(configPath))
669
1317
  return configPath;
670
1318
  }
@@ -673,19 +1321,19 @@ class Linter {
673
1321
  #findBiomeRootPath(dir) {
674
1322
  if (Linter.#configPathIn(dir))
675
1323
  return dir;
676
- const parentDir = path2.dirname(dir);
1324
+ const parentDir = path5.dirname(dir);
677
1325
  if (parentDir === dir)
678
1326
  throw new Error(`biome.json not found from ${dir}`);
679
1327
  return this.#findBiomeRootPath(parentDir);
680
1328
  }
681
1329
  #toBiomePath(filePath) {
682
- const relativePath = path2.relative(this.lintRoot, filePath);
683
- if (!relativePath.startsWith("..") && !path2.isAbsolute(relativePath))
1330
+ const relativePath = path5.relative(this.lintRoot, filePath);
1331
+ if (!relativePath.startsWith("..") && !path5.isAbsolute(relativePath))
684
1332
  return relativePath;
685
1333
  return filePath;
686
1334
  }
687
1335
  #resolveFilePath(filePath) {
688
- return path2.isAbsolute(filePath) ? filePath : path2.join(this.lintRoot, filePath);
1336
+ return path5.isAbsolute(filePath) ? filePath : path5.join(this.lintRoot, filePath);
689
1337
  }
690
1338
  async#runBiome(args, input) {
691
1339
  return await new Promise((resolve, reject) => {
@@ -706,18 +1354,11 @@ class Linter {
706
1354
  proc.stdin.end(input);
707
1355
  });
708
1356
  }
709
- #parseBiomeReport(output) {
710
- const jsonStart = output.indexOf("{");
711
- const jsonEnd = output.lastIndexOf("}");
712
- if (jsonStart === -1 || jsonEnd === -1 || jsonEnd < jsonStart)
713
- throw new Error(output.trim() || "No Biome JSON output");
714
- return JSON.parse(output.slice(jsonStart, jsonEnd + 1));
715
- }
716
1357
  #diagnosticFilePath(diagnostic, fallbackFilePath) {
717
1358
  const diagnosticPath = diagnostic.location?.path;
718
1359
  if (!diagnosticPath)
719
1360
  return fallbackFilePath;
720
- return path2.isAbsolute(diagnosticPath) ? diagnosticPath : path2.join(this.lintRoot, diagnosticPath);
1361
+ return path5.isAbsolute(diagnosticPath) ? diagnosticPath : path5.join(this.lintRoot, diagnosticPath);
721
1362
  }
722
1363
  #createLintMessage(diagnostic) {
723
1364
  const start = diagnostic.location?.start;
@@ -785,7 +1426,7 @@ class Linter {
785
1426
  this.configPath,
786
1427
  this.#toBiomePath(filePath)
787
1428
  ]);
788
- const report = this.#parseBiomeReport(stdout || stderr);
1429
+ const report = parseBiomeReport(stdout || stderr);
789
1430
  const results = this.#toLintResults(report, filePath);
790
1431
  const { errors, warnings } = this.#splitMessages(results);
791
1432
  const output = write && existsSync(filePath) ? readFileSync(filePath, "utf8") : undefined;
@@ -907,6 +1548,25 @@ ${errorText}, ${warningText} found`];
907
1548
  const lintResult = await this.lintFile(resolvedFilePath);
908
1549
  return { ...lintResult, fixed: stdout !== source, output: stdout };
909
1550
  }
1551
+ async fixFiles(filePaths) {
1552
+ const resolved = filePaths.map((filePath) => this.#resolveFilePath(filePath)).filter(existsSync);
1553
+ if (resolved.length === 0)
1554
+ return { fixed: [] };
1555
+ const before = new Map(resolved.map((filePath) => [filePath, readFileSync(filePath, "utf8")]));
1556
+ await this.#runBiome([
1557
+ "check",
1558
+ "--write",
1559
+ "--reporter=json",
1560
+ "--max-diagnostics=none",
1561
+ "--no-errors-on-unmatched",
1562
+ "--config-path",
1563
+ this.configPath,
1564
+ ...resolved.map((filePath) => this.#toBiomePath(filePath))
1565
+ ]);
1566
+ return {
1567
+ fixed: resolved.filter((filePath) => readFileSync(filePath, "utf8") !== before.get(filePath))
1568
+ };
1569
+ }
910
1570
  async getConfigForFile(filePath) {
911
1571
  const resolvedFilePath = this.#resolveFilePath(filePath);
912
1572
  if (!existsSync(resolvedFilePath))
@@ -928,7 +1588,7 @@ ${errorText}, ${warningText} found`];
928
1588
 
929
1589
  // pkgs/@akanjs/devkit/repoIdentity.ts
930
1590
  import { execFileSync } from "child_process";
931
- import path3 from "path";
1591
+ import path6 from "path";
932
1592
  var resolved = new Map;
933
1593
  var readRemoteName = (workspaceRoot) => {
934
1594
  try {
@@ -946,14 +1606,14 @@ var resolveRepoName = (workspaceRoot) => {
946
1606
  const cached = resolved.get(workspaceRoot);
947
1607
  if (cached)
948
1608
  return cached;
949
- const repoName = readRemoteName(workspaceRoot) ?? path3.basename(workspaceRoot);
1609
+ const repoName = readRemoteName(workspaceRoot) ?? path6.basename(workspaceRoot);
950
1610
  resolved.set(workspaceRoot, repoName);
951
1611
  return repoName;
952
1612
  };
953
1613
 
954
1614
  // pkgs/@akanjs/devkit/scanInfo.ts
955
- import { rm } from "fs/promises";
956
- import path4 from "path";
1615
+ import { rm as rm2 } from "fs/promises";
1616
+ import path7 from "path";
957
1617
 
958
1618
  // pkgs/@akanjs/devkit/workspaceLayout.ts
959
1619
  var appRootAllowedFiles = new Set([
@@ -987,6 +1647,34 @@ var appRootAllowedDirs = new Set([
987
1647
  "ui",
988
1648
  "webkit"
989
1649
  ]);
1650
+ var libRootAllowedFiles = new Set([
1651
+ "AGENTS.md",
1652
+ "CLAUDE.md",
1653
+ "README.md",
1654
+ "akan.config.ts",
1655
+ "akan.lib.json",
1656
+ "client.ts",
1657
+ "index.ts",
1658
+ "package.json",
1659
+ "server.ts",
1660
+ "tsconfig.json",
1661
+ "tsconfig.spec.json",
1662
+ "tsconfig.tsbuildinfo"
1663
+ ]);
1664
+ var libRootAllowedDirs = new Set([
1665
+ "common",
1666
+ "env",
1667
+ "lib",
1668
+ "page",
1669
+ "plugin",
1670
+ "private",
1671
+ "public",
1672
+ "srvkit",
1673
+ "ui",
1674
+ "webkit"
1675
+ ]);
1676
+ var rootAllowedFiles = { app: appRootAllowedFiles, lib: libRootAllowedFiles };
1677
+ var rootAllowedDirs = { app: appRootAllowedDirs, lib: libRootAllowedDirs };
990
1678
  var libFacetRootAllowedFiles = new Set([
991
1679
  "cnst.ts",
992
1680
  "db.ts",
@@ -998,7 +1686,9 @@ var libFacetRootAllowedFiles = new Set([
998
1686
  "useClient.ts",
999
1687
  "useServer.ts"
1000
1688
  ]);
1001
- var isScannedAppRootEntry = (name) => !name.startsWith(".") || appRootAllowedDirs.has(name);
1689
+ var libFacetRootTestPattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(ts|tsx)$/;
1690
+ var isAllowedLibFacetRootFile = (filename) => libFacetRootAllowedFiles.has(filename) || libFacetRootTestPattern.test(filename);
1691
+ var isScannedRootEntry = (type, name) => !name.startsWith(".") || rootAllowedDirs[type].has(name);
1002
1692
 
1003
1693
  // pkgs/@akanjs/devkit/scanInfo.ts
1004
1694
  var databaseFileTypes = [
@@ -1027,18 +1717,16 @@ var moduleUiFileTypes = {
1027
1717
  scalar: new Set(["Template", "Unit"])
1028
1718
  };
1029
1719
  var testFilePattern = /\.(test|spec)\.(ts|tsx)$/;
1030
- var rootSignalTestFilePattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(ts|tsx)$/;
1031
- var createDependencyScanner = async (exec) => (await import("./dependencyScanner-f6grrep3.js")).TypeScriptDependencyScanner.from(exec);
1720
+ var createDependencyScanner = async (exec) => (await import("./dependencyScanner-shs82ynp.js")).TypeScriptDependencyScanner.from(exec);
1032
1721
  var isAllowedTestFile = (filename) => testFilePattern.test(filename);
1033
- var isAllowedLibRootFile = (filename) => libFacetRootAllowedFiles.has(filename) || rootSignalTestFilePattern.test(filename);
1034
- var getScanPath = (exec, relativePath) => path4.posix.join(`${exec.type}s`, exec.name, relativePath.split(path4.sep).join("/"));
1722
+ var getScanPath = (exec, relativePath) => path7.posix.join(`${exec.type}s`, exec.name, relativePath.split(path7.sep).join("/"));
1035
1723
  async function clearGeneratedRootCapacitorConfigs(exec) {
1036
1724
  if (exec.type !== "app")
1037
1725
  return;
1038
- await Promise.all(generatedRootCapacitorConfigFiles.map((filename) => rm(exec.getPath(filename), { force: true })));
1726
+ await Promise.all(generatedRootCapacitorConfigFiles.map((filename) => rm2(exec.getPath(filename), { force: true })));
1039
1727
  }
1040
1728
  var getModuleNameFromPath = (kind, modulePath) => {
1041
- const dirname = path4.basename(modulePath);
1729
+ const dirname = path7.basename(modulePath);
1042
1730
  return kind === "service" ? dirname.replace(/^_+/, "") : dirname;
1043
1731
  };
1044
1732
  async function assertScanConvention(exec, libRoot) {
@@ -1047,28 +1735,31 @@ async function assertScanConvention(exec, libRoot) {
1047
1735
  const addViolation = (relativePath, reason) => {
1048
1736
  violations.push(`${getScanPath(exec, relativePath)}: ${reason}`);
1049
1737
  };
1050
- if (exec.type === "app") {
1051
- const { files, dirs } = await exec.getFilesAndDirs(".");
1052
- files.filter((filename) => !appRootAllowedFiles.has(filename)).forEach((filename) => {
1053
- addViolation(filename, "unsupported app root file");
1054
- });
1055
- dirs.filter((dirname) => !appRootAllowedDirs.has(dirname)).forEach((dirname) => {
1056
- addViolation(dirname, "unsupported app root folder");
1057
- });
1058
- }
1059
- libRoot.files.filter((filename) => !isAllowedLibRootFile(filename)).forEach((filename) => {
1060
- addViolation(path4.join("lib", filename), "unsupported lib root file");
1738
+ const allowedRootFiles = rootAllowedFiles[exec.type];
1739
+ const allowedRootDirs = rootAllowedDirs[exec.type];
1740
+ const { files, dirs } = await exec.getFilesAndDirs(".");
1741
+ files.filter((filename) => !allowedRootFiles.has(filename)).forEach((filename) => {
1742
+ addViolation(filename, `unsupported ${exec.type} root file`);
1743
+ });
1744
+ dirs.filter((dirname) => !allowedRootDirs.has(dirname)).forEach((dirname) => {
1745
+ addViolation(dirname, `unsupported ${exec.type} root folder`);
1746
+ });
1747
+ if (exec.type === "lib")
1748
+ for (const { relativePath, reason } of await exec.getPageConventionViolations())
1749
+ addViolation(relativePath, reason);
1750
+ libRoot.files.filter((filename) => !isAllowedLibFacetRootFile(filename)).forEach((filename) => {
1751
+ addViolation(path7.join("lib", filename), "unsupported lib facet root file");
1061
1752
  });
1062
1753
  libRoot.dirs.filter((dirname) => dirname.startsWith("__") && !internalLibDirs.has(dirname)).forEach((dirname) => {
1063
- addViolation(path4.join("lib", dirname), "unsupported internal lib folder");
1754
+ addViolation(path7.join("lib", dirname), "unsupported internal lib folder");
1064
1755
  });
1065
1756
  const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
1066
1757
  const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
1067
1758
  const scalarDirs = await exec.readdir("lib/__scalar");
1068
1759
  await Promise.all([
1069
- ...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path4.join("lib", dirname))),
1070
- ...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path4.join("lib", dirname))),
1071
- ...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path4.join("lib/__scalar", dirname)))
1760
+ ...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path7.join("lib", dirname))),
1761
+ ...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path7.join("lib", dirname))),
1762
+ ...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path7.join("lib/__scalar", dirname)))
1072
1763
  ]);
1073
1764
  if (violations.length > 0) {
1074
1765
  throw new Error(`[scan-convention]
@@ -1080,11 +1771,11 @@ async function validateModuleFiles(exec, violations, kind, modulePath) {
1080
1771
  const { files, dirs } = await exec.getFilesAndDirs(modulePath);
1081
1772
  const moduleName = getModuleNameFromPath(kind, modulePath);
1082
1773
  dirs.forEach((dirname) => {
1083
- violations.push(`${getScanPath(exec, path4.join(modulePath, dirname))}: unsupported module folder`);
1774
+ violations.push(`${getScanPath(exec, path7.join(modulePath, dirname))}: unsupported module folder`);
1084
1775
  });
1085
1776
  const uiModuleName = moduleName[0].toUpperCase() + moduleName.slice(1);
1086
1777
  files.forEach((filename) => {
1087
- const filePath = path4.join(modulePath, filename);
1778
+ const filePath = path7.join(modulePath, filename);
1088
1779
  if (filename === "index.ts" || filename === "index.tsx" || isAllowedTestFile(filename))
1089
1780
  return;
1090
1781
  if (filename === `${moduleName}.abstract.md`)
@@ -1128,7 +1819,7 @@ class ScanInfo {
1128
1819
  { all: new Set, databases: new Set, services: new Set, scalars: new Set }
1129
1820
  ]));
1130
1821
  static async getScanResult(exec) {
1131
- const [akanConfig2, scanner, pkgs, libs] = await Promise.all([
1822
+ const [akanConfig, scanner, pkgs, libs] = await Promise.all([
1132
1823
  exec.getConfig(),
1133
1824
  createDependencyScanner(exec),
1134
1825
  exec.workspace.getPkgs(),
@@ -1164,7 +1855,7 @@ class ScanInfo {
1164
1855
  });
1165
1856
  await Promise.all([
1166
1857
  ...databaseDirs.map(async (name) => {
1167
- const filenames = await exec.readdir(path4.join("lib", name));
1858
+ const filenames = await exec.readdir(path7.join("lib", name));
1168
1859
  filenames.forEach((filename) => {
1169
1860
  if (filename.endsWith(".constant.ts"))
1170
1861
  files.constant.databases.push(name);
@@ -1192,7 +1883,7 @@ class ScanInfo {
1192
1883
  }),
1193
1884
  ...serviceDirs.map(async (dirname) => {
1194
1885
  const name = dirname.slice(1);
1195
- const filenames = await exec.readdir(path4.join("lib", dirname));
1886
+ const filenames = await exec.readdir(path7.join("lib", dirname));
1196
1887
  filenames.forEach((filename) => {
1197
1888
  if (filename.endsWith(".dictionary.ts"))
1198
1889
  files.dictionary.services.push(name);
@@ -1215,7 +1906,7 @@ class ScanInfo {
1215
1906
  });
1216
1907
  }),
1217
1908
  ...scalarDirs.map(async (name) => {
1218
- const filenames = await exec.readdir(path4.join("lib/__scalar", name));
1909
+ const filenames = await exec.readdir(path7.join("lib/__scalar", name));
1219
1910
  filenames.forEach((filename) => {
1220
1911
  if (filename.endsWith(".constant.ts"))
1221
1912
  files.constant.scalars.push(name);
@@ -1237,12 +1928,11 @@ class ScanInfo {
1237
1928
  })
1238
1929
  ]);
1239
1930
  const routes = exec.type === "lib" ? [] : await exec.getPageKeys();
1240
- const scanResult = {
1931
+ const common = {
1241
1932
  name: exec.name,
1242
1933
  type: exec.type,
1243
1934
  repoName: exec.workspace.repoName,
1244
- serveDomain: WorkspaceExecutor.getBaseDevEnv(path4.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
1245
- akanConfig: akanConfig2,
1935
+ serveDomain: WorkspaceExecutor.getBaseDevEnv(path7.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
1246
1936
  files,
1247
1937
  libDeps,
1248
1938
  pkgDeps,
@@ -1250,6 +1940,7 @@ class ScanInfo {
1250
1940
  devDependencies: npmDevDeps.filter((dep) => !isAkanFrameworkDependency(dep)),
1251
1941
  routes
1252
1942
  };
1943
+ const scanResult = akanConfig instanceof AkanAppConfig ? { ...common, akanConfig } : { ...common, akanConfig };
1253
1944
  return scanResult;
1254
1945
  }
1255
1946
  constructor(scanResult) {
@@ -1443,16 +2134,16 @@ class LibInfo extends ScanInfo {
1443
2134
  class PkgInfo {
1444
2135
  exec;
1445
2136
  name;
1446
- scanResult;
2137
+ #scanResult;
1447
2138
  static async scanExecutor(exec) {
1448
2139
  const [tsconfig, rootPackageJson] = await Promise.all([exec.getTsConfig(), exec.workspace.getPackageJson()]);
1449
2140
  const scanner = await createDependencyScanner(exec);
1450
2141
  const npmSet = new Set(Object.keys({ ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies }));
1451
- const pkgPathSet = new Set(Object.keys(tsconfig.compilerOptions.paths ?? {}).filter((path5) => tsconfig.compilerOptions.paths?.[path5]?.some((resolve) => resolve.startsWith("pkgs/"))).map((path5) => path5.replace("/*", "")));
2142
+ const pkgPathSet = new Set(Object.keys(tsconfig.compilerOptions.paths ?? {}).filter((path) => tsconfig.compilerOptions.paths?.[path]?.some((resolve) => resolve.startsWith("pkgs/"))).map((path) => path.replace("/*", "")));
1452
2143
  const [npmDepSet, pkgPathDepSet] = await scanner.getImportSets([npmSet, pkgPathSet]);
1453
- const pkgDeps = [...pkgPathDepSet].map((path5) => {
1454
- const pathSplitLength = path5.split("/").length;
1455
- return (tsconfig.compilerOptions.paths?.[path5]?.[0] ?? "*").split("/").slice(1, 1 + pathSplitLength).join("/");
2144
+ const pkgDeps = [...pkgPathDepSet].map((path) => {
2145
+ const pathSplitLength = path.split("/").length;
2146
+ return (tsconfig.compilerOptions.paths?.[path]?.[0] ?? "*").split("/").slice(1, 1 + pathSplitLength).join("/");
1456
2147
  }).filter((pkg) => pkg !== exec.name);
1457
2148
  const pkgScanResult = {
1458
2149
  name: exec.name,
@@ -1474,10 +2165,10 @@ class PkgInfo {
1474
2165
  constructor(exec, scanResult) {
1475
2166
  this.exec = exec;
1476
2167
  this.name = exec.name;
1477
- this.scanResult = scanResult;
2168
+ this.#scanResult = scanResult;
1478
2169
  }
1479
2170
  getScanResult() {
1480
- return this.scanResult;
2171
+ return this.#scanResult;
1481
2172
  }
1482
2173
  }
1483
2174
 
@@ -1638,16 +2329,16 @@ class Executor {
1638
2329
  linter = null;
1639
2330
  constructor(name, cwdPath) {
1640
2331
  this.name = name;
1641
- this.logger = new Logger(name);
2332
+ this.logger = new Logger2(name);
1642
2333
  this.logs = [];
1643
2334
  this.cwdPath = cwdPath;
1644
2335
  }
1645
2336
  #stdout(data) {
1646
2337
  if (Executor.verbose)
1647
- Logger.raw(chalk2.dim(data.toString()));
2338
+ Logger2.raw(chalk2.dim(data.toString()));
1648
2339
  }
1649
2340
  #stderr(data) {
1650
- Logger.raw(chalk2.red(data.toString()));
2341
+ Logger2.raw(chalk2.red(data.toString()));
1651
2342
  }
1652
2343
  exec(command, options = {}) {
1653
2344
  const cwd = options.cwd?.toString() ?? this.cwdPath;
@@ -1675,7 +2366,7 @@ class Executor {
1675
2366
  }));
1676
2367
  });
1677
2368
  proc.on("exit", (code, signal) => {
1678
- if (!!code || signal)
2369
+ if (code || signal)
1679
2370
  reject(new CommandExecutionError({
1680
2371
  command,
1681
2372
  cwd,
@@ -1773,7 +2464,7 @@ class Executor {
1773
2464
  }));
1774
2465
  });
1775
2466
  proc.on("exit", (code, signal) => {
1776
- if (!!code || signal)
2467
+ if (code || signal)
1777
2468
  reject(new CommandExecutionError({
1778
2469
  command: modulePath,
1779
2470
  args,
@@ -1789,10 +2480,10 @@ class Executor {
1789
2480
  });
1790
2481
  }
1791
2482
  getPath(filePath) {
1792
- if (path5.isAbsolute(filePath))
2483
+ if (path8.isAbsolute(filePath))
1793
2484
  return filePath;
1794
2485
  if (filePath.startsWith("."))
1795
- return path5.join(this.cwdPath, filePath);
2486
+ return path8.join(this.cwdPath, filePath);
1796
2487
  const baseParts = this.cwdPath.split(/[\\/]/).filter(Boolean);
1797
2488
  const targetParts = filePath.split(/[\\/]/).filter(Boolean);
1798
2489
  let overlapLength = 0;
@@ -1806,12 +2497,12 @@ class Executor {
1806
2497
  if (isOverlap)
1807
2498
  overlapLength = i;
1808
2499
  }
1809
- return path5.join(this.cwdPath, ...targetParts.slice(overlapLength));
2500
+ return path8.join(this.cwdPath, ...targetParts.slice(overlapLength));
1810
2501
  }
1811
2502
  async mkdir(dirPath) {
1812
2503
  const writePath = this.getPath(dirPath);
1813
2504
  if (!await FileSys.dirExists(writePath))
1814
- await mkdir(writePath, { recursive: true });
2505
+ await mkdir3(writePath, { recursive: true });
1815
2506
  this.logger.verbose(`Make directory ${writePath}`);
1816
2507
  return this;
1817
2508
  }
@@ -1849,18 +2540,19 @@ class Executor {
1849
2540
  return this;
1850
2541
  }
1851
2542
  async removeDir(dirPath) {
1852
- const readPath = path5.join(this.getPath(dirPath), ".");
2543
+ const readPath = path8.join(this.getPath(dirPath), ".");
1853
2544
  if (await FileSys.entryExists(readPath))
1854
- await rm2(readPath, { recursive: true, force: true });
2545
+ await rm3(readPath, { recursive: true, force: true });
1855
2546
  this.logger.verbose(`Remove directory ${readPath}`);
1856
2547
  return this;
1857
2548
  }
1858
2549
  async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
1859
2550
  const writePath = this.getPath(filePath);
1860
- const dir = path5.dirname(writePath);
2551
+ const dir = path8.dirname(writePath);
1861
2552
  if (!await FileSys.dirExists(dir))
1862
- await mkdir(dir, { recursive: true });
1863
- let contentStr = typeof content === "string" ? content : JSON.stringify(content, null, 2);
2553
+ await mkdir3(dir, { recursive: true });
2554
+ let contentStr = typeof content === "string" ? content : `${JSON.stringify(content, null, 2)}
2555
+ `;
1864
2556
  if (await FileSys.fileExists(writePath)) {
1865
2557
  const currentContent = await FileSys.readText(writePath);
1866
2558
  if (currentContent === contentStr || !overwrite) {
@@ -1868,7 +2560,7 @@ class Executor {
1868
2560
  contentStr = currentContent;
1869
2561
  } else {
1870
2562
  await FileSys.writeText(writePath, contentStr);
1871
- if (Logger.isVerbose())
2563
+ if (Logger2.isVerbose())
1872
2564
  this.logger.rawLog(chalk2.yellow(`File Update: ${filePath}`));
1873
2565
  }
1874
2566
  } else {
@@ -1882,7 +2574,7 @@ class Executor {
1882
2574
  await this.writeFile(filePath, content);
1883
2575
  }
1884
2576
  async getLocalFile(targetPath) {
1885
- const filePath = path5.isAbsolute(targetPath) ? targetPath : targetPath.replace(this.cwdPath, "");
2577
+ const filePath = path8.isAbsolute(targetPath) ? targetPath : targetPath.replace(this.cwdPath, "");
1886
2578
  const content = await this.readFile(filePath);
1887
2579
  return { filePath, content };
1888
2580
  }
@@ -1899,9 +2591,9 @@ class Executor {
1899
2591
  const dest = this.getPath(destPath);
1900
2592
  if (!await FileSys.exists(src))
1901
2593
  return;
1902
- const isDirectory = (await stat(src)).isDirectory();
2594
+ const isDirectory = (await stat2(src)).isDirectory();
1903
2595
  if (!await FileSys.exists(dest) && isDirectory)
1904
- await mkdir(dest, { recursive: true });
2596
+ await mkdir3(dest, { recursive: true });
1905
2597
  if (dereference)
1906
2598
  await cpEntry(src, dest, { recursive: isDirectory, dereference: true, force: true });
1907
2599
  else
@@ -1979,26 +2671,26 @@ class Executor {
1979
2671
  const result = await getContent.default(scanInfo ?? null, dict, options);
1980
2672
  if (result === null)
1981
2673
  return null;
1982
- const filename = typeof result === "object" ? result.filename : path5.basename(targetPath).replace(".js", ".ts");
2674
+ const filename = typeof result === "object" ? result.filename : path8.basename(targetPath).replace(".js", ".ts");
1983
2675
  const content = typeof result === "object" ? result.content : result;
1984
- const dirname = path5.dirname(targetPath);
1985
- const convertedTargetPath = Object.entries(dict).reduce((path6, [key, value]) => path6.replace(new RegExp(`__${key}__`, "g"), value), `${dirname}/${filename}`);
2676
+ const dirname = path8.dirname(targetPath);
2677
+ const convertedTargetPath = Object.entries(dict).reduce((path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value), `${dirname}/${filename}`);
1986
2678
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
1987
2679
  return this.writeFile(convertedTargetPath, content, { overwrite });
1988
2680
  } else if (targetPath.endsWith(".template")) {
1989
2681
  const content = await FileSys.readText(templatePath);
1990
- const convertedTargetPath = Object.entries(dict).reduce((path6, [key, value]) => path6.replace(new RegExp(`__${key}__`, "g"), value), targetPath.slice(0, -9));
2682
+ const convertedTargetPath = Object.entries(dict).reduce((path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value), targetPath.slice(0, -9));
1991
2683
  const convertedContent = Object.entries(dict).reduce((data, [key, value]) => data.replace(new RegExp(`<%= ${key} %>`, "g"), value), content);
1992
2684
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
1993
2685
  return this.writeFile(convertedTargetPath, convertedContent, {
1994
2686
  overwrite
1995
2687
  });
1996
- } else if (staticTemplateFileExtensions.has(path5.extname(targetPath).toLowerCase())) {
1997
- const convertedTargetPath = Object.entries(dict).reduce((path6, [key, value]) => path6.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
2688
+ } else if (staticTemplateFileExtensions.has(path8.extname(targetPath).toLowerCase())) {
2689
+ const convertedTargetPath = Object.entries(dict).reduce((path, [key, value]) => path.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
1998
2690
  const writePath = this.getPath(convertedTargetPath);
1999
- const dirname = path5.dirname(writePath);
2691
+ const dirname = path8.dirname(writePath);
2000
2692
  if (!await FileSys.dirExists(dirname))
2001
- await mkdir(dirname, { recursive: true });
2693
+ await mkdir3(dirname, { recursive: true });
2002
2694
  await copyFile(templatePath, writePath);
2003
2695
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
2004
2696
  return { filePath: writePath, content: "" };
@@ -2016,11 +2708,11 @@ class Executor {
2016
2708
  const templateRoot = await this.#resolveTemplateRoot();
2017
2709
  const templatePath = `${templateRoot}${template ? `/${template}` : ""}`;
2018
2710
  const prefixTemplatePath = templatePath;
2019
- if ((await stat(prefixTemplatePath)).isFile()) {
2020
- const filename = path5.basename(prefixTemplatePath);
2711
+ if ((await stat2(prefixTemplatePath)).isFile()) {
2712
+ const filename = path8.basename(prefixTemplatePath);
2021
2713
  const fileContent = await this.#applyTemplateFile({
2022
2714
  templatePath: prefixTemplatePath,
2023
- targetPath: path5.join(basePath, filename),
2715
+ targetPath: path8.join(basePath, filename),
2024
2716
  scanInfo,
2025
2717
  overwrite
2026
2718
  }, dict, options);
@@ -2028,19 +2720,19 @@ class Executor {
2028
2720
  } else {
2029
2721
  const subdirs = await readDirEntries(templatePath);
2030
2722
  const fileContents = (await Promise.all(subdirs.map(async (subdir) => {
2031
- const subpath = path5.join(templatePath, subdir);
2032
- if ((await stat(subpath)).isFile()) {
2723
+ const subpath = path8.join(templatePath, subdir);
2724
+ if ((await stat2(subpath)).isFile()) {
2033
2725
  const fileContent = await this.#applyTemplateFile({
2034
2726
  templatePath: subpath,
2035
- targetPath: path5.join(basePath, subdir),
2727
+ targetPath: path8.join(basePath, subdir),
2036
2728
  scanInfo,
2037
2729
  overwrite
2038
2730
  }, dict, options);
2039
2731
  return fileContent ? [fileContent] : [];
2040
2732
  } else
2041
2733
  return await this._applyTemplate({
2042
- basePath: path5.join(basePath, subdir),
2043
- template: path5.join(template, subdir),
2734
+ basePath: path8.join(basePath, subdir),
2735
+ template: path8.join(template, subdir),
2044
2736
  scanInfo,
2045
2737
  dict,
2046
2738
  overwrite,
@@ -2052,8 +2744,8 @@ class Executor {
2052
2744
  }
2053
2745
  async#resolveTemplateRoot() {
2054
2746
  const candidates = [
2055
- path5.resolve(getDirname(import.meta.url), "templates"),
2056
- path5.resolve(getDirname(import.meta.url), "../cli/templates")
2747
+ path8.resolve(getDirname2(import.meta.url), "templates"),
2748
+ path8.resolve(getDirname2(import.meta.url), "../cli/templates")
2057
2749
  ];
2058
2750
  for (const candidate of candidates) {
2059
2751
  if (await FileSys.dirExists(candidate))
@@ -2066,29 +2758,43 @@ class Executor {
2066
2758
  ...options.dict ?? {},
2067
2759
  ...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
2068
2760
  };
2069
- return this._applyTemplate({ ...options, dict });
2761
+ const fileContents = await this._applyTemplate({ ...options, dict });
2762
+ await this.#formatAppliedTemplate(fileContents);
2763
+ return fileContents;
2764
+ }
2765
+ async#formatAppliedTemplate(fileContents) {
2766
+ const filePaths = fileContents.map((fileContent) => fileContent.filePath).filter((filePath) => filePath.endsWith(".ts") || filePath.endsWith(".tsx"));
2767
+ if (filePaths.length === 0)
2768
+ return;
2769
+ try {
2770
+ const { fixed } = await this.getLinter().fixFiles(filePaths);
2771
+ if (fixed.length > 0)
2772
+ this.logger.verbose(`Formatted ${fixed.length} scaffolded file(s)`);
2773
+ } catch (err) {
2774
+ this.logger.verbose(`Skipped formatting scaffolded files: ${err instanceof Error ? err.message : String(err)}`);
2775
+ }
2070
2776
  }
2071
2777
  async getTypeChecker() {
2072
- const { TypeChecker } = await import("./typeChecker-kravn7ns.js");
2778
+ const { TypeChecker } = await import("./typeChecker-3xgrzc2v.js");
2073
2779
  this.typeChecker ??= new TypeChecker(this);
2074
2780
  return this.typeChecker;
2075
2781
  }
2076
2782
  async typeCheck(filePath) {
2077
- const path6 = this.getPath(filePath);
2783
+ const path = this.getPath(filePath);
2078
2784
  const typeChecker = await this.getTypeChecker();
2079
- const { fileDiagnostics, fileErrors, fileWarnings } = typeChecker.check(path6);
2785
+ const { fileDiagnostics, fileErrors, fileWarnings } = typeChecker.check(path);
2080
2786
  const message = typeChecker.formatDiagnostics(fileDiagnostics);
2081
2787
  return { fileDiagnostics, fileErrors, fileWarnings, message };
2082
2788
  }
2083
2789
  async typeCheckAsync(filePath) {
2084
- const path6 = this.getPath(filePath);
2790
+ const path = this.getPath(filePath);
2085
2791
  const entry = await this.#resolveTypecheckWorkerEntry();
2086
2792
  const proc = Bun.spawn([process.execPath, entry], {
2087
2793
  cwd: this.cwdPath,
2088
2794
  env: {
2089
2795
  ...process.env,
2090
2796
  AKAN_TYPECHECK_CWD: this.cwdPath,
2091
- AKAN_TYPECHECK_FILE: path6
2797
+ AKAN_TYPECHECK_FILE: path
2092
2798
  },
2093
2799
  stdout: "pipe",
2094
2800
  stderr: "pipe"
@@ -2109,13 +2815,13 @@ class Executor {
2109
2815
  };
2110
2816
  }
2111
2817
  async#resolveTypecheckWorkerEntry() {
2112
- const dirname = getDirname(import.meta.url);
2818
+ const dirname = getDirname2(import.meta.url);
2113
2819
  const candidates = [
2114
- path5.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
2115
- path5.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
2116
- path5.join(dirname, "typecheck/typecheck.proc.ts"),
2117
- path5.join(dirname, "typecheck.proc.js"),
2118
- path5.join(dirname, "typecheck.proc.ts")
2820
+ path8.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
2821
+ path8.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
2822
+ path8.join(dirname, "typecheck/typecheck.proc.ts"),
2823
+ path8.join(dirname, "typecheck.proc.js"),
2824
+ path8.join(dirname, "typecheck.proc.ts")
2119
2825
  ];
2120
2826
  for (const candidate of candidates)
2121
2827
  if (await Bun.file(candidate).exists())
@@ -2127,9 +2833,9 @@ class Executor {
2127
2833
  return this.linter;
2128
2834
  }
2129
2835
  async lint(filePath, { fix = false, dryRun = false } = {}) {
2130
- const path6 = this.getPath(filePath);
2836
+ const path = this.getPath(filePath);
2131
2837
  const linter = this.getLinter();
2132
- const { results, errors, warnings } = await linter.lint(path6, {
2838
+ const { results, errors, warnings } = await linter.lint(path, {
2133
2839
  fix,
2134
2840
  dryRun
2135
2841
  });
@@ -2242,8 +2948,8 @@ class WorkspaceExecutor extends Executor {
2242
2948
  await Promise.all(dirs.map(async (dir) => {
2243
2949
  if (dir.includes("_") || AVOID_DIRS.includes(dir))
2244
2950
  return;
2245
- const dirPath = path5.join(dirname, dir);
2246
- if ((await stat(dirPath)).isDirectory()) {
2951
+ const dirPath = path8.join(dirname, dir);
2952
+ if ((await stat2(dirPath)).isDirectory()) {
2247
2953
  results.push(`${prefix}${dir}`);
2248
2954
  if (maxDepth > 0)
2249
2955
  await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
@@ -2255,10 +2961,20 @@ class WorkspaceExecutor extends Executor {
2255
2961
  }
2256
2962
  async commit(message, { init = false, add = true } = {}) {
2257
2963
  if (init)
2258
- await this.exec(`git init --quiet`);
2964
+ await this.spawn("git", ["init", "--quiet"]);
2259
2965
  if (add)
2260
- await this.exec(`git add .`);
2261
- await this.exec(`git commit --quiet -m "${message}"`);
2966
+ await this.spawn("git", ["add", "."]);
2967
+ await this.spawn("git", ["commit", "--quiet", "-m", message]);
2968
+ }
2969
+ async hasChanges() {
2970
+ return !!(await this.spawn("git", ["status", "--porcelain"])).trim();
2971
+ }
2972
+ async listGitFiles(paths, { untracked = false } = {}) {
2973
+ if (!paths.length)
2974
+ return [];
2975
+ const mode = untracked ? ["--cached", "--others", "--exclude-standard"] : ["--cached"];
2976
+ const stdout = await this.spawn("git", ["ls-files", "-z", ...mode, "--", ...paths]);
2977
+ return stdout.split("\x00").filter((file) => !!file).sort();
2262
2978
  }
2263
2979
  async#getDirHasFile(basePath, targetFilename) {
2264
2980
  const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
@@ -2267,9 +2983,9 @@ class WorkspaceExecutor extends Executor {
2267
2983
  await Promise.all(dirs.map(async (dir) => {
2268
2984
  if (AVOID_DIRS.includes(dir))
2269
2985
  return;
2270
- const dirPath = path5.join(dirname, dir);
2986
+ const dirPath = path8.join(dirname, dir);
2271
2987
  if (await FileSys.dirExists(dirPath)) {
2272
- const hasTargetFile = await FileSys.fileExists(path5.join(dirPath, targetFilename));
2988
+ const hasTargetFile = await FileSys.fileExists(path8.join(dirPath, targetFilename));
2273
2989
  if (hasTargetFile)
2274
2990
  results.push(`${prefix}${dir}`);
2275
2991
  if (maxDepth > 0)
@@ -2335,8 +3051,20 @@ class SysExecutor extends Executor {
2335
3051
  return this.#akanConfig;
2336
3052
  }
2337
3053
  async getModules() {
2338
- const path6 = this.type === "app" ? `apps/${this.name}/lib` : `libs/${this.name}/lib`;
2339
- return await this.workspace.getDirInModule(path6, this.name);
3054
+ const path = this.type === "app" ? `apps/${this.name}/lib` : `libs/${this.name}/lib`;
3055
+ return await this.workspace.getDirInModule(path, this.name);
3056
+ }
3057
+ async getPageConventionViolations() {
3058
+ if (!await this.exists("page"))
3059
+ return [];
3060
+ const violations = [];
3061
+ for (const entry of await this.getAllFiles("**/*", { cwd: this.getPath("page") })) {
3062
+ const relativePath = entry.split(path8.sep).join("/");
3063
+ const reason = getPageSourceFileViolation(relativePath);
3064
+ if (reason)
3065
+ violations.push({ relativePath: `page/${relativePath}`, reason });
3066
+ }
3067
+ return violations;
2340
3068
  }
2341
3069
  #scanInfo = null;
2342
3070
  hasScanInfo() {
@@ -2394,18 +3122,19 @@ class SysExecutor extends Executor {
2394
3122
  }) : await LibInfo.fromExecutor(this, {
2395
3123
  refresh
2396
3124
  });
2397
- if (write) {
2398
- await Promise.all(this.#getScanTemplateTasks(scanInfo));
2399
- await this.writeJson(`akan.${this.type}.json`, scanInfo.getScanResult());
2400
- if (this.type === "lib")
2401
- this.#updateDependencies(scanInfo);
2402
- if (writeLib) {
2403
- const libInfos = [...scanInfo.getLibInfos().values()];
2404
- await this.#updateDependencies(scanInfo);
2405
- await Promise.all(libInfos.flatMap((libInfo) => libInfo.exec.#getScanTemplateTasks(libInfo)));
2406
- }
2407
- await this.syncAgentsIndex(scanInfo);
2408
- }
3125
+ if (write)
3126
+ await CodegenLock.run(this.workspace.workspaceRoot, `scan:${this.name}`, async () => {
3127
+ await Promise.all(this.#getScanTemplateTasks(scanInfo));
3128
+ await this.writeJson(`akan.${this.type}.json`, scanInfo.getScanResult());
3129
+ if (this.type === "lib")
3130
+ this.#updateDependencies(scanInfo);
3131
+ if (writeLib) {
3132
+ const libInfos = [...scanInfo.getLibInfos().values()];
3133
+ await this.#updateDependencies(scanInfo);
3134
+ await Promise.all(libInfos.flatMap((libInfo) => libInfo.exec.#getScanTemplateTasks(libInfo)));
3135
+ }
3136
+ await this.syncAgentsIndex(scanInfo);
3137
+ });
2409
3138
  this.#scanInfo = scanInfo;
2410
3139
  return scanInfo;
2411
3140
  }
@@ -2440,7 +3169,7 @@ class SysExecutor extends Executor {
2440
3169
  await this.setPackageJson(libPkgJsonWithDeps);
2441
3170
  }
2442
3171
  async getLocalFile(targetPath) {
2443
- const filePath = path5.isAbsolute(targetPath) ? targetPath : `${this.type}s/${this.name}/${targetPath}`;
3172
+ const filePath = path8.isAbsolute(targetPath) ? targetPath : `${this.type}s/${this.name}/${targetPath}`;
2444
3173
  const content = await this.workspace.readFile(filePath);
2445
3174
  return { filePath, content };
2446
3175
  }
@@ -2526,7 +3255,7 @@ class SysExecutor extends Executor {
2526
3255
  class AppExecutor extends SysExecutor {
2527
3256
  static #routeSourceValidator = null;
2528
3257
  static async#getRouteSourceValidator() {
2529
- AppExecutor.#routeSourceValidator ??= (await import("./routeSourceValidator-e4jmbfcx.js")).RouteSourceValidator;
3258
+ AppExecutor.#routeSourceValidator ??= (await import("./routeSourceValidator-apyqw7wk.js")).RouteSourceValidator;
2530
3259
  return AppExecutor.#routeSourceValidator;
2531
3260
  }
2532
3261
  dist;
@@ -2537,9 +3266,9 @@ class AppExecutor extends SysExecutor {
2537
3266
  }
2538
3267
  static #execs = new Map;
2539
3268
  static from(executor, name) {
2540
- const exec2 = AppExecutor.#execs.get(name);
2541
- if (exec2)
2542
- return exec2;
3269
+ const exec = AppExecutor.#execs.get(name);
3270
+ if (exec)
3271
+ return exec;
2543
3272
  else if (executor instanceof WorkspaceExecutor)
2544
3273
  return new AppExecutor({ workspace: executor, name });
2545
3274
  else
@@ -2575,33 +3304,43 @@ class AppExecutor extends SysExecutor {
2575
3304
  };
2576
3305
  }
2577
3306
  async prepareCommand(type) {
2578
- const akanConfig2 = await this.getConfig();
2579
- const databaseMode = process.env.AKAN_DATABASE_MODE ?? akanConfig2.defaultDatabaseMode ?? "single";
3307
+ const akanConfig = await this.getConfig();
3308
+ const databaseMode = process.env.AKAN_DATABASE_MODE ?? akanConfig.defaultDatabaseMode ?? "single";
2580
3309
  const routeEnv = {
2581
- AKAN_PUBLIC_BASE_PATHS: [...akanConfig2.basePaths].join(","),
3310
+ AKAN_PUBLIC_BASE_PATHS: [...akanConfig.basePaths].join(","),
3311
+ AKAN_PUBLIC_API_PREFIX: akanConfig.api.prefix,
3312
+ AKAN_PUBLIC_WS_PREFIX: akanConfig.api.websocketPrefix,
2582
3313
  AKAN_DATABASE_MODE: databaseMode
2583
3314
  };
2584
3315
  Object.assign(process.env, routeEnv);
2585
3316
  if (type === "build") {
2586
3317
  this.#excludeDevOnlyPages = true;
2587
3318
  this.#pageKeys = null;
2588
- if (await this.exists(this.dist.cwdPath))
2589
- await this.dist.exec(`rm -rf ${this.dist.cwdPath}`);
3319
+ await this.dist.removeDir(this.dist.cwdPath);
2590
3320
  await Promise.all([this.dist.mkdir("private"), this.dist.mkdir("public")]);
2591
3321
  await Promise.all([
2592
3322
  this.cp("private", `${this.dist.cwdPath}/private`, { dereference: true }),
2593
- this.cp("public", `${this.dist.cwdPath}/public`, { dereference: true })
3323
+ ...akanConfig.web.ssr ? [this.cp("public", `${this.dist.cwdPath}/public`, { dereference: true })] : []
2594
3324
  ]);
2595
- } else
3325
+ } else {
2596
3326
  await this.removeDir(".akan");
3327
+ if (!akanConfig.web.ssr || !akanConfig.web.csr)
3328
+ this.logger.verbose(`akan.config.ts disables web.${!akanConfig.web.ssr ? "ssr" : "csr"}; \`akan start\` still serves it, \`akan build\` will not`);
3329
+ }
3330
+ if (type === "start")
3331
+ process.env.NODE_ENV = "development";
2597
3332
  const devPort = type === "start" ? (await this.getDevPort()).toString() : undefined;
2598
3333
  const env = this.getCommandEnv({
2599
3334
  AKAN_COMMAND_TYPE: type,
2600
3335
  ...routeEnv,
2601
3336
  ...devPort ? { PORT: devPort, AKAN_PUBLIC_CLIENT_PORT: devPort, AKAN_PUBLIC_SERVER_PORT: devPort } : {}
2602
3337
  });
2603
- if (type === "build")
2604
- Object.assign(process.env, env);
3338
+ if (type === "build") {
3339
+ const buildEnv = { ...env };
3340
+ delete buildEnv.AKAN_PUBLIC_CLIENT_PORT;
3341
+ delete buildEnv.AKAN_PUBLIC_SERVER_PORT;
3342
+ Object.assign(process.env, buildEnv);
3343
+ }
2605
3344
  return { env };
2606
3345
  }
2607
3346
  #publicEnv = null;
@@ -2641,7 +3380,7 @@ class AppExecutor extends SysExecutor {
2641
3380
  async getPageKeys({ refresh } = {}) {
2642
3381
  if (this.#pageKeys && !refresh)
2643
3382
  return this.#pageKeys;
2644
- const akanConfig2 = await this.getConfig();
3383
+ const akanConfig = await this.getConfig();
2645
3384
  const glob = new Bun.Glob("**/*");
2646
3385
  const pageKeys = [];
2647
3386
  const owners = new Map;
@@ -2655,16 +3394,16 @@ class AppExecutor extends SysExecutor {
2655
3394
  absolute: false,
2656
3395
  onlyFiles: true
2657
3396
  })) {
2658
- const segments = rel.split(path5.sep);
3397
+ const segments = rel.split(path8.sep);
2659
3398
  if (segments.some((s) => s === "node_modules"))
2660
3399
  continue;
2661
3400
  const posix = `${root.keyPrefix}${segments.join("/")}`;
2662
- const absPath = path5.join(root.dir, ...segments);
3401
+ const absPath = path8.join(root.dir, ...segments);
2663
3402
  validatePageSourceFile(posix, { filePath: absPath });
2664
3403
  if (!isRouteSourceFile(posix))
2665
3404
  continue;
2666
3405
  const key = `./${posix}`;
2667
- validateSubRoutePageKey(key, akanConfig2.basePaths, {
3406
+ validateSubRoutePageKey(key, akanConfig.basePaths, {
2668
3407
  appName: this.name,
2669
3408
  filePath: absPath
2670
3409
  });
@@ -2682,14 +3421,16 @@ class AppExecutor extends SysExecutor {
2682
3421
  }
2683
3422
  if (!owner)
2684
3423
  owners.set(routeId, { absPath, fromLib });
2685
- const isRootLayout = parsed.kind === "layout" && parsed.moduleSegments.at(-1) === "_layout";
3424
+ const layoutSegments = getRootBoundarySegments(key);
3425
+ const isRootLayout = layoutSegments !== null && isRootBoundarySegments(layoutSegments, akanConfig.basePaths);
2686
3426
  const routeSource = await Bun.file(absPath).text();
2687
3427
  const validator = await AppExecutor.#getRouteSourceValidator();
2688
3428
  if (parsed.kind === "overrides")
2689
3429
  validator.validateOverridesSourceExports(routeSource, absPath);
2690
3430
  else {
2691
3431
  const info = validator.validateRouteSourceExports(routeSource, absPath, parsed.kind, {
2692
- rootLayout: isRootLayout
3432
+ rootLayout: isRootLayout,
3433
+ pattern: parsed.pattern
2693
3434
  });
2694
3435
  if (info.devOnly) {
2695
3436
  devOnlyKeys.add(key);
@@ -2713,10 +3454,10 @@ class AppExecutor extends SysExecutor {
2713
3454
  return pageKeys.filter((key) => !isDevOnly(key));
2714
3455
  }
2715
3456
  async getPageRoots() {
2716
- const akanConfig2 = await this.getConfig();
3457
+ const akanConfig = await this.getConfig();
2717
3458
  const pageDir = `${this.cwdPath}/page`;
2718
3459
  const roots = [{ dir: pageDir, realDir: pageDir, keyPrefix: "" }];
2719
- for (const parent of AppExecutor.#pageLibParents(akanConfig2.basePaths)) {
3460
+ for (const parent of AppExecutor.#pageLibParents(akanConfig.basePaths)) {
2720
3461
  const libsDir = `${pageDir}/${parent}${AppExecutor.#pageLibsDir}`;
2721
3462
  const entries = await readDirEntries(libsDir).catch(() => []);
2722
3463
  for (const entry of entries.sort()) {
@@ -2737,9 +3478,9 @@ class AppExecutor extends SysExecutor {
2737
3478
  return parents.length ? parents : [""];
2738
3479
  }
2739
3480
  async syncPages(libDeps) {
2740
- const akanConfig2 = await this.getConfig();
2741
- const parents = AppExecutor.#pageLibParents(akanConfig2.basePaths);
2742
- const libs = await this.#resolvePageLibs(akanConfig2.syncPageLibs, libDeps);
3481
+ const akanConfig = await this.getConfig();
3482
+ const parents = AppExecutor.#pageLibParents(akanConfig.basePaths);
3483
+ const libs = await this.#resolvePageLibs(akanConfig.syncPageLibs, libDeps);
2743
3484
  const linked = (await Promise.all(parents.map(async (parent) => {
2744
3485
  const libsDir = `${this.cwdPath}/page/${parent}${AppExecutor.#pageLibsDir}`;
2745
3486
  const entries = await readDirEntries(libsDir).catch(() => []);
@@ -2797,12 +3538,12 @@ class AppExecutor extends SysExecutor {
2797
3538
  static async#linkLibAsset(targetPath, linkPath) {
2798
3539
  const isWindows = process.platform === "win32";
2799
3540
  try {
2800
- const target = isWindows ? targetPath : path5.relative(path5.dirname(linkPath), targetPath);
3541
+ const target = isWindows ? targetPath : path8.relative(path8.dirname(linkPath), targetPath);
2801
3542
  await symlink(target, linkPath, isWindows ? "junction" : "dir");
2802
3543
  } catch (error) {
2803
3544
  if (!isWindows)
2804
3545
  throw error;
2805
- await mkdir(linkPath, { recursive: true });
3546
+ await mkdir3(linkPath, { recursive: true });
2806
3547
  await cpEntry(targetPath, linkPath, { recursive: true, dereference: true, force: true });
2807
3548
  }
2808
3549
  }
@@ -2839,7 +3580,7 @@ class AppExecutor extends SysExecutor {
2839
3580
  await this.writeFile(rel, content, opts);
2840
3581
  },
2841
3582
  readEnvClient: async () => {
2842
- const envClientPath = path5.join(this.cwdPath, "env", "env.client.ts");
3583
+ const envClientPath = path8.join(this.cwdPath, "env", "env.client.ts");
2843
3584
  if (!await FileSys.fileExists(envClientPath))
2844
3585
  return null;
2845
3586
  try {
@@ -2898,9 +3639,9 @@ class LibExecutor extends SysExecutor {
2898
3639
  }
2899
3640
  static #execs = new Map;
2900
3641
  static from(executor, name) {
2901
- const exec2 = LibExecutor.#execs.get(name);
2902
- if (exec2)
2903
- return exec2;
3642
+ const exec = LibExecutor.#execs.get(name);
3643
+ if (exec)
3644
+ return exec;
2904
3645
  else if (executor instanceof WorkspaceExecutor)
2905
3646
  return new LibExecutor({ workspace: executor, name });
2906
3647
  else
@@ -2910,7 +3651,7 @@ class LibExecutor extends SysExecutor {
2910
3651
  async getConfig({ refresh } = {}) {
2911
3652
  if (this.#akanConfig && !refresh)
2912
3653
  return this.#akanConfig;
2913
- this.#akanConfig = await AkanLibConfig.from(this);
3654
+ this.#akanConfig = await AkanLibConfig.from(this, { bustImportCache: refresh });
2914
3655
  return this.#akanConfig;
2915
3656
  }
2916
3657
  }
@@ -2949,7 +3690,7 @@ class PkgExecutor extends Executor {
2949
3690
  return rootVersion;
2950
3691
  try {
2951
3692
  const packageJsonPath = `pkgs/${dep}/package.json`;
2952
- if (!await Bun.file(path5.join(this.workspace.workspaceRoot, packageJsonPath)).exists())
3693
+ if (!await Bun.file(path8.join(this.workspace.workspaceRoot, packageJsonPath)).exists())
2953
3694
  return;
2954
3695
  const packageJson = await this.workspace.readJson(packageJsonPath);
2955
3696
  if (packageJson.name === dep)
@@ -3007,7 +3748,7 @@ class PkgExecutor extends Executor {
3007
3748
  default: "./index.ts"
3008
3749
  }
3009
3750
  },
3010
- engines: { bun: ">=1.3.13" },
3751
+ engines: { bun: ">=1.4.0" },
3011
3752
  ...dependencyMaps
3012
3753
  };
3013
3754
  await Promise.all([this.dist.writeJson("package.json", distPkgJson), this.writeJson("package.json", distPkgJson)]);
@@ -3050,4 +3791,4 @@ class ModuleExecutor extends Executor {
3050
3791
  }
3051
3792
  }
3052
3793
 
3053
- export { AGENT_BLOCK_START, AGENT_BLOCK_END, stampBlockVersion, extractBlockVersion, readDevkitVersion, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, appRootAllowedFiles, appRootAllowedDirs, libFacetRootAllowedFiles, isScannedAppRootEntry, CommandExecutionError, Executor, WorkspaceExecutor, SysExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };
3794
+ export { AGENT_BLOCK_START, AGENT_BLOCK_END, stampBlockVersion, extractBlockVersion, readDevkitVersion, upsertAgentBlock, extractAgentBlock, renderRecipeEntries, collectScopeRecipeSources, renderScopeAgentBlock, CLAUDE_COMMENT_RULE, AsyncDefaultExportDetector, resolveSsrPageEntriesForApp, CodegenLock, rootAllowedFiles, rootAllowedDirs, isAllowedLibFacetRootFile, isScannedRootEntry, AppInfo, Spinner, CommandExecutionError, Executor, WorkspaceExecutor, SysExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };