@akanjs/cli 2.1.1-rc.2 → 2.1.2-rc.0

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.
@@ -205,11 +205,13 @@ class HttpClient {
205
205
  class CloudApi {
206
206
  #api;
207
207
  #accessToken = null;
208
- static async fromHost(host) {
208
+ #workspace;
209
+ static async fromHost(workspace, host) {
209
210
  const hostConfig = await GlobalConfig.getHostConfig(host);
210
- return new CloudApi(hostConfig);
211
+ return new CloudApi(workspace, hostConfig);
211
212
  }
212
- constructor(hostConfig) {
213
+ constructor(workspace, hostConfig) {
214
+ this.#workspace = workspace;
213
215
  const host = akanCloudHost;
214
216
  this.#api = new HttpClient(`${host}/api`);
215
217
  this.#accessToken = hostConfig.auth?.accessToken ?? null;
@@ -225,16 +227,17 @@ class CloudApi {
225
227
  const data = await this.#api.post(`/uploadEnv/${devProjectId}`, formData);
226
228
  return data;
227
229
  }
228
- async downloadEnv(devProjectId, localPath) {
230
+ async downloadEnv(devProjectId) {
231
+ const localPath = `${this.#workspace.workspaceRoot}/local/env.tar`;
229
232
  await this.#api.getFile(`/downloadEnv/${devProjectId}`, localPath);
230
233
  }
231
234
  async getRemoteAuthToken(remoteId) {
232
235
  try {
233
236
  if (this.#accessToken) {
234
237
  if (GlobalConfig.needRefreshToken(this.#accessToken))
235
- return await this.refreshAuthToken();
238
+ return await this.#refreshAuthToken();
236
239
  else
237
- return await this.refreshAuthToken();
240
+ return await this.#refreshAuthToken();
238
241
  }
239
242
  const accessToken = await this.#api.get(`/getRemoteAuthToken/${remoteId}`);
240
243
  this.#accessToken = GlobalConfig.toAccessToken(accessToken);
@@ -246,10 +249,14 @@ class CloudApi {
246
249
  return null;
247
250
  }
248
251
  }
249
- async refreshAuthToken() {
250
- const response = await this.#api.post(`/refreshRemoteAuthToken`, {
251
- refreshToken: this.#accessToken?.refreshToken
252
- });
252
+ async#refreshAuthToken() {
253
+ const refreshToken = this.#accessToken?.refreshToken;
254
+ if (!refreshToken)
255
+ throw new Error("No refresh token");
256
+ return await this.refreshAuthToken(refreshToken);
257
+ }
258
+ async refreshAuthToken(refreshToken) {
259
+ const response = await this.#api.post(`/refreshRemoteAuthToken`, { refreshToken });
253
260
  this.#accessToken = GlobalConfig.toAccessToken(response);
254
261
  this.#api.setHeaders({ Authorization: `Bearer ${this.#accessToken.jwt}` });
255
262
  return this.#accessToken;
@@ -329,6 +336,31 @@ var supportedLlmModels = [
329
336
  "deepseek-chat",
330
337
  "deepseek-reasoner"
331
338
  ];
339
+ var parseTypescriptFileBlocks = (text) => {
340
+ const fileBlocks = [];
341
+ const codeBlockRegex = /```(?:typescript|ts|tsx)\s*\n([\s\S]*?)```/gi;
342
+ const filePathRegex = /^\s*\/\/\s*File:\s*(.+?)\s*$/im;
343
+ for (const codeBlock of text.matchAll(codeBlockRegex)) {
344
+ const content = codeBlock[1]?.trim();
345
+ if (!content)
346
+ continue;
347
+ const filePath = filePathRegex.exec(content)?.[1]?.trim();
348
+ if (!filePath)
349
+ continue;
350
+ fileBlocks.push({
351
+ filePath,
352
+ content: content.replace(filePathRegex, "").trim()
353
+ });
354
+ }
355
+ return fileBlocks;
356
+ };
357
+ var preserveTypescriptResponseContent = (previousContent, nextContent) => {
358
+ const previousWrites = parseTypescriptFileBlocks(previousContent);
359
+ const nextWrites = parseTypescriptFileBlocks(nextContent);
360
+ if (previousWrites.length > 0 && nextWrites.length === 0)
361
+ return previousContent;
362
+ return nextContent;
363
+ };
332
364
 
333
365
  class AiSession {
334
366
  static #cacheDir = "node_modules/.cache/akan/aiSession";
@@ -448,7 +480,7 @@ class AiSession {
448
480
  const humanMessage = new HumanMessage(question);
449
481
  this.messageHistory.push(humanMessage);
450
482
  const stream = await AiSession.#chat.stream(this.messageHistory);
451
- let reasoningResponse = "", fullResponse = "", tokenIdx = 0;
483
+ let reasoningResponse = "", fullResponse = "";
452
484
  for await (const chunk of stream) {
453
485
  if (loader.isSpinning())
454
486
  loader.succeed(`${AiSession.#chat.model} responded`);
@@ -469,7 +501,6 @@ class AiSession {
469
501
  fullResponse += content;
470
502
  onChunk(content);
471
503
  }
472
- tokenIdx++;
473
504
  }
474
505
  fullResponse += `
475
506
  `;
@@ -477,7 +508,7 @@ class AiSession {
477
508
  `);
478
509
  this.messageHistory.push(new AIMessage(fullResponse));
479
510
  return { content: fullResponse, messageHistory: this.messageHistory };
480
- } catch (error) {
511
+ } catch {
481
512
  loader.fail(`${AiSession.#chat.model} failed to respond`);
482
513
  throw new Error("Failed to stream response");
483
514
  }
@@ -487,7 +518,8 @@ class AiSession {
487
518
  onReasoning,
488
519
  maxTry = MAX_ASK_TRY,
489
520
  validate,
490
- approve
521
+ approve,
522
+ fallbackToPreviousTypescript
491
523
  } = {}) {
492
524
  for (let tryCount = 0;tryCount < maxTry; tryCount++) {
493
525
  let response = await this.ask(question, { onChunk, onReasoning });
@@ -495,7 +527,14 @@ class AiSession {
495
527
  const validateQuestion = `Double check if the response meets the requirements and conditions, and follow the instructions. If not, rewrite it.
496
528
  ${validate.map((v) => `- ${v}`).join(`
497
529
  `)}`;
498
- response = await this.ask(validateQuestion, { onChunk, onReasoning });
530
+ const validateResponse = await this.ask(validateQuestion, {
531
+ onChunk,
532
+ onReasoning
533
+ });
534
+ response = {
535
+ ...validateResponse,
536
+ content: fallbackToPreviousTypescript ? preserveTypescriptResponseContent(response.content, validateResponse.content) : validateResponse.content
537
+ };
499
538
  }
500
539
  const isConfirmed = approve ? true : await select({
501
540
  message: "Do you want to edit the response?",
@@ -528,15 +567,28 @@ ${validate.map((v) => `- ${v}`).join(`
528
567
  return this;
529
568
  }
530
569
  async writeTypescripts(question, executor, options = {}) {
531
- const content = await this.edit(question, options);
570
+ const content = await this.edit(question, {
571
+ ...options,
572
+ fallbackToPreviousTypescript: true
573
+ });
532
574
  const writes = this.#getTypescriptCodes(content);
575
+ if (!writes.length)
576
+ throw new Error("No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.");
533
577
  for (const write of writes)
534
578
  await executor.writeFile(write.filePath, write.content);
535
579
  return await this.#tryFixTypescripts(writes, executor, options);
536
580
  }
537
- async#editTypescripts(question, options = {}) {
538
- const content = await this.edit(question, options);
539
- return this.#getTypescriptCodes(content);
581
+ async#editTypescripts(question, options = {}, fallbackWrites) {
582
+ const content = await this.edit(question, {
583
+ ...options,
584
+ fallbackToPreviousTypescript: true
585
+ });
586
+ const writes = this.#getTypescriptCodes(content);
587
+ if (!writes.length && fallbackWrites?.length)
588
+ return fallbackWrites;
589
+ if (!writes.length)
590
+ throw new Error("No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.");
591
+ return writes;
540
592
  }
541
593
  async#tryFixTypescripts(writes, executor, options = {}) {
542
594
  const MAX_EDIT_TRY = 5;
@@ -545,13 +597,15 @@ ${validate.map((v) => `- ${v}`).join(`
545
597
  prefix: `\uD83E\uDD16akan-editor`
546
598
  }).start();
547
599
  const fileChecks = await Promise.all(writes.map(async ({ filePath }) => {
548
- const typeCheckResult = executor.typeCheck(filePath);
549
- const lintResult = await executor.lint(filePath);
550
- const needFix2 = !!typeCheckResult.fileErrors.length || !!lintResult.errors.length;
551
- return { filePath, typeCheckResult, lintResult, needFix: needFix2 };
600
+ const lintResult = await executor.lint(filePath, { fix: true });
601
+ const typeCheckResult = await executor.typeCheckAsync(filePath);
602
+ const hasTypeErrors = typeCheckResult.fileErrors.length > 0;
603
+ const hasLintErrors = lintResult.errors.length > 0;
604
+ const needFix = hasTypeErrors || hasLintErrors;
605
+ return { filePath, typeCheckResult, lintResult, needFix };
552
606
  }));
553
- const needFix = fileChecks.some((fileCheck) => fileCheck.needFix);
554
- if (needFix) {
607
+ const hasAnyFix = fileChecks.some((fileCheck) => fileCheck.needFix);
608
+ if (hasAnyFix) {
555
609
  loader.fail("Type checking and linting has some errors, try to fix them");
556
610
  fileChecks.forEach((fileCheck) => {
557
611
  Logger2.rawLog(`TypeCheck Result
@@ -567,7 +621,7 @@ ${fileCheck.lintResult.message}`);
567
621
  ...options,
568
622
  validate: undefined,
569
623
  approve: true
570
- });
624
+ }, writes);
571
625
  for (const write of writes)
572
626
  await executor.writeFile(write.filePath, write.content);
573
627
  } else {
@@ -578,21 +632,7 @@ ${fileCheck.lintResult.message}`);
578
632
  throw new Error("Failed to create scalar");
579
633
  }
580
634
  #getTypescriptCodes(text) {
581
- const codes = text.match(/```(typescript|tsx)([\s\S]*?)```/g);
582
- if (!codes)
583
- return [];
584
- const result = codes.map((code) => {
585
- const content = /```(typescript|tsx)([\s\S]*?)```/.exec(code)?.[2];
586
- if (!content)
587
- return null;
588
- const filePath = /\/\/ File: (.*?)(?:\n|$)/.exec(content)?.[1]?.trim();
589
- if (!filePath)
590
- return null;
591
- const contentWithoutFilepath = content.replace(`// File: ${filePath}
592
- `, "").trim();
593
- return { filePath, content: contentWithoutFilepath };
594
- });
595
- return result.filter((code) => code !== null);
635
+ return parseTypescriptFileBlocks(text);
596
636
  }
597
637
  async editMarkdown(request, options = {}) {
598
638
  const content = await this.edit(request, options);
@@ -610,13 +650,13 @@ ${fileCheck.lintResult.message}`);
610
650
  }
611
651
  // pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
612
652
  import path9 from "path";
613
- import { Logger as Logger6 } from "akanjs/common";
653
+ import { Logger as Logger5 } from "akanjs/common";
614
654
 
615
655
  // pkgs/@akanjs/devkit/executors.ts
616
656
  import {
617
657
  exec,
618
658
  fork,
619
- spawn
659
+ spawn as spawn2
620
660
  } from "child_process";
621
661
  import { readFileSync as readFileSync3 } from "fs";
622
662
  import { copyFile, mkdir as mkdir2, readdir as readDirEntries, stat as stat2 } from "fs/promises";
@@ -624,7 +664,7 @@ import path7 from "path";
624
664
  import {
625
665
  capitalize,
626
666
  isRouteSourceFile,
627
- Logger as Logger4,
667
+ Logger as Logger3,
628
668
  parseRouteModuleKey,
629
669
  validatePageSourceFile,
630
670
  validateSubRoutePageKey
@@ -671,8 +711,18 @@ var DEFAULT_OPTIMIZE_IMPORTS = [
671
711
  "mui-core",
672
712
  "react-icons/*"
673
713
  ];
674
- var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
675
- var SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"];
714
+ var WORKSPACE_BARREL_FACETS = [
715
+ "ui",
716
+ "webkit",
717
+ "common",
718
+ "client",
719
+ "server"
720
+ ];
721
+ var SSR_RUNTIME_PACKAGES = [
722
+ "react",
723
+ "react-dom",
724
+ "react-server-dom-webpack"
725
+ ];
676
726
  var NATIVE_RUNTIME_PACKAGES = ["sharp"];
677
727
  var DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"];
678
728
  var DATABASE_MODE_RUNTIME_PACKAGES = {
@@ -732,7 +782,12 @@ class AkanAppConfig {
732
782
  ...libs.flatMap((lib) => WORKSPACE_BARREL_FACETS.map((facet) => `@libs/${lib}/${facet}`)),
733
783
  ...config?.barrelImports ?? []
734
784
  ];
735
- this.optimizeImports = [...new Set([...DEFAULT_OPTIMIZE_IMPORTS, ...config?.optimizeImports ?? []])];
785
+ this.optimizeImports = [
786
+ ...new Set([
787
+ ...DEFAULT_OPTIMIZE_IMPORTS,
788
+ ...config?.optimizeImports ?? []
789
+ ])
790
+ ];
736
791
  this.images = mergeImageConfig(config?.images);
737
792
  this.i18n = resolveAkanI18nConfig(config?.i18n);
738
793
  process.env.AKAN_PUBLIC_DEFAULT_LOCALE = this.i18n.defaultLocale;
@@ -843,7 +898,13 @@ class AkanAppConfig {
843
898
  }
844
899
  #makeDockerContent(docker) {
845
900
  if (docker.content)
846
- return { content: docker.content, image: {}, preRuns: [], postRuns: [], command: [] };
901
+ return {
902
+ content: docker.content,
903
+ image: {},
904
+ preRuns: [],
905
+ postRuns: [],
906
+ command: []
907
+ };
847
908
  const preRunScripts = this.#getDockerRunScripts(docker.preRuns ?? []);
848
909
  const postRunScripts = this.#getDockerRunScripts(docker.postRuns ?? []);
849
910
  const imageScript = docker.image ? this.#getDockerImageScript(docker.image, "oven/bun:1-slim") : "FROM oven/bun:1-slim";
@@ -874,7 +935,13 @@ ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
874
935
  ENV AKAN_PUBLIC_OPERATION_MODE=cloud
875
936
 
876
937
  CMD [${command.map((c) => `"${c}"`).join(",")}]`;
877
- return { content, image: imageScript, preRuns: docker.preRuns ?? [], postRuns: docker.postRuns ?? [], command };
938
+ return {
939
+ content,
940
+ image: imageScript,
941
+ preRuns: docker.preRuns ?? [],
942
+ postRuns: docker.postRuns ?? [],
943
+ command
944
+ };
878
945
  }
879
946
  static async from(app) {
880
947
  const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
@@ -900,9 +967,24 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
900
967
  ...SSR_RUNTIME_PACKAGES,
901
968
  ...NATIVE_RUNTIME_PACKAGES,
902
969
  ...DEFAULT_BACKEND_RUNTIME_PACKAGES,
903
- ...DATABASE_MODE_RUNTIME_PACKAGES[this.defaultDatabaseMode]
970
+ ...this.getDatabaseModeRuntimePackages()
904
971
  ];
905
972
  }
973
+ getDatabaseModeRuntimePackages(databaseMode = this.defaultDatabaseMode) {
974
+ return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
975
+ }
976
+ getMissingDatabaseModeDependencySpecs(databaseMode = this.defaultDatabaseMode) {
977
+ const rootDependencies = {
978
+ ...this.rootPackageJson.dependencies,
979
+ ...this.rootPackageJson.devDependencies
980
+ };
981
+ return this.getDatabaseModeRuntimePackages(databaseMode).filter((lib) => !rootDependencies[lib]).map((lib) => {
982
+ const version = this.#resolveProductionDependencyVersion(lib);
983
+ if (!version)
984
+ throw new Error(`Dependency ${lib} not found in package.json`);
985
+ return `${lib}@${version}`;
986
+ });
987
+ }
906
988
  getProductionPackageJson(data = {}) {
907
989
  return {
908
990
  name: this.app.name,
@@ -938,7 +1020,12 @@ function getAkanPackageJson() {
938
1020
  return akanPackageJson;
939
1021
  } catch {}
940
1022
  }
941
- akanPackageJson = { name: "akanjs", version: "0.0.0", description: "akanjs", dependencies: {} };
1023
+ akanPackageJson = {
1024
+ name: "akanjs",
1025
+ version: "0.0.0",
1026
+ description: "akanjs",
1027
+ dependencies: {}
1028
+ };
942
1029
  return akanPackageJson;
943
1030
  }
944
1031
  function mergeImageConfig(config = {}) {
@@ -962,7 +1049,9 @@ class AkanLibConfig {
962
1049
  this.externalLibs = config?.externalLibs ?? [];
963
1050
  }
964
1051
  static async from(lib) {
965
- const [configImp] = await Promise.all([import(`${lib.cwdPath}/akan.config.ts`).then((mod) => mod.default)]);
1052
+ const [configImp] = await Promise.all([
1053
+ import(`${lib.cwdPath}/akan.config.ts`).then((mod) => mod.default)
1054
+ ]);
966
1055
  const config = typeof configImp === "function" ? configImp(lib) : configImp;
967
1056
  return new AkanLibConfig(lib, config);
968
1057
  }
@@ -987,23 +1076,146 @@ import path2 from "path";
987
1076
  var getDirname = (url) => path2.dirname(new URL(url).pathname);
988
1077
 
989
1078
  // pkgs/@akanjs/devkit/linter.ts
1079
+ import { spawn } from "child_process";
990
1080
  import { existsSync, readFileSync } from "fs";
991
- import * as path3 from "path";
992
- import { Logger as Logger3 } from "akanjs/common";
1081
+ import path3 from "path";
993
1082
  import chalk2 from "chalk";
994
1083
 
995
1084
  class Linter {
996
- #logger = new Logger3("Linter");
997
1085
  lintRoot;
1086
+ #biomeBin;
998
1087
  constructor(cwdPath) {
999
- this.lintRoot = this.#findEslintRootPath(cwdPath);
1088
+ this.lintRoot = this.#findBiomeRootPath(cwdPath);
1089
+ const localBiomeBin = path3.join(this.lintRoot, "node_modules/.bin/biome");
1090
+ this.#biomeBin = existsSync(localBiomeBin) ? localBiomeBin : "biome";
1000
1091
  }
1001
- #findEslintRootPath(dir) {
1002
- const configPath2 = path3.join(dir, "eslint.config.ts");
1092
+ #findBiomeRootPath(dir) {
1093
+ const configPath2 = path3.join(dir, "biome.json");
1003
1094
  if (existsSync(configPath2))
1004
1095
  return dir;
1005
1096
  const parentDir = path3.dirname(dir);
1006
- return this.#findEslintRootPath(parentDir);
1097
+ if (parentDir === dir)
1098
+ throw new Error(`biome.json not found from ${dir}`);
1099
+ return this.#findBiomeRootPath(parentDir);
1100
+ }
1101
+ #toBiomePath(filePath) {
1102
+ const relativePath = path3.relative(this.lintRoot, filePath);
1103
+ if (!relativePath.startsWith("..") && !path3.isAbsolute(relativePath))
1104
+ return relativePath;
1105
+ return filePath;
1106
+ }
1107
+ #resolveFilePath(filePath) {
1108
+ return path3.isAbsolute(filePath) ? filePath : path3.join(this.lintRoot, filePath);
1109
+ }
1110
+ async#runBiome(args, input2) {
1111
+ return await new Promise((resolve, reject) => {
1112
+ const proc = spawn(this.#biomeBin, args, {
1113
+ cwd: this.lintRoot,
1114
+ stdio: ["pipe", "pipe", "pipe"]
1115
+ });
1116
+ let stdout = "";
1117
+ let stderr = "";
1118
+ proc.stdout.on("data", (data) => {
1119
+ stdout += data.toString();
1120
+ });
1121
+ proc.stderr.on("data", (data) => {
1122
+ stderr += data.toString();
1123
+ });
1124
+ proc.on("error", reject);
1125
+ proc.on("close", (code) => resolve({ stdout, stderr, code }));
1126
+ proc.stdin.end(input2);
1127
+ });
1128
+ }
1129
+ #parseBiomeReport(output) {
1130
+ const jsonStart = output.indexOf("{");
1131
+ const jsonEnd = output.lastIndexOf("}");
1132
+ if (jsonStart === -1 || jsonEnd === -1 || jsonEnd < jsonStart)
1133
+ throw new Error(output.trim() || "No Biome JSON output");
1134
+ return JSON.parse(output.slice(jsonStart, jsonEnd + 1));
1135
+ }
1136
+ #diagnosticFilePath(diagnostic, fallbackFilePath) {
1137
+ const diagnosticPath = diagnostic.location?.path;
1138
+ if (!diagnosticPath)
1139
+ return fallbackFilePath;
1140
+ return path3.isAbsolute(diagnosticPath) ? diagnosticPath : path3.join(this.lintRoot, diagnosticPath);
1141
+ }
1142
+ #createLintMessage(diagnostic) {
1143
+ const start = diagnostic.location?.start;
1144
+ const end = diagnostic.location?.end;
1145
+ return {
1146
+ line: Math.max(1, start?.line ?? 1),
1147
+ column: Math.max(1, start?.column ?? 1),
1148
+ endLine: end?.line,
1149
+ endColumn: end?.column,
1150
+ message: diagnostic.message,
1151
+ ruleId: diagnostic.category ?? null,
1152
+ severity: diagnostic.severity === "error" ? 2 : 1
1153
+ };
1154
+ }
1155
+ #toLintResults(report, filePath) {
1156
+ const resultsByPath = new Map;
1157
+ for (const diagnostic of report.diagnostics ?? []) {
1158
+ if (diagnostic.severity !== "error" && diagnostic.severity !== "warning")
1159
+ continue;
1160
+ const diagnosticFilePath = this.#diagnosticFilePath(diagnostic, filePath);
1161
+ const result = resultsByPath.get(diagnosticFilePath) ?? {
1162
+ filePath: diagnosticFilePath,
1163
+ messages: [],
1164
+ errorCount: 0,
1165
+ warningCount: 0,
1166
+ fixableErrorCount: 0,
1167
+ fixableWarningCount: 0
1168
+ };
1169
+ const message = this.#createLintMessage(diagnostic);
1170
+ result.messages.push(message);
1171
+ if (message.severity === 2)
1172
+ result.errorCount += 1;
1173
+ else
1174
+ result.warningCount += 1;
1175
+ resultsByPath.set(diagnosticFilePath, result);
1176
+ }
1177
+ return [
1178
+ resultsByPath.get(filePath) ?? {
1179
+ filePath,
1180
+ messages: [],
1181
+ errorCount: 0,
1182
+ warningCount: 0,
1183
+ fixableErrorCount: 0,
1184
+ fixableWarningCount: 0
1185
+ },
1186
+ ...[...resultsByPath.entries()].filter(([resultPath]) => resultPath !== filePath).map(([, result]) => result)
1187
+ ];
1188
+ }
1189
+ #splitMessages(results) {
1190
+ const messages = results.flatMap((result) => result.messages);
1191
+ return {
1192
+ errors: messages.filter((message) => message.severity === 2),
1193
+ warnings: messages.filter((message) => message.severity === 1)
1194
+ };
1195
+ }
1196
+ async#checkFile(filePath, { write = false } = {}) {
1197
+ const originalContent = existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
1198
+ const { stdout, stderr } = await this.#runBiome([
1199
+ "check",
1200
+ ...write ? ["--write"] : [],
1201
+ "--reporter=json",
1202
+ "--max-diagnostics=none",
1203
+ "--no-errors-on-unmatched",
1204
+ "--config-path",
1205
+ path3.join(this.lintRoot, "biome.json"),
1206
+ this.#toBiomePath(filePath)
1207
+ ]);
1208
+ const report = this.#parseBiomeReport(stdout || stderr);
1209
+ const results = this.#toLintResults(report, filePath);
1210
+ const { errors, warnings } = this.#splitMessages(results);
1211
+ const output = write && existsSync(filePath) ? readFileSync(filePath, "utf8") : undefined;
1212
+ return {
1213
+ fixed: write && output !== originalContent,
1214
+ output,
1215
+ results,
1216
+ errors,
1217
+ warnings
1218
+ };
1007
1219
  }
1008
1220
  async lint(filePath, { fix = false, dryRun = false } = {}) {
1009
1221
  if (fix)
@@ -1011,9 +1223,10 @@ class Linter {
1011
1223
  return await this.lintFile(filePath);
1012
1224
  }
1013
1225
  async lintFile(filePath) {
1014
- if (!existsSync(filePath))
1226
+ const resolvedFilePath = this.#resolveFilePath(filePath);
1227
+ if (!existsSync(resolvedFilePath))
1015
1228
  throw new Error(`File not found: ${filePath}`);
1016
- return { fixed: false, results: [], errors: [], warnings: [] };
1229
+ return await this.#checkFile(resolvedFilePath);
1017
1230
  }
1018
1231
  formatLintResults(results) {
1019
1232
  if (results.length === 0)
@@ -1033,12 +1246,12 @@ ${chalk2.cyan(result.filePath)}`);
1033
1246
  const sourceContent = readFileSync(result.filePath, "utf8");
1034
1247
  sourceLines = sourceContent.split(`
1035
1248
  `);
1036
- } catch (error) {}
1249
+ } catch {}
1037
1250
  }
1038
1251
  result.messages.forEach((message) => {
1039
1252
  const type = message.severity === 2 ? "error" : "warning";
1040
1253
  const typeColor = message.severity === 2 ? chalk2.red : chalk2.yellow;
1041
- const icon = message.severity === 2 ? "\u274C" : "\u26A0\uFE0F";
1254
+ const icon = message.severity === 2 ? "x" : "!";
1042
1255
  const ruleInfo = message.ruleId ? chalk2.dim(` (${message.ruleId})`) : "";
1043
1256
  output.push(`
1044
1257
  ${icon} ${typeColor(type)}: ${message.message}${ruleInfo}`);
@@ -1057,7 +1270,7 @@ ${chalk2.dim(`${lineNumber} |`)} ${sourceLine}`);
1057
1270
  }
1058
1271
  });
1059
1272
  if (totalErrors === 0 && totalWarnings === 0)
1060
- return chalk2.bold("\u2705 No ESLint errors or warnings found");
1273
+ return chalk2.bold("No Biome errors or warnings found");
1061
1274
  const errorText = totalErrors > 0 ? chalk2.red(`${totalErrors} error(s)`) : "0 errors";
1062
1275
  const warningText = totalWarnings > 0 ? chalk2.yellow(`${totalWarnings} warning(s)`) : "0 warnings";
1063
1276
  const summary = [`
@@ -1072,23 +1285,26 @@ ${errorText}, ${warningText} found`];
1072
1285
  column: message.column,
1073
1286
  message: message.message,
1074
1287
  ruleId: message.ruleId,
1075
- severity: message.severity === 2 ? "error" : "warning",
1076
- fix: message.fix,
1077
- suggestions: message.suggestions
1288
+ severity: message.severity === 2 ? "error" : "warning"
1078
1289
  })));
1079
1290
  const stats = results.reduce((acc, result) => ({
1080
1291
  errorCount: acc.errorCount + result.errorCount,
1081
1292
  warningCount: acc.warningCount + result.warningCount,
1082
1293
  fixableErrorCount: acc.fixableErrorCount + result.fixableErrorCount,
1083
1294
  fixableWarningCount: acc.fixableWarningCount + result.fixableWarningCount
1084
- }), { errorCount: 0, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0 });
1295
+ }), {
1296
+ errorCount: 0,
1297
+ warningCount: 0,
1298
+ fixableErrorCount: 0,
1299
+ fixableWarningCount: 0
1300
+ });
1085
1301
  return { results, details, stats };
1086
1302
  }
1087
1303
  async hasNoLintErrors(filePath) {
1088
1304
  try {
1089
1305
  const { results } = await this.lintFile(filePath);
1090
1306
  return results.every((result) => result.errorCount === 0);
1091
- } catch (error) {
1307
+ } catch {
1092
1308
  return false;
1093
1309
  }
1094
1310
  }
@@ -1101,12 +1317,28 @@ ${errorText}, ${warningText} found`];
1101
1317
  return results.flatMap((result) => result.messages.filter((message) => message.severity === 1));
1102
1318
  }
1103
1319
  async fixFile(filePath, dryRun = false) {
1104
- if (!existsSync(filePath))
1320
+ const resolvedFilePath = this.#resolveFilePath(filePath);
1321
+ if (!existsSync(resolvedFilePath))
1105
1322
  throw new Error(`File not found: ${filePath}`);
1106
- return { fixed: false, output: undefined, results: [], errors: [], warnings: [] };
1323
+ if (!dryRun)
1324
+ return await this.#checkFile(resolvedFilePath, { write: true });
1325
+ const source = readFileSync(resolvedFilePath, "utf8");
1326
+ const { stdout } = await this.#runBiome([
1327
+ "check",
1328
+ "--write",
1329
+ "--config-path",
1330
+ path3.join(this.lintRoot, "biome.json"),
1331
+ "--stdin-file-path",
1332
+ this.#toBiomePath(resolvedFilePath)
1333
+ ], source);
1334
+ const lintResult = await this.lintFile(resolvedFilePath);
1335
+ return { ...lintResult, fixed: stdout !== source, output: stdout };
1107
1336
  }
1108
1337
  async getConfigForFile(filePath) {
1109
- return {};
1338
+ const resolvedFilePath = this.#resolveFilePath(filePath);
1339
+ if (!existsSync(resolvedFilePath))
1340
+ throw new Error(`File not found: ${filePath}`);
1341
+ return JSON.parse(readFileSync(path3.join(this.lintRoot, "biome.json"), "utf8"));
1110
1342
  }
1111
1343
  async getProblematicRules(filePath) {
1112
1344
  const { results } = await this.lintFile(filePath);
@@ -1496,23 +1728,23 @@ async function assertScanConvention(exec, libRoot) {
1496
1728
  files.filter((filename) => !appRootAllowedFiles.has(filename)).forEach((filename) => {
1497
1729
  addViolation(filename, "unsupported app root file");
1498
1730
  });
1499
- dirs.filter((dirname2) => !appRootAllowedDirs.has(dirname2)).forEach((dirname2) => {
1500
- addViolation(dirname2, "unsupported app root folder");
1731
+ dirs.filter((dirname) => !appRootAllowedDirs.has(dirname)).forEach((dirname) => {
1732
+ addViolation(dirname, "unsupported app root folder");
1501
1733
  });
1502
1734
  }
1503
1735
  libRoot.files.filter((filename) => !isAllowedLibRootFile(filename)).forEach((filename) => {
1504
1736
  addViolation(path5.join("lib", filename), "unsupported lib root file");
1505
1737
  });
1506
- libRoot.dirs.filter((dirname2) => dirname2.startsWith("__") && !internalLibDirs.has(dirname2)).forEach((dirname2) => {
1507
- addViolation(path5.join("lib", dirname2), "unsupported internal lib folder");
1738
+ libRoot.dirs.filter((dirname) => dirname.startsWith("__") && !internalLibDirs.has(dirname)).forEach((dirname) => {
1739
+ addViolation(path5.join("lib", dirname), "unsupported internal lib folder");
1508
1740
  });
1509
- const databaseDirs = libRoot.dirs.filter((dirname2) => !dirname2.startsWith("_"));
1510
- const serviceDirs = libRoot.dirs.filter((dirname2) => dirname2.startsWith("_") && !dirname2.startsWith("__"));
1741
+ const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
1742
+ const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
1511
1743
  const scalarDirs = await exec.readdir("lib/__scalar");
1512
1744
  await Promise.all([
1513
- ...databaseDirs.map((dirname2) => validateModuleFiles(exec, violations, "database", path5.join("lib", dirname2))),
1514
- ...serviceDirs.map((dirname2) => validateModuleFiles(exec, violations, "service", path5.join("lib", dirname2))),
1515
- ...scalarDirs.map((dirname2) => validateModuleFiles(exec, violations, "scalar", path5.join("lib/__scalar", dirname2)))
1745
+ ...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path5.join("lib", dirname))),
1746
+ ...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path5.join("lib", dirname))),
1747
+ ...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path5.join("lib/__scalar", dirname)))
1516
1748
  ]);
1517
1749
  if (violations.length > 0) {
1518
1750
  throw new Error(`[scan-convention]
@@ -1522,8 +1754,8 @@ ${violations.sort().map((violation) => `- ${violation}`).join(`
1522
1754
  }
1523
1755
  async function validateModuleFiles(exec, violations, kind, modulePath) {
1524
1756
  const { files, dirs } = await exec.getFilesAndDirs(modulePath);
1525
- dirs.forEach((dirname2) => {
1526
- violations.push(`${getScanPath(exec, path5.join(modulePath, dirname2))}: unsupported module folder`);
1757
+ dirs.forEach((dirname) => {
1758
+ violations.push(`${getScanPath(exec, path5.join(modulePath, dirname))}: unsupported module folder`);
1527
1759
  });
1528
1760
  files.forEach((filename) => {
1529
1761
  const filePath = path5.join(modulePath, filename);
@@ -1622,9 +1854,9 @@ class ScanInfo {
1622
1854
  files.zone.databases.push(name);
1623
1855
  });
1624
1856
  }),
1625
- ...serviceDirs.map(async (dirname2) => {
1626
- const name = dirname2.slice(1);
1627
- const filenames = await exec.readdir(path5.join("lib", dirname2));
1857
+ ...serviceDirs.map(async (dirname) => {
1858
+ const name = dirname.slice(1);
1859
+ const filenames = await exec.readdir(path5.join("lib", dirname));
1628
1860
  filenames.forEach((filename) => {
1629
1861
  if (filename.endsWith(".dictionary.ts"))
1630
1862
  files.dictionary.services.push(name);
@@ -2254,9 +2486,11 @@ var ROOT_LAYOUT_EXPORTS = new Set([
2254
2486
  "reconnect",
2255
2487
  "layoutStyle",
2256
2488
  "gaTrackingId",
2257
- "Loading"
2489
+ "Loading",
2490
+ "NotFound",
2491
+ "Error"
2258
2492
  ]);
2259
- var LAYOUT_ROUTE_EXPORTS = new Set(["default", "head", "generateHead", "Loading"]);
2493
+ var LAYOUT_ROUTE_EXPORTS = new Set(["default", "head", "generateHead", "Loading", "NotFound", "Error"]);
2260
2494
  function validateRouteSourceExports(source, filePath, kind, options = {}) {
2261
2495
  const sourceFile = ts3.createSourceFile(filePath, source, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
2262
2496
  const allowed = kind === "page" ? PAGE_ROUTE_EXPORTS : options.rootLayout ? ROOT_LAYOUT_EXPORTS : LAYOUT_ROUTE_EXPORTS;
@@ -2325,16 +2559,16 @@ class Executor {
2325
2559
  linter = null;
2326
2560
  constructor(name, cwdPath) {
2327
2561
  this.name = name;
2328
- this.logger = new Logger4(name);
2562
+ this.logger = new Logger3(name);
2329
2563
  this.logs = [];
2330
2564
  this.cwdPath = cwdPath;
2331
2565
  }
2332
2566
  #stdout(data) {
2333
2567
  if (Executor.verbose)
2334
- Logger4.raw(chalk4.dim(data.toString()));
2568
+ Logger3.raw(chalk4.dim(data.toString()));
2335
2569
  }
2336
2570
  #stderr(data) {
2337
- Logger4.raw(chalk4.red(data.toString()));
2571
+ Logger3.raw(chalk4.red(data.toString()));
2338
2572
  }
2339
2573
  exec(command, options = {}) {
2340
2574
  const cwd = options.cwd?.toString() ?? this.cwdPath;
@@ -2378,7 +2612,7 @@ class Executor {
2378
2612
  }
2379
2613
  spawn(command, args = [], options = {}) {
2380
2614
  const cwd = options.cwd?.toString() ?? this.cwdPath;
2381
- const proc = spawn(command, args, {
2615
+ const proc = spawn2(command, args, {
2382
2616
  cwd: this.cwdPath,
2383
2617
  ...options
2384
2618
  });
@@ -2424,7 +2658,7 @@ class Executor {
2424
2658
  });
2425
2659
  }
2426
2660
  spawnSync(command, args = [], options = {}) {
2427
- const proc = spawn(command, args, {
2661
+ const proc = spawn2(command, args, {
2428
2662
  cwd: this.cwdPath,
2429
2663
  ...options
2430
2664
  });
@@ -2556,7 +2790,7 @@ class Executor {
2556
2790
  contentStr = currentContent;
2557
2791
  } else {
2558
2792
  await FileSys.writeText(writePath, contentStr);
2559
- if (Logger4.isVerbose())
2793
+ if (Logger3.isVerbose())
2560
2794
  this.logger.rawLog(chalk4.yellow(`File Update: ${filePath}`));
2561
2795
  }
2562
2796
  } else {
@@ -2665,8 +2899,8 @@ class Executor {
2665
2899
  return null;
2666
2900
  const filename = typeof result === "object" ? result.filename : path7.basename(targetPath).replace(".js", ".ts");
2667
2901
  const content = typeof result === "object" ? result.content : result;
2668
- const dirname3 = path7.dirname(targetPath);
2669
- const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), `${dirname3}/${filename}`);
2902
+ const dirname2 = path7.dirname(targetPath);
2903
+ const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), `${dirname2}/${filename}`);
2670
2904
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
2671
2905
  return this.writeFile(convertedTargetPath, content, { overwrite });
2672
2906
  } else if (targetPath.endsWith(".template")) {
@@ -2680,9 +2914,9 @@ class Executor {
2680
2914
  } else if (staticTemplateFileExtensions.has(path7.extname(targetPath).toLowerCase())) {
2681
2915
  const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
2682
2916
  const writePath = this.getPath(convertedTargetPath);
2683
- const dirname3 = path7.dirname(writePath);
2684
- if (!await FileSys.dirExists(dirname3))
2685
- await mkdir2(dirname3, { recursive: true });
2917
+ const dirname2 = path7.dirname(writePath);
2918
+ if (!await FileSys.dirExists(dirname2))
2919
+ await mkdir2(dirname2, { recursive: true });
2686
2920
  await copyFile(templatePath, writePath);
2687
2921
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
2688
2922
  return { filePath: writePath, content: "" };
@@ -2763,6 +2997,48 @@ class Executor {
2763
2997
  const message = typeChecker.formatDiagnostics(fileDiagnostics);
2764
2998
  return { fileDiagnostics, fileErrors, fileWarnings, message };
2765
2999
  }
3000
+ async typeCheckAsync(filePath) {
3001
+ const path8 = this.getPath(filePath);
3002
+ const entry = await this.#resolveTypecheckWorkerEntry();
3003
+ const proc = Bun.spawn([process.execPath, entry], {
3004
+ cwd: this.cwdPath,
3005
+ env: {
3006
+ ...process.env,
3007
+ AKAN_TYPECHECK_CWD: this.cwdPath,
3008
+ AKAN_TYPECHECK_FILE: path8
3009
+ },
3010
+ stdout: "pipe",
3011
+ stderr: "pipe"
3012
+ });
3013
+ const [stdout, stderr, exitCode] = await Promise.all([
3014
+ new Response(proc.stdout).text(),
3015
+ new Response(proc.stderr).text(),
3016
+ proc.exited
3017
+ ]);
3018
+ if (exitCode !== 0)
3019
+ throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
3020
+ const result = JSON.parse(stdout);
3021
+ return {
3022
+ fileDiagnostics: Array.from({ length: result.fileDiagnosticsCount }),
3023
+ fileErrors: Array.from({ length: result.fileErrorsCount }),
3024
+ fileWarnings: Array.from({ length: result.fileWarningsCount }),
3025
+ message: result.message
3026
+ };
3027
+ }
3028
+ async#resolveTypecheckWorkerEntry() {
3029
+ const dirname2 = getDirname(import.meta.url);
3030
+ const candidates = [
3031
+ path7.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
3032
+ path7.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
3033
+ path7.join(dirname2, "typecheck/typecheck.proc.ts"),
3034
+ path7.join(dirname2, "typecheck.proc.js"),
3035
+ path7.join(dirname2, "typecheck.proc.ts")
3036
+ ];
3037
+ for (const candidate of candidates)
3038
+ if (await Bun.file(candidate).exists())
3039
+ return candidate;
3040
+ throw new Error(`[devkit] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
3041
+ }
2766
3042
  getLinter() {
2767
3043
  this.linter ??= new Linter(this.cwdPath);
2768
3044
  return this.linter;
@@ -2810,7 +3086,15 @@ class WorkspaceExecutor extends Executor {
2810
3086
  const env = sourceEnv.AKAN_PUBLIC_ENV ?? "debug";
2811
3087
  if (!env)
2812
3088
  throw new Error("AKAN_PUBLIC_ENV is not set");
2813
- return { ...appName ? { appName } : {}, workspaceRoot, repoName, serveDomain, env, portOffset, workspaceId };
3089
+ return {
3090
+ ...appName ? { appName } : {},
3091
+ workspaceRoot,
3092
+ repoName,
3093
+ serveDomain,
3094
+ env,
3095
+ portOffset,
3096
+ workspaceId
3097
+ };
2814
3098
  }
2815
3099
  getWorkspaceId({
2816
3100
  allowEmpty
@@ -2870,12 +3154,12 @@ class WorkspaceExecutor extends Executor {
2870
3154
  }
2871
3155
  async getDirInModule(basePath2, name) {
2872
3156
  const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
2873
- const getDirs = async (dirname3, maxDepth = 3, results = [], prefix = "") => {
2874
- const dirs = await this.readdir(dirname3);
3157
+ const getDirs = async (dirname2, maxDepth = 3, results = [], prefix = "") => {
3158
+ const dirs = await this.readdir(dirname2);
2875
3159
  await Promise.all(dirs.map(async (dir) => {
2876
3160
  if (dir.includes("_") || AVOID_DIRS.includes(dir))
2877
3161
  return;
2878
- const dirPath = path7.join(dirname3, dir);
3162
+ const dirPath = path7.join(dirname2, dir);
2879
3163
  if ((await stat2(dirPath)).isDirectory()) {
2880
3164
  results.push(`${prefix}${dir}`);
2881
3165
  if (maxDepth > 0)
@@ -2895,12 +3179,12 @@ class WorkspaceExecutor extends Executor {
2895
3179
  }
2896
3180
  async#getDirHasFile(basePath2, targetFilename) {
2897
3181
  const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
2898
- const getDirs = async (dirname3, maxDepth = 3, results = [], prefix = "") => {
2899
- const dirs = await this.readdir(dirname3);
3182
+ const getDirs = async (dirname2, maxDepth = 3, results = [], prefix = "") => {
3183
+ const dirs = await this.readdir(dirname2);
2900
3184
  await Promise.all(dirs.map(async (dir) => {
2901
3185
  if (AVOID_DIRS.includes(dir))
2902
3186
  return;
2903
- const dirPath = path7.join(dirname3, dir);
3187
+ const dirPath = path7.join(dirname2, dir);
2904
3188
  if ((await stat2(dirPath)).isDirectory()) {
2905
3189
  const hasTargetFile = await FileSys.fileExists(path7.join(dirPath, targetFilename));
2906
3190
  if (hasTargetFile)
@@ -3165,6 +3449,13 @@ class AppExecutor extends SysExecutor {
3165
3449
  getEnv() {
3166
3450
  return WorkspaceExecutor.getBaseDevEnv().env;
3167
3451
  }
3452
+ async getDevPort() {
3453
+ const basePort = 8282;
3454
+ const appNames = (await this.workspace.getApps()).sort((a, b) => a.localeCompare(b));
3455
+ const appIndex = Math.max(appNames.indexOf(this.name), 0);
3456
+ const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
3457
+ return basePort + appIndex + portOffset;
3458
+ }
3168
3459
  getCommandEnv(env = {}) {
3169
3460
  const basePort = 8282;
3170
3461
  const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
@@ -3198,7 +3489,12 @@ class AppExecutor extends SysExecutor {
3198
3489
  ]);
3199
3490
  } else
3200
3491
  await this.removeDir(".akan");
3201
- const env = this.getCommandEnv({ AKAN_COMMAND_TYPE: type, ...routeEnv });
3492
+ const devPort = type === "start" ? (await this.getDevPort()).toString() : undefined;
3493
+ const env = this.getCommandEnv({
3494
+ AKAN_COMMAND_TYPE: type,
3495
+ ...routeEnv,
3496
+ ...devPort ? { PORT: devPort, AKAN_PUBLIC_CLIENT_PORT: devPort, AKAN_PUBLIC_SERVER_PORT: devPort } : {}
3497
+ });
3202
3498
  return { env };
3203
3499
  }
3204
3500
  #publicEnv = null;
@@ -3617,7 +3913,7 @@ var createTunnel = async (service, { app, environment, port = service === "postg
3617
3913
 
3618
3914
  // pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.host.ts
3619
3915
  import path8 from "path";
3620
- import { Logger as Logger5 } from "akanjs/common";
3916
+ import { Logger as Logger4 } from "akanjs/common";
3621
3917
  var builderMsgTypeSet = new Set([
3622
3918
  "build-route-res",
3623
3919
  "builder-ready",
@@ -3629,7 +3925,7 @@ var builderMsgTypeSet = new Set([
3629
3925
  class IncrementalBuilderHost {
3630
3926
  static #restartBaseDelayMs = 1000;
3631
3927
  static #restartMaxDelayMs = 30000;
3632
- logger = new Logger5("IncrementalBuilderHost");
3928
+ logger = new Logger4("IncrementalBuilderHost");
3633
3929
  entry;
3634
3930
  env;
3635
3931
  app;
@@ -3877,7 +4173,7 @@ class BackendImportGraph {
3877
4173
 
3878
4174
  class AkanAppHost {
3879
4175
  app;
3880
- logger = new Logger6("AkanAppHost");
4176
+ logger = new Logger5("AkanAppHost");
3881
4177
  withInk;
3882
4178
  env;
3883
4179
  #backend = null;
@@ -4160,19 +4456,19 @@ class AkanAppHost {
4160
4456
  }
4161
4457
  }
4162
4458
  // pkgs/@akanjs/devkit/applicationBuildReporter.ts
4163
- import { Logger as Logger7 } from "akanjs/common";
4459
+ import { Logger as Logger6 } from "akanjs/common";
4164
4460
 
4165
4461
  class ApplicationBuildReporter {
4166
4462
  static create() {
4167
4463
  return {
4168
- phaseDone: (phase) => Logger7.rawLog(ApplicationBuildReporter.formatPhaseLine(phase))
4464
+ phaseDone: (phase) => Logger6.rawLog(ApplicationBuildReporter.formatPhaseLine(phase))
4169
4465
  };
4170
4466
  }
4171
4467
  static printSummary(result) {
4172
- Logger7.rawLog("");
4173
- Logger7.rawLog(`Route artifacts: ${result.artifactDir}`);
4174
- Logger7.rawLog(`Server output: ${result.outputDir}`);
4175
- Logger7.rawLog(`Done in ${ApplicationBuildReporter.formatDuration(result.durationMs)}`);
4468
+ Logger6.rawLog("");
4469
+ Logger6.rawLog(`Route artifacts: ${result.artifactDir}`);
4470
+ Logger6.rawLog(`Server output: ${result.outputDir}`);
4471
+ Logger6.rawLog(`Done in ${ApplicationBuildReporter.formatDuration(result.durationMs)}`);
4176
4472
  }
4177
4473
  static formatError(error) {
4178
4474
  if (error instanceof AggregateError) {
@@ -4324,6 +4620,9 @@ export async function generateHead(props: PageProps) {
4324
4620
  return inheritedLayout.head;
4325
4621
  }
4326
4622
 
4623
+ export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
4624
+ export const Error = userLayout.Error ?? inheritedLayout.Error;
4625
+
4327
4626
  export default function GeneratedLayout({ children, params, searchParams }: LayoutProps) {
4328
4627
  return (
4329
4628
  <System.Provider
@@ -4353,6 +4652,9 @@ export async function generateHead(props: PageProps) {
4353
4652
  return inheritedLayout.head;
4354
4653
  }
4355
4654
 
4655
+ export const NotFound = userLayout.NotFound ?? inheritedLayout.NotFound;
4656
+ export const Error = userLayout.Error ?? inheritedLayout.Error;
4657
+
4356
4658
  export default function GeneratedLayout({ children, params, searchParams }: LayoutProps) {
4357
4659
  return <UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>;
4358
4660
  }
@@ -4471,13 +4773,13 @@ import path14 from "path";
4471
4773
 
4472
4774
  // pkgs/@akanjs/devkit/transforms/barrelAnalyzer.ts
4473
4775
  import path12 from "path";
4474
- import { Logger as Logger8 } from "akanjs/common";
4776
+ import { Logger as Logger7 } from "akanjs/common";
4475
4777
  var REEXPORT_RE = /(?:^|\n)\s*export\s+(?:type\s+)?(?:(\*)(?:\s+as\s+(\w+))?|\{\s*([^}]*?)\s*\})\s+from\s+(["'])([^"']+)\4;?/g;
4476
4778
  var LOCAL_NAMED_RE = /(?:^|\n)\s*export\s+\{\s*([^}]*?)\s*\}(?!\s*from)/g;
4477
4779
  var CANDIDATE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
4478
4780
 
4479
4781
  class BarrelAnalyzer {
4480
- #logger = new Logger8("BarrelAnalyzer");
4782
+ #logger = new Logger7("BarrelAnalyzer");
4481
4783
  #opts;
4482
4784
  #cache = new Map;
4483
4785
  #tsTranspiler = new Bun.Transpiler({ loader: "ts" });
@@ -6177,7 +6479,7 @@ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
6177
6479
  }
6178
6480
  // pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
6179
6481
  import path23 from "path";
6180
- import { Logger as Logger9 } from "akanjs/common";
6482
+ import { Logger as Logger8 } from "akanjs/common";
6181
6483
  import { compile } from "tailwindcss";
6182
6484
 
6183
6485
  // pkgs/@akanjs/devkit/frontendBuild/cssImportResolver.ts
@@ -6322,7 +6624,7 @@ var NODE_MODULES_RE3 = /[\\/]node_modules[\\/]/;
6322
6624
  var AKANJS_NODE_MODULE_RE3 = /[\\/]node_modules[\\/]akanjs[\\/]/;
6323
6625
 
6324
6626
  class CssCompiler {
6325
- #logger = new Logger9("CssCompiler");
6627
+ #logger = new Logger8("CssCompiler");
6326
6628
  #transpiler = new Bun.Transpiler({ loader: "tsx" });
6327
6629
  #app;
6328
6630
  #cssImportResolver = null;
@@ -7730,7 +8032,7 @@ class ApplicationBuildRunner {
7730
8032
  import { cp, mkdir as mkdir8, rm as rm3 } from "fs/promises";
7731
8033
 
7732
8034
  // pkgs/@akanjs/devkit/uploadRelease.ts
7733
- import { HttpClient as HttpClient2, Logger as Logger10 } from "akanjs/common";
8035
+ import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
7734
8036
  var spinning = (message) => {
7735
8037
  const spinner = new Spinner(message, { prefix: message, enableSpin: true }).start();
7736
8038
  return spinner;
@@ -7743,7 +8045,7 @@ var uploadRelease = async (appName, {
7743
8045
  os,
7744
8046
  local
7745
8047
  }) => {
7746
- const logger = new Logger10("uploadRelease");
8048
+ const logger = new Logger9("uploadRelease");
7747
8049
  const basePath2 = local ? "http://localhost:8282/backend" : "https://cloud.akanjs.com/backend";
7748
8050
  const httpClient = new HttpClient2(basePath2);
7749
8051
  const buildPath = `${workspaceRoot}/releases/builds/${appName}-release.tar.gz`;
@@ -8729,7 +9031,7 @@ var Workspace = createInternalArgToken("Workspace");
8729
9031
  // pkgs/@akanjs/devkit/commandDecorators/command.ts
8730
9032
  import path36 from "path";
8731
9033
  import { confirm, input as input2, select as select2 } from "@inquirer/prompts";
8732
- import { Logger as Logger11 } from "akanjs/common";
9034
+ import { Logger as Logger10 } from "akanjs/common";
8733
9035
  import chalk6 from "chalk";
8734
9036
  import { program } from "commander";
8735
9037
 
@@ -9014,7 +9316,7 @@ var printCliError = (error) => {
9014
9316
  if (loggedCliErrorMessages.has(message))
9015
9317
  return;
9016
9318
  loggedCliErrorMessages.add(message);
9017
- Logger11.rawLog(`
9319
+ Logger10.rawLog(`
9018
9320
  ${chalk6.red(message)}`);
9019
9321
  };
9020
9322
  var handleOption = (programCommand, argMeta) => {
@@ -9243,7 +9545,7 @@ var runCommands = async (...commands) => {
9243
9545
  const hasCommand = process.argv.length > 2 && !process.argv[2]?.startsWith("-");
9244
9546
  if (hasHelpFlag || !hasCommand) {
9245
9547
  if (process.argv.length === 2 || process.argv.length === 3 && hasHelpFlag) {
9246
- Logger11.rawLog(formatHelp(commands, process.env.AKAN_VERSION));
9548
+ Logger10.rawLog(formatHelp(commands, process.env.AKAN_VERSION));
9247
9549
  process.exit(0);
9248
9550
  }
9249
9551
  }
@@ -9252,7 +9554,7 @@ var runCommands = async (...commands) => {
9252
9554
  });
9253
9555
  const installedAkanPackageJson = await FileSys.fileExists("./node_modules/akanjs/package.json") ? await FileSys.readJson("./node_modules/akanjs/package.json") : null;
9254
9556
  if (installedAkanPackageJson && installedAkanPackageJson.version !== process.env.AKAN_VERSION) {
9255
- Logger11.rawLog(chalk6.yellow(`
9557
+ Logger10.rawLog(chalk6.yellow(`
9256
9558
  Akan CLI version is mismatch with installed package. ${process.env.AKAN_VERSION} (global) vs ${installedAkanPackageJson.version} (akanjs)
9257
9559
  It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`));
9258
9560
  }
@@ -9288,7 +9590,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
9288
9590
  return formatCommandHelp(command, targetMeta.key);
9289
9591
  };
9290
9592
  programCommand.action(async (...args) => {
9291
- Logger11.rawLog();
9593
+ Logger10.rawLog();
9292
9594
  const cmdArgs = args.slice(0, args.length - 2);
9293
9595
  const opt = args[args.length - 2];
9294
9596
  const commandArgs = [];
@@ -9312,7 +9614,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
9312
9614
  const cmd = CommandContainer.get(command);
9313
9615
  try {
9314
9616
  await targetMeta.handler.call(cmd, ...commandArgs);
9315
- Logger11.rawLog();
9617
+ Logger10.rawLog();
9316
9618
  } catch (e) {
9317
9619
  printCliError(e);
9318
9620
  throw e;
@@ -9652,8 +9954,11 @@ import fsPromise from "fs/promises";
9652
9954
  import { input as input3, select as select3 } from "@inquirer/prompts";
9653
9955
  class Prompter {
9654
9956
  static async#getGuidelineRoot() {
9655
- const dirname3 = getDirname(import.meta.url);
9656
- const candidates = [`${dirname3}/guidelines`, `${dirname3}/../cli/guidelines`];
9957
+ const dirname2 = getDirname(import.meta.url);
9958
+ const candidates = [
9959
+ `${dirname2}/guidelines`,
9960
+ `${dirname2}/../cli/guidelines`
9961
+ ];
9657
9962
  for (const candidate of candidates) {
9658
9963
  try {
9659
9964
  await fsPromise.access(candidate);
@@ -9665,7 +9970,10 @@ class Prompter {
9665
9970
  static async selectGuideline() {
9666
9971
  const guidelineRoot = await Prompter.#getGuidelineRoot();
9667
9972
  const guideNames = (await fsPromise.readdir(guidelineRoot)).filter((name) => !name.startsWith("_"));
9668
- return await select3({ message: "Select a guideline", choices: guideNames.map((name) => ({ name, value: name })) });
9973
+ return await select3({
9974
+ message: "Select a guideline",
9975
+ choices: guideNames.map((name) => ({ name, value: name }))
9976
+ });
9669
9977
  }
9670
9978
  static async getGuideJson(guideName) {
9671
9979
  const guidelineRoot = await Prompter.#getGuidelineRoot();
@@ -9680,13 +9988,18 @@ class Prompter {
9680
9988
  return content;
9681
9989
  }
9682
9990
  static async getUpdateRequest(guideName) {
9683
- return await input3({ message: `What do you want to update in ${guideName}?` });
9991
+ return await input3({
9992
+ message: `What do you want to update in ${guideName}?`
9993
+ });
9684
9994
  }
9685
9995
  async makeTsFileUpdatePrompt({ context, request }) {
9686
9996
  return `You are a senior developer writing TypeScript-based programs using Akan.js, an in-house framework. Here's an overview of the Akan.js framework:
9687
9997
  ${await this.getDocumentation("framework")}
9688
9998
  Please understand the following background information, write code that meets the requirements, verify that it satisfies the validation conditions, and return the result.
9689
9999
 
10000
+ # Code Style
10001
+ - Use double quotes for all string literals in TypeScript/TSX code. Do not use single quotes.
10002
+
9690
10003
  # Background Information
9691
10004
  \`\`\`markdown
9692
10005
  ${context}
@@ -9733,10 +10046,10 @@ import { useEffect as useEffect3, useState as useState3 } from "react";
9733
10046
  import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
9734
10047
  "use client";
9735
10048
  // pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
9736
- import { Logger as Logger12 } from "akanjs/common";
10049
+ import { Logger as Logger11 } from "akanjs/common";
9737
10050
 
9738
10051
  class IncrementalBuilder {
9739
- #logger = new Logger12("IncrementalBuilder");
10052
+ #logger = new Logger11("IncrementalBuilder");
9740
10053
  #app;
9741
10054
  #artifact;
9742
10055
  #watch;