@akanjs/cli 2.4.0 → 2.4.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/agent.command-h4afc69n.js +26 -0
  2. package/application.command-4ctkfdan.js +165 -0
  3. package/applicationBuildRunner-esa1kj7v.js +284 -0
  4. package/applicationReleasePackager-brvth6rs.js +245 -0
  5. package/capacitorApp-y0h6cgft.js +58 -0
  6. package/cloud.command-rpztn4fh.js +94 -0
  7. package/commandManifest.json +1 -0
  8. package/context.command-nqbtak4f.js +82 -0
  9. package/dependencyScanner-m4x5maek.js +9 -0
  10. package/guideline.command-ya0dh44f.js +356 -0
  11. package/incrementalBuilder.proc.js +89 -17394
  12. package/index-1xdrsbry.js +1447 -0
  13. package/index-45aj5ry0.js +990 -0
  14. package/index-5vvwc0cz.js +559 -0
  15. package/index-61keag0s.js +40 -0
  16. package/index-6pz1j0zj.js +62 -0
  17. package/index-73pr2cmy.js +534 -0
  18. package/index-76rn3g2c.js +76 -0
  19. package/index-85msc0wg.js +161 -0
  20. package/index-8pkbzj26.js +840 -0
  21. package/index-8rc0bm04.js +514 -0
  22. package/index-9sp6fsc5.js +1884 -0
  23. package/index-a6sbyy0b.js +2769 -0
  24. package/index-b0brjbp3.js +83 -0
  25. package/index-fgc8r6dj.js +33 -0
  26. package/index-h6ca6qg0.js +2777 -0
  27. package/index-hdqztm58.js +758 -0
  28. package/index-hwzpw9c1.js +202 -0
  29. package/index-pmm9e2jf.js +120 -0
  30. package/index-qaq13qk3.js +80 -0
  31. package/index-qhtr07v8.js +1072 -0
  32. package/index-r24hmh0q.js +4 -0
  33. package/index-ss469dec.js +11 -0
  34. package/index-swf4bmbg.js +25 -0
  35. package/index-w7fyqjrw.js +462 -0
  36. package/index-wnp7hwq7.js +193 -0
  37. package/index-wq8jwx8z.js +224 -0
  38. package/index-x53a5nya.js +301 -0
  39. package/index-xmc2w32q.js +4359 -0
  40. package/index-y3hdhy4p.js +229 -0
  41. package/index.js +54 -23340
  42. package/library.command-r15zdqvp.js +33 -0
  43. package/localRegistry.command-5tcahs3f.js +178 -0
  44. package/module.command-qrj3kmyz.js +54 -0
  45. package/package.command-r8sq5kzp.js +43 -0
  46. package/package.json +2 -2
  47. package/page.command-c6xdx0xm.js +24 -0
  48. package/primitive.command-pv9ssmtf.js +62 -0
  49. package/quality.command-es67wvdp.js +809 -0
  50. package/repair.command-677675vw.js +67 -0
  51. package/routeSourceValidator-wbhmbwpj.js +132 -0
  52. package/scalar.command-kabkd6wd.js +35 -0
  53. package/templates/facetIndex/index.ts +1 -1
  54. package/typeChecker-kravn7ns.js +8 -0
  55. package/typecheck.proc.js +4 -194
  56. package/workflow.command-64r6cw0w.js +108 -0
  57. package/workspace.command-vrws0rgx.js +435 -0
  58. package/README.ko.md +0 -72
  59. package/README.md +0 -85
