@akanjs/cli 2.1.1-rc.2 → 2.1.1

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.
@@ -329,6 +329,31 @@ var supportedLlmModels = [
329
329
  "deepseek-chat",
330
330
  "deepseek-reasoner"
331
331
  ];
332
+ var parseTypescriptFileBlocks = (text) => {
333
+ const fileBlocks = [];
334
+ const codeBlockRegex = /```(?:typescript|ts|tsx)\s*\n([\s\S]*?)```/gi;
335
+ const filePathRegex = /^\s*\/\/\s*File:\s*(.+?)\s*$/im;
336
+ for (const codeBlock of text.matchAll(codeBlockRegex)) {
337
+ const content = codeBlock[1]?.trim();
338
+ if (!content)
339
+ continue;
340
+ const filePath = filePathRegex.exec(content)?.[1]?.trim();
341
+ if (!filePath)
342
+ continue;
343
+ fileBlocks.push({
344
+ filePath,
345
+ content: content.replace(filePathRegex, "").trim()
346
+ });
347
+ }
348
+ return fileBlocks;
349
+ };
350
+ var preserveTypescriptResponseContent = (previousContent, nextContent) => {
351
+ const previousWrites = parseTypescriptFileBlocks(previousContent);
352
+ const nextWrites = parseTypescriptFileBlocks(nextContent);
353
+ if (previousWrites.length > 0 && nextWrites.length === 0)
354
+ return previousContent;
355
+ return nextContent;
356
+ };
332
357
 
