@akanjs/cli 2.3.9-rc.9 → 2.3.9

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.
@@ -2,7 +2,7 @@
2
2
  var __require = import.meta.require;
3
3
 
4
4
  // pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
5
- import path42 from "path";
5
+ import path44 from "path";
6
6
 
7
7
  // pkgs/@akanjs/devkit/aiEditor.ts
8
8
  import { input, select } from "@inquirer/prompts";
@@ -12504,6 +12504,7 @@ void run().catch((error) => {
12504
12504
  }
12505
12505
  // pkgs/@akanjs/devkit/applicationReleasePackager.ts
12506
12506
  import { cp, mkdir as mkdir9, rm as rm4 } from "fs/promises";
12507
+ import path38 from "path";
12507
12508
 
12508
12509
  // pkgs/@akanjs/devkit/uploadRelease.ts
12509
12510
  import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
@@ -12619,13 +12620,9 @@ class ApplicationReleasePackager {
12619
12620
  await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
12620
12621
  await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
12621
12622
  await rm4(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
12622
- await this.#app.workspace.spawn("tar", [
12623
- "-zcf",
12624
- `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-release.tar.gz`,
12625
- "-C",
12626
- buildRoot,
12627
- "./"
12628
- ]);
12623
+ const releaseRoot = this.#app.workspace.workspaceRoot;
12624
+ const releaseArchivePath = path38.relative(releaseRoot, `${releaseRoot}/releases/builds/${this.#app.name}-release.tar.gz`).split(path38.sep).join("/");
12625
+ await this.#app.workspace.spawn("tar", ["-zcf", releaseArchivePath, "-C", buildRoot, "./"], { cwd: releaseRoot });
12629
12626
  await this.#writeCsrZipIfPresent();
12630
12627
  return { platformVersion };
12631
12628
  }
@@ -12646,22 +12643,18 @@ class ApplicationReleasePackager {
12646
12643
  await cp(this.#app.dist.cwdPath, `${sourceRoot}/apps/${this.#app.name}`, { recursive: true });
12647
12644
  const libDeps = ["social", "shared", "platform", "util"];
12648
12645
  await Promise.all(libDeps.map((lib) => cp(`${this.#app.workspace.cwdPath}/libs/${lib}`, `${sourceRoot}/libs/${lib}`, { recursive: true })));
12649
- await Promise.all([".next", "ios", "android", "public/libs"].map(async (path38) => {
12650
- const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path38}`;
12646
+ await Promise.all([".next", "ios", "android", "public/libs"].map(async (path39) => {
12647
+ const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path39}`;
12651
12648
  if (await FileSys.dirExists(targetPath))
12652
12649
  await rm4(targetPath, { recursive: true, force: true });
12653
12650
  }));
12654
12651
  const syncPaths = [".husky", ".gitignore", "package.json"];
12655
- await Promise.all(syncPaths.map((path38) => cp(`${this.#app.workspace.cwdPath}/${path38}`, `${sourceRoot}/${path38}`, { recursive: true })));
12652
+ await Promise.all(syncPaths.map((path39) => cp(`${this.#app.workspace.cwdPath}/${path39}`, `${sourceRoot}/${path39}`, { recursive: true })));
12656
12653
  await this.#writeSourceTsconfig(sourceRoot, libDeps);
12657
12654
  await Bun.write(`${sourceRoot}/README.md`, readme);
12658
- await this.#app.workspace.spawn("tar", [
12659
- "-zcf",
12660
- `${this.#app.workspace.cwdPath}/releases/sources/${this.#app.name}-source.tar.gz`,
12661
- "-C",
12662
- sourceRoot,
12663
- "./"
12664
- ]);
12655
+ const sourceCwd = this.#app.workspace.cwdPath;
12656
+ const sourceArchivePath = path38.relative(sourceCwd, `${sourceCwd}/releases/sources/${this.#app.name}-source.tar.gz`).split(path38.sep).join("/");
12657
+ await this.#app.workspace.spawn("tar", ["-zcf", sourceArchivePath, "-C", sourceRoot, "./"], { cwd: sourceCwd });
12665
12658
  }
12666
12659
  async#resetSourceRoot(sourceRoot) {
12667
12660
  if (await FileSys.dirExists(sourceRoot)) {
@@ -12743,20 +12736,20 @@ class ApplicationReleasePackager {
12743
12736
  }
12744
12737
  }
12745
12738
  // pkgs/@akanjs/devkit/applicationTestPreload.ts
12746
- import path38 from "path";
12739
+ import path39 from "path";
12747
12740
  var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