@@ -0,0 +1,2769 @@
1
+ // @bun
2
+ import {
3
+ FileSys
4
+ } from "./index-61keag0s.js";
5
+ import {
6
+ Spinner
7
+ } from "./index-6pz1j0zj.js";
8
+ import {
9
+ __require
10
+ } from "./index-r24hmh0q.js";
11
+
12
+ // pkgs/@akanjs/devkit/executors.ts
13
+ import {
14
+ exec,
15
+ fork,
16
+ spawn as spawn2
17
+ } from "child_process";
18
+ import { readFileSync as readFileSync2 } from "fs";
19
+ import { copyFile, mkdir, readdir as readDirEntries, stat } from "fs/promises";
20
+ import path5 from "path";
21
+ import { pathToFileURL } from "url";
22
+ import {
23
+ capitalize,
24
+ isRouteSourceFile,
25
+ Logger,
26
+ parseRouteModuleKey,
27
+ validatePageSourceFile,
28
+ validateSubRoutePageKey
29
+ } from "akanjs/common";
30
+ var {$ } = globalThis.Bun;
31
+ import chalk2 from "chalk";
32
+
33
+ // pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
34
+ import fs from "fs";
35
+ import path from "path";
36
+ import { fileURLToPath } from "url";
37
+ import { resolveAkanI18nConfig } from "akanjs/common";
38
+
39
+ // pkgs/@akanjs/devkit/akanConfig/types.ts
40
+ import { archs } from "akanjs";
41
+
42
+ // pkgs/@akanjs/devkit/akanConfig/akanConfig.ts
43
+ var DEFAULT_BARREL_IMPORTS = ["akanjs/webkit", "akanjs/common", "akanjs/ui", "akanjs/server"];
44
+ var DEFAULT_OPTIMIZE_IMPORTS = [
45
+ "lucide-react",
46
+ "date-fns",
47
+ "lodash-es",
48
+ "ramda",
49
+ "antd",
50
+ "react-bootstrap",
51
+ "ahooks",
52
+ "@ant-design/icons",
53
+ "@headlessui/react",
54
+ "@headlessui-float/react",
55
+ "@heroicons/react/20/solid",
56
+ "@heroicons/react/24/solid",
57
+ "@heroicons/react/24/outline",
58
+ "@visx/visx",
59
+ "@tremor/react",
60
+ "rxjs",
61
+ "@mui/material",
62
+ "@mui/icons-material",
63
+ "recharts",
64
+ "react-use",
65
+ "@material-ui/core",
66
+ "@material-ui/icons",
67
+ "@tabler/icons-react",
68
+ "mui-core",
69
+ "react-icons/*"
70
+ ];
71
+ var WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"];
72
+ var SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"];
73
+ var NATIVE_RUNTIME_PACKAGES = ["sharp"];
74
+ var DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"];
75
+ var MOBILE_RUNTIME_PACKAGES = [
76
+ "@capacitor/cli",
77
+ "@capacitor/core",
78
+ "@capacitor/ios",
79
+ "@capacitor/android",
80
+ "@capacitor/assets"
81
+ ];
82
+ var MOBILE_APP_CAPACITOR_PLUGINS = [
83
+ "@capacitor/app",
84
+ "@capacitor/browser",
85
+ "@capacitor/camera",
86
+ "@capacitor/core",
87
+ "@capacitor/device",
88
+ "@capacitor/geolocation",
89
+ "@capacitor/haptics",
90
+ "@capacitor/inappbrowser",
91
+ "@capacitor/keyboard",
92
+ "@capacitor/preferences",
93
+ "@capacitor/push-notifications",
94
+ "capacitor-plugin-safe-area"
95
+ ];
96
+ var DATABASE_MODE_RUNTIME_PACKAGES = {
97
+ single: [],
98
+ multiple: ["@libsql/client", "bullmq", "ioredis", "protobufjs"],
99
+ cluster: ["bullmq", "ioredis", "postgres", "protobufjs"]
100
+ };
101
+ var AKAN_RUNTIME_PACKAGES = new Set([
102
+ ...SSR_RUNTIME_PACKAGES,
103
+ ...NATIVE_RUNTIME_PACKAGES,
104
+ ...DEFAULT_BACKEND_RUNTIME_PACKAGES,
105
+ ...MOBILE_RUNTIME_PACKAGES,
106
+ ...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat()
107
+ ]);
108
+ var DEFAULT_AKAN_IMAGE_CONFIG = {
109
+ deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
110
+ imageSizes: [32, 48, 64, 96, 128, 256, 384],
111
+ formats: ["image/webp"],
112
+ qualities: [75],
113
+ minimumCacheTTL: 14400,
114
+ remotePatterns: [],
115
+ localPatterns: [{ pathname: "/**" }],
116
+ dangerouslyAllowSVG: false,
117
+ maximumRedirects: 3,
118
+ fetchTimeoutMs: 7000,
119
+ maxRemoteBytes: 25 * 1024 * 1024
120
+ };
121
+ var normalizeIndexPath = (indexPath) => {
122
+ const normalized = indexPath?.trim();
123
+ if (!normalized)
124
+ return;
125
+ const path2 = `/${normalized.replace(/^\/+|\/+$/g, "")}`;
126
+ return path2 === "/" ? "/" : path2;
127
+ };
128
+ var normalizeStringList = (values) => {
129
+ const normalized = values?.map((value) => value.trim()).filter(Boolean) ?? [];
130
+ return normalized.length > 0 ? [...new Set(normalized)] : undefined;
131
+ };
132
+ var sanitizeAppIdSegment = (value) => {
133
+ const cleaned = value.toLowerCase().replace(/[^a-z0-9]+/g, "");
134
+ if (!cleaned)
135
+ return "app";
136
+ return /^[a-z]/.test(cleaned) ? cleaned : `app${cleaned}`;
137
+ };
138
+ var deriveDefaultAppId = (orgName, appName) => `com.${sanitizeAppIdSegment(orgName)}.${sanitizeAppIdSegment(appName)}`;
139
+ var normalizeDeepLinkDomain = (domain) => {
140
+ const normalized = domain.trim();
141
+ if (!normalized)
142
+ return "";
143
+ try {
144
+ const url = new URL(normalized.includes("://") ? normalized : `https://${normalized}`);
145
+ return url.host.toLowerCase();
146
+ } catch {
147
+ return normalized.replace(/^https?:\/\//, "").replace(/\/+$/g, "").toLowerCase();
148
+ }
149
+ };
150
+ var normalizeDeepLinks = (deepLinks) => {
151
+ if (!deepLinks)
152
+ return;
153
+ const schemes = normalizeStringList(deepLinks.schemes);
154
+ const domains = normalizeStringList(deepLinks.domains?.map(normalizeDeepLinkDomain));
155
+ const teamId = deepLinks.ios?.teamId?.trim();
156
+ const sha256CertFingerprints = normalizeStringList(deepLinks.android?.sha256CertFingerprints);
157
+ if (!schemes && !domains && !teamId && !sha256CertFingerprints)
158
+ return;
159
+ return {
160
+ ...schemes ? { schemes } : {},
161
+ ...domains ? { domains } : {},
162
+ ...teamId ? { ios: { teamId } } : {},
163
+ ...sha256CertFingerprints ? { android: { sha256CertFingerprints } } : {}
164
+ };
165
+ };
166
+
167
+ class AkanAppConfig {
168
+ app;
169
+ rootPackageJson;
170
+ docker;
171
+ defaultDatabaseMode;
172
+ externalLibs;
173
+ barrelImports;
174
+ optimizeImports;
175
+ images;
176
+ i18n;
177
+ publicEnv;
178
+ mobile;
179
+ hasMobileConfig;
180
+ secrets;
181
+ baseDevEnv;
182
+ libs;
183
+ plugins;
184
+ domains = new Set;
185
+ subRoutes = new Map;
186
+ basePaths = new Set;
187
+ branches = new Set(["debug", "develop", "main"]);
188
+ constructor(app, libs, rootPackageJson, config, baseDevEnv, plugins = []) {
189
+ this.app = app;
190
+ this.rootPackageJson = rootPackageJson;
191
+ this.libs = libs;
192
+ this.baseDevEnv = baseDevEnv;
193
+ this.plugins = plugins;
194
+ this.#applyRoutes(config?.routes);
195
+ this.defaultDatabaseMode = config?.defaultDatabaseMode ?? "single";
196
+ this.externalLibs = config?.externalLibs ?? [];
197
+ this.barrelImports = [
198
+ ...DEFAULT_BARREL_IMPORTS,
199
+ ...WORKSPACE_BARREL_FACETS.map((facet) => `@apps/${app.name}/${facet}`),
200
+ ...libs.flatMap((lib) => WORKSPACE_BARREL_FACETS.map((facet) => `@libs/${lib}/${facet}`)),
201
+ ...config?.barrelImports ?? []
202
+ ];
203
+ this.optimizeImports = [...new Set([...DEFAULT_OPTIMIZE_IMPORTS, ...config?.optimizeImports ?? []])];
204
+ this.images = mergeImageConfig(config?.images);
205
+ this.i18n = resolveAkanI18nConfig(config?.i18n);
206
+ process.env.AKAN_PUBLIC_DEFAULT_LOCALE = this.i18n.defaultLocale;
207
+ process.env.AKAN_PUBLIC_LOCALES = this.i18n.locales.join(",");
208
+ this.publicEnv = config?.publicEnv ?? [];
209
+ this.secrets = config?.secrets ?? [];
210
+ this.hasMobileConfig = Boolean(config.mobile);
211
+ this.mobile = this.#resolveMobileConfig(config.mobile);
212
+ this.docker = this.#makeDockerContent(config?.docker ?? {});
213
+ }
214
+ #resolveMobileConfig(mobile) {
215
+ const {
216
+ targets: rawTargets,
217
+ indexPath: _indexPath,
218
+ ...rawMobile
219
+ } = mobile ?? {};
220
+ const appName = rawMobile.appName ?? this.app.name;
221
+ const appId = rawMobile.appId ?? deriveDefaultAppId(this.baseDevEnv.repoName, this.app.name);
222
+ const version = rawMobile.version ?? "0.0.1";
223
+ const buildNum = rawMobile.buildNum ?? 1;
224
+ const defaultTargetName = this.#defaultMobileTargetName(rawTargets);
225
+ const targetEntries = Object.entries(rawTargets ?? {
226
+ [defaultTargetName]: {}
227
+ });
228
+ const targets = Object.fromEntries(targetEntries.map(([name, rawTarget]) => {
229
+ const target = rawTarget;
230
+ const fallbackBasePath = !rawTargets && this.basePaths.has(name) ? name : undefined;
231
+ const basePath = (target.basePath ?? fallbackBasePath)?.replace(/^\/+|\/+$/g, "") || undefined;
232
+ const indexPath = normalizeIndexPath(target.indexPath);
233
+ const deepLinks = normalizeDeepLinks(target.deepLinks);
234
+ if (basePath && !this.basePaths.has(basePath)) {
235
+ throw new Error(`Mobile target '${name}' uses unknown basePath '${basePath}' in apps/${this.app.name}/akan.config.ts`);
236
+ }
237
+ const resolved = {
238
+ ...rawMobile,
239
+ ...target,
240
+ name,
241
+ basePath,
242
+ indexPath,
243
+ deepLinks,
244
+ appName: target.appName ?? appName,
245
+ appId: target.appId ?? appId,
246
+ version: target.version ?? version,
247
+ buildNum: target.buildNum ?? buildNum,
248
+ plugins: {
249
+ ...rawMobile.plugins,
250
+ ...target.plugins
251
+ },
252
+ android: {
253
+ ...rawMobile.android,
254
+ ...target.android
255
+ },
256
+ ios: {
257
+ ...rawMobile.ios,
258
+ ...target.ios
259
+ }
260
+ };
261
+ return [name, resolved];
262
+ }));
263
+ return {
264
+ ...rawMobile,
265
+ appName,
266
+ appId,
267
+ version,
268
+ buildNum,
269
+ targets,
270
+ plugins: rawMobile.plugins
271
+ };
272
+ }
273
+ #defaultMobileTargetName(rawTargets) {
274
+ if (rawTargets && Object.keys(rawTargets).length > 0)
275
+ return Object.keys(rawTargets)[0];
276
+ return this.basePaths.has(this.app.name) ? this.app.name : "default";
277
+ }
278
+ #applyRoutes(routes = []) {
279
+ for (const route of routes) {
280
+ if (route.basePath) {
281
+ const basePath = route.basePath.replace(/^\/+|\/+$/g, "");
282
+ this.basePaths.add(basePath);
283
+ const domains = this.subRoutes.getOrInsert(basePath, new Set);
284
+ Object.keys(route.domains).forEach((branch) => void this.branches.add(branch));
285
+ Object.values(route.domains).flat().forEach((domain) => {
286
+ if (domain)
287
+ domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
288
+ });
289
+ } else {
290
+ Object.keys(route.domains).forEach((branch) => void this.branches.add(branch));
291
+ Object.values(route.domains).flat().forEach((domain) => {
292
+ if (domain)
293
+ this.domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
294
+ });
295
+ }
296
+ }
297
+ const appName = this.app.name.toLowerCase();
298
+ const serveDomain = this.baseDevEnv.serveDomain.toLowerCase();
299
+ if (this.subRoutes.size === 0)
300
+ this.branches.forEach((branch) => void this.domains.add(`${appName}-${branch}.${serveDomain}`));
301
+ else
302
+ Array.from(this.subRoutes.entries()).forEach(([basePath, domains]) => {
303
+ this.branches.forEach((domain) => void domains.add(`${basePath}-${domain}.${serveDomain}`));
304
+ });
305
+ }
306
+ #getDockerRunScripts(runs) {
307
+ return runs.map((run) => {
308
+ if (typeof run === "string")
309
+ return `RUN ${run}`;
310
+ else
311
+ return Object.entries(run).map(([arch, script]) => `RUN if [ "$TARGETARCH" = "${arch}" ]; then ${script}; fi`).join(`
312
+ `);
313
+ });
314
+ }
315
+ #getDockerImageScript(image, defaultImage) {
316
+ if (typeof image === "string")
317
+ return `FROM ${image}`;
318
+ else
319
+ return archs.map((arch) => `FROM ${image[arch] ?? defaultImage} AS ${arch}`).join(`
320
+ `);
321
+ }
322
+ #makeDockerContent(docker) {
323
+ if (docker.content)
324
+ return {
325
+ content: docker.content,
326
+ image: {},
327
+ preRuns: [],
328
+ postRuns: [],
329
+ command: []
330
+ };
331
+ const preRunScripts = this.#getDockerRunScripts(docker.preRuns ?? []);
332
+ const postRunScripts = this.#getDockerRunScripts(docker.postRuns ?? []);
333
+ const imageScript = docker.image ? this.#getDockerImageScript(docker.image, "oven/bun:1-slim") : "FROM oven/bun:1-slim";
334
+ const command = docker.command ?? ["bun", "main.js"];
335
+ const content = `${imageScript}
336
+ RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
337
+ RUN apt-get update && apt-get upgrade -y
338
+ RUN apt-get install -y --no-install-recommends git redis build-essential python3 ca-certificates fonts-liberation libappindicator3-1 libasound2 libatk-bridge2.0-0 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libglib2.0-0 libgtk-3-0 libnspr4 libnss3 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 lsb-release wget xdg-utils udev ffmpeg
339
+ ARG TARGETARCH
340
+ ${preRunScripts.join(`
341
+ `)}
342
+ RUN mkdir -p /workspace
343
+ WORKDIR /workspace
344
+ COPY ./package.json ./package.json
345
+ RUN bun install --production
346
+ ${postRunScripts.join(`
347
+ `)}
348
+ COPY . .
349
+ ENV PORT=8282
350
+ ENV NODE_ENV=production
351
+ ENV AKAN_PUBLIC_REPO_NAME=${this.baseDevEnv.repoName}
352
+ ENV AKAN_PUBLIC_SERVE_DOMAIN=${this.baseDevEnv.serveDomain}
353
+ ENV AKAN_PUBLIC_APP_NAME=${this.app.name}
354
+ ENV AKAN_PUBLIC_ENV=${this.baseDevEnv.env}
355
+ ${this.basePaths.size ? `ENV AKAN_PUBLIC_BASE_PATHS=${[...this.basePaths].join(",")}` : ""}
356
+ ENV AKAN_PUBLIC_DEFAULT_LOCALE=${this.i18n.defaultLocale}
357
+ ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
358
+ ENV AKAN_PUBLIC_OPERATION_MODE=cloud
359
+
360
+ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
361
+ return {
362
+ content,
363
+ image: imageScript,
364
+ preRuns: docker.preRuns ?? [],
365
+ postRuns: docker.postRuns ?? [],
366
+ command
367
+ };
368
+ }
369
+ static #importGeneration = 0;
370
+ static async importConfigModule(cwdPath, { bustImportCache = false } = {}) {
371
+ const configPath = `${cwdPath}/akan.config.ts`;
372
+ const importPath = bustImportCache ? `${configPath}?akanConfigGeneration=${++AkanAppConfig.#importGeneration}` : configPath;
373
+ return await import(importPath).then((mod) => mod.default);
374
+ }
375
+ static async from(app, { bustImportCache = false } = {}) {
376
+ const [configImp, baseDevEnv, libs, rootPackageJson] = await Promise.all([
377
+ AkanAppConfig.importConfigModule(app.cwdPath, { bustImportCache }),
378
+ WorkspaceExecutor.getBaseDevEnv(path.join(app.workspace.workspaceRoot, ".env")),
379
+ app.workspace.getLibs(),
380
+ app.workspace.getPackageJson()
381
+ ]);
382
+ const resolved = typeof configImp === "function" ? configImp(app) : configImp;
383
+ const { plugins, ...config } = resolved ?? {};
384
+ return new AkanAppConfig(app, libs, rootPackageJson, config, baseDevEnv, plugins ?? []);
385
+ }
386
+ #resolveProductionDependencyVersion(lib) {
387
+ const rootVersion = this.rootPackageJson.dependencies?.[lib] ?? this.rootPackageJson.devDependencies?.[lib];
388
+ if (rootVersion)
389
+ return rootVersion;
390
+ const akanPackageJson = getAkanPackageJson();
391
+ return akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
392
+ }
393
+ #getProductionRuntimePackages() {
394
+ return [
395
+ ...this.externalLibs,
396
+ ...SSR_RUNTIME_PACKAGES,
397
+ ...NATIVE_RUNTIME_PACKAGES,
398
+ ...DEFAULT_BACKEND_RUNTIME_PACKAGES,
399
+ ...this.getDatabaseModeRuntimePackages()
400
+ ];
401
+ }
402
+ getDatabaseModeRuntimePackages(databaseMode = this.defaultDatabaseMode) {
403
+ return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
404
+ }
405
+ getMissingDatabaseModeDependencySpecs(databaseMode = this.defaultDatabaseMode) {
406
+ return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
407
+ }
408
+ getMobileRuntimePackages() {
409
+ return [...new Set([...MOBILE_RUNTIME_PACKAGES, ...MOBILE_APP_CAPACITOR_PLUGINS])];
410
+ }
411
+ getMissingMobileDependencySpecs() {
412
+ return this.#getMissingDependencySpecs(this.getMobileRuntimePackages());
413
+ }
414
+ getMobileAppCapacitorPlugins() {
415
+ return [...MOBILE_APP_CAPACITOR_PLUGINS];
416
+ }
417
+ #getMissingDependencySpecs(libs) {
418
+ const rootDependencies = {
419
+ ...this.rootPackageJson.dependencies,
420
+ ...this.rootPackageJson.devDependencies
421
+ };
422
+ return libs.filter((lib) => !rootDependencies[lib]).map((lib) => {
423
+ const version = this.#resolveProductionDependencyVersion(lib);
424
+ if (!version)
425
+ throw new Error(`Dependency ${lib} not found in package.json`);
426
+ return `${lib}@${version}`;
427
+ });
428
+ }
429
+ getProductionPackageJson(data = {}) {
430
+ return {
431
+ name: this.app.name,
432
+ description: this.app.name,
433
+ version: "1.0.0",
434
+ main: "./main.js",
435
+ dependencies: Object.fromEntries([...new Set(this.#getProductionRuntimePackages())].map((lib) => {
436
+ const version = this.#resolveProductionDependencyVersion(lib);
437
+ if (!version)
438
+ throw new Error(`Dependency ${lib} not found in package.json`);
439
+ return [lib, version];
440
+ })),
441
+ ...data
442
+ };
443
+ }
444
+ }
445
+ var akanPackageJson = null;
446
+ function getAkanPackageJson() {
447
+ if (akanPackageJson)
448
+ return akanPackageJson;
449
+ const sourceDir = path.dirname(fileURLToPath(import.meta.url));
450
+ const packageJsonPaths = [
451
+ path.join(sourceDir, "../../../akanjs/package.json"),
452
+ path.join(process.cwd(), "pkgs/akanjs/package.json"),
453
+ path.join(path.dirname(Bun.main), "node_modules/akanjs/package.json")
454
+ ];
455
+ try {
456
+ packageJsonPaths.unshift(Bun.resolveSync("akanjs/package.json", sourceDir));
457
+ } catch {}
458
+ for (const packageJsonPath of packageJsonPaths) {
459
+ try {
460
+ akanPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
461
+ return akanPackageJson;
462
+ } catch {}
463
+ }
464
+ akanPackageJson = {
465
+ name: "akanjs",
466
+ version: "0.0.0",
467
+ description: "akanjs",
468
+ dependencies: {}
469
+ };
470
+ return akanPackageJson;
471
+ }
472
+ function mergeImageConfig(config = {}) {
473
+ return {
474
+ ...DEFAULT_AKAN_IMAGE_CONFIG,
475
+ ...config,
476
+ deviceSizes: config.deviceSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.deviceSizes,
477
+ imageSizes: config.imageSizes ?? DEFAULT_AKAN_IMAGE_CONFIG.imageSizes,
478
+ formats: config.formats ?? DEFAULT_AKAN_IMAGE_CONFIG.formats,
479
+ qualities: config.qualities ?? DEFAULT_AKAN_IMAGE_CONFIG.qualities,
480
+ remotePatterns: config.remotePatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.remotePatterns,
481
+ localPatterns: config.localPatterns ?? DEFAULT_AKAN_IMAGE_CONFIG.localPatterns
482
+ };
483
+ }
484
+
485
+ class AkanLibConfig {
486
+ lib;
487
+ externalLibs;
488
+ plugins;
489
+ constructor(lib, config, plugins = []) {
490
+ this.lib = lib;
491
+ this.externalLibs = config?.externalLibs ?? [];
492
+ this.plugins = plugins;
493
+ }
494
+ static async from(lib) {
495
+ const [configImp] = await Promise.all([import(`${lib.cwdPath}/akan.config.ts`).then((mod) => mod.default)]);
496
+ const resolved = typeof configImp === "function" ? configImp(lib) : configImp;
497
+ const { plugins, ...config } = resolved ?? {};
498
+ return new AkanLibConfig(lib, config, plugins ?? []);
499
+ }
500
+ }
501
+ //! need to refactor
502
+ var increaseBuildNum = async (app) => {
503
+ const appConfig = await AkanAppConfig.from(app);
504
+ const akanConfigPath = path.join(app.cwdPath, "akan.config.ts");
505
+ const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
506
+ const akanConfigContent = akanConfig.replace(`buildNum: ${appConfig.mobile.buildNum}`, `buildNum: ${appConfig.mobile.buildNum + 1}`);
507
+ fs.writeFileSync(akanConfigPath, akanConfigContent);
508
+ };
509
+ var decreaseBuildNum = async (app) => {
510
+ const appConfig = await AkanAppConfig.from(app);
511
+ const akanConfigPath = path.join(app.cwdPath, "akan.config.ts");
512
+ const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
513
+ const akanConfigContent = akanConfig.replace(`buildNum: ${appConfig.mobile.buildNum}`, `buildNum: ${appConfig.mobile.buildNum - 1}`);
514
+ fs.writeFileSync(akanConfigPath, akanConfigContent);
515
+ };
516
+ // pkgs/@akanjs/devkit/getDirname.ts
517
+ import path2 from "path";
518
+ import { fileURLToPath as fileURLToPath2 } from "url";
519
+ var getDirname = (url) => path2.dirname(fileURLToPath2(url));
520
+
521
+ // pkgs/@akanjs/devkit/linter.ts
522
+ import { spawn } from "child_process";
523
+ import { existsSync, readFileSync } from "fs";
524
+ import path3 from "path";
525
+ import chalk from "chalk";
526
+
527
+ class Linter {
528
+ lintRoot;
529
+ #biomeBin;
530
+ constructor(cwdPath) {
531
+ this.lintRoot = this.#findBiomeRootPath(cwdPath);
532
+ const localBiomeBin = path3.join(this.lintRoot, "node_modules/.bin/biome");
533
+ this.#biomeBin = existsSync(localBiomeBin) ? localBiomeBin : "biome";
534
+ }
535
+ #findBiomeRootPath(dir) {
536
+ const configPath = path3.join(dir, "biome.json");
537
+ if (existsSync(configPath))
538
+ return dir;
539
+ const parentDir = path3.dirname(dir);
540
+ if (parentDir === dir)
541
+ throw new Error(`biome.json not found from ${dir}`);
542
+ return this.#findBiomeRootPath(parentDir);
543
+ }
544
+ #toBiomePath(filePath) {
545
+ const relativePath = path3.relative(this.lintRoot, filePath);
546
+ if (!relativePath.startsWith("..") && !path3.isAbsolute(relativePath))
547
+ return relativePath;
548
+ return filePath;
549
+ }
550
+ #resolveFilePath(filePath) {
551
+ return path3.isAbsolute(filePath) ? filePath : path3.join(this.lintRoot, filePath);
552
+ }
553
+ async#runBiome(args, input) {
554
+ return await new Promise((resolve, reject) => {
555
+ const proc = spawn(this.#biomeBin, args, {
556
+ cwd: this.lintRoot,
557
+ stdio: ["pipe", "pipe", "pipe"]
558
+ });
559
+ let stdout = "";
560
+ let stderr = "";
561
+ proc.stdout.on("data", (data) => {
562
+ stdout += data.toString();
563
+ });
564
+ proc.stderr.on("data", (data) => {
565
+ stderr += data.toString();
566
+ });
567
+ proc.on("error", reject);
568
+ proc.on("close", (code) => resolve({ stdout, stderr, code }));
569
+ proc.stdin.end(input);
570
+ });
571
+ }
572
+ #parseBiomeReport(output) {
573
+ const jsonStart = output.indexOf("{");
574
+ const jsonEnd = output.lastIndexOf("}");
575
+ if (jsonStart === -1 || jsonEnd === -1 || jsonEnd < jsonStart)
576
+ throw new Error(output.trim() || "No Biome JSON output");
577
+ return JSON.parse(output.slice(jsonStart, jsonEnd + 1));
578
+ }
579
+ #diagnosticFilePath(diagnostic, fallbackFilePath) {
580
+ const diagnosticPath = diagnostic.location?.path;
581
+ if (!diagnosticPath)
582
+ return fallbackFilePath;
583
+ return path3.isAbsolute(diagnosticPath) ? diagnosticPath : path3.join(this.lintRoot, diagnosticPath);
584
+ }
585
+ #createLintMessage(diagnostic) {
586
+ const start = diagnostic.location?.start;
587
+ const end = diagnostic.location?.end;
588
+ return {
589
+ line: Math.max(1, start?.line ?? 1),
590
+ column: Math.max(1, start?.column ?? 1),
591
+ endLine: end?.line,
592
+ endColumn: end?.column,
593
+ message: diagnostic.message,
594
+ ruleId: diagnostic.category ?? null,
595
+ severity: diagnostic.severity === "error" ? 2 : 1
596
+ };
597
+ }
598
+ #toLintResults(report, filePath) {
599
+ const resultsByPath = new Map;
600
+ for (const diagnostic of report.diagnostics ?? []) {
601
+ if (diagnostic.severity !== "error" && diagnostic.severity !== "warning")
602
+ continue;
603
+ const diagnosticFilePath = this.#diagnosticFilePath(diagnostic, filePath);
604
+ const result = resultsByPath.get(diagnosticFilePath) ?? {
605
+ filePath: diagnosticFilePath,
606
+ messages: [],
607
+ errorCount: 0,
608
+ warningCount: 0,
609
+ fixableErrorCount: 0,
610
+ fixableWarningCount: 0
611
+ };
612
+ const message = this.#createLintMessage(diagnostic);
613
+ result.messages.push(message);
614
+ if (message.severity === 2)
615
+ result.errorCount += 1;
616
+ else
617
+ result.warningCount += 1;
618
+ resultsByPath.set(diagnosticFilePath, result);
619
+ }
620
+ return [
621
+ resultsByPath.get(filePath) ?? {
622
+ filePath,
623
+ messages: [],
624
+ errorCount: 0,
625
+ warningCount: 0,
626
+ fixableErrorCount: 0,
627
+ fixableWarningCount: 0
628
+ },
629
+ ...[...resultsByPath.entries()].filter(([resultPath]) => resultPath !== filePath).map(([, result]) => result)
630
+ ];
631
+ }
632
+ #splitMessages(results) {
633
+ const messages = results.flatMap((result) => result.messages);
634
+ return {
635
+ errors: messages.filter((message) => message.severity === 2),
636
+ warnings: messages.filter((message) => message.severity === 1)
637
+ };
638
+ }
639
+ async#checkFile(filePath, { write = false } = {}) {
640
+ const originalContent = existsSync(filePath) ? readFileSync(filePath, "utf8") : "";
641
+ const { stdout, stderr } = await this.#runBiome([
642
+ "check",
643
+ ...write ? ["--write"] : [],
644
+ "--reporter=json",
645
+ "--max-diagnostics=none",
646
+ "--no-errors-on-unmatched",
647
+ "--config-path",
648
+ path3.join(this.lintRoot, "biome.json"),
649
+ this.#toBiomePath(filePath)
650
+ ]);
651
+ const report = this.#parseBiomeReport(stdout || stderr);
652
+ const results = this.#toLintResults(report, filePath);
653
+ const { errors, warnings } = this.#splitMessages(results);
654
+ const output = write && existsSync(filePath) ? readFileSync(filePath, "utf8") : undefined;
655
+ return {
656
+ fixed: write && output !== originalContent,
657
+ output,
658
+ results,
659
+ errors,
660
+ warnings
661
+ };
662
+ }
663
+ async lint(filePath, { fix = false, dryRun = false } = {}) {
664
+ if (fix)
665
+ return await this.fixFile(filePath, dryRun);
666
+ return await this.lintFile(filePath);
667
+ }
668
+ async lintFile(filePath) {
669
+ const resolvedFilePath = this.#resolveFilePath(filePath);
670
+ if (!existsSync(resolvedFilePath))
671
+ throw new Error(`File not found: ${filePath}`);
672
+ return await this.#checkFile(resolvedFilePath);
673
+ }
674
+ formatLintResults(results) {
675
+ if (results.length === 0)
676
+ return "No files to lint";
677
+ const output = [];
678
+ let totalErrors = 0;
679
+ let totalWarnings = 0;
680
+ results.forEach((result) => {
681
+ totalErrors += result.errorCount;
682
+ totalWarnings += result.warningCount;
683
+ if (result.messages.length > 0) {
684
+ output.push(`
685
+ ${chalk.cyan(result.filePath)}`);
686
+ let sourceLines = [];
687
+ if (existsSync(result.filePath)) {
688
+ try {
689
+ const sourceContent = readFileSync(result.filePath, "utf8");
690
+ sourceLines = sourceContent.split(`
691
+ `);
692
+ } catch {}
693
+ }
694
+ result.messages.forEach((message) => {
695
+ const type = message.severity === 2 ? "error" : "warning";
696
+ const typeColor = message.severity === 2 ? chalk.red : chalk.yellow;
697
+ const icon = message.severity === 2 ? "x" : "!";
698
+ const ruleInfo = message.ruleId ? chalk.dim(` (${message.ruleId})`) : "";
699
+ output.push(`
700
+ ${icon} ${typeColor(type)}: ${message.message}${ruleInfo}`);
701
+ output.push(` ${chalk.gray("at")} ${result.filePath}:${chalk.bold(`${message.line}:${message.column}`)}`);
702
+ if (sourceLines.length > 0 && message.line <= sourceLines.length) {
703
+ const sourceLine = sourceLines[message.line - 1];
704
+ const lineNumber = message.line.toString().padStart(5, " ");
705
+ output.push(`
706
+ ${chalk.dim(`${lineNumber} |`)} ${sourceLine}`);
707
+ const underlinePrefix = " ".repeat(message.column - 1);
708
+ const underlineLength = message.endColumn ? message.endColumn - message.column : 1;
709
+ const underline = "^".repeat(Math.max(1, underlineLength));
710
+ output.push(`${chalk.dim(`${" ".repeat(lineNumber.length)} |`)} ${underlinePrefix}${typeColor(underline)}`);
711
+ }
712
+ });
713
+ }
714
+ });
715
+ if (totalErrors === 0 && totalWarnings === 0)
716
+ return chalk.bold("No Biome errors or warnings found");
717
+ const errorText = totalErrors > 0 ? chalk.red(`${totalErrors} error(s)`) : "0 errors";
718
+ const warningText = totalWarnings > 0 ? chalk.yellow(`${totalWarnings} warning(s)`) : "0 warnings";
719
+ const summary = [`
720
+ ${errorText}, ${warningText} found`];
721
+ return summary.concat(output).join(`
722
+ `);
723
+ }
724
+ async getDetailedLintInfo(filePath) {
725
+ const { results } = await this.lintFile(filePath);
726
+ const details = results.flatMap((result) => result.messages.map((message) => ({
727
+ line: message.line,
728
+ column: message.column,
729
+ message: message.message,
730
+ ruleId: message.ruleId,
731
+ severity: message.severity === 2 ? "error" : "warning"
732
+ })));
733
+ const stats = results.reduce((acc, result) => ({
734
+ errorCount: acc.errorCount + result.errorCount,
735
+ warningCount: acc.warningCount + result.warningCount,
736
+ fixableErrorCount: acc.fixableErrorCount + result.fixableErrorCount,
737
+ fixableWarningCount: acc.fixableWarningCount + result.fixableWarningCount
738
+ }), {
739
+ errorCount: 0,
740
+ warningCount: 0,
741
+ fixableErrorCount: 0,
742
+ fixableWarningCount: 0
743
+ });
744
+ return { results, details, stats };
745
+ }
746
+ async hasNoLintErrors(filePath) {
747
+ try {
748
+ const { results } = await this.lintFile(filePath);
749
+ return results.every((result) => result.errorCount === 0);
750
+ } catch {
751
+ return false;
752
+ }
753
+ }
754
+ async getErrors(filePath) {
755
+ const { results } = await this.lintFile(filePath);
756
+ return results.flatMap((result) => result.messages.filter((message) => message.severity === 2));
757
+ }
758
+ async getWarnings(filePath) {
759
+ const { results } = await this.lintFile(filePath);
760
+ return results.flatMap((result) => result.messages.filter((message) => message.severity === 1));
761
+ }
762
+ async fixFile(filePath, dryRun = false) {
763
+ const resolvedFilePath = this.#resolveFilePath(filePath);
764
+ if (!existsSync(resolvedFilePath))
765
+ throw new Error(`File not found: ${filePath}`);
766
+ if (!dryRun)
767
+ return await this.#checkFile(resolvedFilePath, { write: true });
768
+ const source = readFileSync(resolvedFilePath, "utf8");
769
+ const { stdout } = await this.#runBiome([
770
+ "check",
771
+ "--write",
772
+ "--config-path",
773
+ path3.join(this.lintRoot, "biome.json"),
774
+ "--stdin-file-path",
775
+ this.#toBiomePath(resolvedFilePath)
776
+ ], source);
777
+ const lintResult = await this.lintFile(resolvedFilePath);
778
+ return { ...lintResult, fixed: stdout !== source, output: stdout };
779
+ }
780
+ async getConfigForFile(filePath) {
781
+ const resolvedFilePath = this.#resolveFilePath(filePath);
782
+ if (!existsSync(resolvedFilePath))
783
+ throw new Error(`File not found: ${filePath}`);
784
+ return JSON.parse(readFileSync(path3.join(this.lintRoot, "biome.json"), "utf8"));
785
+ }
786
+ async getProblematicRules(filePath) {
787
+ const { results } = await this.lintFile(filePath);
788
+ const ruleCounts = {};
789
+ results.forEach((result) => {
790
+ result.messages.forEach((message) => {
791
+ if (message.ruleId)
792
+ ruleCounts[message.ruleId] = (ruleCounts[message.ruleId] || 0) + 1;
793
+ });
794
+ });
795
+ return ruleCounts;
796
+ }
797
+ }
798
+
799
+ // pkgs/@akanjs/devkit/scanInfo.ts
800
+ import { rm } from "fs/promises";
801
+ import path4 from "path";
802
+ var databaseFileTypes = [
803
+ "constant",
804
+ "dictionary",
805
+ "document",
806
+ "service",
807
+ "signal",
808
+ "store",
809
+ "template",
810
+ "unit",
811
+ "util",
812
+ "view",
813
+ "zone"
814
+ ];
815
+ var appRootAllowedFiles = new Set([
816
+ "akan.app.json",
817
+ "akan.config.ts",
818
+ "capacitor.config.ts",
819
+ "client.ts",
820
+ "main.ts",
821
+ "package.json",
822
+ "server.ts",
823
+ "tsconfig.json",
824
+ "tsconfig.tsbuildinfo"
825
+ ]);
826
+ var generatedRootCapacitorConfigFiles = ["capacitor.config.js", "capacitor.config.json"];
827
+ var appRootAllowedDirs = new Set([
828
+ ".akan",
829
+ "android",
830
+ "env",
831
+ "ios",
832
+ "lib",
833
+ "mobile",
834
+ "page",
835
+ "private",
836
+ "public",
837
+ "script",
838
+ "ui",
839
+ "srvkit",
840
+ "webkit",
841
+ "common",
842
+ "secrets"
843
+ ]);
844
+ var libRootAllowedFiles = new Set([
845
+ "cnst.ts",
846
+ "db.ts",
847
+ "dict.ts",
848
+ "option.ts",
849
+ "sig.ts",
850
+ "srv.ts",
851
+ "st.ts",
852
+ "useClient.ts",
853
+ "useServer.ts"
854
+ ]);
855
+ var internalLibDirs = new Set(["__lib", "__scalar"]);
856
+ var moduleNonUiFileTypes = {
857
+ database: new Set(["constant", "dictionary", "document", "service", "signal", "store"]),
858
+ service: new Set(["dictionary", "service", "signal", "store"]),
859
+ scalar: new Set(["constant", "dictionary", "document"])
860
+ };
861
+ var moduleUiFileTypes = {
862
+ database: new Set(["Template", "Unit", "Util", "View", "Zone"]),
863
+ service: new Set(["Util", "Zone"]),
864
+ scalar: new Set(["Template", "Unit"])
865
+ };
866
+ var testFilePattern = /\.(test|spec)\.(ts|tsx)$/;
867
+ var rootSignalTestFilePattern = /^[A-Za-z][A-Za-z0-9_-]*\.signal\.(test|spec)\.(ts|tsx)$/;
868
+ var createDependencyScanner = async (exec) => (await import("./dependencyScanner-m4x5maek.js")).TypeScriptDependencyScanner.from(exec);
869
+ var isAllowedTestFile = (filename) => testFilePattern.test(filename);
870
+ var isAllowedLibRootFile = (filename) => libRootAllowedFiles.has(filename) || rootSignalTestFilePattern.test(filename);
871
+ var getScanPath = (exec, relativePath) => path4.posix.join(`${exec.type}s`, exec.name, relativePath.split(path4.sep).join("/"));
872
+ async function clearGeneratedRootCapacitorConfigs(exec) {
873
+ if (exec.type !== "app")
874
+ return;
875
+ await Promise.all(generatedRootCapacitorConfigFiles.map((filename) => rm(exec.getPath(filename), { force: true })));
876
+ }
877
+ var getModuleNameFromPath = (kind, modulePath) => {
878
+ const dirname = path4.basename(modulePath);
879
+ return kind === "service" ? dirname.replace(/^_+/, "") : dirname;
880
+ };
881
+ async function assertScanConvention(exec, libRoot) {
882
+ await clearGeneratedRootCapacitorConfigs(exec);
883
+ const violations = [];
884
+ const addViolation = (relativePath, reason) => {
885
+ violations.push(`${getScanPath(exec, relativePath)}: ${reason}`);
886
+ };
887
+ if (exec.type === "app") {
888
+ const { files, dirs } = await exec.getFilesAndDirs(".");
889
+ files.filter((filename) => !appRootAllowedFiles.has(filename)).forEach((filename) => {
890
+ addViolation(filename, "unsupported app root file");
891
+ });
892
+ dirs.filter((dirname) => !appRootAllowedDirs.has(dirname)).forEach((dirname) => {
893
+ addViolation(dirname, "unsupported app root folder");
894
+ });
895
+ }
896
+ libRoot.files.filter((filename) => !isAllowedLibRootFile(filename)).forEach((filename) => {
897
+ addViolation(path4.join("lib", filename), "unsupported lib root file");
898
+ });
899
+ libRoot.dirs.filter((dirname) => dirname.startsWith("__") && !internalLibDirs.has(dirname)).forEach((dirname) => {
900
+ addViolation(path4.join("lib", dirname), "unsupported internal lib folder");
901
+ });
902
+ const databaseDirs = libRoot.dirs.filter((dirname) => !dirname.startsWith("_"));
903
+ const serviceDirs = libRoot.dirs.filter((dirname) => dirname.startsWith("_") && !dirname.startsWith("__"));
904
+ const scalarDirs = await exec.readdir("lib/__scalar");
905
+ await Promise.all([
906
+ ...databaseDirs.map((dirname) => validateModuleFiles(exec, violations, "database", path4.join("lib", dirname))),
907
+ ...serviceDirs.map((dirname) => validateModuleFiles(exec, violations, "service", path4.join("lib", dirname))),
908
+ ...scalarDirs.map((dirname) => validateModuleFiles(exec, violations, "scalar", path4.join("lib/__scalar", dirname)))
909
+ ]);
910
+ if (violations.length > 0) {
911
+ throw new Error(`[scan-convention]
912
+ ${violations.sort().map((violation) => `- ${violation}`).join(`
913
+ `)}`);
914
+ }
915
+ }
916
+ async function validateModuleFiles(exec, violations, kind, modulePath) {
917
+ const { files, dirs } = await exec.getFilesAndDirs(modulePath);
918
+ const moduleName = getModuleNameFromPath(kind, modulePath);
919
+ dirs.forEach((dirname) => {
920
+ violations.push(`${getScanPath(exec, path4.join(modulePath, dirname))}: unsupported module folder`);
921
+ });
922
+ const uiModuleName = moduleName[0].toUpperCase() + moduleName.slice(1);
923
+ files.forEach((filename) => {
924
+ const filePath = path4.join(modulePath, filename);
925
+ if (filename === "index.ts" || filename === "index.tsx" || isAllowedTestFile(filename))
926
+ return;
927
+ if (filename === `${moduleName}.abstract.md`)
928
+ return;
929
+ const uiMatch = filename.match(/^([A-Z][A-Za-z0-9]+)\.([A-Z][A-Za-z0-9]*)\.tsx$/);
930
+ if (uiMatch) {
931
+ const fileModuleName = uiMatch[1];
932
+ const fileType = uiMatch[2];
933
+ if (fileModuleName !== uiModuleName) {
934
+ violations.push(`${getScanPath(exec, filePath)}: module name mismatch: expected '${uiModuleName}', got '${fileModuleName}'`);
935
+ }
936
+ if (!moduleUiFileTypes[kind].has(fileType)) {
937
+ violations.push(`${getScanPath(exec, filePath)}: unsupported ${kind} UI file`);
938
+ }
939
+ return;
940
+ }
941
+ const nonUiMatch = filename.match(/^([a-z][a-zA-Z0-9]*)\.([a-z][a-z0-9]*)\.ts$/);
942
+ if (nonUiMatch) {
943
+ const fileModuleName = nonUiMatch[1];
944
+ const fileType = nonUiMatch[2];
945
+ if (fileModuleName !== moduleName) {
946
+ violations.push(`${getScanPath(exec, filePath)}: module name mismatch: expected '${moduleName}', got '${fileModuleName}'`);
947
+ }
948
+ if (!moduleNonUiFileTypes[kind].has(fileType)) {
949
+ violations.push(`${getScanPath(exec, filePath)}: unsupported ${kind} file`);
950
+ }
951
+ return;
952
+ }
953
+ violations.push(`${getScanPath(exec, filePath)}: unsupported module file`);
954
+ });
955
+ }
956
+
957
+ class ScanInfo {
958
+ scanResult;
959
+ name;
960
+ scalar = new Map;
961
+ service = new Map;
962
+ database = new Map;
963
+ file = Object.fromEntries(databaseFileTypes.map((type) => [
964
+ type,
965
+ { all: new Set, databases: new Set, services: new Set, scalars: new Set }
966
+ ]));
967
+ static async getScanResult(exec) {
968
+ const [akanConfig2, scanner, pkgs, libs] = await Promise.all([
969
+ exec.getConfig(),
970
+ createDependencyScanner(exec),
971
+ exec.workspace.getPkgs(),
972
+ exec.workspace.getLibs()
973
+ ]);
974
+ const { pkgDeps, libDeps, npmDeps, npmDevDeps } = await scanner.getMonorepoDependencies(exec.name, { pkgs, libs });
975
+ const files = {
976
+ constant: { databases: [], scalars: [] },
977
+ dictionary: { databases: [], services: [], scalars: [] },
978
+ document: { databases: [], scalars: [] },
979
+ service: { databases: [], services: [] },
980
+ signal: { databases: [], services: [] },
981
+ store: { databases: [], services: [] },
982
+ template: { databases: [], services: [], scalars: [] },
983
+ unit: { databases: [], services: [], scalars: [] },
984
+ util: { databases: [], services: [], scalars: [] },
985
+ view: { databases: [], services: [], scalars: [] },
986
+ zone: { databases: [], services: [], scalars: [] }
987
+ };
988
+ const [libRoot, scalarDirs] = await Promise.all([exec.getFilesAndDirs("lib"), exec.readdir("lib/__scalar")]);
989
+ await assertScanConvention(exec, libRoot);
990
+ const { dirs: dirnames } = libRoot;
991
+ const databaseDirs = [];
992
+ const serviceDirs = [];
993
+ dirnames.forEach((name) => {
994
+ if (name.startsWith("_")) {
995
+ if (name.startsWith("__"))
996
+ return;
997
+ else
998
+ serviceDirs.push(name);
999
+ } else
1000
+ databaseDirs.push(name);
1001
+ });
1002
+ await Promise.all([
1003
+ ...databaseDirs.map(async (name) => {
1004
+ const filenames = await exec.readdir(path4.join("lib", name));
1005
+ filenames.forEach((filename) => {
1006
+ if (filename.endsWith(".constant.ts"))
1007
+ files.constant.databases.push(name);
1008
+ else if (filename.endsWith(".dictionary.ts"))
1009
+ files.dictionary.databases.push(name);
1010
+ else if (filename.endsWith(".document.ts"))
1011
+ files.document.databases.push(name);
1012
+ else if (filename.endsWith(".service.ts"))
1013
+ files.service.databases.push(name);
1014
+ else if (filename.endsWith(".signal.ts"))
1015
+ files.signal.databases.push(name);
1016
+ else if (filename.endsWith(".store.ts"))
1017
+ files.store.databases.push(name);
1018
+ else if (filename.endsWith(".Template.tsx"))
1019
+ files.template.databases.push(name);
1020
+ else if (filename.endsWith(".Unit.tsx"))
1021
+ files.unit.databases.push(name);
1022
+ else if (filename.endsWith(".Util.tsx"))
1023
+ files.util.databases.push(name);
1024
+ else if (filename.endsWith(".View.tsx"))
1025
+ files.view.databases.push(name);
1026
+ else if (filename.endsWith(".Zone.tsx"))
1027
+ files.zone.databases.push(name);
1028
+ });
1029
+ }),
1030
+ ...serviceDirs.map(async (dirname) => {
1031
+ const name = dirname.slice(1);
1032
+ const filenames = await exec.readdir(path4.join("lib", dirname));
1033
+ filenames.forEach((filename) => {
1034
+ if (filename.endsWith(".dictionary.ts"))
1035
+ files.dictionary.services.push(name);
1036
+ else if (filename.endsWith(".service.ts"))
1037
+ files.service.services.push(name);
1038
+ else if (filename.endsWith(".signal.ts"))
1039
+ files.signal.services.push(name);
1040
+ else if (filename.endsWith(".store.ts"))
1041
+ files.store.services.push(name);
1042
+ else if (filename.endsWith(".Template.tsx"))
1043
+ files.template.services.push(name);
1044
+ else if (filename.endsWith(".Unit.tsx"))
1045
+ files.unit.services.push(name);
1046
+ else if (filename.endsWith(".Util.tsx"))
1047
+ files.util.services.push(name);
1048
+ else if (filename.endsWith(".View.tsx"))
1049
+ files.view.services.push(name);
1050
+ else if (filename.endsWith(".Zone.tsx"))
1051
+ files.zone.services.push(name);
1052
+ });
1053
+ }),
1054
+ ...scalarDirs.map(async (name) => {
1055
+ const filenames = await exec.readdir(path4.join("lib/__scalar", name));
1056
+ filenames.forEach((filename) => {
1057
+ if (filename.endsWith(".constant.ts"))
1058
+ files.constant.scalars.push(name);
1059
+ else if (filename.endsWith(".dictionary.ts"))
1060
+ files.dictionary.scalars.push(name);
1061
+ else if (filename.endsWith(".document.ts"))
1062
+ files.document.scalars.push(name);
1063
+ else if (filename.endsWith(".Template.tsx"))
1064
+ files.template.scalars.push(name);
1065
+ else if (filename.endsWith(".Unit.tsx"))
1066
+ files.unit.scalars.push(name);
1067
+ else if (filename.endsWith(".Util.tsx"))
1068
+ files.util.scalars.push(name);
1069
+ else if (filename.endsWith(".View.tsx"))
1070
+ files.view.scalars.push(name);
1071
+ else if (filename.endsWith(".Zone.tsx"))
1072
+ files.zone.scalars.push(name);
1073
+ });
1074
+ })
1075
+ ]);
1076
+ const routes = exec.type === "lib" ? [] : await exec.getPageKeys();
1077
+ const scanResult = {
1078
+ name: exec.name,
1079
+ type: exec.type,
1080
+ repoName: exec.workspace.repoName,
1081
+ serveDomain: WorkspaceExecutor.getBaseDevEnv(path4.join(exec.workspace.workspaceRoot, ".env")).serveDomain,
1082
+ akanConfig: akanConfig2,
1083
+ files,
1084
+ libDeps,
1085
+ pkgDeps,
1086
+ dependencies: npmDeps.filter((dep) => !isAkanFrameworkDependency(dep)),
1087
+ devDependencies: npmDevDeps.filter((dep) => !isAkanFrameworkDependency(dep)),
1088
+ routes
1089
+ };
1090
+ return scanResult;
1091
+ }
1092
+ constructor(scanResult) {
1093
+ this.name = scanResult.name;
1094
+ this.scanResult = scanResult;
1095
+ Object.entries(scanResult.files).forEach(([_key, value]) => {
1096
+ const key = _key;
1097
+ const { databases, services, scalars } = value;
1098
+ databases.forEach((modelName) => {
1099
+ const model = this.database.get(modelName) ?? new Set;
1100
+ model.add(key);
1101
+ this.database.set(modelName, model);
1102
+ this.file[key].all.add(modelName);
1103
+ this.file[key].databases.add(modelName);
1104
+ });
1105
+ services?.forEach((serviceName) => {
1106
+ const service = this.service.get(serviceName) ?? new Set;
1107
+ service.add(key);
1108
+ this.service.set(serviceName, service);
1109
+ this.file[key].all.add(serviceName);
1110
+ this.file[key].services.add(serviceName);
1111
+ });
1112
+ scalars?.forEach((scalarName) => {
1113
+ const scalar = this.scalar.get(scalarName) ?? new Set;
1114
+ scalar.add(key);
1115
+ this.scalar.set(scalarName, scalar);
1116
+ this.file[key].all.add(scalarName);
1117
+ this.file[key].scalars.add(scalarName);
1118
+ });
1119
+ });
1120
+ }
1121
+ getScanResult() {
1122
+ return this.scanResult;
1123
+ }
1124
+ getDatabaseModules() {
1125
+ return [...this.database.keys()];
1126
+ }
1127
+ getServiceModules() {
1128
+ return [...this.service.keys()];
1129
+ }
1130
+ getScalarModules() {
1131
+ return [...this.scalar.keys()];
1132
+ }
1133
+ }
1134
+ var isAkanFrameworkDependency = (dep) => dep === "akanjs" || dep.startsWith("akanjs/");
1135
+
1136
+ class AppInfo extends ScanInfo {
1137
+ type = "app";
1138
+ exec;
1139
+ akanConfig;
1140
+ libDeps;
1141
+ static appInfos = new Map;
1142
+ static async fromExecutor(exec, options = {}) {
1143
+ const existingAppInfo = AppInfo.appInfos.get(exec.name);
1144
+ if (existingAppInfo && !options.refresh)
1145
+ return existingAppInfo;
1146
+ const scanResult = await ScanInfo.getScanResult(exec);
1147
+ await Promise.all(scanResult.libDeps.map(async (libName) => {
1148
+ LibInfo.loadedLibs.add(libName);
1149
+ const libExecutor = LibExecutor.from(exec, libName);
1150
+ LibInfo.libInfos.set(libName, await LibInfo.fromExecutor(libExecutor));
1151
+ }));
1152
+ const libDeps = await AppInfo.#getAllLibDeps(exec, scanResult.libDeps);
1153
+ const appInfo = new AppInfo(exec, scanResult, libDeps);
1154
+ AppInfo.appInfos.set(exec.name, appInfo);
1155
+ return appInfo;
1156
+ }
1157
+ constructor(exec, scanResult, libDeps) {
1158
+ super(scanResult);
1159
+ this.exec = exec;
1160
+ this.akanConfig = scanResult.akanConfig;
1161
+ this.libDeps = libDeps;
1162
+ }
1163
+ getScanResult() {
1164
+ return this.scanResult;
1165
+ }
1166
+ static async#getAllLibDeps(exec, libDeps, libSet = new Set) {
1167
+ await Promise.all(libDeps.map(async (libName) => {
1168
+ if (libSet.has(libName))
1169
+ return;
1170
+ libSet.add(libName);
1171
+ const libExecutor = LibExecutor.from(exec, libName);
1172
+ const libInfo = await LibInfo.fromExecutor(libExecutor);
1173
+ const libScanResult = libInfo.getScanResult();
1174
+ if (libScanResult.libDeps.length > 0)
1175
+ await AppInfo.#getAllLibDeps(exec, libScanResult.libDeps, libSet);
1176
+ }));
1177
+ return [...libSet];
1178
+ }
1179
+ #sortedLibs = null;
1180
+ #getSortedLibs() {
1181
+ if (this.#sortedLibs)
1182
+ return this.#sortedLibs;
1183
+ const libIndices = LibInfo.getSortedLibIndices();
1184
+ this.#sortedLibs = this.libDeps.sort((libNameA, libNameB) => {
1185
+ const indexA = libIndices.get(libNameA);
1186
+ const indexB = libIndices.get(libNameB);
1187
+ if (indexA === undefined || indexB === undefined)
1188
+ throw new Error(`LibInfo not found: ${libNameA} or ${libNameB}`);
1189
+ return indexA - indexB;
1190
+ });
1191
+ return this.#sortedLibs;
1192
+ }
1193
+ getLibs() {
1194
+ return this.#getSortedLibs();
1195
+ }
1196
+ getLibInfos() {
1197
+ return new Map(this.#getSortedLibs().map((libName) => {
1198
+ const libInfo = LibInfo.libInfos.get(libName);
1199
+ if (!libInfo)
1200
+ throw new Error(`LibInfo not found: ${libName}`);
1201
+ return [libName, libInfo];
1202
+ }));
1203
+ }
1204
+ }
1205
+
1206
+ class LibInfo extends ScanInfo {
1207
+ type = "lib";
1208
+ exec;
1209
+ akanConfig;
1210
+ static loadedLibs = new Set;
1211
+ static libInfos = new Map;
1212
+ static #sortedLibIndices = null;
1213
+ static getSortedLibIndices() {
1214
+ if (LibInfo.#sortedLibIndices)
1215
+ return LibInfo.#sortedLibIndices;
1216
+ LibInfo.#sortedLibIndices = new Map([...LibInfo.libInfos.entries()].sort(([_, libInfoA], [__, libInfoB]) => libInfoA.getScanResult().libDeps.includes(libInfoB.name) ? 1 : -1).map(([libName], index) => [libName, index]));
1217
+ return LibInfo.#sortedLibIndices;
1218
+ }
1219
+ static async fromExecutor(exec, { refresh } = {}) {
1220
+ const existingLibInfo = LibInfo.libInfos.get(exec.name);
1221
+ if (existingLibInfo && !refresh)
1222
+ return existingLibInfo;
1223
+ const scanResult = await ScanInfo.getScanResult(exec);
1224
+ await Promise.all(scanResult.libDeps.filter((libName) => !LibInfo.loadedLibs.has(libName)).map(async (libName) => {
1225
+ LibInfo.loadedLibs.add(libName);
1226
+ const libExecutor = LibExecutor.from(exec, libName);
1227
+ LibInfo.libInfos.set(libName, await LibInfo.fromExecutor(libExecutor));
1228
+ }));
1229
+ const libInfo = new LibInfo(exec, scanResult);
1230
+ LibInfo.libInfos.set(exec.name, libInfo);
1231
+ LibInfo.#sortedLibIndices = null;
1232
+ return libInfo;
1233
+ }
1234
+ constructor(exec, scanResult) {
1235
+ super(scanResult);
1236
+ this.exec = exec;
1237
+ this.akanConfig = scanResult.akanConfig;
1238
+ }
1239
+ getScanResult() {
1240
+ return this.scanResult;
1241
+ }
1242
+ #sortedLibs = null;
1243
+ #getSortedLibs() {
1244
+ if (this.#sortedLibs)
1245
+ return this.#sortedLibs;
1246
+ const libs = LibInfo.getSortedLibIndices();
1247
+ this.#sortedLibs = this.scanResult.libDeps.sort((libNameA, libNameB) => {
1248
+ const indexA = libs.get(libNameA);
1249
+ const indexB = libs.get(libNameB);
1250
+ if (indexA === undefined || indexB === undefined)
1251
+ throw new Error(`LibInfo not found: ${libNameA} or ${libNameB}`);
1252
+ return indexA - indexB;
1253
+ });
1254
+ return this.#sortedLibs;
1255
+ }
1256
+ getLibs() {
1257
+ return this.#getSortedLibs();
1258
+ }
1259
+ getLibInfo(libName) {
1260
+ if (!this.getScanResult().libDeps.includes(libName))
1261
+ return;
1262
+ const libSet = new Set(this.#getSortedLibs());
1263
+ if (!libSet.has(libName))
1264
+ throw new Error(`LibInfo is invalid: ${libName}`);
1265
+ return LibInfo.libInfos.get(libName);
1266
+ }
1267
+ getLibInfos() {
1268
+ return new Map(this.#getSortedLibs().map((libName) => {
1269
+ const libInfo = LibInfo.libInfos.get(libName);
1270
+ if (!libInfo)
1271
+ throw new Error(`LibInfo not found: ${libName}`);
1272
+ return [libName, libInfo];
1273
+ }));
1274
+ }
1275
+ }
1276
+
1277
+ class PkgInfo {
1278
+ exec;
1279
+ name;
1280
+ scanResult;
1281
+ static async scanExecutor(exec) {
1282
+ const [tsconfig, rootPackageJson] = await Promise.all([exec.getTsConfig(), exec.workspace.getPackageJson()]);
1283
+ const scanner = await createDependencyScanner(exec);
1284
+ const npmSet = new Set(Object.keys({ ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies }));
1285
+ const pkgPathSet = new Set(Object.keys(tsconfig.compilerOptions.paths ?? {}).filter((path5) => tsconfig.compilerOptions.paths?.[path5]?.some((resolve) => resolve.startsWith("pkgs/"))).map((path5) => path5.replace("/*", "")));
1286
+ const [npmDepSet, pkgPathDepSet] = await scanner.getImportSets([npmSet, pkgPathSet]);
1287
+ const pkgDeps = [...pkgPathDepSet].map((path5) => {
1288
+ const pathSplitLength = path5.split("/").length;
1289
+ return (tsconfig.compilerOptions.paths?.[path5]?.[0] ?? "*").split("/").slice(1, 1 + pathSplitLength).join("/");
1290
+ }).filter((pkg) => pkg !== exec.name);
1291
+ const pkgScanResult = {
1292
+ name: exec.name,
1293
+ pkgDeps,
1294
+ dependencies: [...npmDepSet]
1295
+ };
1296
+ return pkgScanResult;
1297
+ }
1298
+ static #pkgInfos = new Map;
1299
+ static async fromExecutor(exec, options = {}) {
1300
+ const existingPkgInfo = PkgInfo.#pkgInfos.get(exec.name);
1301
+ if (existingPkgInfo && !options.refresh)
1302
+ return existingPkgInfo;
1303
+ const scanResult = await PkgInfo.scanExecutor(exec);
1304
+ const pkgInfo = new PkgInfo(exec, scanResult);
1305
+ PkgInfo.#pkgInfos.set(exec.name, pkgInfo);
1306
+ return pkgInfo;
1307
+ }
1308
+ constructor(exec, scanResult) {
1309
+ this.exec = exec;
1310
+ this.name = exec.name;
1311
+ this.scanResult = scanResult;
1312
+ }
1313
+ getScanResult() {
1314
+ return this.scanResult;
1315
+ }
1316
+ }
1317
+
1318
+ class WorkspaceInfo {
1319
+ appInfos;
1320
+ libInfos;
1321
+ pkgInfos;
1322
+ constructor(appInfos = new Map, libInfos = new Map, pkgInfos = new Map) {
1323
+ this.appInfos = appInfos;
1324
+ this.libInfos = libInfos;
1325
+ this.pkgInfos = pkgInfos;
1326
+ }
1327
+ static #workspaceInfos = new Map;
1328
+ static async fromExecutor(exec, options = {}) {
1329
+ const existingWorkspaceInfo = WorkspaceInfo.#workspaceInfos.get(exec.name);
1330
+ if (existingWorkspaceInfo && !options.refresh)
1331
+ return existingWorkspaceInfo;
1332
+ const [appNames, libNames, pkgNames] = await Promise.all([exec.getApps(), exec.getLibs(), exec.getPkgs()]);
1333
+ const [appInfos, libInfos, pkgInfos] = await Promise.all([
1334
+ Promise.all(appNames.map(async (appName) => {
1335
+ const app = AppExecutor.from(exec, appName);
1336
+ return await app.scan();
1337
+ })),
1338
+ Promise.all(libNames.map(async (libName) => {
1339
+ const lib = LibExecutor.from(exec, libName);
1340
+ return await lib.scan();
1341
+ })),
1342
+ Promise.all(pkgNames.map(async (pkgName) => {
1343
+ return await PkgExecutor.from(exec, pkgName).scan();
1344
+ }))
1345
+ ]);
1346
+ const workspaceInfo = new WorkspaceInfo(new Map(appInfos.map((app) => [app.exec.name, app])), new Map(libInfos.map((lib) => [lib.exec.name, lib])), new Map(pkgInfos.map((pkg) => [pkg.exec.name, pkg])));
1347
+ WorkspaceInfo.#workspaceInfos.set(exec.name, workspaceInfo);
1348
+ return workspaceInfo;
1349
+ }
1350
+ }
1351
+
1352
+ // pkgs/@akanjs/devkit/executors.ts
1353
+ var staticTemplateFileExtensions = new Set([
1354
+ ".avif",
1355
+ ".bmp",
1356
+ ".cjs",
1357
+ ".css",
1358
+ ".eot",
1359
+ ".gif",
1360
+ ".html",
1361
+ ".ico",
1362
+ ".jpeg",
1363
+ ".jpg",
1364
+ ".js",
1365
+ ".json",
1366
+ ".map",
1367
+ ".md",
1368
+ ".mjs",
1369
+ ".mp3",
1370
+ ".mp4",
1371
+ ".ogg",
1372
+ ".otf",
1373
+ ".pdf",
1374
+ ".png",
1375
+ ".svg",
1376
+ ".ttf",
1377
+ ".txt",
1378
+ ".wasm",
1379
+ ".wav",
1380
+ ".webm",
1381
+ ".webp",
1382
+ ".woff",
1383
+ ".woff2",
1384
+ ".xml"
1385
+ ]);
1386
+ var formatCommandArg = (value) => /^[\w@%+=:,./-]+$/.test(value) ? value : JSON.stringify(value);
1387
+ var formatCommandForDisplay = (command, args = []) => [command, ...args].map(formatCommandArg).join(" ");
1388
+
1389
+ class CommandExecutionError extends Error {
1390
+ command;
1391
+ args;
1392
+ cwd;
1393
+ code;
1394
+ signal;
1395
+ stdout;
1396
+ stderr;
1397
+ constructor({
1398
+ command,
1399
+ args = [],
1400
+ cwd,
1401
+ code,
1402
+ signal,
1403
+ stdout = "",
1404
+ stderr = "",
1405
+ cause
1406
+ }) {
1407
+ const displayCommand = formatCommandForDisplay(command, args);
1408
+ const status = signal ? `signal: ${signal}` : `exit code: ${code ?? "unknown"}`;
1409
+ const output = (stderr || stdout).trim();
1410
+ super([`Command failed: ${displayCommand}`, `cwd: ${cwd}`, status, output ? `
1411
+ ${output}` : ""].join(`
1412
+ `), {
1413
+ cause
1414
+ });
1415
+ this.name = "CommandExecutionError";
1416
+ this.command = command;
1417
+ this.args = args;
1418
+ this.cwd = cwd;
1419
+ this.code = code;
1420
+ this.signal = signal;
1421
+ this.stdout = stdout;
1422
+ this.stderr = stderr;
1423
+ }
1424
+ }
1425
+ var execEmoji = {
1426
+ workspace: "\uD83C\uDFE0",
1427
+ app: "\uD83D\uDE80",
1428
+ lib: "\uD83D\uDD27",
1429
+ pkg: "\uD83D\uDCE6",
1430
+ dist: "\uD83D\uDCBF",
1431
+ module: "\u2699\uFE0F",
1432
+ default: "\u2708\uFE0F"
1433
+ };
1434
+ var parseEnvFile = (envPath) => {
1435
+ const env = {};
1436
+ const content = (() => {
1437
+ try {
1438
+ return readFileSync2(envPath, "utf8");
1439
+ } catch {
1440
+ return "";
1441
+ }
1442
+ })();
1443
+ for (const line of content.split(/\r?\n/)) {
1444
+ const trimmed = line.trim();
1445
+ if (!trimmed || trimmed.startsWith("#"))
1446
+ continue;
1447
+ const normalized = trimmed.startsWith("export ") ? trimmed.slice("export ".length).trim() : trimmed;
1448
+ const separatorIndex = normalized.indexOf("=");
1449
+ if (separatorIndex <= 0)
1450
+ continue;
1451
+ const key = normalized.slice(0, separatorIndex).trim();
1452
+ let value = normalized.slice(separatorIndex + 1).trim();
1453
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
1454
+ value = value.slice(1, -1);
1455
+ }
1456
+ env[key] = value;
1457
+ }
1458
+ return env;
1459
+ };
1460
+
1461
+ class Executor {
1462
+ static verbose = false;
1463
+ static setVerbose(verbose) {
1464
+ Executor.verbose = verbose;
1465
+ }
1466
+ name;
1467
+ logger;
1468
+ logs;
1469
+ cwdPath;
1470
+ emoji = execEmoji.default;
1471
+ typeChecker = null;
1472
+ linter = null;
1473
+ constructor(name, cwdPath) {
1474
+ this.name = name;
1475
+ this.logger = new Logger(name);
1476
+ this.logs = [];
1477
+ this.cwdPath = cwdPath;
1478
+ }
1479
+ #stdout(data) {
1480
+ if (Executor.verbose)
1481
+ Logger.raw(chalk2.dim(data.toString()));
1482
+ }
1483
+ #stderr(data) {
1484
+ Logger.raw(chalk2.red(data.toString()));
1485
+ }
1486
+ exec(command, options = {}) {
1487
+ const cwd = options.cwd?.toString() ?? this.cwdPath;
1488
+ const proc = exec(command, { cwd: this.cwdPath, ...options });
1489
+ let stdout = "";
1490
+ let stderr = "";
1491
+ proc.stdout?.on("data", (data) => {
1492
+ stdout += data.toString();
1493
+ this.#stdout(data);
1494
+ });
1495
+ proc.stderr?.on("data", (data) => {
1496
+ stderr += data.toString();
1497
+ this.#stdout(data);
1498
+ });
1499
+ return new Promise((resolve, reject) => {
1500
+ proc.on("error", (error) => {
1501
+ reject(new CommandExecutionError({
1502
+ command,
1503
+ cwd,
1504
+ code: null,
1505
+ signal: null,
1506
+ stdout,
1507
+ stderr,
1508
+ cause: error
1509
+ }));
1510
+ });
1511
+ proc.on("exit", (code, signal) => {
1512
+ if (!!code || signal)
1513
+ reject(new CommandExecutionError({
1514
+ command,
1515
+ cwd,
1516
+ code,
1517
+ signal,
1518
+ stdout,
1519
+ stderr
1520
+ }));
1521
+ else
1522
+ resolve({ code, signal });
1523
+ });
1524
+ });
1525
+ }
1526
+ spawn(command, args = [], options = {}) {
1527
+ const cwd = options.cwd?.toString() ?? this.cwdPath;
1528
+ const proc = spawn2(command, args, {
1529
+ cwd: this.cwdPath,
1530
+ ...options
1531
+ });
1532
+ let stdout = "";
1533
+ let stderr = "";
1534
+ proc.stdout?.on("data", (data) => {
1535
+ stdout += data.toString();
1536
+ this.logs.push(data.toString());
1537
+ this.#stdout(data);
1538
+ });
1539
+ proc.stderr?.on("data", (data) => {
1540
+ stderr += data.toString();
1541
+ this.logs.push(data.toString());
1542
+ this.#stdout(data);
1543
+ });
1544
+ return new Promise((resolve, reject) => {
1545
+ proc.on("error", (error) => {
1546
+ reject(new CommandExecutionError({
1547
+ command,
1548
+ args,
1549
+ cwd,
1550
+ code: null,
1551
+ signal: null,
1552
+ stdout,
1553
+ stderr,
1554
+ cause: error
1555
+ }));
1556
+ });
1557
+ proc.on("close", (code, signal) => {
1558
+ if (code !== 0 || signal)
1559
+ reject(new CommandExecutionError({
1560
+ command,
1561
+ args,
1562
+ cwd,
1563
+ code,
1564
+ signal,
1565
+ stdout,
1566
+ stderr
1567
+ }));
1568
+ else
1569
+ resolve(stdout);
1570
+ });
1571
+ });
1572
+ }
1573
+ spawnSync(command, args = [], options = {}) {
1574
+ const proc = spawn2(command, args, {
1575
+ cwd: this.cwdPath,
1576
+ ...options
1577
+ });
1578
+ return proc;
1579
+ }
1580
+ fork(modulePath, args = [], options = {}) {
1581
+ const cwd = options.cwd?.toString() ?? this.cwdPath;
1582
+ const proc = fork(modulePath, args, {
1583
+ cwd: this.cwdPath,
1584
+ ...options
1585
+ });
1586
+ let stdout = "";
1587
+ let stderr = "";
1588
+ proc.stdout?.on("data", (data) => {
1589
+ stdout += data.toString();
1590
+ this.#stdout(data);
1591
+ });
1592
+ proc.stderr?.on("data", (data) => {
1593
+ stderr += data.toString();
1594
+ this.#stderr(data);
1595
+ });
1596
+ return new Promise((resolve, reject) => {
1597
+ proc.on("error", (error) => {
1598
+ reject(new CommandExecutionError({
1599
+ command: modulePath,
1600
+ args,
1601
+ cwd,
1602
+ code: null,
1603
+ signal: null,
1604
+ stdout,
1605
+ stderr,
1606
+ cause: error
1607
+ }));
1608
+ });
1609
+ proc.on("exit", (code, signal) => {
1610
+ if (!!code || signal)
1611
+ reject(new CommandExecutionError({
1612
+ command: modulePath,
1613
+ args,
1614
+ cwd,
1615
+ code,
1616
+ signal,
1617
+ stdout,
1618
+ stderr
1619
+ }));
1620
+ else
1621
+ resolve({ code, signal });
1622
+ });
1623
+ });
1624
+ }
1625
+ getPath(filePath) {
1626
+ if (path5.isAbsolute(filePath))
1627
+ return filePath;
1628
+ if (filePath.startsWith("."))
1629
+ return path5.join(this.cwdPath, filePath);
1630
+ const baseParts = this.cwdPath.split(/[\\/]/).filter(Boolean);
1631
+ const targetParts = filePath.split(/[\\/]/).filter(Boolean);
1632
+ let overlapLength = 0;
1633
+ for (let i = 1;i <= Math.min(baseParts.length, targetParts.length); i++) {
1634
+ let isOverlap = true;
1635
+ for (let j = 0;j < i; j++)
1636
+ if (baseParts[baseParts.length - i + j] !== targetParts[j]) {
1637
+ isOverlap = false;
1638
+ break;
1639
+ }
1640
+ if (isOverlap)
1641
+ overlapLength = i;
1642
+ }
1643
+ return path5.join(this.cwdPath, ...targetParts.slice(overlapLength));
1644
+ }
1645
+ async mkdir(dirPath) {
1646
+ const writePath = this.getPath(dirPath);
1647
+ if (!await FileSys.dirExists(writePath))
1648
+ await mkdir(writePath, { recursive: true });
1649
+ this.logger.verbose(`Make directory ${writePath}`);
1650
+ return this;
1651
+ }
1652
+ async readdir(dirPath) {
1653
+ const readPath = this.getPath(dirPath);
1654
+ try {
1655
+ const glob = new Bun.Glob("*");
1656
+ return Array.from(glob.scanSync({ cwd: readPath, onlyFiles: false }));
1657
+ } catch {
1658
+ return [];
1659
+ }
1660
+ }
1661
+ async getAllFiles(pattern = "**/*", { cwd } = {}) {
1662
+ const glob = new Bun.Glob(pattern);
1663
+ return Array.from(glob.scanSync({ cwd: cwd ?? this.cwdPath, onlyFiles: true }));
1664
+ }
1665
+ async getFilesAndDirs(dirPath) {
1666
+ const fullDirPath = this.getPath(dirPath);
1667
+ const fileGlob = new Bun.Glob("*");
1668
+ const files = Array.from(fileGlob.scanSync({ cwd: fullDirPath, onlyFiles: true }));
1669
+ const dirGlob = new Bun.Glob("*");
1670
+ const allEntries = Array.from(dirGlob.scanSync({ cwd: fullDirPath, onlyFiles: false }));
1671
+ const dirs = allEntries.filter((entry) => !files.includes(entry));
1672
+ return { files, dirs };
1673
+ }
1674
+ async exists(filePath) {
1675
+ const readPath = this.getPath(filePath);
1676
+ return await FileSys.exists(readPath);
1677
+ }
1678
+ async remove(filePath) {
1679
+ const readPath = this.getPath(filePath);
1680
+ if (await FileSys.fileExists(readPath))
1681
+ await FileSys.delete(readPath);
1682
+ this.logger.verbose(`Remove file ${readPath}`);
1683
+ return this;
1684
+ }
1685
+ async removeDir(dirPath) {
1686
+ const readPath = this.getPath(dirPath);
1687
+ if (await FileSys.dirExists(readPath))
1688
+ await $`rm -rf ${readPath}`;
1689
+ this.logger.verbose(`Remove directory ${readPath}`);
1690
+ return this;
1691
+ }
1692
+ async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
1693
+ const writePath = this.getPath(filePath);
1694
+ const dir = path5.dirname(writePath);
1695
+ if (!await FileSys.dirExists(dir))
1696
+ await mkdir(dir, { recursive: true });
1697
+ let contentStr = typeof content === "string" ? content : JSON.stringify(content, null, 2);
1698
+ if (await FileSys.fileExists(writePath)) {
1699
+ const currentContent = await FileSys.readText(writePath);
1700
+ if (currentContent === contentStr || !overwrite) {
1701
+ this.logger.verbose(`File ${writePath} is unchanged`);
1702
+ contentStr = currentContent;
1703
+ } else {
1704
+ await FileSys.writeText(writePath, contentStr);
1705
+ if (Logger.isVerbose())
1706
+ this.logger.rawLog(chalk2.yellow(`File Update: ${filePath}`));
1707
+ }
1708
+ } else {
1709
+ await FileSys.writeText(writePath, contentStr);
1710
+ if (!silent)
1711
+ this.logger.rawLog(chalk2.green(`File Create: ${filePath}`));
1712
+ }
1713
+ return { filePath: writePath, content: contentStr };
1714
+ }
1715
+ async writeJson(filePath, content) {
1716
+ await this.writeFile(filePath, content);
1717
+ }
1718
+ async getLocalFile(targetPath) {
1719
+ const filePath = path5.isAbsolute(targetPath) ? targetPath : targetPath.replace(this.cwdPath, "");
1720
+ const content = await this.readFile(filePath);
1721
+ return { filePath, content };
1722
+ }
1723
+ async readFile(filePath) {
1724
+ const readPath = this.getPath(filePath);
1725
+ return await FileSys.readText(readPath);
1726
+ }
1727
+ async readJson(filePath) {
1728
+ const readPath = this.getPath(filePath);
1729
+ return await FileSys.readJson(readPath);
1730
+ }
1731
+ async cp(srcPath, destPath) {
1732
+ const src = this.getPath(srcPath);
1733
+ const dest = this.getPath(destPath);
1734
+ if (!await FileSys.exists(src))
1735
+ return;
1736
+ const isDirectory = (await stat(src)).isDirectory();
1737
+ if (!await FileSys.exists(dest) && isDirectory)
1738
+ await mkdir(dest, { recursive: true });
1739
+ await $`cp -r ${src}${isDirectory ? "/." : ""} ${dest}`;
1740
+ }
1741
+ log(msg) {
1742
+ this.logger.info(msg);
1743
+ return this;
1744
+ }
1745
+ verbose(msg) {
1746
+ this.logger.verbose(msg);
1747
+ return this;
1748
+ }
1749
+ debug(msg) {
1750
+ this.logger.debug(msg);
1751
+ return this;
1752
+ }
1753
+ spinning(msg, { prefix = `${this.emoji}${this.name}`, indent = 0, enableSpin = !Executor.verbose } = {}) {
1754
+ return new Spinner(msg, { prefix, indent, enableSpin }).start();
1755
+ }
1756
+ #tsconfig = null;
1757
+ async getTsConfig(pathname = "tsconfig.json", { refresh } = {}) {
1758
+ if (this.#tsconfig && !refresh)
1759
+ return this.#tsconfig;
1760
+ const tsconfig = await this.readJson(pathname);
1761
+ if (tsconfig.extends) {
1762
+ const extendsTsconfig = await this.getTsConfig(tsconfig.extends);
1763
+ const result = {
1764
+ ...extendsTsconfig,
1765
+ ...tsconfig,
1766
+ compilerOptions: {
1767
+ ...extendsTsconfig.compilerOptions,
1768
+ ...tsconfig.compilerOptions
1769
+ }
1770
+ };
1771
+ this.#tsconfig = result;
1772
+ return result;
1773
+ }
1774
+ this.#tsconfig = tsconfig;
1775
+ return tsconfig;
1776
+ }
1777
+ async setTsConfig(tsconfig) {
1778
+ await this.writeJson("tsconfig.json", tsconfig);
1779
+ this.#tsconfig = tsconfig;
1780
+ }
1781
+ #packageJson = null;
1782
+ async getPackageJson({ refresh } = {}) {
1783
+ if (this.#packageJson && !refresh)
1784
+ return this.#packageJson;
1785
+ const packageJson = await this.readJson("package.json");
1786
+ this.#packageJson = packageJson;
1787
+ return packageJson;
1788
+ }
1789
+ async setPackageJson(packageJson) {
1790
+ await this.writeJson("package.json", packageJson);
1791
+ this.#packageJson = packageJson;
1792
+ }
1793
+ #gitignorePatterns = [];
1794
+ async getGitignorePatterns() {
1795
+ if (this.#gitignorePatterns.length)
1796
+ return this.#gitignorePatterns;
1797
+ const gitignore = await this.readFile(".gitignore");
1798
+ this.#gitignorePatterns = gitignore.split(`
1799
+ `).map((line) => line.trim()).filter((line) => !!line && !line.startsWith("#"));
1800
+ return this.#gitignorePatterns;
1801
+ }
1802
+ async#applyTemplateFile({
1803
+ templatePath,
1804
+ targetPath,
1805
+ scanInfo,
1806
+ overwrite = true
1807
+ }, dict = {}, options = {}) {
1808
+ if (targetPath.endsWith(".ts") || targetPath.endsWith(".tsx")) {
1809
+ const getContent = await import(templatePath);
1810
+ const result = await getContent.default(scanInfo ?? null, dict, options);
1811
+ if (result === null)
1812
+ return null;
1813
+ const filename = typeof result === "object" ? result.filename : path5.basename(targetPath).replace(".js", ".ts");
1814
+ const content = typeof result === "object" ? result.content : result;
1815
+ const dirname = path5.dirname(targetPath);
1816
+ const convertedTargetPath = Object.entries(dict).reduce((path6, [key, value]) => path6.replace(new RegExp(`__${key}__`, "g"), value), `${dirname}/${filename}`);
1817
+ this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
1818
+ return this.writeFile(convertedTargetPath, content, { overwrite });
1819
+ } else if (targetPath.endsWith(".template")) {
1820
+ const content = await FileSys.readText(templatePath);
1821
+ const convertedTargetPath = Object.entries(dict).reduce((path6, [key, value]) => path6.replace(new RegExp(`__${key}__`, "g"), value), targetPath.slice(0, -9));
1822
+ const convertedContent = Object.entries(dict).reduce((data, [key, value]) => data.replace(new RegExp(`<%= ${key} %>`, "g"), value), content);
1823
+ this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
1824
+ return this.writeFile(convertedTargetPath, convertedContent, {
1825
+ overwrite
1826
+ });
1827
+ } else if (staticTemplateFileExtensions.has(path5.extname(targetPath).toLowerCase())) {
1828
+ const convertedTargetPath = Object.entries(dict).reduce((path6, [key, value]) => path6.replace(new RegExp(`__${key}__`, "g"), value), targetPath);
1829
+ const writePath = this.getPath(convertedTargetPath);
1830
+ const dirname = path5.dirname(writePath);
1831
+ if (!await FileSys.dirExists(dirname))
1832
+ await mkdir(dirname, { recursive: true });
1833
+ await copyFile(templatePath, writePath);
1834
+ this.logger.verbose(`Apply template ${templatePath} to ${convertedTargetPath}`);
1835
+ return { filePath: writePath, content: "" };
1836
+ } else
1837
+ return null;
1838
+ }
1839
+ async _applyTemplate({
1840
+ basePath,
1841
+ template,
1842
+ scanInfo,
1843
+ dict = {},
1844
+ options = {},
1845
+ overwrite = true
1846
+ }) {
1847
+ const templateRoot = await this.#resolveTemplateRoot();
1848
+ const templatePath = `${templateRoot}${template ? `/${template}` : ""}`;
1849
+ const prefixTemplatePath = templatePath;
1850
+ if ((await stat(prefixTemplatePath)).isFile()) {
1851
+ const filename = path5.basename(prefixTemplatePath);
1852
+ const fileContent = await this.#applyTemplateFile({
1853
+ templatePath: prefixTemplatePath,
1854
+ targetPath: path5.join(basePath, filename),
1855
+ scanInfo,
1856
+ overwrite
1857
+ }, dict, options);
1858
+ return fileContent ? [fileContent] : [];
1859
+ } else {
1860
+ const subdirs = await readDirEntries(templatePath);
1861
+ const fileContents = (await Promise.all(subdirs.map(async (subdir) => {
1862
+ const subpath = path5.join(templatePath, subdir);
1863
+ if ((await stat(subpath)).isFile()) {
1864
+ const fileContent = await this.#applyTemplateFile({
1865
+ templatePath: subpath,
1866
+ targetPath: path5.join(basePath, subdir),
1867
+ scanInfo,
1868
+ overwrite
1869
+ }, dict, options);
1870
+ return fileContent ? [fileContent] : [];
1871
+ } else
1872
+ return await this._applyTemplate({
1873
+ basePath: path5.join(basePath, subdir),
1874
+ template: path5.join(template, subdir),
1875
+ scanInfo,
1876
+ dict,
1877
+ overwrite,
1878
+ options
1879
+ });
1880
+ }))).flat();
1881
+ return fileContents;
1882
+ }
1883
+ }
1884
+ async#resolveTemplateRoot() {
1885
+ const candidates = [
1886
+ path5.resolve(getDirname(import.meta.url), "templates"),
1887
+ path5.resolve(getDirname(import.meta.url), "../cli/templates")
1888
+ ];
1889
+ for (const candidate of candidates) {
1890
+ if (await FileSys.dirExists(candidate))
1891
+ return candidate;
1892
+ }
1893
+ return candidates[0];
1894
+ }
1895
+ async applyTemplate(options) {
1896
+ const dict = {
1897
+ ...options.dict ?? {},
1898
+ ...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
1899
+ };
1900
+ return this._applyTemplate({ ...options, dict });
1901
+ }
1902
+ async getTypeChecker() {
1903
+ const { TypeChecker } = await import("./typeChecker-kravn7ns.js");
1904
+ this.typeChecker ??= new TypeChecker(this);
1905
+ return this.typeChecker;
1906
+ }
1907
+ async typeCheck(filePath) {
1908
+ const path6 = this.getPath(filePath);
1909
+ const typeChecker = await this.getTypeChecker();
1910
+ const { fileDiagnostics, fileErrors, fileWarnings } = typeChecker.check(path6);
1911
+ const message = typeChecker.formatDiagnostics(fileDiagnostics);
1912
+ return { fileDiagnostics, fileErrors, fileWarnings, message };
1913
+ }
1914
+ async typeCheckAsync(filePath) {
1915
+ const path6 = this.getPath(filePath);
1916
+ const entry = await this.#resolveTypecheckWorkerEntry();
1917
+ const proc = Bun.spawn([process.execPath, entry], {
1918
+ cwd: this.cwdPath,
1919
+ env: {
1920
+ ...process.env,
1921
+ AKAN_TYPECHECK_CWD: this.cwdPath,
1922
+ AKAN_TYPECHECK_FILE: path6
1923
+ },
1924
+ stdout: "pipe",
1925
+ stderr: "pipe"
1926
+ });
1927
+ const [stdout, stderr, exitCode] = await Promise.all([
1928
+ new Response(proc.stdout).text(),
1929
+ new Response(proc.stderr).text(),
1930
+ proc.exited
1931
+ ]);
1932
+ if (exitCode !== 0)
1933
+ throw new Error((stderr || stdout).trim() || `Typecheck failed with exit code ${exitCode}`);
1934
+ const result = JSON.parse(stdout);
1935
+ return {
1936
+ fileDiagnostics: Array.from({ length: result.fileDiagnosticsCount }),
1937
+ fileErrors: Array.from({ length: result.fileErrorsCount }),
1938
+ fileWarnings: Array.from({ length: result.fileWarningsCount }),
1939
+ message: result.message
1940
+ };
1941
+ }
1942
+ async#resolveTypecheckWorkerEntry() {
1943
+ const dirname = getDirname(import.meta.url);
1944
+ const candidates = [
1945
+ path5.join(process.cwd(), "pkgs/@akanjs/devkit/typecheck/typecheck.proc.ts"),
1946
+ path5.join(process.cwd(), "node_modules/@akanjs/devkit/typecheck/typecheck.proc.ts"),
1947
+ path5.join(dirname, "typecheck/typecheck.proc.ts"),
1948
+ path5.join(dirname, "typecheck.proc.js"),
1949
+ path5.join(dirname, "typecheck.proc.ts")
1950
+ ];
1951
+ for (const candidate of candidates)
1952
+ if (await Bun.file(candidate).exists())
1953
+ return candidate;
1954
+ throw new Error(`[devkit] typecheck worker entry not found; looked in: ${candidates.join(", ")}`);
1955
+ }
1956
+ getLinter() {
1957
+ this.linter ??= new Linter(this.cwdPath);
1958
+ return this.linter;
1959
+ }
1960
+ async lint(filePath, { fix = false, dryRun = false } = {}) {
1961
+ const path6 = this.getPath(filePath);
1962
+ const linter = this.getLinter();
1963
+ const { results, errors, warnings } = await linter.lint(path6, {
1964
+ fix,
1965
+ dryRun
1966
+ });
1967
+ const message = linter.formatLintResults(results);
1968
+ return { results, message, errors, warnings };
1969
+ }
1970
+ }
1971
+
1972
+ class WorkspaceExecutor extends Executor {
1973
+ workspaceRoot;
1974
+ repoName;
1975
+ emoji = execEmoji.workspace;
1976
+ constructor({ workspaceRoot, repoName }) {
1977
+ super("workspace", workspaceRoot);
1978
+ this.workspaceRoot = workspaceRoot;
1979
+ this.repoName = repoName;
1980
+ }
1981
+ static #execs = new Map;
1982
+ static fromRoot({
1983
+ workspaceRoot = process.cwd(),
1984
+ repoName = path5.basename(process.cwd())
1985
+ } = {}) {
1986
+ return WorkspaceExecutor.#execs.get(repoName) ?? new WorkspaceExecutor({ workspaceRoot, repoName });
1987
+ }
1988
+ static getBaseDevEnv(envPath) {
1989
+ const sourceEnv = envPath ? { ...process.env, ...parseEnvFile(envPath) } : process.env;
1990
+ const appName = sourceEnv.AKAN_PUBLIC_APP_NAME;
1991
+ const workspaceRoot = sourceEnv.AKAN_WORKSPACE_ROOT;
1992
+ const workspaceId = sourceEnv.AKAN_WORKSPACE_ID;
1993
+ const repoName = sourceEnv.AKAN_PUBLIC_REPO_NAME;
1994
+ if (!repoName)
1995
+ throw new Error("AKAN_PUBLIC_REPO_NAME is not set");
1996
+ const serveDomain = sourceEnv.AKAN_PUBLIC_SERVE_DOMAIN;
1997
+ if (!serveDomain)
1998
+ throw new Error("AKAN_PUBLIC_SERVE_DOMAIN is not set");
1999
+ const portOffset = parseInt(sourceEnv.PORT_OFFSET ?? "0");
2000
+ const env = sourceEnv.AKAN_PUBLIC_ENV ?? "debug";
2001
+ if (!env)
2002
+ throw new Error("AKAN_PUBLIC_ENV is not set");
2003
+ return {
2004
+ ...appName ? { appName } : {},
2005
+ workspaceRoot,
2006
+ repoName,
2007
+ serveDomain,
2008
+ env,
2009
+ portOffset,
2010
+ workspaceId
2011
+ };
2012
+ }
2013
+ getWorkspaceId({
2014
+ allowEmpty
2015
+ } = {}) {
2016
+ const { workspaceId } = WorkspaceExecutor.getBaseDevEnv();
2017
+ if (!workspaceId && !allowEmpty)
2018
+ throw new Error("Workspace ID is not found");
2019
+ return workspaceId;
2020
+ }
2021
+ async scan() {
2022
+ return await WorkspaceInfo.fromExecutor(this);
2023
+ }
2024
+ async getApps() {
2025
+ if (!await FileSys.dirExists(`${this.workspaceRoot}/apps`))
2026
+ return [];
2027
+ return await this.#getDirHasFile(`${this.workspaceRoot}/apps`, "akan.config.ts");
2028
+ }
2029
+ async getLibs() {
2030
+ if (!await FileSys.dirExists(`${this.workspaceRoot}/libs`))
2031
+ return [];
2032
+ return await this.#getDirHasFile(`${this.workspaceRoot}/libs`, "akan.config.ts");
2033
+ }
2034
+ async getSyss() {
2035
+ const [appNames, libNames] = await Promise.all([this.getApps(), this.getLibs()]);
2036
+ return [appNames, libNames];
2037
+ }
2038
+ async getPkgs() {
2039
+ if (!await FileSys.dirExists(`${this.workspaceRoot}/pkgs`))
2040
+ return [];
2041
+ return await this.#getDirHasFile(`${this.workspaceRoot}/pkgs`, "package.json");
2042
+ }
2043
+ async getExecs() {
2044
+ const [appNames, libNames, pkgNames] = await Promise.all([this.getApps(), this.getLibs(), this.getPkgs()]);
2045
+ return [appNames, libNames, pkgNames];
2046
+ }
2047
+ async setPkgTsPaths(name) {
2048
+ const rootTsConfig = await this.readJson("tsconfig.json");
2049
+ rootTsConfig.compilerOptions.paths ??= {};
2050
+ rootTsConfig.compilerOptions.paths[name] = [`./pkgs/${name}/index.ts`];
2051
+ rootTsConfig.compilerOptions.paths[`${name}/*`] = [`./pkgs/${name}/*`];
2052
+ if (rootTsConfig.references) {
2053
+ if (!rootTsConfig.references.some((ref) => ref.path === `./pkgs/${name}/tsconfig.json`))
2054
+ rootTsConfig.references.push({ path: `./pkgs/${name}/tsconfig.json` });
2055
+ }
2056
+ await this.writeJson("tsconfig.json", rootTsConfig);
2057
+ return this;
2058
+ }
2059
+ async unsetPkgTsPaths(name) {
2060
+ const rootTsConfig = await this.readJson("tsconfig.json");
2061
+ const filteredKeys = Object.keys(rootTsConfig.compilerOptions.paths ?? {}).filter((key) => key !== name && key !== `${name}/*`);
2062
+ rootTsConfig.compilerOptions.paths = Object.fromEntries(filteredKeys.map((key) => [key, rootTsConfig.compilerOptions.paths?.[key] ?? []]));
2063
+ if (rootTsConfig.references) {
2064
+ rootTsConfig.references = rootTsConfig.references.filter((ref) => ref.path !== `./pkgs/${name}/tsconfig.json`);
2065
+ }
2066
+ await this.writeJson("tsconfig.json", rootTsConfig);
2067
+ return this;
2068
+ }
2069
+ async getDirInModule(basePath, name) {
2070
+ const AVOID_DIRS = ["__lib", "__scalar", `_`, `_${name}`];
2071
+ const getDirs = async (dirname, maxDepth = 3, results = [], prefix = "") => {
2072
+ const dirs = await this.readdir(dirname);
2073
+ await Promise.all(dirs.map(async (dir) => {
2074
+ if (dir.includes("_") || AVOID_DIRS.includes(dir))
2075
+ return;
2076
+ const dirPath = path5.join(dirname, dir);
2077
+ if ((await stat(dirPath)).isDirectory()) {
2078
+ results.push(`${prefix}${dir}`);
2079
+ if (maxDepth > 0)
2080
+ await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
2081
+ }
2082
+ }));
2083
+ return results;
2084
+ };
2085
+ return await getDirs(basePath);
2086
+ }
2087
+ async commit(message, { init = false, add = true } = {}) {
2088
+ if (init)
2089
+ await this.exec(`git init --quiet`);
2090
+ if (add)
2091
+ await this.exec(`git add .`);
2092
+ await this.exec(`git commit --quiet -m "${message}"`);
2093
+ }
2094
+ async#getDirHasFile(basePath, targetFilename) {
2095
+ const AVOID_DIRS = ["node_modules", "dist", "public", "webkit"];
2096
+ const getDirs = async (dirname, maxDepth = 3, results = [], prefix = "") => {
2097
+ const dirs = await this.readdir(dirname);
2098
+ await Promise.all(dirs.map(async (dir) => {
2099
+ if (AVOID_DIRS.includes(dir))
2100
+ return;
2101
+ const dirPath = path5.join(dirname, dir);
2102
+ if ((await stat(dirPath)).isDirectory()) {
2103
+ const hasTargetFile = await FileSys.fileExists(path5.join(dirPath, targetFilename));
2104
+ if (hasTargetFile)
2105
+ results.push(`${prefix}${dir}`);
2106
+ if (maxDepth > 0)
2107
+ await getDirs(dirPath, maxDepth - 1, results, `${prefix}${dir}/`);
2108
+ }
2109
+ }));
2110
+ return results;
2111
+ };
2112
+ return await getDirs(basePath);
2113
+ }
2114
+ async getScalarConstantFiles() {
2115
+ const [appNames, libNames] = await this.getSyss();
2116
+ const scalarConstantExampleFiles = [
2117
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getScalarConstantFiles()))).flat(),
2118
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getScalarConstantFiles()))).flat()
2119
+ ];
2120
+ return scalarConstantExampleFiles;
2121
+ }
2122
+ async getConstantFiles() {
2123
+ const [appNames, libNames] = await this.getSyss();
2124
+ const moduleConstantExampleFiles = [
2125
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getConstantFiles()))).flat(),
2126
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getConstantFiles()))).flat()
2127
+ ];
2128
+ return moduleConstantExampleFiles;
2129
+ }
2130
+ async getDictionaryFiles() {
2131
+ const [appNames, libNames] = await this.getSyss();
2132
+ const moduleDictionaryExampleFiles = [
2133
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getDictionaryFiles()))).flat(),
2134
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getDictionaryFiles()))).flat()
2135
+ ];
2136
+ return moduleDictionaryExampleFiles;
2137
+ }
2138
+ async getViewFiles() {
2139
+ const [appNames, libNames] = await this.getSyss();
2140
+ const viewExampleFiles = [
2141
+ ...(await Promise.all(appNames.map((appName) => AppExecutor.from(this, appName).getViewsSourceCode()))).flat(),
2142
+ ...(await Promise.all(libNames.map((libName) => LibExecutor.from(this, libName).getViewsSourceCode()))).flat()
2143
+ ];
2144
+ return viewExampleFiles;
2145
+ }
2146
+ }
2147
+ var scanFacetDirs = ["ui", "webkit", "srvkit", "common", "plugin"];
2148
+
2149
+ class SysExecutor extends Executor {
2150
+ workspace;
2151
+ name;
2152
+ type;
2153
+ emoji;
2154
+ constructor({ workspace = WorkspaceExecutor.fromRoot(), name, type }) {
2155
+ super(name, `${workspace.workspaceRoot}/${type}s/${name}`);
2156
+ this.workspace = workspace;
2157
+ this.name = name;
2158
+ this.type = type;
2159
+ this.emoji = execEmoji[type];
2160
+ }
2161
+ #akanConfig = null;
2162
+ async getConfig({ refresh } = {}) {
2163
+ if (this.#akanConfig && !refresh)
2164
+ return this.#akanConfig;
2165
+ this.#akanConfig = this.type === "app" ? await AkanAppConfig.from(this) : await AkanLibConfig.from(this);
2166
+ return this.#akanConfig;
2167
+ }
2168
+ async getModules() {
2169
+ const path6 = this.type === "app" ? `apps/${this.name}/lib` : `libs/${this.name}/lib`;
2170
+ return await this.workspace.getDirInModule(path6, this.name);
2171
+ }
2172
+ #scanInfo = null;
2173
+ hasScanInfo() {
2174
+ return this.#scanInfo !== null;
2175
+ }
2176
+ getScanInfo({
2177
+ allowEmpty
2178
+ } = {}) {
2179
+ if (!this.hasScanInfo() && !allowEmpty)
2180
+ throw new Error("Scan info is not available");
2181
+ return this.#scanInfo;
2182
+ }
2183
+ #getScanTemplateTasks(scanInfo) {
2184
+ return [
2185
+ this._applyTemplate({ basePath: "env", template: "env", scanInfo }),
2186
+ this._applyTemplate({ basePath: "lib", template: "lib", scanInfo }),
2187
+ this._applyTemplate({ basePath: ".", template: "server.ts", scanInfo }),
2188
+ this._applyTemplate({ basePath: ".", template: "client.ts", scanInfo }),
2189
+ this.type === "lib" ? this._applyTemplate({ basePath: ".", template: "index.ts", scanInfo }) : null,
2190
+ ...scanFacetDirs.map((facet) => this._applyTemplate({
2191
+ basePath: facet,
2192
+ template: "facetIndex/index.ts",
2193
+ scanInfo,
2194
+ options: { exec: this, facet }
2195
+ })),
2196
+ ...scanInfo.getDatabaseModules().map((model) => this._applyTemplate({
2197
+ basePath: `lib/${model}`,
2198
+ template: "moduleRoot",
2199
+ scanInfo,
2200
+ dict: { model, Model: capitalize(model) }
2201
+ })),
2202
+ ...scanInfo.getServiceModules().map((model) => this._applyTemplate({
2203
+ basePath: `lib/_${model}`,
2204
+ template: "moduleRoot",
2205
+ scanInfo,
2206
+ dict: { model, Model: capitalize(model) }
2207
+ })),
2208
+ ...scanInfo.getScalarModules().map((model) => this._applyTemplate({
2209
+ basePath: `lib/__scalar/${model}`,
2210
+ template: "moduleRoot",
2211
+ scanInfo,
2212
+ dict: { model, Model: capitalize(model) }
2213
+ }))
2214
+ ];
2215
+ }
2216
+ async scan({
2217
+ refresh = false,
2218
+ write = true,
2219
+ writeLib = write
2220
+ } = {}) {
2221
+ if (this.#scanInfo && !refresh)
2222
+ return this.#scanInfo;
2223
+ const scanInfo = this.type === "app" ? await AppInfo.fromExecutor(this, {
2224
+ refresh
2225
+ }) : await LibInfo.fromExecutor(this, {
2226
+ refresh
2227
+ });
2228
+ if (write) {
2229
+ await Promise.all(this.#getScanTemplateTasks(scanInfo));
2230
+ await this.writeJson(`akan.${this.type}.json`, scanInfo.getScanResult());
2231
+ if (this.type === "lib")
2232
+ this.#updateDependencies(scanInfo);
2233
+ if (writeLib) {
2234
+ const libInfos = [...scanInfo.getLibInfos().values()];
2235
+ await this.#updateDependencies(scanInfo);
2236
+ await Promise.all(libInfos.flatMap((libInfo) => libInfo.exec.#getScanTemplateTasks(libInfo)));
2237
+ }
2238
+ }
2239
+ this.#scanInfo = scanInfo;
2240
+ return scanInfo;
2241
+ }
2242
+ async#updateDependencies(scanInfo) {
2243
+ const rootPackageJson = await this.workspace.getPackageJson();
2244
+ const libPackageJson = await this.getPackageJson();
2245
+ const dependencies = scanInfo.getScanResult().dependencies;
2246
+ const devDependencies = scanInfo.getScanResult().devDependencies;
2247
+ const dependencySet = new Set(dependencies);
2248
+ const devDependencySet = new Set(devDependencies);
2249
+ const libPkgJsonWithDeps = {
2250
+ ...libPackageJson,
2251
+ dependencies: {
2252
+ ...Object.fromEntries(Object.entries(libPackageJson.dependencies ?? {}).filter(([dep]) => !devDependencySet.has(dep))),
2253
+ ...Object.fromEntries(dependencies.filter((dep) => rootPackageJson.dependencies?.[dep]).sort().map((dep) => [dep, rootPackageJson.dependencies?.[dep]]))
2254
+ },
2255
+ devDependencies: {
2256
+ ...Object.fromEntries(Object.entries(libPackageJson.devDependencies ?? {}).filter(([dep]) => !dependencySet.has(dep))),
2257
+ ...Object.fromEntries(devDependencies.filter((dep) => rootPackageJson.dependencies?.[dep] || rootPackageJson.devDependencies?.[dep]).sort().map((dep) => [dep, rootPackageJson.devDependencies?.[dep] ?? rootPackageJson.dependencies?.[dep]]))
2258
+ }
2259
+ };
2260
+ await this.setPackageJson(libPkgJsonWithDeps);
2261
+ }
2262
+ async getLocalFile(targetPath) {
2263
+ const filePath = path5.isAbsolute(targetPath) ? targetPath : `${this.type}s/${this.name}/${targetPath}`;
2264
+ const content = await this.workspace.readFile(filePath);
2265
+ return { filePath, content };
2266
+ }
2267
+ async getDatabaseModules() {
2268
+ const databaseModules = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.constant.ts`).exists());
2269
+ return databaseModules;
2270
+ }
2271
+ async getServiceModules() {
2272
+ const serviceModules = (await this.readdir("lib")).filter((name) => name.startsWith("_") && !name.startsWith("__")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.service.ts`).exists());
2273
+ return serviceModules;
2274
+ }
2275
+ async getScalarModules() {
2276
+ const scalarModules = (await this.readdir("lib/__scalar")).filter((name) => !name.startsWith("_")).filter((name) => Bun.file(`${this.cwdPath}/lib/__scalar/${name}/${name}.constant.ts`).exists());
2277
+ return scalarModules;
2278
+ }
2279
+ async getViewComponents() {
2280
+ const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.View.tsx`).exists());
2281
+ return viewComponents;
2282
+ }
2283
+ async getUnitComponents() {
2284
+ const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Unit.tsx`).exists());
2285
+ return unitComponents;
2286
+ }
2287
+ async getTemplateComponents() {
2288
+ const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Template.tsx`).exists());
2289
+ return templateComponents;
2290
+ }
2291
+ async getViewsSourceCode() {
2292
+ const viewComponents = await this.getViewComponents();
2293
+ return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
2294
+ }
2295
+ async getUnitsSourceCode() {
2296
+ const unitComponents = await this.getUnitComponents();
2297
+ return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
2298
+ }
2299
+ async getTemplatesSourceCode() {
2300
+ const templateComponents = await this.getTemplateComponents();
2301
+ return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
2302
+ }
2303
+ async getScalarConstantFiles() {
2304
+ const scalarModules = await this.getScalarModules();
2305
+ return Promise.all(scalarModules.map((scalarModule) => this.getLocalFile(`lib/__scalar/${scalarModule}/${scalarModule}.constant.ts`)));
2306
+ }
2307
+ async getScalarDictionaryFiles() {
2308
+ const scalarModules = await this.getScalarModules();
2309
+ return Promise.all(scalarModules.map((scalarModule) => this.getLocalFile(`lib/${scalarModule}/${scalarModule}.dictionary.ts`)));
2310
+ }
2311
+ async getConstantFiles() {
2312
+ const modules = await this.getModules();
2313
+ return Promise.all(modules.map((module) => this.getLocalFile(`lib/${module}/${module}.constant.ts`)));
2314
+ }
2315
+ async getConstantFilesWithLibs() {
2316
+ const scanInfo = this.type === "app" ? await AppInfo.fromExecutor(this) : await LibInfo.fromExecutor(this);
2317
+ const sysContantFiles = await this.getConstantFiles();
2318
+ const sysScalarConstantFiles = await this.getScalarConstantFiles();
2319
+ const libDeps = scanInfo.getLibs();
2320
+ const libConstantFiles = await Promise.all(libDeps.map(async (lib) => [
2321
+ ...await LibExecutor.from(this, lib).getConstantFiles(),
2322
+ ...await LibExecutor.from(this, lib).getScalarConstantFiles()
2323
+ ]));
2324
+ return [...sysContantFiles, ...sysScalarConstantFiles, ...libConstantFiles.flat()];
2325
+ }
2326
+ async getDictionaryFiles() {
2327
+ const modules = await this.getModules();
2328
+ return Promise.all(modules.map((module) => this.getLocalFile(`lib/${module}/${module}.dictionary.ts`)));
2329
+ }
2330
+ async applyTemplate(options) {
2331
+ const dict = {
2332
+ ...options.dict ?? {},
2333
+ ...Object.fromEntries(Object.entries(options.dict ?? {}).map(([key, value]) => [capitalize(key), capitalize(value)]))
2334
+ };
2335
+ const scanInfo = await this.scan();
2336
+ const fileContents = await this._applyTemplate({
2337
+ ...options,
2338
+ scanInfo,
2339
+ dict
2340
+ });
2341
+ await this.scan();
2342
+ return fileContents;
2343
+ }
2344
+ }
2345
+
2346
+ class AppExecutor extends SysExecutor {
2347
+ static #routeSourceValidator = null;
2348
+ static async#getRouteSourceValidator() {
2349
+ AppExecutor.#routeSourceValidator ??= (await import("./routeSourceValidator-wbhmbwpj.js")).RouteSourceValidator;
2350
+ return AppExecutor.#routeSourceValidator;
2351
+ }
2352
+ dist;
2353
+ emoji = execEmoji.app;
2354
+ constructor({ workspace, name }) {
2355
+ super({ workspace, name, type: "app" });
2356
+ this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/apps/${name}`);
2357
+ }
2358
+ static #execs = new Map;
2359
+ static from(executor, name) {
2360
+ const exec2 = AppExecutor.#execs.get(name);
2361
+ if (exec2)
2362
+ return exec2;
2363
+ else if (executor instanceof WorkspaceExecutor)
2364
+ return new AppExecutor({ workspace: executor, name });
2365
+ else
2366
+ return new AppExecutor({ workspace: executor.workspace, name });
2367
+ }
2368
+ getEnv() {
2369
+ return WorkspaceExecutor.getBaseDevEnv().env;
2370
+ }
2371
+ async getDevPort() {
2372
+ const basePort = 8282;
2373
+ const appNames = (await this.workspace.getApps()).sort((a, b) => a.localeCompare(b));
2374
+ const appIndex = Math.max(appNames.indexOf(this.name), 0);
2375
+ const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
2376
+ return basePort + appIndex + portOffset;
2377
+ }
2378
+ getCommandEnv(env = {}) {
2379
+ const basePort = 8282;
2380
+ const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
2381
+ const PORT = (basePort + portOffset).toString();
2382
+ const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
2383
+ return {
2384
+ ...process.env,
2385
+ AKAN_PUBLIC_APP_NAME: this.name,
2386
+ AKAN_WORKSPACE_ROOT: this.workspace.workspaceRoot,
2387
+ NODE_NO_WARNINGS: "1",
2388
+ PORT,
2389
+ AKAN_PUBLIC_CLIENT_PORT: PORT,
2390
+ ...AKAN_PUBLIC_SERVER_PORT ? { AKAN_PUBLIC_SERVER_PORT } : {},
2391
+ ...env
2392
+ };
2393
+ }
2394
+ async prepareCommand(type) {
2395
+ const akanConfig2 = await this.getConfig();
2396
+ const databaseMode = process.env.AKAN_DATABASE_MODE ?? akanConfig2.defaultDatabaseMode ?? "single";
2397
+ const routeEnv = {
2398
+ AKAN_PUBLIC_BASE_PATHS: [...akanConfig2.basePaths].join(","),
2399
+ AKAN_DATABASE_MODE: databaseMode
2400
+ };
2401
+ Object.assign(process.env, routeEnv);
2402
+ if (type === "build") {
2403
+ if (await this.exists(this.dist.cwdPath))
2404
+ await this.dist.exec(`rm -rf ${this.dist.cwdPath}`);
2405
+ await Promise.all([this.dist.mkdir("private"), this.dist.mkdir("public")]);
2406
+ await Promise.all([
2407
+ this.cp("private", `${this.dist.cwdPath}/private`),
2408
+ this.cp("public", `${this.dist.cwdPath}/public`)
2409
+ ]);
2410
+ } else
2411
+ await this.removeDir(".akan");
2412
+ const devPort = type === "start" ? (await this.getDevPort()).toString() : undefined;
2413
+ const env = this.getCommandEnv({
2414
+ AKAN_COMMAND_TYPE: type,
2415
+ ...routeEnv,
2416
+ ...devPort ? { PORT: devPort, AKAN_PUBLIC_CLIENT_PORT: devPort, AKAN_PUBLIC_SERVER_PORT: devPort } : {}
2417
+ });
2418
+ if (type === "build")
2419
+ Object.assign(process.env, env);
2420
+ return { env };
2421
+ }
2422
+ #publicEnv = null;
2423
+ getPublicEnv(...patterns) {
2424
+ if (this.#publicEnv)
2425
+ return this.#publicEnv;
2426
+ const searchPatterns = [...patterns, "AKAN_PUBLIC_*"];
2427
+ const regexes = searchPatterns.map((pattern) => {
2428
+ let body = "";
2429
+ for (const ch of pattern) {
2430
+ if (ch === "*")
2431
+ body += ".*";
2432
+ else
2433
+ body += ch.replace(/[.+^${}()|[\]\\?]/g, "\\$&");
2434
+ }
2435
+ return new RegExp(`^${body}$`);
2436
+ });
2437
+ const publicEnv = {};
2438
+ for (const [k, v] of Object.entries(process.env)) {
2439
+ if (typeof v !== "string")
2440
+ continue;
2441
+ if (regexes.some((r) => r.test(k)))
2442
+ publicEnv[k] = v;
2443
+ }
2444
+ this.#publicEnv = publicEnv;
2445
+ return publicEnv;
2446
+ }
2447
+ #akanConfig = null;
2448
+ async getConfig({ refresh } = {}) {
2449
+ if (this.#akanConfig && !refresh)
2450
+ return this.#akanConfig;
2451
+ this.#akanConfig = await AkanAppConfig.from(this, { bustImportCache: refresh });
2452
+ return this.#akanConfig;
2453
+ }
2454
+ #pageKeys = null;
2455
+ async getPageKeys({ refresh } = {}) {
2456
+ if (this.#pageKeys && !refresh)
2457
+ return this.#pageKeys;
2458
+ const akanConfig2 = await this.getConfig();
2459
+ const glob = new Bun.Glob("**/*");
2460
+ const pageKeys = [];
2461
+ const pageDir = `${this.cwdPath}/page`;
2462
+ if (!await FileSys.dirExists(pageDir)) {
2463
+ this.#pageKeys = [];
2464
+ return this.#pageKeys;
2465
+ }
2466
+ for await (const rel of glob.scan({
2467
+ cwd: pageDir,
2468
+ absolute: false,
2469
+ onlyFiles: true
2470
+ })) {
2471
+ const segments = rel.split(path5.sep);
2472
+ if (segments.some((s) => s === "node_modules"))
2473
+ continue;
2474
+ const posix = segments.join("/");
2475
+ const absPath = path5.join(pageDir, posix);
2476
+ validatePageSourceFile(posix, { filePath: absPath });
2477
+ if (!isRouteSourceFile(posix))
2478
+ continue;
2479
+ const key = `./${posix}`;
2480
+ validateSubRoutePageKey(key, akanConfig2.basePaths, {
2481
+ appName: this.name,
2482
+ filePath: absPath
2483
+ });
2484
+ const parsed = parseRouteModuleKey(key);
2485
+ if (parsed.isInternalRootLayout) {
2486
+ throw new Error(`[route-convention] __root_layout is reserved for Akan.js generated root layout: ${absPath}`);
2487
+ }
2488
+ const isRootLayout = parsed.kind === "layout" && parsed.moduleSegments.at(-1) === "_layout";
2489
+ const routeSource = await Bun.file(absPath).text();
2490
+ const validator = await AppExecutor.#getRouteSourceValidator();
2491
+ if (parsed.kind === "overrides")
2492
+ validator.validateOverridesSourceExports(routeSource, absPath);
2493
+ else
2494
+ validator.validateRouteSourceExports(routeSource, absPath, parsed.kind, { rootLayout: isRootLayout });
2495
+ pageKeys.push(key);
2496
+ }
2497
+ pageKeys.sort();
2498
+ this.#pageKeys = pageKeys;
2499
+ return this.#pageKeys;
2500
+ }
2501
+ setPageKeys(pageKeys) {
2502
+ this.#pageKeys = pageKeys;
2503
+ }
2504
+ async syncAssets(libDeps) {
2505
+ const projectPublicPath = `${this.cwdPath}/public`;
2506
+ const projectAssetsPath = `${this.cwdPath}/private`;
2507
+ const projectPublicLibPath = `${projectPublicPath}/libs`;
2508
+ const projectAssetsLibPath = `${projectAssetsPath}/libs`;
2509
+ await Promise.all([this.removeDir(projectPublicLibPath), this.removeDir(projectAssetsLibPath)]);
2510
+ const targetPublicDeps = [];
2511
+ for (const dep of libDeps) {
2512
+ if (await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/public`))
2513
+ targetPublicDeps.push(dep);
2514
+ }
2515
+ const targetAssetsDeps = [];
2516
+ for (const dep of libDeps) {
2517
+ if (await this.exists(`${this.workspace.workspaceRoot}/libs/${dep}/private`))
2518
+ targetAssetsDeps.push(dep);
2519
+ }
2520
+ await Promise.all(targetPublicDeps.map((dep) => this.mkdir(`${projectPublicLibPath}/${dep}`)));
2521
+ await Promise.all(targetAssetsDeps.map((dep) => this.mkdir(`${projectAssetsLibPath}/${dep}`)));
2522
+ await Promise.all([
2523
+ ...targetPublicDeps.map((dep) => this.cp(`${this.workspace.workspaceRoot}/libs/${dep}/public`, `${projectPublicLibPath}/${dep}`)),
2524
+ ...targetAssetsDeps.map((dep) => this.cp(`${this.workspace.workspaceRoot}/libs/${dep}/private`, `${projectAssetsLibPath}/${dep}`))
2525
+ ]);
2526
+ }
2527
+ async scanSync({ refresh = false, write = true } = {}) {
2528
+ const scanInfo = await this.scan({
2529
+ refresh,
2530
+ write,
2531
+ writeLib: write
2532
+ });
2533
+ if (write)
2534
+ await this.syncAssets(scanInfo.getScanResult().libDeps);
2535
+ if (write)
2536
+ await this.#runPluginSyncAssets();
2537
+ return scanInfo;
2538
+ }
2539
+ async#runPluginSyncAssets() {
2540
+ const plugins = await this.collectPlugins();
2541
+ if (!plugins.some((plugin) => plugin.syncAssets))
2542
+ return;
2543
+ const ctx = this.#makeSyncContext();
2544
+ for (const plugin of plugins)
2545
+ await plugin.syncAssets?.(ctx);
2546
+ }
2547
+ #makeSyncContext() {
2548
+ return {
2549
+ appName: this.name,
2550
+ appPath: this.cwdPath,
2551
+ executor: this,
2552
+ getPath: (rel) => this.getPath(rel),
2553
+ fileExists: (rel) => FileSys.fileExists(this.getPath(rel)),
2554
+ writeFile: async (rel, content, opts) => {
2555
+ await this.writeFile(rel, content, opts);
2556
+ },
2557
+ readEnvClient: async () => {
2558
+ const envClientPath = path5.join(this.cwdPath, "env", "env.client.ts");
2559
+ if (!await FileSys.fileExists(envClientPath))
2560
+ return null;
2561
+ try {
2562
+ const envUrl = pathToFileURL(envClientPath);
2563
+ envUrl.searchParams.set("t", String(Date.now()));
2564
+ const envModule = await import(envUrl.href);
2565
+ return envModule.env ?? null;
2566
+ } catch {
2567
+ return null;
2568
+ }
2569
+ }
2570
+ };
2571
+ }
2572
+ async collectPlugins() {
2573
+ const scanInfo = await this.scan({ write: false });
2574
+ const libDeps = scanInfo.getLibs();
2575
+ const appConfig = await this.getConfig();
2576
+ const collected = [...appConfig.plugins];
2577
+ for (const libName of libDeps) {
2578
+ const libConfig = await LibExecutor.from(this, libName).getConfig().catch(() => null);
2579
+ if (libConfig)
2580
+ collected.push(...libConfig.plugins);
2581
+ }
2582
+ const seen = new Set;
2583
+ return collected.filter((plugin) => {
2584
+ if (seen.has(plugin.name))
2585
+ return false;
2586
+ seen.add(plugin.name);
2587
+ return true;
2588
+ });
2589
+ }
2590
+ async getPluginRuntimePackages() {
2591
+ const plugins = await this.collectPlugins();
2592
+ const appConfig = await this.getConfig();
2593
+ const ctx = {
2594
+ appName: this.name,
2595
+ mobile: appConfig.mobile,
2596
+ hasMobilePermission: (permission) => Object.values(appConfig.mobile.targets).some((target) => target.permissions?.includes(permission) ?? false)
2597
+ };
2598
+ return [...new Set(plugins.flatMap((plugin) => plugin.runtimePackages?.(ctx) ?? []))];
2599
+ }
2600
+ async increaseBuildNum() {
2601
+ await increaseBuildNum(this);
2602
+ }
2603
+ async decreaseBuildNum() {
2604
+ await decreaseBuildNum(this);
2605
+ }
2606
+ }
2607
+
2608
+ class LibExecutor extends SysExecutor {
2609
+ dist;
2610
+ emoji = execEmoji.lib;
2611
+ constructor({ workspace, name }) {
2612
+ super({ workspace, name, type: "lib" });
2613
+ this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/libs/${name}`);
2614
+ }
2615
+ static #execs = new Map;
2616
+ static from(executor, name) {
2617
+ const exec2 = LibExecutor.#execs.get(name);
2618
+ if (exec2)
2619
+ return exec2;
2620
+ else if (executor instanceof WorkspaceExecutor)
2621
+ return new LibExecutor({ workspace: executor, name });
2622
+ else
2623
+ return new LibExecutor({ workspace: executor.workspace, name });
2624
+ }
2625
+ #akanConfig = null;
2626
+ async getConfig({ refresh } = {}) {
2627
+ if (this.#akanConfig && !refresh)
2628
+ return this.#akanConfig;
2629
+ this.#akanConfig = await AkanLibConfig.from(this);
2630
+ return this.#akanConfig;
2631
+ }
2632
+ }
2633
+
2634
+ class PkgExecutor extends Executor {
2635
+ workspace;
2636
+ name;
2637
+ dist;
2638
+ emoji = execEmoji.pkg;
2639
+ constructor({ workspace = WorkspaceExecutor.fromRoot(), name }) {
2640
+ super(name, `${workspace.workspaceRoot}/pkgs/${name}`);
2641
+ this.workspace = workspace;
2642
+ this.name = name;
2643
+ this.dist = new Executor(`dist/${name}`, `${this.workspace.workspaceRoot}/dist/pkgs/${name}`);
2644
+ }
2645
+ static from(executor, name) {
2646
+ if (executor instanceof WorkspaceExecutor)
2647
+ return new PkgExecutor({ workspace: executor, name });
2648
+ return new PkgExecutor({ workspace: executor.workspace, name });
2649
+ }
2650
+ #scanInfo = null;
2651
+ async scan({ refresh } = {}) {
2652
+ if (this.#scanInfo && !refresh)
2653
+ return this.#scanInfo;
2654
+ const scanInfo = await PkgInfo.fromExecutor(this, { refresh });
2655
+ this.#scanInfo = scanInfo;
2656
+ return scanInfo;
2657
+ }
2658
+ async#getDependencyVersion(rootPackageJson, dep) {
2659
+ const rootDeps = {
2660
+ ...rootPackageJson.dependencies,
2661
+ ...rootPackageJson.devDependencies
2662
+ };
2663
+ const rootVersion = rootDeps[dep];
2664
+ if (rootVersion)
2665
+ return rootVersion;
2666
+ try {
2667
+ const packageJsonPath = `pkgs/${dep}/package.json`;
2668
+ if (!await Bun.file(path5.join(this.workspace.workspaceRoot, packageJsonPath)).exists())
2669
+ return;
2670
+ const packageJson = await this.workspace.readJson(packageJsonPath);
2671
+ if (packageJson.name === dep)
2672
+ return packageJson.version;
2673
+ } catch {
2674
+ return;
2675
+ }
2676
+ }
2677
+ async#toDependencyMap(rootPackageJson, dependencies = [], devDependencies = []) {
2678
+ const dependencyNames = [...new Set(dependencies)].sort();
2679
+ const devDependencyNames = [...new Set(devDependencies)].filter((dep) => !dependencyNames.includes(dep)).sort();
2680
+ const dependencyVersions = new Map;
2681
+ const missingDeps = [];
2682
+ for (const dep of [...dependencyNames, ...devDependencyNames]) {
2683
+ const version = await this.#getDependencyVersion(rootPackageJson, dep);
2684
+ if (version)
2685
+ dependencyVersions.set(dep, version);
2686
+ else
2687
+ missingDeps.push(dep);
2688
+ }
2689
+ if (missingDeps.length > 0)
2690
+ throw new Error(`Missing dependency versions in root package.json: ${missingDeps.join(", ")}`);
2691
+ const toDependencyEntries = (names) => names.map((dep) => {
2692
+ const version = dependencyVersions.get(dep);
2693
+ if (!version)
2694
+ throw new Error(`Missing dependency versions in root package.json: ${dep}`);
2695
+ return [dep, version];
2696
+ });
2697
+ return {
2698
+ dependencies: Object.fromEntries(toDependencyEntries(dependencyNames)),
2699
+ devDependencies: Object.fromEntries(toDependencyEntries(devDependencyNames))
2700
+ };
2701
+ }
2702
+ async updatePackageJsonDependencies(dependencies = [], devDependencies = []) {
2703
+ const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
2704
+ const dependencyMaps = await this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
2705
+ const newPkgJson = {
2706
+ ...pkgJson,
2707
+ ...dependencyMaps
2708
+ };
2709
+ await this.writeJson("package.json", newPkgJson);
2710
+ return newPkgJson;
2711
+ }
2712
+ async generateDistPackageJson(dependencies = [], devDependencies = []) {
2713
+ const [rootPackageJson, pkgJson] = await Promise.all([this.workspace.getPackageJson(), this.getPackageJson()]);
2714
+ const dependencyMaps = await this.#toDependencyMap(rootPackageJson, dependencies, devDependencies);
2715
+ const distPkgJson = {
2716
+ ...pkgJson,
2717
+ type: "module",
2718
+ exports: {
2719
+ ...pkgJson.exports,
2720
+ ".": {
2721
+ import: "./index.ts",
2722
+ types: "./index.ts",
2723
+ default: "./index.ts"
2724
+ }
2725
+ },
2726
+ engines: { bun: ">=1.3.13" },
2727
+ ...dependencyMaps
2728
+ };
2729
+ await Promise.all([this.dist.writeJson("package.json", distPkgJson), this.writeJson("package.json", distPkgJson)]);
2730
+ return distPkgJson;
2731
+ }
2732
+ async build() {
2733
+ await Bun.build({
2734
+ root: this.cwdPath,
2735
+ entrypoints: [`${this.cwdPath}/index.ts`],
2736
+ splitting: false,
2737
+ target: "bun"
2738
+ });
2739
+ await this.cp(`${this.cwdPath}/dist`, this.dist.cwdPath);
2740
+ }
2741
+ async generateTsconfigJson() {
2742
+ const [rootTsconfig, pkgTsconfig] = await Promise.all([this.workspace.getTsConfig(), this.getTsConfig()]);
2743
+ const tsconfig = {
2744
+ ...rootTsconfig,
2745
+ ...pkgTsconfig,
2746
+ compilerOptions: {
2747
+ ...rootTsconfig.compilerOptions,
2748
+ ...pkgTsconfig.compilerOptions,
2749
+ paths: {}
2750
+ }
2751
+ };
2752
+ await this.dist.writeJson("tsconfig.json", tsconfig);
2753
+ return tsconfig;
2754
+ }
2755
+ }
2756
+
2757
+ class ModuleExecutor extends Executor {
2758
+ sys;
2759
+ emoji = execEmoji.module;
2760
+ constructor({ sys, name }) {
2761
+ super(name, `${sys.workspace.workspaceRoot}/${sys.type}s/${sys.name}/lib/${name}`);
2762
+ this.sys = sys;
2763
+ }
2764
+ static from(sysExecutor, name) {
2765
+ return new ModuleExecutor({ sys: sysExecutor, name });
2766
+ }
2767
+ }
2768
+
2769
+ export { getDirname, CommandExecutionError, Executor, WorkspaceExecutor, AppExecutor, LibExecutor, PkgExecutor, ModuleExecutor };