333
358
  class AiSession {
334
359
  static #cacheDir = "node_modules/.cache/akan/aiSession";
@@ -448,7 +473,7 @@ class AiSession {
448
473
  const humanMessage = new HumanMessage(question);
449
474
  this.messageHistory.push(humanMessage);
450
475
  const stream = await AiSession.#chat.stream(this.messageHistory);
451
- let reasoningResponse = "", fullResponse = "", tokenIdx = 0;
476
+ let reasoningResponse = "", fullResponse = "";
452
477
  for await (const chunk of stream) {
453
478
  if (loader.isSpinning())
454
479
  loader.succeed(`${AiSession.#chat.model} responded`);
@@ -469,7 +494,6 @@ class AiSession {
469
494
  fullResponse += content;
470
495
  onChunk(content);
471
496
  }
472
- tokenIdx++;
473
497
  }
474
498
  fullResponse += `
475
499
  `;
@@ -477,7 +501,7 @@ class AiSession {
477
501
  `);
478
502
  this.messageHistory.push(new AIMessage(fullResponse));
479
503
  return { content: fullResponse, messageHistory: this.messageHistory };
480
- } catch (error) {
504
+ } catch {
481
505
  loader.fail(`${AiSession.#chat.model} failed to respond`);
482
506
  throw new Error("Failed to stream response");
483
507
  }
@@ -487,7 +511,8 @@ class AiSession {
487
511
  onReasoning,
488
512
  maxTry = MAX_ASK_TRY,
489
513
  validate,
490
- approve
514
+ approve,
515
+ fallbackToPreviousTypescript
491
516
  } = {}) {
492
517
  for (let tryCount = 0;tryCount < maxTry; tryCount++) {
493
518
  let response = await this.ask(question, { onChunk, onReasoning });
@@ -495,7 +520,14 @@ class AiSession {
495
520
  const validateQuestion = `Double check if the response meets the requirements and conditions, and follow the instructions. If not, rewrite it.
496
521
  ${validate.map((v) => `- ${v}`).join(`
497
522
  `)}`;
498
- response = await this.ask(validateQuestion, { onChunk, onReasoning });
523
+ const validateResponse = await this.ask(validateQuestion, {
524
+ onChunk,
525
+ onReasoning
526
+ });
527
+ response = {
528
+ ...validateResponse,
529
+ content: fallbackToPreviousTypescript ? preserveTypescriptResponseContent(response.content, validateResponse.content) : validateResponse.content
530
+ };
499
531
  }
500
532
  const isConfirmed = approve ? true : await select({
501
533
  message: "Do you want to edit the response?",
@@ -528,15 +560,28 @@ ${validate.map((v) => `- ${v}`).join(`
528
560
  return this;
529
561
  }
530
562
  async writeTypescripts(question, executor, options = {}) {
531
- const content = await this.edit(question, options);
563
+ const content = await this.edit(question, {
564
+ ...options,
565
+ fallbackToPreviousTypescript: true
566
+ });
532
567
  const writes = this.#getTypescriptCodes(content);
568
+ if (!writes.length)
569
+ throw new Error("No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.");
533
570
  for (const write of writes)
534
571
  await executor.writeFile(write.filePath, write.content);
535
572
  return await this.#tryFixTypescripts(writes, executor, options);
536
573
  }
537
- async#editTypescripts(question, options = {}) {
538
- const content = await this.edit(question, options);
539
- return this.#getTypescriptCodes(content);
574
+ async#editTypescripts(question, options = {}, fallbackWrites) {
575
+ const content = await this.edit(question, {
576
+ ...options,
577
+ fallbackToPreviousTypescript: true
578
+ });
579
+ const writes = this.#getTypescriptCodes(content);
580
+ if (!writes.length && fallbackWrites?.length)
581
+ return fallbackWrites;
582
+ if (!writes.length)
583
+ throw new Error("No parseable TypeScript file blocks were found in the AI response. Include `// File: <path>` in each code block.");
584
+ return writes;
540
585
  }
541
586
  async#tryFixTypescripts(writes, executor, options = {}) {
542
587
  const MAX_EDIT_TRY = 5;
@@ -545,13 +590,15 @@ ${validate.map((v) => `- ${v}`).join(`
545
590
  prefix: `\uD83E\uDD16akan-editor`
546
591
  }).start();
547
592
  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 };
593
+ const lintResult = await executor.lint(filePath, { fix: true });
594
+ const typeCheckResult = await executor.typeCheckAsync(filePath);
595
+ const hasTypeErrors = typeCheckResult.fileErrors.length > 0;
596
+ const hasLintErrors = lintResult.errors.length > 0;
597
+ const needFix = hasTypeErrors || hasLintErrors;
598
+ return { filePath, typeCheckResult, lintResult, needFix };
552
599
  }));
553
- const needFix = fileChecks.some((fileCheck) => fileCheck.needFix);
554
- if (needFix) {
600
+ const hasAnyFix = fileChecks.some((fileCheck) => fileCheck.needFix);
601
+ if (hasAnyFix) {
555
602
  loader.fail("Type checking and linting has some errors, try to fix them");
556
603
  fileChecks.forEach((fileCheck) => {
557
604
  Logger2.rawLog(`TypeCheck Result
@@ -567,7 +614,7 @@ ${fileCheck.lintResult.message}`);
567
614
  ...options,
568
615
  validate: undefined,
569
616
  approve: true
570
- });
617
+ }, writes);
571
618
  for (const write of writes)
572
619
  await executor.writeFile(write.filePath, write.content);
573
620
  } else {
@@ -578,21 +625,7 @@ ${fileCheck.lintResult.message}`);
578
625
  throw new Error("Failed to create scalar");
579
626
  }
580
627
  #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);
628
+ return parseTypescriptFileBlocks(text);
596
629
  }
597
630
  async editMarkdown(request, options = {}) {
598
631
  const content = await this.edit(request, options);
@@ -610,21 +643,26 @@ ${fileCheck.lintResult.message}`);
610
643
  }
611
644
  // pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
612
645
  import path9 from "path";
613
- import { Logger as Logger6 } from "akanjs/common";
646
+ import { Logger as Logger5 } from "akanjs/common";
614
647
 
615
648
  // pkgs/@akanjs/devkit/executors.ts
616
649
  import {
617
650
  exec,
618
651
  fork,
619
- spawn
652
+ spawn as spawn2
620
653
  } from "child_process";
621
654
  import { readFileSync as readFileSync3 } from "fs";
622
- import { copyFile, mkdir as mkdir2, readdir as readDirEntries, stat as stat2 } from "fs/promises";
655
+ import {
656
+ copyFile,
657
+ mkdir as mkdir2,
658
+ readdir as readDirEntries,
659
+ stat as stat2
660
+ } from "fs/promises";
623
661
  import path7 from "path";
624
662
  import {
625
663
  capitalize,
626
664
  isRouteSourceFile,
627
- Logger as Logger4,
665
+ Logger as Logger3,
628
666
  parseRouteModuleKey,
629
667
  validatePageSourceFile,
630
668
  validateSubRoutePageKey
@@ -987,23 +1025,146 @@ import path2 from "path";
987
1025
  var getDirname = (url) => path2.dirname(new URL(url).pathname);
988
1026
 
989
1027
  // pkgs/@akanjs/devkit/linter.ts
1028
+ import { spawn } from "child_process";
990
1029
  import { existsSync, readFileSync } from "fs";
991
- import * as path3 from "path";
992
- import { Logger as Logger3 } from "akanjs/common";
1030
+ import path3 from "path";
993
1031
  import chalk2 from "chalk";
994
1032
 
995
1033
  class Linter {
996
- #logger = new Logger3("Linter");
997
1034
  lintRoot;
1035
+ #biomeBin;
998
1036
  constructor(cwdPath) {
999
- this.lintRoot = this.#findEslintRootPath(cwdPath);
1037
+ this.lintRoot = this.#findBiomeRootPath(cwdPath);
1038
+ const localBiomeBin = path3.join(this.lintRoot, "node_modules/.bin/biome");
1039
+ this.#biomeBin = existsSync(localBiomeBin) ? localBiomeBin : "biome";
1000
1040
  }
1001
- #findEslintRootPath(dir) {
1002
- const configPath2 = path3.join(dir, "eslint.config.ts");
1041
+ #findBiomeRootPath(dir) {
1042
+ const configPath2 = path3.join(dir, "biome.json");
1003
1043
  if (existsSync(configPath2))
1004
1044
  return dir;
1005
1045
  const parentDir = path3.dirname(dir);
1006
- return this.#findEslintRootPath(parentDir);
1046
+ if (parentDir === dir)
1047
+ throw new Error(`biome.json not found from ${dir}`);
1048
+ return this.#findBiomeRootPath(parentDir);
1049
+ }
1050
+ #toBiomePath(filePath) {
1051
+ const relativePath = path3.relative(this.lintRoot, filePath);
1052
+ if (!relativePath.startsWith("..") && !path3.isAbsolute(relativePath))
1053
+ return relativePath;
1054
+ return filePath;
1055
+ }
1056
+ #resolveFilePath(filePath) {
1057
+ return path3.isAbsolute(filePath) ? filePath : path3.join(this.lintRoot, filePath);
1058
+ }
1059
+ async#runBiome(args, input2) {
1060
+ return await new Promise((resolve, reject) => {
1061
+ const proc = spawn(this.#biomeBin, args, {
1062
+ cwd: this.lintRoot,
1063
+ stdio: ["pipe", "pipe", "pipe"]
1064
+ });
1065
+ let stdout = "";
1066
+ let stderr = "";
1067
+ proc.stdout.on("data", (data) => {
1068
+ stdout += data.toString();
1069
+ });
1070
+ proc.stderr.on("data", (data) => {
1071
+ stderr += data.toString();
1072
+ });
1073
+ proc.on("error", reject);
1074
+ proc.on("close", (code) => resolve({ stdout, stderr, code }));
1075
+ proc.stdin.end(input2);
1076
+ });
1077
+ }
1078
+ #parseBiomeReport(output) {
1079
+ const jsonStart = output.indexOf("{");
1080
+ const jsonEnd = output.lastIndexOf("}");
1081
+ if (jsonStart === -1 || jsonEnd === -1 || jsonEnd < jsonStart)
1082
+ throw new Error(output.trim() || "No Biome JSON output");
1083
+ return JSON.parse(output.slice(jsonStart, jsonEnd + 1));
1084
+ }
1085
+ #diagnosticFilePath(diagnostic, fallbackFilePath) {
1086
+ const diagnosticPath = diagnostic.location?.path;
1087
+ if (!diagnosticPath)
1088
+ return fallbackFilePath;
1089
+ return path3.isAbsolute(diagnosticPath) ? diagnosticPath : path3.join(this.lintRoot, diagnosticPath);
1090
+ }
1091
+ #createLintMessage(diagnostic) {
1092
+ const start = diagnostic.location?.start;
1093
+ const end = diagnostic.location?.end;
1094
+ return {
1095
+ line: Math.max(1, start?.line ?? 1),
1096
+ column: Math.max(1, start?.column ?? 1),
1097
+ endLine: end?.line,
1098
+ endColumn: end?.column,
1099
+ message: diagnostic.message,
1100
+ ruleId: diagnostic.category ?? null,
1101
+ severity: diagnostic.severity === "error" ? 2 : 1
1102
+ };
1103
+ }
1104
+ #toLintResults(report, filePath) {
1105
+ const resultsByPath = new Map;
1106
+ for (const diagnostic of report.diagnostics ?? []) {
1107
+ if (diagnostic.severity !== "error" && diagnostic.severity !== "warning")
1108
+ continue;
1109
+ const diagnosticFilePath = this.#diagnosticFilePath(diagnostic, filePath);
1110
+ const result = resultsByPath.get(diagnosticFilePath) ?? {
1111
+ filePath: diagnosticFilePath,
1112
+ messages: [],
1113
+ errorCount: 0,
1114
+ warningCount: 0,
1115
+ fixableErrorCount: 0,
1116
+ fixableWarningCount: 0
1117
+ };
1118
+ const message = this.#createLintMessage(diagnostic);
1119
+ result.messages.push(message);
1120
+ if (message.severity === 2)
1121
+ result.errorCount += 1;
1122
+ else
1123
+ result.warningCount += 1;
1124
+ resultsByPath.set(diagnosticFilePath, result);
1125
+ }
1126
+ return [
1127
+ resultsByPath.get(filePath) ?? {
1128
+ filePath,
1129
+ messages: [],
1130
+ errorCount: 0,
1131
+ warningCount: 0,
1132
+ fixableErrorCount: 0,
1133
+ fixableWarningCount: 0
1134
+ },
1135
+ ...[...resultsByPath.entries()].filter(([resultPath]) => resultPath !== filePath).map(([, result]) => result)
1136
+ ];
1137
+ }
1138
+ #splitMessages(results) {
1139
+ const messages = results.flatMap((result) => result.messages);
1140
+ return {
1141
+ errors: messages.filter((message) => message.severity === 2),
1142
+ warnings: messages.filter((message) => message.severity === 1)
1143
+ };
1144
+ }
1145
+ async#checkFile(filePath, { write = false } = {}) {
1146
+ const originalContent = existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
1147
+ const { stdout, stderr } = await this.#runBiome([
1148
+ "check",
1149
+ ...write ? ["--write"] : [],
1150
+ "--reporter=json",
1151
+ "--max-diagnostics=none",
1152
+ "--no-errors-on-unmatched",
1153
+ "--config-path",
1154
+ path3.join(this.lintRoot, "biome.json"),
1155
+ this.#toBiomePath(filePath)
1156
+ ]);
1157
+ const report = this.#parseBiomeReport(stdout || stderr);
1158
+ const results = this.#toLintResults(report, filePath);
1159
+ const { errors, warnings } = this.#splitMessages(results);
1160
+ const output = write && existsSync(filePath) ? readFileSync(filePath, "utf8") : undefined;
1161
+ return {
1162
+ fixed: write && output !== originalContent,
1163
+ output,
1164
+ results,
1165
+ errors,
1166
+ warnings
1167
+ };
1007
1168
  }
1008
1169
  async lint(filePath, { fix = false, dryRun = false } = {}) {
1009
1170
  if (fix)
@@ -1011,9 +1172,10 @@ class Linter {
1011
1172
  return await this.lintFile(filePath);
1012
1173
  }
1013
1174
  async lintFile(filePath) {
1014
- if (!existsSync(filePath))
1175
+ const resolvedFilePath = this.#resolveFilePath(filePath);
1176
+ if (!existsSync(resolvedFilePath))
1015
1177
  throw new Error(`File not found: ${filePath}`);
1016
- return { fixed: false, results: [], errors: [], warnings: [] };
1178
+ return await this.#checkFile(resolvedFilePath);
1017
1179
  }
1018
1180
  formatLintResults(results) {
1019
1181
  if (results.length === 0)
@@ -1033,12 +1195,12 @@ ${chalk2.cyan(result.filePath)}`);
1033
1195
  const sourceContent = readFileSync(result.filePath, "utf8");
1034
1196
  sourceLines = sourceContent.split(`
1035
1197
  `);
1036
- } catch (error) {}
1198
+ } catch {}
1037
1199
  }
1038
1200
  result.messages.forEach((message) => {
1039
1201
  const type = message.severity === 2 ? "error" : "warning";
1040
1202
  const typeColor = message.severity === 2 ? chalk2.red : chalk2.yellow;
1041
- const icon = message.severity === 2 ? "\u274C" : "\u26A0\uFE0F";
1203
+ const icon = message.severity === 2 ? "x" : "!";
1042
1204
  const ruleInfo = message.ruleId ? chalk2.dim(` (${message.ruleId})`) : "";
1043
1205
  output.push(`
1044
1206
  ${icon} ${typeColor(type)}: ${message.message}${ruleInfo}`);
@@ -1057,7 +1219,7 @@ ${chalk2.dim(`${lineNumber} |`)} ${sourceLine}`);
1057
1219
  }
1058
1220
  });
1059
1221
  if (totalErrors === 0 && totalWarnings === 0)
1060
- return chalk2.bold("\u2705 No ESLint errors or warnings found");
1222
+ return chalk2.bold("No Biome errors or warnings found");
1061
1223
  const errorText = totalErrors > 0 ? chalk2.red(`${totalErrors} error(s)`) : "0 errors";
1062
1224
  const warningText = totalWarnings > 0 ? chalk2.yellow(`${totalWarnings} warning(s)`) : "0 warnings";
1063
1225
  const summary = [`
@@ -1072,23 +1234,26 @@ ${errorText}, ${warningText} found`];
1072
1234
  column: message.column,
1073
1235
  message: message.message,
1074
1236
  ruleId: message.ruleId,
1075
- severity: message.severity === 2 ? "error" : "warning",
1076
- fix: message.fix,
1077
- suggestions: message.suggestions
1237
+ severity: message.severity === 2 ? "error" : "warning"
1078
1238
  })));
1079
1239
  const stats = results.reduce((acc, result) => ({
1080
1240
  errorCount: acc.errorCount + result.errorCount,
1081
1241
  warningCount: acc.warningCount + result.warningCount,
1082
1242
  fixableErrorCount: acc.fixableErrorCount + result.fixableErrorCount,
1083
1243
  fixableWarningCount: acc.fixableWarningCount + result.fixableWarningCount
1084
- }), { errorCount: 0, warningCount: 0, fixableErrorCount: 0, fixableWarningCount: 0 });
1244
+ }), {
1245
+ errorCount: 0,
1246
+ warningCount: 0,
1247
+ fixableErrorCount: 0,
1248
+ fixableWarningCount: 0
1249
+ });
1085
1250
  return { results, details, stats };
1086
1251
  }
1087
1252
  async hasNoLintErrors(filePath) {
1088
1253
  try {
1089
1254
  const { results } = await this.lintFile(filePath);
1090
1255
  return results.every((result) => result.errorCount === 0);
1091
- } catch (error) {
1256
+ } catch {
1092
1257
  return false;
1093
1258
  }
1094
1259
  }
@@ -1101,12 +1266,28 @@ ${errorText}, ${warningText} found`];
1101
1266
  return results.flatMap((result) => result.messages.filter((message) => message.severity === 1));
1102
1267
  }
1103
1268
  async fixFile(filePath, dryRun = false) {
1104
- if (!existsSync(filePath))
1269
+ const resolvedFilePath = this.#resolveFilePath(filePath);
1270
+ if (!existsSync(resolvedFilePath))
1105
1271
  throw new Error(`File not found: ${filePath}`);
1106
- return { fixed: false, output: undefined, results: [], errors: [], warnings: [] };
1272
+ if (!dryRun)
1273
+ return await this.#checkFile(resolvedFilePath, { write: true });
1274
+ const source = readFileSync(resolvedFilePath, "utf8");
1275
+ const { stdout } = await this.#runBiome([
1276
+ "check",
1277
+ "--write",
1278
+ "--config-path",
1279
+ path3.join(this.lintRoot, "biome.json"),
1280
+ "--stdin-file-path",
1281
+ this.#toBiomePath(resolvedFilePath)
1282
+ ], source);
1283
+ const lintResult = await this.lintFile(resolvedFilePath);
1284
+ return { ...lintResult, fixed: stdout !== source, output: stdout };
1107
1285
  }
1108
1286
  async getConfigForFile(filePath) {
1109
- return {};
1287
+ const resolvedFilePath = this.#resolveFilePath(filePath);
1288
+ if (!existsSync(resolvedFilePath))
1289
+ throw new Error(`File not found: ${filePath}`);
1290
+ return JSON.parse(readFileSync(path3.join(this.lintRoot, "biome.json"), "utf8"));
1110
1291
  }
1111
1292
  async getProblematicRules(filePath) {
1112
1293
  const { results } = await this.lintFile(filePath);
@@ -1496,23 +1677,23 @@ async function assertScanConvention(exec, libRoot) {
1496
1677
  files.filter((filename) => !appRootAllowedFiles.has(filename)).forEach((filename) => {
1497
1678
  addViolation(filename, "unsupported app root file");
1498
1679
  });
1499
- dirs.filter((dirname2) => !appRootAllowedDirs.has(dirname2)).forEach((dirname2) => {
1500
- addViolation(dirname2, "unsupported app root folder");
1680
+ dirs.filter((dirname) => !appRootAllowedDirs.has(dirname)).forEach((dirname) => {
1681
+ addViolation(dirname, "unsupported app root folder");
1501
1682
  });
1502
1683
  }
1503
1684
  libRoot.files.filter((filename) => !isAllowedLibRootFile(filename)).forEach((filename) => {
1504
1685
  addViolation(path5.join("lib", filename), "unsupported lib root file");
1505
1686
  });
1506
- libRoot.dirs.filter((dirname2) => dirname2.startsWith("__") && !internalLibDirs.has(dirname2)).forEach((dirname2) => {
1507
- addViolation(path5.join("lib", dirname2), "unsupported internal lib folder");
1687
+ libRoot.dirs.filter((dirname) => dirname.startsWith("__") && !internalLibDirs.has(dirname)).forEach((dirname) => {
1688
+ addViolation(path5.join("lib", dirname), "unsupported internal lib folder");
1508
1689
  });
1509
- const databaseDirs = libRoot.dirs.filter((dirname2) => !dirname2.startsWith("_"));
1510
- const serviceDirs = libRoot.dirs.filter((dirname2) => dirname2.startsWith("_") && !dirname2.startsWith("__"));
1690
+ const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
1691
+ const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
1511
1692
  const scalarDirs = await exec.readdir("lib/__scalar");
1512
1693
  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)))
1694
+ ...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path5.join("lib", dirname))),
1695
+ ...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path5.join("lib", dirname))),
1696
+ ...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path5.join("lib/__scalar", dirname)))
1516
1697
  ]);