12748
12741
  async function resolveSignalTestPreloadPath(target) {
12749
12742
  const candidates = [];
12750
12743
  const addResolvedPackageCandidate = (basePath2) => {
12751
12744
  try {
12752
- candidates.push(path38.join(path38.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
12745
+ candidates.push(path39.join(path39.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
12753
12746
  } catch {}
12754
12747
  };
12755
12748
  addResolvedPackageCandidate(target.cwdPath);
12756
12749
  addResolvedPackageCandidate(process.cwd());
12757
- addResolvedPackageCandidate(path38.dirname(Bun.main));
12750
+ addResolvedPackageCandidate(path39.dirname(Bun.main));
12758
12751
  addResolvedPackageCandidate(import.meta.dir);
12759
- candidates.push(path38.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(path38.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
12752
+ candidates.push(path39.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(path39.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
12760
12753
  for (const candidate of [...new Set(candidates)]) {
12761
12754
  if (await Bun.file(candidate).exists())
12762
12755
  return candidate;
@@ -12766,7 +12759,7 @@ async function resolveSignalTestPreloadPath(target) {
12766
12759
  // pkgs/@akanjs/devkit/builder.ts
12767
12760
  import { existsSync as existsSync2 } from "fs";
12768
12761
  import { mkdir as mkdir10 } from "fs/promises";
12769
- import path39 from "path";
12762
+ import path40 from "path";
12770
12763
  var SKIP_ENTRY_DIR_SET = new Set(["node_modules", "dist", "build", ".git", ".next"]);
12771
12764
  var assetExtensions = [".css", ".md", ".js", ".png", ".ico", ".svg", ".json", ".template"];
12772
12765
  var assetLoader = Object.fromEntries(assetExtensions.map((ext) => [ext, "file"]));
@@ -12783,14 +12776,14 @@ class Builder {
12783
12776
  #globEntrypoints(cwd, pattern) {
12784
12777
  const glob = new Bun.Glob(pattern);
12785
12778
  return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
12786
- const segments = relativePath.split(path39.sep);
12779
+ const segments = relativePath.split(path40.sep);
12787
12780
  return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
12788
- }).map((rel) => path39.join(cwd, rel));
12781
+ }).map((rel) => path40.join(cwd, rel));
12789
12782
  }
12790
12783
  #globFiles(cwd, pattern = "**/*.*") {
12791
12784
  const glob = new Bun.Glob(pattern);
12792
12785
  return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
12793
- const segments = relativePath.split(path39.sep);
12786
+ const segments = relativePath.split(path40.sep);
12794
12787
  return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
12795
12788
  });
12796
12789
  }
@@ -12798,17 +12791,17 @@ class Builder {
12798
12791
  const out = [];
12799
12792
  for (const p of additionalEntryPoints) {
12800
12793
  if (p.includes("*")) {
12801
- const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path39.sep}`) ? p.slice(cwd.length + 1) : p;
12794
+ const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path40.sep}`) ? p.slice(cwd.length + 1) : p;
12802
12795
  out.push(...this.#globEntrypoints(cwd, rel));
12803
12796
  } else
12804
- out.push(path39.isAbsolute(p) ? p : path39.join(cwd, p));
12797
+ out.push(path40.isAbsolute(p) ? p : path40.join(cwd, p));
12805
12798
  }
12806
12799
  return out;
12807
12800
  }
12808
12801
  #getBuildOptions({ bundle = false, additionalEntryPoints = [] } = {}) {
12809
12802
  const cwd = this.#executor.cwdPath;
12810
12803
  const entrypoints = [
12811
- ...bundle ? [path39.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
12804
+ ...bundle ? [path40.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
12812
12805
  ...this.#resolveAdditionalEntrypoints(cwd, additionalEntryPoints)
12813
12806
  ];
12814
12807
  return {
@@ -12829,9 +12822,9 @@ class Builder {
12829
12822
  for (const relativePath of this.#globFiles(cwd)) {
12830
12823
  if (relativePath === "package.json")
12831
12824
  continue;
12832
- const sourcePath = path39.join(cwd, relativePath);
12833
- const targetPath = path39.join(this.#distExecutor.cwdPath, relativePath);
12834
- await mkdir10(path39.dirname(targetPath), { recursive: true });
12825
+ const sourcePath = path40.join(cwd, relativePath);
12826
+ const targetPath = path40.join(this.#distExecutor.cwdPath, relativePath);
12827
+ await mkdir10(path40.dirname(targetPath), { recursive: true });
12835
12828
  await Bun.write(targetPath, Bun.file(sourcePath));
12836
12829
  }
12837
12830
  }
@@ -12842,13 +12835,13 @@ class Builder {
12842
12835
  return withoutFormatDir;
12843
12836
  if (!hasDotSlash && withoutFormatDir === publishedPath)
12844
12837
  return publishedPath;
12845
- const parsed = path39.posix.parse(withoutFormatDir);
12838
+ const parsed = path40.posix.parse(withoutFormatDir);
12846
12839
  if (![".js", ".mjs", ".cjs"].includes(parsed.ext))
12847
12840
  return withoutFormatDir;
12848
- const withoutExt = path39.posix.join(parsed.dir, parsed.name);
12841
+ const withoutExt = path40.posix.join(parsed.dir, parsed.name);
12849
12842
  const sourcePath = withoutExt.startsWith("./") ? withoutExt.slice(2) : withoutExt;
12850
12843
  const sourceCandidates = [`${sourcePath}.ts`, `${sourcePath}.tsx`];
12851
- const matchedSource = sourceCandidates.find((candidate) => existsSync2(path39.join(this.#executor.cwdPath, candidate)));
12844
+ const matchedSource = sourceCandidates.find((candidate) => existsSync2(path40.join(this.#executor.cwdPath, candidate)));
12852
12845
  if (!matchedSource)
12853
12846
  return withoutFormatDir;
12854
12847
  return hasDotSlash ? `./${matchedSource}` : matchedSource;
@@ -12899,7 +12892,7 @@ class Builder {
12899
12892
  // pkgs/@akanjs/devkit/capacitorApp.ts
12900
12893
  import { cp as cp2, mkdir as mkdir11, rm as rm5 } from "fs/promises";
12901
12894
  import os from "os";
12902
- import path40 from "path";
12895
+ import path41 from "path";
12903
12896
  import { select as select2 } from "@inquirer/prompts";
12904
12897
  import { MobileProject } from "@trapezedev/project";
12905
12898
  import { capitalize as capitalize5 } from "akanjs/common";
@@ -13106,13 +13099,13 @@ var rootCapacitorConfigFilenames = [
13106
13099
  "capacitor.config.js",
13107
13100
  "capacitor.config.json"
13108
13101
  ];
13109
- var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path40.join(appRoot, file));
13102
+ var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path41.join(appRoot, file));
13110
13103
  async function clearRootCapacitorConfigs(appRoot) {
13111
13104
  await Promise.all(rootCapacitorConfigPaths(appRoot).map((file) => rm5(file, { force: true })));
13112
13105
  }
13113
13106
  async function writeRootCapacitorConfig(appRoot, content) {
13114
13107
  await clearRootCapacitorConfigs(appRoot);
13115
- await Bun.write(path40.join(appRoot, "capacitor.config.json"), content);
13108
+ await Bun.write(path41.join(appRoot, "capacitor.config.json"), content);
13116
13109
  }
13117
13110
  var getLocalIP = () => {
13118
13111
  const interfaces = os.networkInterfaces();
@@ -13221,13 +13214,13 @@ function buildIosNativeRunCommand({
13221
13214
  scheme = "App",
13222
13215
  configuration = "Debug"
13223
13216
  }) {
13224
- const derivedDataPath = path40.join(appRoot, "ios/DerivedData", device.id);
13217
+ const derivedDataPath = path41.join(appRoot, "ios/DerivedData", device.id);
13225
13218
  const productPlatform = device.kind === "device" ? "iphoneos" : "iphonesimulator";
13226
13219
  const destination = device.kind === "device" ? `id=${device.xcodebuildId ?? device.id}` : `platform=iOS Simulator,id=${device.id}`;
13227
13220
  return {
13228
13221
  configuration,
13229
13222
  derivedDataPath,
13230
- appPath: path40.join(derivedDataPath, "Build/Products", `${configuration}-${productPlatform}`, "App.app"),
13223
+ appPath: path41.join(derivedDataPath, "Build/Products", `${configuration}-${productPlatform}`, "App.app"),
13231
13224
  xcodebuildArgs: [
13232
13225
  "-project",
13233
13226
  "App.xcodeproj",
@@ -13437,7 +13430,7 @@ function materializeCapacitorConfig(target, { operation, localServerUrl, localIp
13437
13430
  ...capacitorConfig,
13438
13431
  appId,
13439
13432
  appName,
13440
- webDir: path40.posix.join(".akan", "mobile", name, "www"),
13433
+ webDir: path41.posix.join(".akan", "mobile", name, "www"),
13441
13434
  plugins: {
13442
13435
  CapacitorCookies: { enabled: true },
13443
13436
  ...pluginsConfig,
@@ -13494,10 +13487,10 @@ class CapacitorApp {
13494
13487
  constructor(app, target) {
13495
13488
  this.app = app;
13496
13489
  this.target = target;
13497
- this.targetRootPath = path40.posix.join(".akan", "mobile", this.target.name);
13498
- this.targetRoot = path40.join(this.app.cwdPath, this.targetRootPath);
13499
- this.targetWebRoot = path40.join(this.targetRoot, "www");
13500
- this.targetAssetRoot = path40.join(this.targetRoot, "assets");
13490
+ this.targetRootPath = path41.posix.join(".akan", "mobile", this.target.name);
13491
+ this.targetRoot = path41.join(this.app.cwdPath, this.targetRootPath);
13492
+ this.targetWebRoot = path41.join(this.targetRoot, "www");
13493
+ this.targetAssetRoot = path41.join(this.targetRoot, "assets");
13501
13494
  this.project = new MobileProject(this.app.cwdPath, {
13502
13495
  android: { path: this.androidRootPath },
13503
13496
  ios: { path: this.iosProjectPath }
@@ -13512,9 +13505,9 @@ class CapacitorApp {
13512
13505
  await mkdir11(this.targetRoot, { recursive: true });
13513
13506
  if (regenerate) {
13514
13507
  if (!platform || platform === "ios")
13515
- await rm5(path40.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
13508
+ await rm5(path41.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
13516
13509
  if (!platform || platform === "android")
13517
- await rm5(path40.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
13510
+ await rm5(path41.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
13518
13511
  }
13519
13512
  const project = this.project;
13520
13513
  await this.project.load();
@@ -13637,7 +13630,7 @@ ${textError instanceof Error ? textError.message : ""}`);
13637
13630
  const xcodebuildArgs = noAllowProvisioningUpdates ? command.xcodebuildArgs : [...command.xcodebuildArgs.slice(0, -1), "-allowProvisioningUpdates", ...command.xcodebuildArgs.slice(-1)];
13638
13631
  try {
13639
13632
  await this.#spawn("xcodebuild", xcodebuildArgs, {
13640
- cwd: path40.join(this.app.cwdPath, this.iosProjectPath),
13633
+ cwd: path41.join(this.app.cwdPath, this.iosProjectPath),
13641
13634
  env: mobileEnv
13642
13635
  });
13643
13636
  const devicectlId = runTarget.devicectlId ?? runTarget.id;
@@ -13662,7 +13655,7 @@ ${textError instanceof Error ? textError.message : ""}`);
13662
13655
  return isRecord2(this.target.ios) && typeof this.target.ios.scheme === "string" ? this.target.ios.scheme : "App";
13663
13656
  }
13664
13657
  async#getIosDevelopmentTeam() {
13665
- const pbxprojPath = path40.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
13658
+ const pbxprojPath = path41.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
13666
13659
  if (!await Bun.file(pbxprojPath).exists())
13667
13660
  return;
13668
13661
  return (await Bun.file(pbxprojPath).text()).match(/DEVELOPMENT_TEAM = ([^;]+);/)?.[1]?.trim();
@@ -13688,7 +13681,7 @@ ${error.message}`;
13688
13681
  await this.#spawnMobile("npx", ["cap", "sync", "android"], { operation, env });
13689
13682
  }
13690
13683
  async#updateAndroidBuildTypes() {
13691
- const appGradle = await FileEditor.create(path40.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
13684
+ const appGradle = await FileEditor.create(path41.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
13692
13685
  const buildTypesBlock = `
13693
13686
  debug {
13694
13687
  applicationIdSuffix ".debug"
@@ -13732,7 +13725,7 @@ ${error.message}`;
13732
13725
  const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
13733
13726
  await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
13734
13727
  stdio: "inherit",
13735
- cwd: path40.join(this.app.cwdPath, this.androidRootPath),
13728
+ cwd: path41.join(this.app.cwdPath, this.androidRootPath),
13736
13729
  env: await this.#commandEnv("release", env)
13737
13730
  });
13738
13731
  }
@@ -13740,10 +13733,10 @@ ${error.message}`;
13740
13733
  await this.#spawnMobile("npx", ["cap", "open", "android"], { operation: "local", env: "local" });
13741
13734
  }
13742
13735
  async#ensureAndroidAssetsDir() {
13743
- await mkdir11(path40.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
13736
+ await mkdir11(path41.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
13744
13737
  }
13745
13738
  async#ensureAndroidDebugKeystore() {
13746
- const keystorePath = path40.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
13739
+ const keystorePath = path41.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
13747
13740
  if (await Bun.file(keystorePath).exists())
13748
13741
  return;
13749
13742
  await this.#spawn("keytool", [
@@ -13782,7 +13775,7 @@ ${error.message}`;
13782
13775
  await this.#spawnMobile("npx", args, { operation, env }, { stdio: "inherit" });
13783
13776
  }
13784
13777
  async#assertAndroidReleaseSigningConfig() {
13785
- const gradlePropertiesPath = path40.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
13778
+ const gradlePropertiesPath = path41.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
13786
13779
  const gradleProperties = await Bun.file(gradlePropertiesPath).exists() ? await Bun.file(gradlePropertiesPath).text() : "";
13787
13780
  const missingKeys = getMissingAndroidReleaseSigningKeys({ gradleProperties });
13788
13781
  if (missingKeys.length > 0)
@@ -13809,12 +13802,12 @@ ${error.message}`;
13809
13802
  await this.#prepareAndroid({ operation: "release", env: "main" });
13810
13803
  }
13811
13804
  async prepareWww() {
13812
- const htmlSource = path40.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
13805
+ const htmlSource = path41.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
13813
13806
  if (!await Bun.file(htmlSource).exists())
13814
13807
  throw new Error(`CSR html for mobile target '${this.target.name}' not found: ${htmlSource}`);
13815
13808
  await rm5(this.targetWebRoot, { recursive: true, force: true });
13816
13809
  await mkdir11(this.targetWebRoot, { recursive: true });
13817
- await Bun.write(path40.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
13810
+ await Bun.write(path41.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
13818
13811
  }
13819
13812
  #injectMobileTargetMeta(html) {
13820
13813
  const basePath2 = this.target.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
@@ -13834,7 +13827,7 @@ ${error.message}`;
13834
13827
  });
13835
13828
  const content = `${JSON.stringify(config, null, 2)}
13836
13829
  `;
13837
- await Bun.write(path40.join(this.targetRoot, "capacitor.config.json"), content);
13830
+ await Bun.write(path41.join(this.targetRoot, "capacitor.config.json"), content);
13838
13831
  return content;
13839
13832
  }
13840
13833
  async#prepareTargetAssets() {
@@ -13842,11 +13835,11 @@ ${error.message}`;
13842
13835
  return;
13843
13836
  await mkdir11(this.targetAssetRoot, { recursive: true });
13844
13837
  if (this.target.assets.icon)
13845
- await cp2(path40.join(this.app.cwdPath, this.target.assets.icon), path40.join(this.targetAssetRoot, "icon.png"), {
13838
+ await cp2(path41.join(this.app.cwdPath, this.target.assets.icon), path41.join(this.targetAssetRoot, "icon.png"), {
13846
13839
  force: true
13847
13840
  });
13848
13841
  if (this.target.assets.splash)
13849
- await cp2(path40.join(this.app.cwdPath, this.target.assets.splash), path40.join(this.targetAssetRoot, "splash.png"), {
13842
+ await cp2(path41.join(this.app.cwdPath, this.target.assets.splash), path41.join(this.targetAssetRoot, "splash.png"), {
13850
13843
  force: true
13851
13844
  });
13852
13845
  }
@@ -13854,11 +13847,11 @@ ${error.message}`;
13854
13847
  const files = this.target.files?.[platform];
13855
13848
  if (!files)
13856
13849
  return;
13857
- const platformRoot = path40.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
13850
+ const platformRoot = path41.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
13858
13851
  await Promise.all(Object.entries(files).map(async ([to, from]) => {
13859
- const targetPath = path40.join(platformRoot, to);
13860
- await mkdir11(path40.dirname(targetPath), { recursive: true });
13861
- await cp2(path40.join(this.app.cwdPath, from), targetPath, { force: true });
13852
+ const targetPath = path41.join(platformRoot, to);
13853
+ await mkdir11(path41.dirname(targetPath), { recursive: true });
13854
+ await cp2(path41.join(this.app.cwdPath, from), targetPath, { force: true });
13862
13855
  }));
13863
13856
  }
13864
13857
  async#generateAssets({ operation, env }) {
@@ -13868,7 +13861,7 @@ ${error.message}`;
13868
13861
  "@capacitor/assets",
13869
13862
  "generate",
13870
13863
  "--assetPath",
13871
- path40.posix.join(this.targetRootPath, "assets"),
13864
+ path41.posix.join(this.targetRootPath, "assets"),
13872
13865
  "--iosProject",
13873
13866
  this.iosProjectPath,
13874
13867
  "--androidProject",
@@ -14078,7 +14071,7 @@ var Pkg = createInternalArgToken("Pkg");
14078
14071
  var Module = createInternalArgToken("Module");
14079
14072
  var Workspace = createInternalArgToken("Workspace");
14080
14073
  // pkgs/@akanjs/devkit/commandDecorators/command.ts
14081
- import path41 from "path";
14074
+ import path42 from "path";
14082
14075
  import { confirm, input as input2, select as select3 } from "@inquirer/prompts";
14083
14076
  import { Logger as Logger10 } from "akanjs/common";
14084
14077
  import chalk6 from "chalk";
@@ -14582,7 +14575,7 @@ var runCommands = async (...commands) => {
14582
14575
  process.exit(1);
14583
14576
  });
14584
14577
  const __dirname2 = getDirname(import.meta.url);
14585
- const packageJsonCandidates = [`${path41.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
14578
+ const packageJsonCandidates = [`${path42.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
14586
14579
  let cliPackageJson = null;
14587
14580
  for (const packageJsonPath of packageJsonCandidates) {
14588
14581
  if (!await FileSys.fileExists(packageJsonPath))
@@ -14862,8 +14855,8 @@ var scanModuleSpecifiers = (source2, filePath, includeExports) => {
14862
14855
  return importSpecifiers;
14863
14856
  };
14864
14857
  var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
14865
- const configFile = ts10.readConfigFile(tsConfigPath, (path42) => {
14866
- return ts10.sys.readFile(path42);
14858
+ const configFile = ts10.readConfigFile(tsConfigPath, (path43) => {
14859
+ return ts10.sys.readFile(path43);
14867
14860
  });
14868
14861
  return ts10.parseJsonConfigFileContent(configFile.config, ts10.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
14869
14862
  };
@@ -15079,6 +15072,571 @@ ${document}
15079
15072
  `;
15080
15073
  }
15081
15074
  }
15075
+ // pkgs/@akanjs/devkit/qualityScanner.ts
15076
+ import { createHash } from "crypto";
15077
+ import { readdir as readdir3, readFile as readFile2, stat as stat4 } from "fs/promises";
15078
+ import path43 from "path";
15079
+ import ignore2 from "ignore";
15080
+ import ts11 from "typescript";
15081
+ var MAX_FILE_LINES = 2000;
15082
+ var PLACEHOLDER_EXPORT_NAMES = new Set([
15083
+ "aa",
15084
+ "dumb",
15085
+ "dumb2",
15086
+ "someBaseLogic",
15087
+ "someCommonLogic",
15088
+ "someFrontendLogic",
15089
+ "someBackendLogic"
15090
+ ]);
15091
+ var SUGGESTED_RULES = [
15092
+ "Keep generated scanSync index files out of hand-written changes; generated indexes should only contain one-depth export statements.",
15093
+ "Generated Akan index files should not contain placeholder exports such as aa, dumb, dumb2, or some*Logic.",
15094
+ "Dictionary text should not contain scaffold wording, misspellings, or stale copied domain nouns.",
15095
+ "Warn earlier on very long Akan files: 500 lines for services, 800 lines for Template/Zone files, and 1000 lines for Util files.",
15096
+ "Global declarations, Window augmentation, and prototype mutation should stay in explicitly approved low-level integration files.",
15097
+ "Keep app root folders small and conventional: application code belongs under common, env, lib, page, private, public, script, srvkit, ui, or webkit.",
15098
+ "Keep apps/*/lib and libs/*/lib root files limited to generated support facets such as cnst.ts, db.ts, dict.ts, sig.ts, srv.ts, st.ts, useClient.ts, and useServer.ts.",
15099
+ "Use domain module folders consistently: lib/<model> for database modules, lib/_<service> for service modules, and lib/__scalar/<scalar> for scalar modules.",
15100
+ "Keep module UI filenames predictable: database modules use <Model>.Template/Unit/Util/View/Zone.tsx, service modules use <Service>.Util/Zone.tsx, and scalar modules use <Scalar>.Template/Unit.tsx.",
15101
+ "Move shared app utilities to apps/*/common instead of creating apps/*/base.",
15102
+ "Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
15103
+ ];
15104
+ var APP_ROOT_FILES = new Set([
15105
+ "akan.app.json",
15106
+ "akan.config.ts",
15107
+ "capacitor.config.ts",
15108
+ "client.ts",
15109
+ "main.ts",
15110
+ "package.json",
15111
+ "server.ts",
15112
+ "tsconfig.json"
15113
+ ]);
15114
+ var LIB_ROOT_FILES = new Set([
15115
+ "cnst.ts",
15116
+ "db.ts",
15117
+ "dict.ts",
15118
+ "option.ts",
15119
+ "sig.ts",
15120
+ "srv.ts",
15121
+ "st.ts",
15122
+ "useClient.ts",
15123
+ "useServer.ts"
15124
+ ]);
15125
+ var CONVENTION_SUFFIXES = [
15126
+ ".constant.ts",
15127
+ ".dictionary.ts",
15128
+ ".document.ts",
15129
+ ".service.ts",
15130
+ ".signal.ts",
15131
+ ".store.ts"
15132
+ ];
15133
+
15134
+ class AkanQualityScanner {
15135
+ async scan(workspaceRoot) {
15136
+ const targetFiles = await this.#collectTargetFiles(workspaceRoot);
15137
+ const sourceFiles = await Promise.all(targetFiles.map((file) => this.#readSourceFile(workspaceRoot, file)));
15138
+ const warnings = [
15139
+ ...this.#scanGlobalQuality(sourceFiles),
15140
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanSingleFileQuality(sourceFile2)),
15141
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanConventionQuality(sourceFile2)),
15142
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanLayoutQuality(sourceFile2))
15143
+ ];
15144
+ return {
15145
+ workspaceRoot,
15146
+ scannedFiles: sourceFiles.length,
15147
+ warnings: warnings.sort(compareWarnings),
15148
+ suggestedRules: SUGGESTED_RULES
15149
+ };
15150
+ }
15151
+ async#collectTargetFiles(workspaceRoot) {
15152
+ const ignoreFilter = ignore2().add(await this.#readGitIgnore(workspaceRoot));
15153
+ const files = [];
15154
+ for (const targetRoot of ["apps", "libs"]) {
15155
+ const absoluteTargetRoot = path43.join(workspaceRoot, targetRoot);
15156
+ if (!await isDirectory(absoluteTargetRoot))
15157
+ continue;
15158
+ await this.#walkTargetFiles(workspaceRoot, absoluteTargetRoot, ignoreFilter, files);
15159
+ }
15160
+ return files.sort();
15161
+ }
15162
+ async#readGitIgnore(workspaceRoot) {
15163
+ const gitIgnorePath = path43.join(workspaceRoot, ".gitignore");
15164
+ if (!await Bun.file(gitIgnorePath).exists())
15165
+ return [];
15166
+ return (await readFile2(gitIgnorePath, "utf8")).split(/\r?\n/);
15167
+ }
15168
+ async#walkTargetFiles(workspaceRoot, currentPath, ignoreFilter, files) {
15169
+ const entries = await readdir3(currentPath, { withFileTypes: true });
15170
+ for (const entry of entries) {
15171
+ const absolutePath = path43.join(currentPath, entry.name);
15172
+ const relativePath = toPosix(path43.relative(workspaceRoot, absolutePath));
15173
+ if (shouldSkipPath(relativePath, entry.isDirectory(), ignoreFilter))
15174
+ continue;
15175
+ if (entry.isDirectory()) {
15176
+ await this.#walkTargetFiles(workspaceRoot, absolutePath, ignoreFilter, files);
15177
+ continue;
15178
+ }
15179
+ if ((relativePath.endsWith(".ts") || relativePath.endsWith(".tsx")) && !relativePath.endsWith(".d.ts")) {
15180
+ files.push(relativePath);
15181
+ }
15182
+ }
15183
+ }
15184
+ async#readSourceFile(workspaceRoot, file) {
15185
+ const absolutePath = path43.join(workspaceRoot, file);
15186
+ const content = await readFile2(absolutePath, "utf8");
15187
+ return {
15188
+ file,
15189
+ absolutePath,
15190
+ content,
15191
+ sourceFile: ts11.createSourceFile(file, content, ts11.ScriptTarget.Latest, true, getScriptKind(file))
15192
+ };
15193
+ }
15194
+ #scanGlobalQuality(sourceFiles) {
15195
+ const exportedFunctionLikes = sourceFiles.flatMap((sourceFile2) => getExportedFunctionLikes(sourceFile2));
15196
+ const warnings = [];
15197
+ for (const [name, declarations] of groupBy(exportedFunctionLikes, (declaration) => declaration.name)) {
15198
+ if (declarations.length < 2)
15199
+ continue;
15200
+ warnings.push({
15201
+ rule: "akan.global.duplicate-exported-function-name",
15202
+ scope: "global",
15203
+ severity: "warning",
15204
+ message: `Exported function or class name "${name}" is declared in ${declarations.length} files.`,
15205
+ locations: declarations.map(({ file, line }) => ({ file, line }))
15206
+ });
15207
+ }
15208
+ const declarationsWithBody = exportedFunctionLikes.filter((declaration) => declaration.bodyFingerprint);
15209
+ for (const [fingerprint, declarations] of groupBy(declarationsWithBody, (declaration) => declaration.bodyFingerprint ?? "")) {
15210
+ const uniqueNames = new Set(declarations.map((declaration) => declaration.name));
15211
+ if (declarations.length < 2 || uniqueNames.size < 2 || fingerprint === "")
15212
+ continue;
15213
+ warnings.push({
15214
+ rule: "akan.global.duplicate-exported-function-body",
15215
+ scope: "global",
15216
+ severity: "warning",
15217
+ message: `Exported functions/classes share the same implementation body: ${declarations.map((declaration) => declaration.name).join(", ")}.`,
15218
+ locations: declarations.map(({ file, line }) => ({ file, line }))
15219
+ });
15220
+ }
15221
+ return warnings;
15222
+ }
15223
+ #scanSingleFileQuality(sourceFile2) {
15224
+ const warnings = [];
15225
+ const lineCount = sourceFile2.content.split(/\r?\n/).length;
15226
+ const recommendedLineLimit = getRecommendedLineLimit(sourceFile2.file);
15227
+ if (recommendedLineLimit && lineCount > recommendedLineLimit) {
15228
+ warnings.push({
15229
+ rule: "akan.file.recommended-max-lines",
15230
+ scope: "file",
15231
+ severity: "warning",
15232
+ file: sourceFile2.file,
15233
+ message: `File has ${lineCount} lines. Recommended limit for this file type is ${recommendedLineLimit} lines.`
15234
+ });
15235
+ }
15236
+ if (lineCount > MAX_FILE_LINES) {
15237
+ warnings.push({
15238
+ rule: "akan.file.max-lines",
15239
+ scope: "file",
15240
+ severity: "warning",
15241
+ file: sourceFile2.file,
15242
+ message: `File has ${lineCount} lines. Keep single files under ${MAX_FILE_LINES} lines.`
15243
+ });
15244
+ }
15245
+ warnings.push(...getPlaceholderExportWarnings(sourceFile2));
15246
+ warnings.push(...getDictionaryTextWarnings(sourceFile2));
15247
+ warnings.push(...getGlobalMutationWarnings(sourceFile2));
15248
+ const exportedClassNames = getExportedClassNames(sourceFile2.sourceFile);
15249
+ if (exportedClassNames.length === 0)
15250
+ return warnings;
15251
+ const allowedInterfaceNames = new Set(exportedClassNames.map((name) => `${name}Options`));
15252
+ for (const declaration of getTopLevelDeclarations(sourceFile2)) {
15253
+ if (declaration.kind === "class" && exportedClassNames.includes(declaration.name))
15254
+ continue;
15255
+ if (declaration.kind === "interface" && allowedInterfaceNames.has(declaration.name))
15256
+ continue;
15257
+ warnings.push({
15258
+ rule: "akan.file.class-export-global-declaration",
15259
+ scope: "file",
15260
+ severity: "warning",
15261
+ file: sourceFile2.file,
15262
+ line: declaration.line,
15263
+ message: `Class export files should not declare top-level ${declaration.kind} "${declaration.name}". Move helpers to another file and import them.`
15264
+ });
15265
+ }
15266
+ return warnings;
15267
+ }
15268
+ #scanConventionQuality(sourceFile2) {
15269
+ const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile2.file.endsWith(candidate));
15270
+ if (!suffix)
15271
+ return [];
15272
+ const modelName = toPascalCase(path43.basename(sourceFile2.file, suffix));
15273
+ const warnings = [];
15274
+ for (const declaration of getTopLevelDeclarations(sourceFile2)) {
15275
+ if (isAllowedConventionDeclaration(suffix, modelName, declaration))
15276
+ continue;
15277
+ warnings.push({
15278
+ rule: `akan.convention${suffix.replace(".ts", "")}`,
15279
+ scope: "convention",
15280
+ severity: "warning",
15281
+ file: sourceFile2.file,
15282
+ line: declaration.line,
15283
+ message: `${path43.basename(sourceFile2.file)} should not declare top-level ${declaration.kind} "${declaration.name}". Allowed declarations: ${getConventionDescription(suffix, modelName)}.`
15284
+ });
15285
+ }
15286
+ return warnings;
15287
+ }
15288
+ #scanLayoutQuality(sourceFile2) {
15289
+ const segments = sourceFile2.file.split("/");
15290
+ const warnings = [];
15291
+ if (segments[0] === "apps" && segments.length === 3 && !APP_ROOT_FILES.has(segments[2])) {
15292
+ warnings.push({
15293
+ rule: "akan.layout.app-root-file",
15294
+ scope: "layout",
15295
+ severity: "warning",
15296
+ file: sourceFile2.file,
15297
+ message: `Unexpected app root file "${segments[2]}". Keep application code in conventional app folders.`
15298
+ });
15299
+ }
15300
+ const libRootFile = getLibRootFile(sourceFile2.file);
15301
+ if (libRootFile && !LIB_ROOT_FILES.has(libRootFile)) {
15302
+ warnings.push({
15303
+ rule: "akan.layout.lib-root-file",
15304
+ scope: "layout",
15305
+ severity: "warning",
15306
+ file: sourceFile2.file,
15307
+ message: `Unexpected lib root file "${libRootFile}". Keep direct lib root files limited to generated support facets.`
15308
+ });
15309
+ }
15310
+ const moduleUiWarning = getModuleUiWarning(sourceFile2.file);
15311
+ if (moduleUiWarning)
15312
+ warnings.push(moduleUiWarning);
15313
+ return warnings;
15314
+ }
15315
+ }
15316
+ function formatQualityScanResult(result) {
15317
+ const sections = [
15318
+ "Akan Code Quality Scan",
15319
+ `workspace: ${result.workspaceRoot}`,
15320
+ `scanned files: ${result.scannedFiles}`,
15321
+ `warnings: ${result.warnings.length}`,
15322
+ "",
15323
+ "Warnings:",
15324
+ "",
15325
+ ...formatQualityWarnings(result.warnings),
15326
+ "",
15327
+ "Suggested quality rules:",
15328
+ "",
15329
+ ...result.suggestedRules.map((rule) => ` - ${rule}`)
15330
+ ];
15331
+ return sections.join(`
15332
+ `);
15333
+ }
15334
+ function formatQualityWarnings(warnings) {
15335
+ if (warnings.length === 0)
15336
+ return ["No warnings found."];
15337
+ return warnings.flatMap((warning) => {
15338
+ const location = formatQualityLocation(warning.file, warning.line);
15339
+ const lines = [`${location} - warning ${warning.rule}: ${warning.message}`];
15340
+ if (warning.locations?.length) {
15341
+ lines.push(...warning.locations.map(({ file, line }) => ` note: related location ${formatQualityLocation(file, line)}`));
15342
+ }
15343
+ return lines;
15344
+ });
15345
+ }
15346
+ function formatQualityLocation(file, line) {
15347
+ return `${file ?? "<global>"}:${line ?? 1}:1`;
15348
+ }
15349
+ function getExportedFunctionLikes(sourceFile2) {
15350
+ const declarations = [];
15351
+ for (const statement of sourceFile2.sourceFile.statements) {
15352
+ if (ts11.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
15353
+ declarations.push({
15354
+ name: statement.name.text,
15355
+ kind: "function",
15356
+ file: sourceFile2.file,
15357
+ line: getLine(sourceFile2.sourceFile, statement),
15358
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement.body)
15359
+ });
15360
+ }
15361
+ if (ts11.isClassDeclaration(statement) && statement.name && isExported(statement)) {
15362
+ declarations.push({
15363
+ name: statement.name.text,
15364
+ kind: "class",
15365
+ file: sourceFile2.file,
15366
+ line: getLine(sourceFile2.sourceFile, statement),
15367
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement)
15368
+ });
15369
+ }
15370
+ if (ts11.isVariableStatement(statement) && isExported(statement)) {
15371
+ for (const declaration of statement.declarationList.declarations) {
15372
+ if (!ts11.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
15373
+ continue;
15374
+ declarations.push({
15375
+ name: declaration.name.text,
15376
+ kind: "function-variable",
15377
+ file: sourceFile2.file,
15378
+ line: getLine(sourceFile2.sourceFile, declaration),
15379
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, declaration.initializer)
15380
+ });
15381
+ }
15382
+ }
15383
+ }
15384
+ return declarations;
15385
+ }
15386
+ function getExportedClassNames(sourceFile2) {
15387
+ return sourceFile2.statements.filter((statement) => ts11.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
15388
+ }
15389
+ function getPlaceholderExportWarnings(sourceFile2) {
15390
+ if (!sourceFile2.file.endsWith("/index.ts") && !sourceFile2.file.endsWith("/index.tsx"))
15391
+ return [];
15392
+ return getTopLevelDeclarations(sourceFile2).filter((declaration) => declaration.exported && PLACEHOLDER_EXPORT_NAMES.has(declaration.name)).map((declaration) => ({
15393
+ rule: "akan.file.placeholder-export",
15394
+ scope: "file",
15395
+ severity: "warning",
15396
+ file: sourceFile2.file,
15397
+ line: declaration.line,
15398
+ message: `Generated or barrel index file should not export placeholder "${declaration.name}".`
15399
+ }));
15400
+ }
15401
+ function getDictionaryTextWarnings(sourceFile2) {
15402
+ if (!sourceFile2.file.endsWith(".dictionary.ts"))
15403
+ return [];
15404
+ const stalePatterns = [
15405
+ { pattern: /\b[A-Z][A-Za-z0-9]* description\b/, label: "scaffold description text" },
15406
+ { pattern: /settting/, label: "misspelling: settting" },
15407
+ { pattern: /\uBC30\uB108 \uC218/, label: "stale copied Korean domain noun: \uBC30\uB108 \uC218" }
15408
+ ];
15409
+ return stalePatterns.flatMap(({ pattern, label }) => findPatternLines(sourceFile2.content, pattern).map((line) => ({
15410
+ rule: "akan.file.dictionary-stale-text",
15411
+ scope: "file",
15412
+ severity: "warning",
15413
+ file: sourceFile2.file,
15414
+ line,
15415
+ message: `Dictionary text appears to contain ${label}.`
15416
+ })));
15417
+ }
15418
+ function getGlobalMutationWarnings(sourceFile2) {
15419
+ const warnings = [];
15420
+ for (const statement of sourceFile2.sourceFile.statements) {
15421
+ if (ts11.isModuleDeclaration(statement) && statement.name.getText(sourceFile2.sourceFile) === "global") {
15422
+ warnings.push({
15423
+ rule: "akan.file.global-declaration",
15424
+ scope: "file",
15425
+ severity: "warning",
15426
+ file: sourceFile2.file,
15427
+ line: getLine(sourceFile2.sourceFile, statement),
15428
+ message: "Global declarations require an explicit low-level integration allowlist."
15429
+ });
15430
+ }
15431
+ if (ts11.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
15432
+ warnings.push({
15433
+ rule: "akan.file.window-augmentation",
15434
+ scope: "file",
15435
+ severity: "warning",
15436
+ file: sourceFile2.file,
15437
+ line: getLine(sourceFile2.sourceFile, statement),
15438
+ message: "Window augmentation should be isolated to approved browser integration files."
15439
+ });
15440
+ }
15441
+ if (ts11.isExpressionStatement(statement) && statement.expression.getText(sourceFile2.sourceFile).includes(".prototype.")) {
15442
+ warnings.push({
15443
+ rule: "akan.file.prototype-mutation",
15444
+ scope: "file",
15445
+ severity: "warning",
15446
+ file: sourceFile2.file,
15447
+ line: getLine(sourceFile2.sourceFile, statement),
15448
+ message: "Prototype mutation should be avoided or isolated to approved low-level integration files."
15449
+ });
15450
+ }
15451
+ }
15452
+ return warnings;
15453
+ }
15454
+ function getTopLevelDeclarations(sourceFile2) {
15455
+ return sourceFile2.sourceFile.statements.flatMap((statement) => getTopLevelDeclaration(sourceFile2.sourceFile, statement));
15456
+ }
15457
+ function getTopLevelDeclaration(sourceFile2, statement) {
15458
+ const line = getLine(sourceFile2, statement);
15459
+ if (ts11.isClassDeclaration(statement) && statement.name) {
15460
+ return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
15461
+ }
15462
+ if (ts11.isFunctionDeclaration(statement) && statement.name) {
15463
+ return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
15464
+ }
15465
+ if (ts11.isInterfaceDeclaration(statement)) {
15466
+ return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
15467
+ }
15468
+ if (ts11.isTypeAliasDeclaration(statement)) {
15469
+ return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
15470
+ }
15471
+ if (ts11.isEnumDeclaration(statement)) {
15472
+ return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
15473
+ }
15474
+ if (ts11.isVariableStatement(statement)) {
15475
+ return statement.declarationList.declarations.filter((declaration) => ts11.isIdentifier(declaration.name)).map((declaration) => ({
15476
+ name: declaration.name.text,
15477
+ kind: "variable",
15478
+ line: getLine(sourceFile2, declaration),
15479
+ exported: isExported(statement),
15480
+ node: statement
15481
+ }));
15482
+ }
15483
+ if (ts11.isExportDeclaration(statement)) {
15484
+ return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
15485
+ }
15486
+ return [];
15487
+ }
15488
+ function isAllowedConventionDeclaration(suffix, modelName, declaration) {
15489
+ if (suffix === ".dictionary.ts")
15490
+ return isExportedConst(declaration) && declaration.name === "dictionary";
15491
+ if (suffix === ".constant.ts")
15492
+ return isAllowedConstantDeclaration(modelName, declaration);
15493
+ if (suffix === ".document.ts")
15494
+ return declaration.kind === "class" && [`${modelName}Filter`, modelName, `${modelName}Model`].includes(declaration.name);
15495
+ if (suffix === ".service.ts")
15496
+ return declaration.kind === "class" && declaration.name === `${modelName}Service`;
15497
+ if (suffix === ".signal.ts")
15498
+ return declaration.kind === "class" && [`${modelName}Internal`, `${modelName}Slice`, `${modelName}Endpoint`].includes(declaration.name);
15499
+ if (suffix === ".store.ts")
15500
+ return declaration.kind === "class" && declaration.name === `${modelName}Store`;
15501
+ return false;
15502
+ }
15503
+ function isAllowedConstantDeclaration(modelName, declaration) {
15504
+ if (declaration.kind !== "class")
15505
+ return false;
15506
+ if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
15507
+ return true;
15508
+ if (!ts11.isClassDeclaration(declaration.node))
15509
+ return false;
15510
+ const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts11.SyntaxKind.ExtendsKeyword);
15511
+ const expression = heritageClause?.types[0]?.expression;
15512
+ return !!expression && expression.getText().startsWith("enumOf(");
15513
+ }
15514
+ function getConventionDescription(suffix, modelName) {
15515
+ if (suffix === ".dictionary.ts")
15516
+ return "export const dictionary";
15517
+ if (suffix === ".constant.ts")
15518
+ return `${modelName}Input, ${modelName}Object, ${modelName}, Light${modelName}, ${modelName}Insight, or enumOf classes`;
15519
+ if (suffix === ".document.ts")
15520
+ return `${modelName}Filter, ${modelName}, or ${modelName}Model`;
15521
+ if (suffix === ".service.ts")
15522
+ return `${modelName}Service`;
15523
+ if (suffix === ".signal.ts")
15524
+ return `${modelName}Internal, ${modelName}Slice, or ${modelName}Endpoint`;
15525
+ return `${modelName}Store`;
15526
+ }
15527
+ function getLibRootFile(file) {
15528
+ const segments = file.split("/");
15529
+ if (segments[0] === "libs" && segments.length === 4 && segments[2] === "lib")
15530
+ return segments[3];
15531
+ if (segments[0] === "apps" && segments.length === 5 && segments[2] === "lib")
15532
+ return segments[4];
15533
+ return null;
15534
+ }
15535
+ function getModuleUiWarning(file) {
15536
+ if (!file.endsWith(".tsx"))
15537
+ return null;
15538
+ const moduleInfo = getModuleInfo(file);
15539
+ if (!moduleInfo)
15540
+ return null;
15541
+ const { moduleName, fileName, kind } = moduleInfo;
15542
+ const pascalName = toPascalCase(moduleName.replace(/^_+/, ""));
15543
+ const allowedSuffixes = kind === "database" ? ["Template", "Unit", "Util", "View", "Zone"] : kind === "service" ? ["Util", "Zone"] : ["Template", "Unit"];
15544
+ const allowedFileNames = new Set(allowedSuffixes.map((suffix) => `${pascalName}.${suffix}.tsx`));
15545
+ if (allowedFileNames.has(fileName) || fileName.endsWith(".test.tsx") || fileName.endsWith(".spec.tsx"))
15546
+ return null;
15547
+ return {
15548
+ rule: "akan.layout.module-ui-file",
15549
+ scope: "layout",
15550
+ severity: "warning",
15551
+ file,
15552
+ message: `Unexpected ${kind} module UI filename "${fileName}". Expected one of: ${[...allowedFileNames].join(", ")}.`
15553
+ };
15554
+ }
15555
+ function getModuleInfo(file) {
15556
+ const segments = file.split("/");
15557
+ const libIndex = segments.indexOf("lib");
15558
+ if (libIndex < 0)
15559
+ return null;
15560
+ const moduleName = segments[libIndex + 1];
15561
+ if (moduleName === "__scalar") {
15562
+ if (segments.length !== libIndex + 4)
15563
+ return null;
15564
+ return { moduleName: segments[libIndex + 2], fileName: segments[libIndex + 3], kind: "scalar" };
15565
+ }
15566
+ if (segments.length !== libIndex + 3)
15567
+ return null;
15568
+ const fileName = segments[libIndex + 2];
15569
+ if (moduleName.startsWith("__")) {
15570
+ const scalarName = moduleName === "__scalar" ? segments[libIndex + 2] : moduleName;
15571
+ return { moduleName: scalarName, fileName, kind: "scalar" };
15572
+ }
15573
+ if (moduleName.startsWith("_"))
15574
+ return { moduleName, fileName, kind: "service" };
15575
+ return { moduleName, fileName, kind: "database" };
15576
+ }
15577
+ function isExportedConst(declaration) {
15578
+ return declaration.exported && ts11.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts11.NodeFlags.Const) !== 0;
15579
+ }
15580
+ function isExported(node) {
15581
+ return !!ts11.getCombinedModifierFlags(node) && !!(ts11.getCombinedModifierFlags(node) & ts11.ModifierFlags.Export);
15582
+ }
15583
+ function isFunctionLikeInitializer(node) {
15584
+ return !!node && (ts11.isArrowFunction(node) || ts11.isFunctionExpression(node));
15585
+ }
15586
+ function getBodyFingerprint(sourceFile2, node) {
15587
+ if (!node)
15588
+ return;
15589
+ const normalizedBody = node.getText(sourceFile2).replace(/\s+/g, " ").trim();
15590
+ if (normalizedBody.length < 80)
15591
+ return;
15592
+ return createHash("sha256").update(normalizedBody).digest("hex");
15593
+ }
15594
+ function shouldSkipPath(relativePath, isDirectory, ignoreFilter) {
15595
+ const ignorePath = isDirectory ? `${relativePath}/` : relativePath;
15596
+ return relativePath === ".git" || relativePath.includes("/.git/") || relativePath.includes("/node_modules/") || ignoreFilter.ignores(relativePath) || ignoreFilter.ignores(ignorePath);
15597
+ }
15598
+ async function isDirectory(absolutePath) {
15599
+ try {
15600
+ return (await stat4(absolutePath)).isDirectory();
15601
+ } catch {
15602
+ return false;
15603
+ }
15604
+ }
15605
+ function getScriptKind(file) {
15606
+ return file.endsWith(".tsx") ? ts11.ScriptKind.TSX : ts11.ScriptKind.TS;
15607
+ }
15608
+ function getLine(sourceFile2, node) {
15609
+ return sourceFile2.getLineAndCharacterOfPosition(node.getStart(sourceFile2)).line + 1;
15610
+ }
15611
+ function getRecommendedLineLimit(file) {
15612
+ if (file.endsWith(".service.ts"))
15613
+ return 500;
15614
+ if (file.endsWith(".Template.tsx") || file.endsWith(".Zone.tsx"))
15615
+ return 800;
15616
+ if (file.endsWith(".Util.tsx"))
15617
+ return 1000;
15618
+ return null;
15619
+ }
15620
+ function findPatternLines(content, pattern) {
15621
+ return content.split(/\r?\n/).flatMap((line, index) => pattern.test(line) ? [index + 1] : []);
15622
+ }
15623
+ function toPascalCase(value) {
15624
+ return value.replace(/(^|[-_./])([a-zA-Z0-9])/g, (_, __, char) => char.toUpperCase()).replace(/[-_./]/g, "");
15625
+ }
15626
+ function toPosix(value) {
15627
+ return value.split(path43.sep).join("/");
15628
+ }
15629
+ function groupBy(items, getKey) {
15630
+ const grouped = new Map;
15631
+ for (const item of items) {
15632
+ const key = getKey(item);
15633
+ grouped.set(key, [...grouped.get(key) ?? [], item]);
15634
+ }
15635
+ return grouped;
15636
+ }
15637
+ function compareWarnings(a, b) {
15638
+ return a.scope.localeCompare(b.scope) || (a.file ?? "").localeCompare(b.file ?? "") || (a.line ?? 0) - (b.line ?? 0) || a.rule.localeCompare(b.rule);
15639
+ }
15082
15640
  // pkgs/@akanjs/devkit/selectModel.ts
15083
15641
  import { select as select5 } from "@inquirer/prompts";
15084
15642
  // pkgs/@akanjs/devkit/streamAi.ts
@@ -15196,10 +15754,10 @@ class IncrementalBuilder {
15196
15754
  }
15197
15755
  }
15198
15756
  batchTouchesPagesTree(appDir, batch) {
15199
- const absAppDir = path42.resolve(appDir);
15757
+ const absAppDir = path44.resolve(appDir);
15200
15758
  for (const f of batch.files) {
15201
- const abs = path42.resolve(f);
15202
- if (!abs.startsWith(`${absAppDir}${path42.sep}`) && abs !== absAppDir)
15759
+ const abs = path44.resolve(f);
15760
+ if (!abs.startsWith(`${absAppDir}${path44.sep}`) && abs !== absAppDir)
15203
15761
  continue;
15204
15762
  if (/\.(tsx|ts|jsx|js)$/.test(abs))
15205
15763
  return true;
@@ -15207,15 +15765,15 @@ class IncrementalBuilder {
15207
15765
  return false;
15208
15766
  }
15209
15767
  async batchMayChangePageKeys(appDir, batch) {
15210
- const absAppDir = path42.resolve(appDir);
15211
- const pageKeys = new Set((await this.#app.getPageKeys()).map((key) => path42.normalize(key)));
15768
+ const absAppDir = path44.resolve(appDir);
15769
+ const pageKeys = new Set((await this.#app.getPageKeys()).map((key) => path44.normalize(key)));
15212
15770
  for (const f of batch.files) {
15213
- const abs = path42.resolve(f);
15214
- if (!abs.startsWith(`${absAppDir}${path42.sep}`) && abs !== absAppDir)
15771
+ const abs = path44.resolve(f);
15772
+ if (!abs.startsWith(`${absAppDir}${path44.sep}`) && abs !== absAppDir)
15215
15773
  continue;
15216
15774
  if (!/\.(tsx|ts|jsx|js)$/.test(abs))
15217
15775
  continue;
15218
- const rel = path42.normalize(path42.relative(absAppDir, abs));
15776
+ const rel = path44.normalize(path44.relative(absAppDir, abs));
15219
15777
  if (!await Bun.file(abs).exists() || !pageKeys.has(rel))
15220
15778
  return true;
15221
15779
  }
@@ -15243,7 +15801,7 @@ class IncrementalBuilder {
15243
15801
  ${cssText}`).toString(36);
15244
15802
  const cssRelPath = `styles/${cssAssetName}-${cssHash}.css`;
15245
15803
  const cssUrl = `/_akan/styles/${cssAssetName}-${cssHash}.css`;
15246
- await Bun.write(path42.join(artifactDir, cssRelPath), cssText);
15804
+ await Bun.write(path44.join(artifactDir, cssRelPath), cssText);
15247
15805
  cssAssetEntries.push([basePath2, { cssUrl, cssRelPath }]);
15248
15806
  cssBase64ByUrl[cssUrl] = Buffer.from(new TextEncoder().encode(cssText)).toString("base64");
15249
15807
  })()
@@ -15311,10 +15869,10 @@ ${cssText}`).toString(36);
15311
15869
  if (changedFiles.length === 0)
15312
15870
  return false;
15313
15871
  return changedFiles.some((file) => {
15314
- const normalized = path42.resolve(file);
15872
+ const normalized = path44.resolve(file);
15315
15873
  if (/\.(woff2?|ttf|otf)$/i.test(normalized))
15316
15874
  return true;
15317
- return this.#optimizedFonts.files.some((fontFile) => path42.resolve(fontFile) === normalized);
15875
+ return this.#optimizedFonts.files.some((fontFile) => path44.resolve(fontFile) === normalized);
15318
15876
  });
15319
15877
  }
15320
15878
  async installWatcher() {