1517
1698
  if (violations.length > 0) {
1518
1699
  throw new Error(`[scan-convention]
@@ -1522,8 +1703,8 @@ ${violations.sort().map((violation) => `- ${violation}`).join(`
1522
1703
  }
1523
1704
  async function validateModuleFiles(exec, violations, kind, modulePath) {
1524
1705
  const { files, dirs } = await exec.getFilesAndDirs(modulePath);
1525
- dirs.forEach((dirname2) => {
1526
- violations.push(`${getScanPath(exec, path5.join(modulePath, dirname2))}: unsupported module folder`);
1706
+ dirs.forEach((dirname) => {
1707
+ violations.push(`${getScanPath(exec, path5.join(modulePath, dirname))}: unsupported module folder`);
1527
1708
  });
1528
1709
  files.forEach((filename) => {
1529
1710
  const filePath = path5.join(modulePath, filename);
@@ -1622,9 +1803,9 @@ class ScanInfo {
1622
1803
  files.zone.databases.push(name);
1623
1804
  });
1624
1805
  }),
1625
- ...serviceDirs.map(async (dirname2) => {
1626
- const name = dirname2.slice(1);
1627
- const filenames = await exec.readdir(path5.join("lib", dirname2));
1806
+ ...serviceDirs.map(async (dirname) => {
1807
+ const name = dirname.slice(1);
1808
+ const filenames = await exec.readdir(path5.join("lib", dirname));
1628
1809
  filenames.forEach((filename) => {
1629
1810
  if (filename.endsWith(".dictionary.ts"))
1630
1811
  files.dictionary.services.push(name);
@@ -2193,8 +2374,13 @@ class CommandExecutionError extends Error {
2193
2374
  const displayCommand = formatCommandForDisplay(command, args);
2194
2375
  const status = signal ? `signal: ${signal}` : `exit code: ${code ?? "unknown"}`;
2195
2376
  const output = (stderr || stdout).trim();
2196
- super([`Command failed: ${displayCommand}`, `cwd: ${cwd}`, status, output ? `
2197
- ${output}` : ""].join(`
2377
+ super([
2378
+ `Command failed: ${displayCommand}`,
2379
+ `cwd: ${cwd}`,
2380
+ status,
2381
+ output ? `
2382
+ ${output}` : ""
2383
+ ].join(`
2198
2384
  `), {
2199
2385
  cause
2200
2386
  });
@@ -2243,7 +2429,13 @@ var parseEnvFile = (envPath) => {
2243
2429
  }
2244
2430
  return env;
2245
2431
  };
2246
- var PAGE_ROUTE_EXPORTS = new Set(["default", "pageConfig", "head", "generateHead", "Loading"]);
2432
+ var PAGE_ROUTE_EXPORTS = new Set([
2433
+ "default",
2434
+ "pageConfig",
2435
+ "head",
2436
+ "generateHead",
2437
+ "Loading"
2438
+ ]);
2247
2439
  var ROOT_LAYOUT_EXPORTS = new Set([
2248
2440
  "default",
2249
2441
  "head",
@@ -2256,7 +2448,12 @@ var ROOT_LAYOUT_EXPORTS = new Set([
2256
2448
  "gaTrackingId",
2257
2449
  "Loading"
2258
2450
  ]);
2259
- var LAYOUT_ROUTE_EXPORTS = new Set(["default", "head", "generateHead", "Loading"]);
2451
+ var LAYOUT_ROUTE_EXPORTS = new Set([
2452
+ "default",
2453
+ "head",
2454
+ "generateHead",
2455
+ "Loading"
2456
+ ]);
2260
2457
  function validateRouteSourceExports(source, filePath, kind, options = {}) {
2261
2458
  const sourceFile = ts3.createSourceFile(filePath, source, ts3.ScriptTarget.Latest, true, ts3.ScriptKind.TSX);
2262
2459
  const allowed = kind === "page" ? PAGE_ROUTE_EXPORTS : options.rootLayout ? ROOT_LAYOUT_EXPORTS : LAYOUT_ROUTE_EXPORTS;
@@ -2325,16 +2522,16 @@ class Executor {
2325
2522
  linter = null;
2326
2523
  constructor(name, cwdPath) {
2327
2524
  this.name = name;
2328
- this.logger = new Logger4(name);
2525
+ this.logger = new Logger3(name);
2329
2526
  this.logs = [];
2330
2527
  this.cwdPath = cwdPath;
2331
2528
  }
2332
2529
  #stdout(data) {
2333
2530
  if (Executor.verbose)
2334
- Logger4.raw(chalk4.dim(data.toString()));
2531
+ Logger3.raw(chalk4.dim(data.toString()));
2335
2532
  }
2336
2533
  #stderr(data) {
2337
- Logger4.raw(chalk4.red(data.toString()));
2534
+ Logger3.raw(chalk4.red(data.toString()));
2338
2535
  }
2339
2536
  exec(command, options = {}) {
2340
2537
  const cwd = options.cwd?.toString() ?? this.cwdPath;
@@ -2378,7 +2575,7 @@ class Executor {
2378
2575
  }
2379
2576
  spawn(command, args = [], options = {}) {
2380
2577
  const cwd = options.cwd?.toString() ?? this.cwdPath;
2381
- const proc = spawn(command, args, {
2578
+ const proc = spawn2(command, args, {
2382
2579
  cwd: this.cwdPath,
2383
2580
  ...options
2384
2581
  });
@@ -2424,7 +2621,7 @@ class Executor {
2424
2621
  });
2425
2622
  }
2426
2623
  spawnSync(command, args = [], options = {}) {
2427
- const proc = spawn(command, args, {
2624
+ const proc = spawn2(command, args, {
2428
2625
  cwd: this.cwdPath,
2429
2626
  ...options
2430
2627
  });
@@ -2556,7 +2753,7 @@ class Executor {
2556
2753
  contentStr = currentContent;
2557
2754
  } else {
2558
2755
  await FileSys.writeText(writePath, contentStr);
2559
- if (Logger4.isVerbose())
2756
+ if (Logger3.isVerbose())
2560
2757
  this.logger.rawLog(chalk4.yellow(`File Update: ${filePath}`));
2561
2758
  }
2562
2759
  } else {
@@ -2603,7 +2800,11 @@ class Executor {
2603
2800
  this.logger.debug(msg);
2604
2801
  return this;
2605
2802
  }
2606
- spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
2803
+ spinning(msg, {
2804
+ prefix = `${this.emoji}${this.name}`,
2805
+ indent = 0,
2806
+ enableSpin = !Executor.verbose
2807
+ } = {}) {
2607
2808
  return new Spinner(msg, { prefix, indent, enableSpin }).start();
2608
2809
  }
2609
2810
  #tsconfig = null;
@@ -2632,7 +2833,9 @@ class Executor {
2632
2833
  this.#tsconfig = tsconfig;
2633
2834
  }
2634
2835
  #packageJson = null;
2635
- async getPackageJson({ refresh } = {}) {
2836
+ async getPackageJson({
2837
+ refresh
2838
+ } = {}) {
2636
2839
  if (this.#packageJson && !refresh)
2637
2840
  return this.#packageJson;
2638
2841
  const packageJson = await this.readJson("package.json");
@@ -2665,8 +2868,8 @@ class Executor {
2665
2868
  return null;
2666
2869
  const filename = typeof result === "object" ? result.filename : path7.basename(targetPath).replace(".js", ".ts");
2667
2870
  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}`);
2871
+ const dirname2 = path7.dirname(targetPath);
2872
+ const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), `${dirname2}/${filename}`);
2670
2873
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
2671
2874
  return this.writeFile(convertedTargetPath, content, { overwrite });
2672
2875
  } else if (targetPath.endsWith(".template")) {
@@ -2680,9 +2883,9 @@ class Executor {
2680
2883
  } else if (staticTemplateFileExtensions.has(path7.extname(targetPath).toLowerCase())) {
2681
2884
  const convertedTargetPath = Object.entries(dict).reduce((path8, [key, value]) => path8.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
2682
2885
  const writePath = this.getPath(convertedTargetPath);
2683
- const dirname3 = path7.dirname(writePath);
2684
- if (!await FileSys.dirExists(dirname3))
2685
- await mkdir2(dirname3, { recursive: true });
2886
+ const dirname2 = path7.dirname(writePath);
2887
+ if (!await FileSys.dirExists(dirname2))
2888
+ await mkdir2(dirname2, { recursive: true });
2686
2889
  await copyFile(templatePath, writePath);
2687
2890
  this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
2688
2891
  return { filePath: writePath, content: "" };
@@ -2748,7 +2951,10 @@ class Executor {
2748
2951
  async applyTemplate(options) {
2749
2952
  const dict = {
2750
2953
  ...options.dict ?? {},
2751
- ...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
2954
+ ...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [
2955
+ capitalize(key),
2956
+ capitalize(value)
2957
+ ]))
2752
2958
  };
2753
2959
  return this._applyTemplate({ ...options, dict });
2754
2960
  }
@@ -2763,6 +2969,48 @@ class Executor {
2763
2969
  const message = typeChecker.formatDiagnostics(fileDiagnostics);
2764
2970
  return { fileDiagnostics, fileErrors, fileWarnings, message };
2765
2971
  }
2972
+ async typeCheckAsync(filePath) {
2973
+ const path8 = this.getPath(filePath);
2974
+ const entry = await this.#resolveTypecheckWorkerEntry();
2975
+ const proc = Bun.spawn([process.execPath, entry], {
2976
+ cwd: this.cwdPath,
2977
+ env: {
2978
+ ...process.env,
2979
+ AKAN_TYPECHECK_CWD: this.cwdPath,
2980
+ AKAN_TYPECHECK_FILE: path8
2981
+ },
2982
+ stdout: "pipe",
2983
+ stderr: "pipe"
2984
+ });
2985
+ const [stdout, stderr, exitCode] = await Promise.all([
2986
+ new Response(proc.stdout).text(),
2987
+ new Response(proc.stderr).text(),
2988
+ proc.exited
2989
+ ]);
2990
+ if (exitCode !== 0)
2991
+ throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
2992
+ const result = JSON.parse(stdout);
2993
+ return {
2994
+ fileDiagnostics: Array.from({ length: result.fileDiagnosticsCount }),
2995
+ fileErrors: Array.from({ length: result.fileErrorsCount }),
2996
+ fileWarnings: Array.from({ length: result.fileWarningsCount }),
2997
+ message: result.message
2998
+ };
2999
+ }
3000
+ async#resolveTypecheckWorkerEntry() {
3001
+ const dirname2 = getDirname(import.meta.url);
3002
+ const candidates = [
3003
+ path7.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
3004
+ path7.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
3005
+ path7.join(dirname2, "typecheck/typecheck.proc.ts"),
3006
+ path7.join(dirname2, "typecheck.proc.js"),
3007
+ path7.join(dirname2, "typecheck.proc.ts")
3008
+ ];
3009
+ for (const candidate of candidates)
3010
+ if (await Bun.file(candidate).exists())
3011
+ return candidate;
3012
+ throw new Error(`[devkit] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
3013
+ }
2766
3014
  getLinter() {
2767
3015
  this.linter ??= new Linter(this.cwdPath);
2768
3016
  return this.linter;
@@ -2810,7 +3058,15 @@ class WorkspaceExecutor extends Executor {
2810
3058
  const env = sourceEnv.AKAN_PUBLIC_ENV ?? "debug";
2811
3059
  if (!env)
2812
3060
  throw new Error("AKAN_PUBLIC_ENV is not set");
2813
- return { ...appName ? { appName } : {}, workspaceRoot, repoName, serveDomain, env, portOffset, workspaceId };
3061
+ return {
3062
+ ...appName ? { appName } : {},
3063
+ workspaceRoot,
3064
+ repoName,
3065
+ serveDomain,
3066
+ env,
3067
+ portOffset,
3068
+ workspaceId
3069
+ };
2814
3070
  }
2815
3071
  getWorkspaceId({
2816
3072
  allowEmpty
@@ -2834,7 +3090,10 @@ class WorkspaceExecutor extends Executor {
2834
3090
  return await this.#getDirHasFile(`${this.workspaceRoot}/libs`, "akan.config.ts");
2835
3091
  }
2836
3092
  async getSyss() {
2837
- const [appNames, libNames] = await Promise.all([this.getApps(), this.getLibs()]);
3093
+ const [appNames, libNames] = await Promise.all([
3094
+ this.getApps(),
3095
+ this.getLibs()
3096
+ ]);
2838
3097
  return [appNames, libNames];
2839
3098
  }
2840
3099
  async getPkgs() {
@@ -2843,7 +3102,11 @@ class WorkspaceExecutor extends Executor {
2843
3102
  return await this.#getDirHasFile(`${this.workspaceRoot}/pkgs`, "package.json");
2844
3103
  }
2845
3104
  async getExecs() {
2846
- const [appNames, libNames, pkgNames] = await Promise.all([this.getApps(), this.getLibs(), this.getPkgs()]);
3105
+ const [appNames, libNames, pkgNames] = await Promise.all([
3106
+ this.getApps(),
3107
+ this.getLibs(),
3108
+ this.getPkgs()
3109
+ ]);
2847
3110
  return [appNames, libNames, pkgNames];
2848
3111
  }
2849
3112
  async setPkgTsPaths(name) {
@@ -2861,7 +3124,10 @@ class WorkspaceExecutor extends Executor {
2861
3124
  async unsetPkgTsPaths(name) {
2862
3125
  const rootTsConfig = await this.readJson("tsconfig.json");
2863
3126
  const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths ?? {}).filter((key) => key !== name && key !== `${name}/*`);
2864
- rootTsConfig.compilerOptions.paths = Object.fromEntries(filteredKeys.map((key) => [key, rootTsConfig.compilerOptions.paths?.[key] ?? []]));
3127
+ rootTsConfig.compilerOptions.paths = Object.fromEntries(filteredKeys.map((key) => [
3128
+ key,
3129
+ rootTsConfig.compilerOptions.paths?.[key] ?? []
3130
+ ]));
2865
3131
  if (rootTsConfig.references) {
2866
3132
  rootTsConfig.references = rootTsConfig.references.filter((ref) => ref.path !== `./pkgs/${name}/tsconfig.json`);
2867
3133
  }
@@ -2870,12 +3136,12 @@ class WorkspaceExecutor extends Executor {
2870
3136
  }
2871
3137
  async getDirInModule(basePath2, name) {
2872
3138
  const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
2873
- const getDirs = async (dirname3, maxDepth = 3, results = [], prefix = "") => {
2874
- const dirs = await this.readdir(dirname3);
3139
+ const getDirs = async (dirname2, maxDepth = 3, results = [], prefix = "") => {
3140
+ const dirs = await this.readdir(dirname2);
2875
3141
  await Promise.all(dirs.map(async (dir) => {
2876
3142
  if (dir.includes("_") || AVOID_DIRS.includes(dir))
2877
3143
  return;
2878
- const dirPath = path7.join(dirname3, dir);
3144
+ const dirPath = path7.join(dirname2, dir);
2879
3145
  if ((await stat2(dirPath)).isDirectory()) {
2880
3146
  results.push(`${prefix}${dir}`);
2881
3147
  if (maxDepth > 0)
@@ -2895,12 +3161,12 @@ class WorkspaceExecutor extends Executor {
2895
3161
  }
2896
3162
  async#getDirHasFile(basePath2, targetFilename) {
2897
3163
  const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
2898
- const getDirs = async (dirname3, maxDepth = 3, results = [], prefix = "") => {
2899
- const dirs = await this.readdir(dirname3);
3164
+ const getDirs = async (dirname2, maxDepth = 3, results = [], prefix = "") => {
3165
+ const dirs = await this.readdir(dirname2);
2900
3166
  await Promise.all(dirs.map(async (dir) => {
2901
3167
  if (AVOID_DIRS.includes(dir))
2902
3168
  return;
2903
- const dirPath = path7.join(dirname3, dir);
3169
+ const dirPath = path7.join(dirname2, dir);
2904
3170
  if ((await stat2(dirPath)).isDirectory()) {
2905
3171
  const hasTargetFile = await FileSys.fileExists(path7.join(dirPath, targetFilename));
2906
3172
  if (hasTargetFile)
@@ -2953,7 +3219,11 @@ class SysExecutor extends Executor {
2953
3219
  name;
2954
3220
  type;
2955
3221
  emoji;
2956
- constructor({ workspace = WorkspaceExecutor.fromRoot(), name, type }) {
3222
+ constructor({
3223
+ workspace = WorkspaceExecutor.fromRoot(),
3224
+ name,
3225
+ type
3226
+ }) {
2957
3227
  super(name, `${workspace.workspaceRoot}/${type}s/${name}`);
2958
3228
  this.workspace = workspace;
2959
3229
  this.name = name;
@@ -3056,7 +3326,10 @@ class SysExecutor extends Executor {
3056
3326
  },
3057
3327
  devDependencies: {
3058
3328
  ...Object.fromEntries(Object.entries(libPackageJson.devDependencies ?? {}).filter(([dep]) => !dependencySet.has(dep))),
3059
- ...Object.fromEntries(devDependencies.filter((dep) => rootPackageJson.dependencies?.[dep] || rootPackageJson.devDependencies?.[dep]).sort().map((dep) => [dep, rootPackageJson.devDependencies?.[dep] ?? rootPackageJson.dependencies?.[dep]]))
3329
+ ...Object.fromEntries(devDependencies.filter((dep) => rootPackageJson.dependencies?.[dep] || rootPackageJson.devDependencies?.[dep]).sort().map((dep) => [
3330
+ dep,
3331
+ rootPackageJson.devDependencies?.[dep] ?? rootPackageJson.dependencies?.[dep]
3332
+ ]))
3060
3333
  }
3061
3334
  };
3062
3335
  await this.setPackageJson(libPkgJsonWithDeps);
@@ -3123,7 +3396,11 @@ class SysExecutor extends Executor {
3123
3396
  ...await LibExecutor.from(this, lib).getConstantFiles(),
3124
3397
  ...await LibExecutor.from(this, lib).getScalarConstantFiles()
3125
3398
  ]));
3126
- return [...sysContantFiles, ...sysScalarConstantFiles, ...libConstantFiles.flat()];
3399
+ return [
3400
+ ...sysContantFiles,
3401
+ ...sysScalarConstantFiles,
3402
+ ...libConstantFiles.flat()
3403
+ ];
3127
3404
  }
3128
3405
  async getDictionaryFiles() {
3129
3406
  const modules = await this.getModules();
@@ -3132,7 +3409,10 @@ class SysExecutor extends Executor {
3132
3409
  async applyTemplate(options) {
3133
3410
  const dict = {
3134
3411
  ...options.dict ?? {},
3135
- ...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
3412
+ ...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [
3413
+ capitalize(key),
3414
+ capitalize(value)
3415
+ ]))
3136
3416
  };
3137
3417
  const scanInfo = await this.scan();
3138
3418
  const fileContents = await this._applyTemplate({
@@ -3191,7 +3471,10 @@ class AppExecutor extends SysExecutor {
3191
3471
  if (type === "build") {
3192
3472
  if (await this.exists(this.dist.cwdPath))
3193
3473
  await this.dist.exec(`rm -rf ${this.dist.cwdPath}`);
3194
- await Promise.all([this.dist.mkdir("private"), this.dist.mkdir("public")]);
3474
+ await Promise.all([
3475
+ this.dist.mkdir("private"),
3476
+ this.dist.mkdir("public")
3477
+ ]);
3195
3478
  await Promise.all([
3196
3479
  this.cp("private", `${this.dist.cwdPath}/private`),
3197
3480
  this.cp("public", `${this.dist.cwdPath}/public`)
@@ -3234,7 +3517,9 @@ class AppExecutor extends SysExecutor {
3234
3517
  return this.#akanConfig;
3235
3518
  }
3236
3519
  #pageKeys = null;
3237
- async getPageKeys({ refresh } = {}) {
3520
+ async getPageKeys({
3521
+ refresh
3522
+ } = {}) {
3238
3523
  if (this.#pageKeys && !refresh)
3239
3524
  return this.#pageKeys;
3240
3525
  const akanConfig2 = await this.getConfig();
@@ -3283,7 +3568,10 @@ class AppExecutor extends SysExecutor {
3283
3568
  const projectAssetsPath = `${this.cwdPath}/private`;
3284
3569
  const projectPublicLibPath = `${projectPublicPath}/libs`;
3285
3570
  const projectAssetsLibPath = `${projectAssetsPath}/libs`;
3286
- await Promise.all([this.removeDir(projectPublicLibPath), this.removeDir(projectAssetsLibPath)]);
3571
+ await Promise.all([
3572
+ this.removeDir(projectPublicLibPath),
3573
+ this.removeDir(projectAssetsLibPath)
3574
+ ]);
3287
3575
  const targetPublicDeps = [];
3288
3576
  for (const dep of libDeps) {
3289
3577
  if (await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/public`))
@@ -3301,7 +3589,10 @@ class AppExecutor extends SysExecutor {
3301
3589
  ...targetAssetsDeps.map((dep) => this.cp(`${this.workspace.workspaceRoot}/libs/${dep}/private`, `${projectAssetsLibPath}/${dep}`))
3302
3590
  ]);
3303
3591
  }
3304
- async scanSync({ refresh = false, write = true } = {}) {
3592
+ async scanSync({
3593
+ refresh = false,
3594
+ write = true
3595
+ } = {}) {
3305
3596
  const scanInfo = await this.scan({
3306
3597
  refresh,
3307
3598
  write,
@@ -3350,7 +3641,10 @@ class PkgExecutor extends Executor {
3350
3641
  name;
3351
3642
  dist;
3352
3643
  emoji = execEmoji.pkg;
3353
- constructor({ workspace = WorkspaceExecutor.fromRoot(), name }) {
3644
+ constructor({
3645
+ workspace = WorkspaceExecutor.fromRoot(),
3646
+ name
3647
+ }) {
3354
3648
  super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
3355
3649
  this.workspace = workspace;
3356
3650
  this.name = name;
@@ -3414,7 +3708,10 @@ class PkgExecutor extends Executor {
3414
3708
  };
3415
3709
  }
3416
3710
  async updatePackageJsonDependencies(dependencies = [], devDependencies = []) {
3417
- const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
3711
+ const [rootPackageJson, pkgJson] = await Promise.all([
3712
+ this.workspace.getPackageJson(),
3713
+ this.getPackageJson()
3714
+ ]);
3418
3715
  const dependencyMaps = await this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
3419
3716
  const newPkgJson = {
3420
3717
  ...pkgJson,
@@ -3424,7 +3721,10 @@ class PkgExecutor extends Executor {
3424
3721
  return newPkgJson;
3425
3722
  }
3426
3723
  async generateDistPackageJson(dependencies = [], devDependencies = []) {
3427
- const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
3724
+ const [rootPackageJson, pkgJson] = await Promise.all([
3725
+ this.workspace.getPackageJson(),
3726
+ this.getPackageJson()
3727
+ ]);
3428
3728
  const dependencyMaps = await this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
3429
3729
  const distPkgJson = {
3430
3730
  ...pkgJson,
@@ -3440,7 +3740,10 @@ class PkgExecutor extends Executor {
3440
3740
  engines: { bun: ">=1.3.13" },
3441
3741
  ...dependencyMaps
3442
3742
  };
3443
- await Promise.all([this.dist.writeJson("package.json", distPkgJson), this.writeJson("package.json", distPkgJson)]);
3743
+ await Promise.all([
3744
+ this.dist.writeJson("package.json", distPkgJson),
3745
+ this.writeJson("package.json", distPkgJson)
3746
+ ]);
3444
3747
  return distPkgJson;
3445
3748
  }
3446
3749
  async build() {
@@ -3453,7 +3756,10 @@ class PkgExecutor extends Executor {
3453
3756
  await this.cp(`${this.cwdPath}/dist`, this.dist.cwdPath);
3454
3757
  }
3455
3758
  async generateTsconfigJson() {
3456
- const [rootTsconfig, pkgTsconfig] = await Promise.all([this.workspace.getTsConfig(), this.getTsConfig()]);
3759
+ const [rootTsconfig, pkgTsconfig] = await Promise.all([
3760
+ this.workspace.getTsConfig(),
3761
+ this.getTsConfig()
3762
+ ]);
3457
3763
  const tsconfig = {
3458
3764
  ...rootTsconfig,
3459
3765
  ...pkgTsconfig,
@@ -3617,7 +3923,7 @@ var createTunnel = async (service, { app, environment, port = service === "postg
3617
3923
 
3618
3924
  // pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.host.ts
3619
3925
  import path8 from "path";
3620
- import { Logger as Logger5 } from "akanjs/common";
3926
+ import { Logger as Logger4 } from "akanjs/common";
3621
3927
  var builderMsgTypeSet = new Set([
3622
3928
  "build-route-res",
3623
3929
  "builder-ready",
@@ -3629,7 +3935,7 @@ var builderMsgTypeSet = new Set([
3629
3935
  class IncrementalBuilderHost {
3630
3936
  static #restartBaseDelayMs = 1000;
3631
3937
  static #restartMaxDelayMs = 30000;
3632
- logger = new Logger5("IncrementalBuilderHost");
3938
+ logger = new Logger4("IncrementalBuilderHost");
3633
3939
  entry;
3634
3940
  env;
3635
3941
  app;
@@ -3877,7 +4183,7 @@ class BackendImportGraph {
3877
4183
 
3878
4184
  class AkanAppHost {
3879
4185
  app;
3880
- logger = new Logger6("AkanAppHost");
4186
+ logger = new Logger5("AkanAppHost");
3881
4187
  withInk;
3882
4188
  env;
3883
4189
  #backend = null;
@@ -4160,19 +4466,19 @@ class AkanAppHost {
4160
4466
  }
4161
4467
  }
4162
4468
  // pkgs/@akanjs/devkit/applicationBuildReporter.ts
4163
- import { Logger as Logger7 } from "akanjs/common";
4469
+ import { Logger as Logger6 } from "akanjs/common";
4164
4470
 
4165
4471
  class ApplicationBuildReporter {
4166
4472
  static create() {
4167
4473
  return {
4168
- phaseDone: (phase) => Logger7.rawLog(ApplicationBuildReporter.formatPhaseLine(phase))
4474
+ phaseDone: (phase) => Logger6.rawLog(ApplicationBuildReporter.formatPhaseLine(phase))
4169
4475
  };
4170
4476
  }
4171
4477
  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)}`);
4478
+ Logger6.rawLog("");
4479
+ Logger6.rawLog(`Route artifacts: ${result.artifactDir}`);
4480
+ Logger6.rawLog(`Server output: ${result.outputDir}`);
4481
+ Logger6.rawLog(`Done in ${ApplicationBuildReporter.formatDuration(result.durationMs)}`);
4176
4482
  }
4177
4483
  static formatError(error) {
4178
4484
  if (error instanceof AggregateError) {
@@ -4471,13 +4777,13 @@ import path14 from "path";
4471
4777
 
4472
4778
  // pkgs/@akanjs/devkit/transforms/barrelAnalyzer.ts
4473
4779
  import path12 from "path";
4474
- import { Logger as Logger8 } from "akanjs/common";
4780
+ import { Logger as Logger7 } from "akanjs/common";
4475
4781
  var REEXPORT_RE = /(?:^|\n)\s*export\s+(?:type\s+)?(?:(\*)(?:\s+as\s+(\w+))?|\{\s*([^}]*?)\s*\})\s+from\s+(["'])([^"']+)\4;?/g;
4476
4782
  var LOCAL_NAMED_RE = /(?:^|\n)\s*export\s+\{\s*([^}]*?)\s*\}(?!\s*from)/g;
4477
4783
  var CANDIDATE_EXTS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
4478
4784
 
4479
4785
  class BarrelAnalyzer {
4480
- #logger = new Logger8("BarrelAnalyzer");
4786
+ #logger = new Logger7("BarrelAnalyzer");
4481
4787
  #opts;
4482
4788
  #cache = new Map;
4483
4789
  #tsTranspiler = new Bun.Transpiler({ loader: "ts" });
@@ -6177,7 +6483,7 @@ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
6177
6483
  }
6178
6484
  // pkgs/@akanjs/devkit/frontendBuild/cssCompiler.ts
6179
6485
  import path23 from "path";
6180
- import { Logger as Logger9 } from "akanjs/common";
6486
+ import { Logger as Logger8 } from "akanjs/common";
6181
6487
  import { compile } from "tailwindcss";
6182
6488
 
6183
6489
  // pkgs/@akanjs/devkit/frontendBuild/cssImportResolver.ts
@@ -6322,7 +6628,7 @@ var NODE_MODULES_RE3 = /[\\/]node_modules[\\/]/;
6322
6628
  var AKANJS_NODE_MODULE_RE3 = /[\\/]node_modules[\\/]akanjs[\\/]/;
6323
6629
 
6324
6630
  class CssCompiler {
6325
- #logger = new Logger9("CssCompiler");
6631
+ #logger = new Logger8("CssCompiler");
6326
6632
  #transpiler = new Bun.Transpiler({ loader: "tsx" });
6327
6633
  #app;
6328
6634
  #cssImportResolver = null;
@@ -7730,7 +8036,7 @@ class ApplicationBuildRunner {
7730
8036
  import { cp, mkdir as mkdir8, rm as rm3 } from "fs/promises";
7731
8037
 
7732
8038
  // pkgs/@akanjs/devkit/uploadRelease.ts
7733
- import { HttpClient as HttpClient2, Logger as Logger10 } from "akanjs/common";
8039
+ import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
7734
8040
  var spinning = (message) => {
7735
8041
  const spinner = new Spinner(message, { prefix: message, enableSpin: true }).start();
7736
8042
  return spinner;
@@ -7743,7 +8049,7 @@ var uploadRelease = async (appName, {
7743
8049
  os,
7744
8050
  local
7745
8051
  }) => {
7746
- const logger = new Logger10("uploadRelease");
8052
+ const logger = new Logger9("uploadRelease");
7747
8053
  const basePath2 = local ? "http://localhost:8282/backend" : "https://cloud.akanjs.com/backend";
7748
8054
  const httpClient = new HttpClient2(basePath2);
7749
8055
  const buildPath = `${workspaceRoot}/releases/builds/${appName}-release.tar.gz`;
@@ -8729,7 +9035,7 @@ var Workspace = createInternalArgToken("Workspace");
8729
9035
  // pkgs/@akanjs/devkit/commandDecorators/command.ts
8730
9036
  import path36 from "path";
8731
9037
  import { confirm, input as input2, select as select2 } from "@inquirer/prompts";
8732
- import { Logger as Logger11 } from "akanjs/common";
9038
+ import { Logger as Logger10 } from "akanjs/common";
8733
9039
  import chalk6 from "chalk";
8734
9040
  import { program } from "commander";
8735
9041
 
@@ -9014,7 +9320,7 @@ var printCliError = (error) => {
9014
9320
  if (loggedCliErrorMessages.has(message))
9015
9321
  return;
9016
9322
  loggedCliErrorMessages.add(message);
9017
- Logger11.rawLog(`
9323
+ Logger10.rawLog(`
9018
9324
  ${chalk6.red(message)}`);
9019
9325
  };
9020
9326
  var handleOption = (programCommand, argMeta) => {
@@ -9243,7 +9549,7 @@ var runCommands = async (...commands) => {
9243
9549
  const hasCommand = process.argv.length > 2 && !process.argv[2]?.startsWith("-");
9244
9550
  if (hasHelpFlag || !hasCommand) {
9245
9551
  if (process.argv.length === 2 || process.argv.length === 3 && hasHelpFlag) {
9246
- Logger11.rawLog(formatHelp(commands, process.env.AKAN_VERSION));
9552
+ Logger10.rawLog(formatHelp(commands, process.env.AKAN_VERSION));
9247
9553
  process.exit(0);
9248
9554
  }
9249
9555
  }
@@ -9252,7 +9558,7 @@ var runCommands = async (...commands) => {
9252
9558
  });
9253
9559
  const installedAkanPackageJson = await FileSys.fileExists("./node_modules/akanjs/package.json") ? await FileSys.readJson("./node_modules/akanjs/package.json") : null;
9254
9560
  if (installedAkanPackageJson && installedAkanPackageJson.version !== process.env.AKAN_VERSION) {
9255
- Logger11.rawLog(chalk6.yellow(`
9561
+ Logger10.rawLog(chalk6.yellow(`
9256
9562
  Akan CLI version is mismatch with installed package. ${process.env.AKAN_VERSION} (global) vs ${installedAkanPackageJson.version} (akanjs)
9257
9563
  It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`));
9258
9564
  }
@@ -9288,7 +9594,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
9288
9594
  return formatCommandHelp(command, targetMeta.key);
9289
9595
  };
9290
9596
  programCommand.action(async (...args) => {
9291
- Logger11.rawLog();
9597
+ Logger10.rawLog();
9292
9598
  const cmdArgs = args.slice(0, args.length - 2);
9293
9599
  const opt = args[args.length - 2];
9294
9600
  const commandArgs = [];
@@ -9312,7 +9618,7 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`)
9312
9618
  const cmd = CommandContainer.get(command);
9313
9619
  try {
9314
9620
  await targetMeta.handler.call(cmd, ...commandArgs);
9315
- Logger11.rawLog();
9621
+ Logger10.rawLog();
9316
9622
  } catch (e) {
9317
9623
  printCliError(e);
9318
9624
  throw e;
@@ -9652,8 +9958,11 @@ import fsPromise from "fs/promises";
9652
9958
  import { input as input3, select as select3 } from "@inquirer/prompts";
9653
9959
  class Prompter {
9654
9960
  static async#getGuidelineRoot() {
9655
- const dirname3 = getDirname(import.meta.url);
9656
- const candidates = [`${dirname3}/guidelines`, `${dirname3}/../cli/guidelines`];
9961
+ const dirname2 = getDirname(import.meta.url);
9962
+ const candidates = [
9963
+ `${dirname2}/guidelines`,
9964
+ `${dirname2}/../cli/guidelines`
9965
+ ];
9657
9966
  for (const candidate of candidates) {
9658
9967
  try {
9659
9968
  await fsPromise.access(candidate);
@@ -9665,7 +9974,10 @@ class Prompter {
9665
9974
  static async selectGuideline() {
9666
9975
  const guidelineRoot = await Prompter.#getGuidelineRoot();
9667
9976
  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 })) });
9977
+ return await select3({
9978
+ message: "Select a guideline",
9979
+ choices: guideNames.map((name) => ({ name, value: name }))
9980
+ });
9669
9981
  }
9670
9982
  static async getGuideJson(guideName) {
9671
9983
  const guidelineRoot = await Prompter.#getGuidelineRoot();
@@ -9680,13 +9992,18 @@ class Prompter {
9680
9992
  return content;
9681
9993
  }
9682
9994
  static async getUpdateRequest(guideName) {
9683
- return await input3({ message: `What do you want to update in ${guideName}?` });
9995
+ return await input3({
9996
+ message: `What do you want to update in ${guideName}?`
9997
+ });
9684
9998
  }
9685
9999
  async makeTsFileUpdatePrompt({ context, request }) {
9686
10000
  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
10001
  ${await this.getDocumentation("framework")}
9688
10002
  Please understand the following background information, write code that meets the requirements, verify that it satisfies the validation conditions, and return the result.
9689
10003
 
10004
+ # Code Style
10005
+ - Use double quotes for all string literals in TypeScript/TSX code. Do not use single quotes.
10006
+
9690
10007
  # Background Information
9691
10008
  \`\`\`markdown
9692
10009
  ${context}
@@ -9733,10 +10050,10 @@ import { useEffect as useEffect3, useState as useState3 } from "react";
9733
10050
  import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
9734
10051
  "use client";
9735
10052
  // pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
9736
- import { Logger as Logger12 } from "akanjs/common";
10053
+ import { Logger as Logger11 } from "akanjs/common";
9737
10054
 
9738
10055
  class IncrementalBuilder {
9739
- #logger = new Logger12("IncrementalBuilder");
10056
+ #logger = new Logger11("IncrementalBuilder");
9740
10057
  #app;
9741
10058
  #artifact;
9742
10059
  #watch;