@akanjs/devkit 3.0.0-beta.2 → 3.0.0-beta.20

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 (265) hide show
  1. package/README.md +1 -0
  2. package/abstractDoc.test.ts +6 -15
  3. package/abstractDoc.ts +6 -8
  4. package/agentsIndex.test.ts +4 -7
  5. package/agentsIndex.ts +6 -22
  6. package/akanApp/BackendImportGraph.test.ts +8 -22
  7. package/akanApp/BackendImportGraph.ts +1 -20
  8. package/akanApp/akanApp.host.test.ts +1 -3
  9. package/akanApp/akanApp.host.ts +82 -254
  10. package/akanApp/devHostPolicy.test.ts +10 -34
  11. package/akanApp/devHostPolicy.ts +12 -98
  12. package/akanConfig/akanConfig.test.ts +85 -69
  13. package/akanConfig/akanConfig.ts +69 -153
  14. package/akanConfig/types.ts +1 -0
  15. package/akanContext.ts +106 -159
  16. package/akanMcpContract.ts +28 -73
  17. package/appSelectionMemory.ts +1 -4
  18. package/applicationBuildReporter.ts +37 -5
  19. package/applicationBuildRunner.test.ts +5 -3
  20. package/applicationBuildRunner.ts +54 -61
  21. package/applicationReleasePackager.ts +24 -44
  22. package/applicationTestPreload.ts +3 -12
  23. package/artifact/implicitRootLayout.test.ts +9 -30
  24. package/artifact/implicitRootLayout.ts +34 -106
  25. package/artifact/routeSeedIndex.test.ts +7 -6
  26. package/artifact/routeSeedIndex.ts +11 -44
  27. package/biome.base.json +6 -1
  28. package/biomeBase.ts +2 -5
  29. package/capacitorApp.test.ts +32 -16
  30. package/capacitorApp.ts +110 -238
  31. package/cloud/cloudApi.test.ts +202 -0
  32. package/cloud/cloudApi.ts +77 -39
  33. package/cloud/configLock.test.ts +64 -0
  34. package/cloud/configLock.ts +96 -0
  35. package/cloud/constants.ts +26 -5
  36. package/cloud/globalConfig.ts +70 -32
  37. package/codeAgent/agent/AkanCodeServices.ts +97 -0
  38. package/codeAgent/agent/AkanContextFiles.ts +94 -0
  39. package/codeAgent/agent/CodeAgent.ts +551 -0
  40. package/codeAgent/agent/CodeAgentAsks.ts +66 -0
  41. package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
  42. package/codeAgent/agent/CodeAgentGate.ts +93 -0
  43. package/codeAgent/agent/CodeAgentProxy.ts +82 -0
  44. package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
  45. package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
  46. package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
  47. package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
  48. package/codeAgent/agent/CodeAgentUi.ts +47 -0
  49. package/codeAgent/agent/CodeMailbox.ts +148 -0
  50. package/codeAgent/agent/CodeSessionFork.ts +56 -0
  51. package/codeAgent/agent/CodeSessionIndex.ts +78 -0
  52. package/codeAgent/agent/SubagentPool.ts +214 -0
  53. package/codeAgent/agent/akanCodeModel.ts +47 -0
  54. package/codeAgent/agent/akanCodePaths.ts +31 -0
  55. package/codeAgent/agent/akanContextFiles.test.ts +114 -0
  56. package/codeAgent/agent/akanSystemPrompt.ts +46 -0
  57. package/codeAgent/agent/codeAgent.test.ts +178 -0
  58. package/codeAgent/agent/codeAgentContract.test.ts +523 -0
  59. package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
  60. package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
  61. package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
  62. package/codeAgent/agent/codeMailbox.test.ts +76 -0
  63. package/codeAgent/agent/codeSessionFork.test.ts +58 -0
  64. package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
  65. package/codeAgent/agent/consoleToStderr.ts +8 -0
  66. package/codeAgent/agent/jsonLines.ts +29 -0
  67. package/codeAgent/agent/stringArg.ts +5 -0
  68. package/codeAgent/feedback/DevLogFeedback.ts +82 -0
  69. package/codeAgent/feedback/PreviewChrome.ts +82 -0
  70. package/codeAgent/feedback/PreviewFeedback.ts +46 -0
  71. package/codeAgent/feedback/PreviewView.ts +95 -0
  72. package/codeAgent/feedback/TurnFeedback.ts +98 -0
  73. package/codeAgent/feedback/VerifyFeedback.ts +40 -0
  74. package/codeAgent/feedback/previewChrome.test.ts +73 -0
  75. package/codeAgent/index.ts +34 -0
  76. package/codeAgent/skills/akan-code/SKILL.md +130 -0
  77. package/codeAgent/skills/akan-debug/SKILL.md +42 -0
  78. package/codeAgent/skills/akan-deps/SKILL.md +50 -0
  79. package/codeAgent/skills/akan-err/SKILL.md +39 -0
  80. package/codeAgent/skills/akan-module/SKILL.md +45 -0
  81. package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
  82. package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
  83. package/codeAgent/skills/akan-service/SKILL.md +34 -0
  84. package/codeAgent/skills/akan-store/SKILL.md +67 -0
  85. package/codeAgent/skills/akan-ui/SKILL.md +63 -0
  86. package/codeAgent/skills/akan-validate/SKILL.md +44 -0
  87. package/codeAgent/tools/AkanCli.ts +50 -0
  88. package/codeAgent/tools/AkanCodePlugins.ts +136 -0
  89. package/codeAgent/tools/AkanEditScope.ts +48 -0
  90. package/codeAgent/tools/AkanEnvKeys.ts +50 -0
  91. package/codeAgent/tools/AkanToolPack.ts +92 -0
  92. package/codeAgent/tools/AkanVerifier.ts +48 -0
  93. package/codeAgent/tools/AskToolPack.ts +99 -0
  94. package/codeAgent/tools/MailToolPack.ts +76 -0
  95. package/codeAgent/tools/McpClient.ts +187 -0
  96. package/codeAgent/tools/McpOAuth.ts +233 -0
  97. package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
  98. package/codeAgent/tools/McpServerConfig.ts +129 -0
  99. package/codeAgent/tools/McpSignIn.ts +158 -0
  100. package/codeAgent/tools/McpTokenStore.ts +81 -0
  101. package/codeAgent/tools/McpToolPack.ts +199 -0
  102. package/codeAgent/tools/SessionToolPack.ts +91 -0
  103. package/codeAgent/tools/WebToolPack.ts +147 -0
  104. package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
  105. package/codeAgent/tools/askToolPack.test.ts +116 -0
  106. package/codeAgent/tools/mcpOAuth.test.ts +612 -0
  107. package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
  108. package/codegenLock.test.ts +4 -13
  109. package/codegenLock.ts +3 -17
  110. package/commandDecorators/argMeta.ts +1 -5
  111. package/commandDecorators/camelToKebabCase.ts +1 -0
  112. package/commandDecorators/command.ts +46 -102
  113. package/commandDecorators/commandBuilder.ts +30 -61
  114. package/commandDecorators/commandDecorators.test.ts +2 -10
  115. package/commandDecorators/dependencyBuilder.ts +10 -1
  116. package/commandDecorators/helpFormatter.ts +68 -145
  117. package/commandDecorators/index.ts +0 -2
  118. package/commandDecorators/targetMeta.ts +9 -7
  119. package/createTunnel.ts +7 -14
  120. package/dependencyScanner.test.ts +4 -16
  121. package/dependencyScanner.ts +31 -80
  122. package/devkitUtils.test.ts +48 -20
  123. package/executors.linkFallback.test.ts +103 -0
  124. package/executors.test.ts +132 -106
  125. package/executors.ts +194 -484
  126. package/fileEditor.ts +9 -37
  127. package/{getDirname.test.ts → fileSys.test.ts} +5 -3
  128. package/fileSys.ts +6 -8
  129. package/formSetterScanner.ts +3 -16
  130. package/frontendBuild/allRoutesBuilder.ts +7 -21
  131. package/frontendBuild/autoImportSync.test.ts +27 -38
  132. package/frontendBuild/autoImportSync.ts +31 -65
  133. package/frontendBuild/buildRouteClient.test.ts +62 -37
  134. package/frontendBuild/bundleDefine.ts +12 -0
  135. package/frontendBuild/clientBuildTypes.ts +5 -36
  136. package/frontendBuild/clientEntriesBundler.ts +9 -40
  137. package/frontendBuild/clientEntryDiscovery.ts +6 -41
  138. package/frontendBuild/csrArtifactBuilder.ts +6 -25
  139. package/frontendBuild/cssCandidateCache.test.ts +0 -6
  140. package/frontendBuild/cssCandidateCache.ts +6 -41
  141. package/frontendBuild/cssCompiler.ts +29 -78
  142. package/frontendBuild/cssImportResolver.ts +6 -19
  143. package/frontendBuild/devChangePlanner.ts +7 -10
  144. package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
  145. package/frontendBuild/devGeneratedIndexSync.ts +1 -4
  146. package/frontendBuild/fontOptimizer.test.ts +6 -14
  147. package/frontendBuild/fontOptimizer.ts +15 -72
  148. package/frontendBuild/fontPruner.test.ts +5 -10
  149. package/frontendBuild/fontPruner.ts +5 -20
  150. package/frontendBuild/frontendBuild.test.ts +46 -69
  151. package/frontendBuild/hmrChangeClassifier.ts +2 -2
  152. package/frontendBuild/hmrWatcher.test.ts +8 -32
  153. package/frontendBuild/hmrWatcher.ts +14 -71
  154. package/frontendBuild/pagesBundleBuilder.ts +4 -29
  155. package/frontendBuild/precompressArtifacts.ts +5 -18
  156. package/frontendBuild/routeClientBuilder.ts +23 -46
  157. package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
  158. package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
  159. package/frontendBuild/sourceMtimeIndex.ts +19 -130
  160. package/frontendBuild/ssrBaseArtifactBuilder.ts +2 -8
  161. package/frontendBuild/styleContract.ts +1 -8
  162. package/frontendBuild/themeValidator.ts +4 -26
  163. package/frontendBuild/watchRootResolver.test.ts +23 -42
  164. package/frontendBuild/watchRootResolver.ts +5 -27
  165. package/getRelatedCnsts.ts +12 -92
  166. package/incrementalBuilder/buildBatch.proc.ts +22 -57
  167. package/incrementalBuilder/buildBatchProtocol.ts +8 -36
  168. package/incrementalBuilder/buildBatchRunner.test.ts +34 -0
  169. package/incrementalBuilder/buildBatchRunner.ts +10 -31
  170. package/incrementalBuilder/builderChannel.test.ts +24 -49
  171. package/incrementalBuilder/builderChannel.ts +6 -37
  172. package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
  173. package/incrementalBuilder/builderRequestRouter.ts +5 -24
  174. package/incrementalBuilder/devWatchBatch.test.ts +21 -24
  175. package/incrementalBuilder/incrementalBuilder.host.test.ts +18 -55
  176. package/incrementalBuilder/incrementalBuilder.host.ts +18 -76
  177. package/incrementalBuilder/incrementalBuilder.proc.test.ts +81 -0
  178. package/incrementalBuilder/incrementalBuilder.proc.ts +49 -161
  179. package/index.ts +5 -17
  180. package/integration/devResourceProbe.ts +50 -56
  181. package/integration/devStability.integration.test.ts +47 -194
  182. package/integration/devStabilityHarness.test.ts +13 -28
  183. package/integration/devStabilityHarness.ts +65 -330
  184. package/integration/ssrMemoryProbe.ts +41 -167
  185. package/libSource.test.ts +4 -15
  186. package/libSource.ts +19 -31
  187. package/lint/gritRules.test.ts +8 -24
  188. package/lint/no-deep-internal-import.grit +2 -2
  189. package/lint/no-redeclare-predefined-endpoint.grit +2 -1
  190. package/linter.test.ts +1 -2
  191. package/linter.ts +26 -116
  192. package/mobile/mobileTarget.ts +14 -13
  193. package/package.json +10 -5
  194. package/packageExports.test.ts +3 -18
  195. package/packageExportsMap.ts +3 -19
  196. package/piDependencyPins.test.ts +46 -0
  197. package/platformTest/BunTestReport.test.ts +60 -0
  198. package/platformTest/BunTestReport.ts +65 -0
  199. package/platformTest/IdleWatchedProcess.test.ts +56 -0
  200. package/platformTest/IdleWatchedProcess.ts +73 -0
  201. package/platformTest/LinuxDockerTarget.ts +105 -0
  202. package/platformTest/PlatformTestRun.test.ts +64 -0
  203. package/platformTest/PlatformTestRun.ts +141 -0
  204. package/platformTest/PlatformTestTarget.ts +142 -0
  205. package/platformTest/TestSnapshot.test.ts +72 -0
  206. package/platformTest/TestSnapshot.ts +103 -0
  207. package/platformTest/WindowsSshTarget.ts +151 -0
  208. package/prompter.ts +3 -13
  209. package/qualityScanner.test.ts +9 -26
  210. package/qualityScanner.ts +197 -285
  211. package/recipeScanner.test.ts +7 -12
  212. package/recipeScanner.ts +9 -38
  213. package/repoIdentity.ts +2 -9
  214. package/routeSourceValidator.test.ts +0 -1
  215. package/routeSourceValidator.ts +7 -42
  216. package/scanInfo.ts +87 -216
  217. package/selectModel.ts +1 -2
  218. package/semver.ts +3 -5
  219. package/slicePlanner.test.ts +6 -24
  220. package/slicePlanner.ts +11 -32
  221. package/spinner.test.ts +3 -8
  222. package/spinner.ts +4 -16
  223. package/sshTunnel.ts +1 -6
  224. package/ssrScanner.test.ts +1 -6
  225. package/ssrScanner.ts +103 -113
  226. package/stripAnsi.ts +3 -0
  227. package/subspace.test.ts +30 -25
  228. package/subspace.ts +81 -115
  229. package/subspaceConfig.ts +11 -9
  230. package/testHelpers.ts +194 -0
  231. package/transforms/asyncDefaultExportDetector.ts +3 -11
  232. package/transforms/barrelAnalyzer.ts +43 -135
  233. package/transforms/barrelImportsPlugin.ts +38 -175
  234. package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
  235. package/transforms/externalizeFrameworkPlugin.ts +13 -87
  236. package/transforms/moduleSyntax.ts +32 -0
  237. package/transforms/rscUseClientTransform.ts +35 -20
  238. package/transforms/transforms.test.ts +153 -44
  239. package/transforms/tsconfigPackageResolver.test.ts +6 -24
  240. package/transforms/useClientBundlePlugin.ts +2 -22
  241. package/typeChecker.ts +7 -63
  242. package/types.ts +1 -0
  243. package/uploadRelease.ts +10 -25
  244. package/workflow/artifacts.ts +18 -0
  245. package/workflow/executor.test.ts +1 -12
  246. package/workflow/executor.ts +101 -110
  247. package/workflow/index.ts +0 -1
  248. package/workflow/moduleIndex.test.ts +4 -15
  249. package/workflow/moduleIndex.ts +9 -49
  250. package/workflow/plan.ts +18 -25
  251. package/workflow/render.ts +80 -96
  252. package/workflow/rolloutGate.test.ts +1 -1
  253. package/workflow/source.ts +52 -69
  254. package/workflow/sourceAst.ts +50 -0
  255. package/workspaceLayout.ts +4 -19
  256. package/abstractCompactor.ts +0 -112
  257. package/aiEditor.test.ts +0 -60
  258. package/aiEditor.ts +0 -387
  259. package/commandDecorators/commandMeta.ts +0 -7
  260. package/commandDecorators/types.ts +0 -10
  261. package/getDirname.ts +0 -4
  262. package/guideline.ts +0 -19
  263. package/local/optimize-resource/ipcprobe/child.ts +0 -1
  264. package/local/optimize-resource/ipcprobe/parent.ts +0 -11
  265. package/workflow/primitive.ts +0 -59
@@ -2,7 +2,7 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import type { AkanPlugin } from "akanjs";
5
- import { normalizeRoutePrefix } from "akanjs/base";
5
+ import { DatabaseModes, normalizeRoutePrefix } from "akanjs/base";
6
6
  import { type AkanI18nConfig, resolveAkanI18nConfig } from "akanjs/common";
7
7
  import type { AkanImageConfig } from "akanjs/server";
8
8
  import type { App, Lib } from "../commandDecorators";
@@ -11,6 +11,7 @@ import type { BaseDevEnv, PackageJson } from "../types";
11
11
  import {
12
12
  type AkanApiConfig,
13
13
  type AkanAssetsConfig,
14
+ type AkanDatabaseConfig,
14
15
  type AkanMobileConfig,
15
16
  type AkanMobileTargetConfig,
16
17
  type AkanRouteConfig,
@@ -31,40 +32,15 @@ import {
31
32
 
32
33
  const DEFAULT_BARREL_IMPORTS = ["akanjs/webkit", "akanjs/common", "akanjs/ui", "akanjs/server"];
33
34
  const DEFAULT_OPTIMIZE_IMPORTS = [
34
- "lucide-react",
35
- "date-fns",
36
- "lodash-es",
37
- "ramda",
38
- "antd",
39
- "react-bootstrap",
40
- "ahooks",
41
- "@ant-design/icons",
42
- "@headlessui/react",
43
- "@headlessui-float/react",
44
- "@heroicons/react/20/solid",
45
- "@heroicons/react/24/solid",
46
- "@heroicons/react/24/outline",
47
- "@visx/visx",
48
- "@tremor/react",
49
- "rxjs",
50
- "@mui/material",
51
- "@mui/icons-material",
52
- "recharts",
53
- "react-use",
54
- "@material-ui/core",
55
- "@material-ui/icons",
56
- "@tabler/icons-react",
57
- "mui-core",
58
- "react-icons/*",
59
- ];
35
+ "lucide-react date-fns lodash-es ramda antd react-bootstrap ahooks @ant-design/icons @headlessui/react",
36
+ "@headlessui-float/react @heroicons/react/20/solid @heroicons/react/24/solid @heroicons/react/24/outline",
37
+ "@visx/visx @tremor/react rxjs @mui/material @mui/icons-material recharts react-use @material-ui/core",
38
+ "@material-ui/icons @tabler/icons-react mui-core react-icons/*",
39
+ ].flatMap((line) => line.split(" "));
60
40
  const WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"] as const;
61
41
  const DEFAULT_DOCKER_IMAGE = "oven/bun:1-slim";
62
42
  const SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"] as const;
63
- // The firebase client (push tokens) and the Capacitor toolchain — `@capacitor/cli` (`npx cap`),
64
- // `@capacitor/assets` (`npx @capacitor/assets`) plus the `@capacitor/core`/`ios`/`android` runtime
65
- // and native-platform packages that `npx cap add`/`sync` resolve from the workspace node_modules.
66
- // All are declared only as optional peers, so a fresh workspace never auto-installs them; the mobile
67
- // preflight installs them (together with MOBILE_APP_CAPACITOR_PLUGINS) at the workspace root.
43
+ // Optional peers only, so a fresh workspace never installs them; the mobile preflight adds them at the root.
68
44
  const MOBILE_RUNTIME_PACKAGES = [
69
45
  "@capacitor/cli",
70
46
  "@capacitor/core",
@@ -72,37 +48,14 @@ const MOBILE_RUNTIME_PACKAGES = [
72
48
  "@capacitor/android",
73
49
  "@capacitor/assets",
74
50
  ] as const;
75
- // Capacitor plugins that must additionally be declared in the *app's* package.json
76
- // (apps/<app>/package.json): `npx cap sync` discovers plugins by scanning the app directory's
77
- // dependencies and registers their native code into the iOS/Android projects; packages present only
78
- // at the workspace root are never registered, so the JS bridge throws
79
- // `Capacitor plugin "Device" is not available.` at runtime. They are installed at the workspace root
80
- // alongside MOBILE_RUNTIME_PACKAGES (pinned via optional peers) and declared in the app with a "*"
81
- // range so bun dedupes them to that hoisted version instead of pinning a second source of truth.
51
+ // `npx cap sync` registers only plugins the app's own package.json declares (else the bridge throws `Capacitor plugin
52
+ // "Device" is not available.`), so they are declared there with "*" and deduped to the root-installed version.
82
53
  const 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",
54
+ ..."app browser camera core device geolocation haptics inappbrowser keyboard preferences push-notifications"
55
+ .split(" ")
56
+ .map((name) => `@capacitor/${name}`),
94
57
  "capacitor-plugin-safe-area",
95
- ] as const;
96
- const DATABASE_MODE_RUNTIME_PACKAGES = {
97
- single: [],
98
- multiple: ["@libsql/client", "bullmq", "ioredis", "protobufjs"],
99
- cluster: ["bullmq", "ioredis", "postgres", "protobufjs"],
100
- } satisfies Record<DatabaseMode, readonly string[]>;
101
- const AKAN_RUNTIME_PACKAGES = new Set<string>([
102
- ...SSR_RUNTIME_PACKAGES,
103
- ...MOBILE_RUNTIME_PACKAGES,
104
- ...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat(),
105
- ]);
58
+ ];
106
59
  const DEFAULT_AKAN_IMAGE_CONFIG: AkanImageConfig = {
107
60
  deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
108
61
  imageSizes: [32, 48, 64, 96, 128, 256, 384],
@@ -130,18 +83,14 @@ const normalizeStringList = (values: string[] | undefined) => {
130
83
  return normalized.length > 0 ? [...new Set(normalized)] : undefined;
131
84
  };
132
85
 
133
- // Reduce a free-form name to a valid reverse-DNS / Android package segment: lowercase, alphanumerics
134
- // only (hyphens/spaces dropped), never empty, never starting with a digit.
86
+ // A reverse-DNS / Android package segment: lowercase alphanumerics, never empty, never starting with a digit.
135
87
  const sanitizeAppIdSegment = (value: string): string => {
136
88
  const cleaned = value.toLowerCase().replace(/[^a-z0-9]+/g, "");
137
89
  if (!cleaned) return "app";
138
90
  return /^[a-z]/.test(cleaned) ? cleaned : `app${cleaned}`;
139
91
  };
140
92
 
141
- // The default mobile bundle id for an app that has not pinned `mobile.appId`. Uses the workspace
142
- // (repo) name as the org segment so the default is far less collision-prone than a bare
143
- // `com.<appName>.app`, which Apple's portal routinely already has claimed. Apps that ship pin an
144
- // explicit appId, so this only affects unconfigured/dev apps.
93
+ // The repo name is the org segment: a bare `com.<appName>.app` is routinely already claimed on Apple's portal.
145
94
  export const deriveDefaultAppId = (orgName: string, appName: string): string =>
146
95
  `com.${sanitizeAppIdSegment(orgName)}.${sanitizeAppIdSegment(appName)}`;
147
96
 
@@ -174,13 +123,11 @@ const normalizeDeepLinks = (deepLinks: DeepPartial<AkanMobileTargetConfig["deepL
174
123
  } satisfies AkanMobileTargetConfig["deepLinks"];
175
124
  };
176
125
 
177
- /** What `akan.config.ts` may write: the resolved shape made partial, with `docker` and `web` in their unions. */
178
126
  type AppConfigDeclaration = Omit<DeepPartial<AppConfigResult>, "docker" | "web"> & {
179
127
  docker?: DockerOption;
180
128
  web?: AkanWebOption;
181
129
  };
182
130
 
183
- /** What the workspace's libs add to an app's build, read off each `libs/<lib>/akan.config.ts`. */
184
131
  export interface LibContributions {
185
132
  externalLibs: string[];
186
133
  docker: LibDockerConfig;
@@ -211,7 +158,7 @@ export class AkanAppConfig implements AppConfigResult {
211
158
  docker: DockerConfig;
212
159
  /** The Dockerfile `akan build` writes: the declared string verbatim, or one assembled from the parts. */
213
160
  dockerfile: string;
214
- defaultDatabaseMode: DatabaseMode;
161
+ database: AkanDatabaseConfig;
215
162
  web: AkanWebConfig;
216
163
  externalLibs: string[];
217
164
  barrelImports: string[];
@@ -225,7 +172,7 @@ export class AkanAppConfig implements AppConfigResult {
225
172
  hasMobileConfig: boolean;
226
173
  secrets: string[];
227
174
  assets: AkanAssetsConfig;
228
- /** Raw setting; resolved against the app's lib deps at sync time (see `AppExecutor.syncPages`). */
175
+ /** Raw setting, resolved against the app's lib deps at sync time. */
229
176
  syncPageLibs: string[] | boolean;
230
177
  baseDevEnv: BaseDevEnv;
231
178
  libs: string[];
@@ -250,7 +197,7 @@ export class AkanAppConfig implements AppConfigResult {
250
197
  this.baseDevEnv = baseDevEnv;
251
198
  this.plugins = plugins;
252
199
  this.#applyRoutes(config?.routes);
253
- this.defaultDatabaseMode = config?.defaultDatabaseMode ?? "single";
200
+ this.database = AkanAppConfig.#database(app, config);
254
201
  this.externalLibs = [...new Set([...(config?.externalLibs ?? []), ...libContributions.externalLibs])];
255
202
  this.barrelImports = [
256
203
  ...DEFAULT_BARREL_IMPORTS,
@@ -365,24 +312,12 @@ export class AkanAppConfig implements AppConfigResult {
365
312
  }
366
313
  #applyRoutes(routes: AkanRouteConfig[] = []) {
367
314
  for (const route of routes) {
368
- if (route.basePath) {
369
- const basePath = route.basePath.replace(/^\/+|\/+$/g, "");
370
- this.basePaths.add(basePath);
371
- const domains = this.subRoutes.getOrInsert(basePath, new Set());
372
- Object.keys(route.domains).forEach((branch) => void this.branches.add(branch));
373
- Object.values(route.domains)
374
- .flat()
375
- .forEach((domain) => {
376
- if (domain) domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
377
- });
378
- } else {
379
- Object.keys(route.domains).forEach((branch) => void this.branches.add(branch));
380
- Object.values(route.domains)
381
- .flat()
382
- .forEach((domain) => {
383
- if (domain) this.domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
384
- });
385
- }
315
+ const basePath = route.basePath ? route.basePath.replace(/^\/+|\/+$/g, "") : null;
316
+ if (basePath !== null) this.basePaths.add(basePath);
317
+ const domains = basePath !== null ? this.subRoutes.getOrInsert(basePath, new Set()) : this.domains;
318
+ for (const branch of Object.keys(route.domains)) this.branches.add(branch);
319
+ for (const domain of Object.values(route.domains).flat())
320
+ if (domain) domains.add(domain.toLowerCase().replace(/:\d+$/, ""));
386
321
  }
387
322
  const appName = this.app.name.toLowerCase();
388
323
  const serveDomain = this.baseDevEnv.serveDomain.toLowerCase();
@@ -426,11 +361,8 @@ export class AkanAppConfig implements AppConfigResult {
426
361
  const preRunScripts = this.#getDockerRunScripts(preRuns);
427
362
  const postRunScripts = this.#getDockerRunScripts(postRuns);
428
363
  const imageScript = this.#getDockerImageScript(image, DEFAULT_DOCKER_IMAGE);
429
- // The image default matches what the build actually produced; a deployment narrows it further with its
430
- // own env, and can never widen it past the artifacts that are in the image.
431
- // File logging is off in the image: a container's writable layer is ephemeral and nothing collects a
432
- // file from it, so the rotating files would only fill the node disk (50MB x 100 at `trace`). stdout is
433
- // the collection path; a deployment that wants the files back sets `AKAN_LOG_TO_FILE=1`.
364
+ // The web env matches what was built (a deployment can only narrow it). File logging is off: a container's
365
+ // writable layer is ephemeral and stdout is the collection path.
434
366
  const webEnvLines = [
435
367
  ...(this.web.ssr ? [] : ["ENV AKAN_SSR=false"]),
436
368
  ...(this.web.csr ? [] : ["ENV AKAN_CSR=false"]),
@@ -458,16 +390,13 @@ ENV AKAN_PUBLIC_LOCALES=${this.i18n.locales.join(",")}
458
390
  ENV AKAN_PUBLIC_API_PREFIX=${this.api.prefix}
459
391
  ENV AKAN_PUBLIC_WS_PREFIX=${this.api.websocketPrefix}
460
392
  ENV AKAN_PUBLIC_OPERATION_MODE=cloud
393
+ ENV AKAN_DATABASE_MODES=${this.database.modes.join(",")}
461
394
  ENV AKAN_LOG_TO_FILE=0
462
395
  ${webEnvLines}
463
396
  CMD [${command.map((c) => `"${c}"`).join(",")}]`;
464
397
  }
465
398
  static #importGeneration = 0;
466
- /**
467
- * Bun caches dynamic imports by path, so a plain re-import after the user edits the config file
468
- * returns the stale module. `bustImportCache` appends a fresh query string to force re-evaluation
469
- * of the config module itself; modules it imports keep their cached instances.
470
- */
399
+ // Bun caches dynamic imports by path; a fresh query string re-evaluates the config module (its imports stay cached).
471
400
  static async importConfigModule<T = unknown>(
472
401
  cwdPath: string,
473
402
  { bustImportCache = false }: { bustImportCache?: boolean } = {},
@@ -519,24 +448,40 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
519
448
  #resolveProductionDependencyVersion(lib: string) {
520
449
  const rootVersion = this.rootPackageJson.dependencies?.[lib] ?? this.rootPackageJson.devDependencies?.[lib];
521
450
  if (rootVersion) return rootVersion;
522
- // Fall back to the framework package's own (peer)dependencies so plugin-declared runtime
523
- // packages (e.g. firebase for the push plugin) resolve a version without the framework
524
- // hardcoding a per-feature package list.
451
+ // The framework's own (peer)dependencies resolve plugin runtime packages without a hardcoded list.
525
452
  const akanPackageJson = getAkanPackageJson();
526
- return akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
453
+ const version = akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
454
+ if (!version) throw new Error(`Dependency ${lib} not found in package.json`);
455
+ return version;
527
456
  }
528
457
  #getProductionRuntimePackages() {
529
- return [...this.externalLibs, ...SSR_RUNTIME_PACKAGES, ...this.getDatabaseModeRuntimePackages()];
458
+ return [
459
+ ...this.externalLibs,
460
+ ...SSR_RUNTIME_PACKAGES,
461
+ ...this.database.modes.flatMap((mode) => this.getDatabaseModeRuntimePackages(mode)),
462
+ ];
530
463
  }
531
- getDatabaseModeRuntimePackages(databaseMode: DatabaseMode = this.defaultDatabaseMode) {
532
- return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
464
+ getDatabaseModeRuntimePackages(databaseMode: DatabaseMode) {
465
+ return [...DatabaseModes.drivers[databaseMode]];
533
466
  }
534
- getMissingDatabaseModeDependencySpecs(databaseMode: DatabaseMode = this.defaultDatabaseMode) {
467
+ getMissingDatabaseModeDependencySpecs(databaseMode: DatabaseMode) {
535
468
  return this.#getMissingDependencySpecs(this.getDatabaseModeRuntimePackages(databaseMode));
536
469
  }
470
+ /** The mode a CLI command runs this app in: the shell's `AKAN_DATABASE_MODE` if the app declares it, else the first. */
471
+ resolveDatabaseMode() {
472
+ return DatabaseModes.resolve({
473
+ requested: process.env.AKAN_DATABASE_MODE,
474
+ declared: this.database.modes.join(","),
475
+ local: true,
476
+ });
477
+ }
478
+ static #database(app: App, config: AppConfigDeclaration): AkanDatabaseConfig {
479
+ const declared = config?.database?.modes?.filter((mode): mode is DatabaseMode => !!mode);
480
+ const modes = declared?.length ? declared : ["single"];
481
+ return { modes: DatabaseModes.parseList(modes.join(","), `database.modes in apps/${app.name}/akan.config.ts`) };
482
+ }
537
483
  getMobileRuntimePackages() {
538
- // The app Capacitor plugins are installed at the workspace root too, so bun can resolve the
539
- // app's "*" declarations to a hoisted, peer-pinned version instead of fetching latest.
484
+ // Installed at the root too, so the app's "*" declarations resolve to the hoisted, peer-pinned version.
540
485
  return [...new Set([...MOBILE_RUNTIME_PACKAGES, ...MOBILE_APP_CAPACITOR_PLUGINS])];
541
486
  }
542
487
  getMissingMobileDependencySpecs() {
@@ -552,11 +497,10 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
552
497
  };
553
498
  return libs
554
499
  .filter((lib) => !rootDependencies[lib])
555
- .map((lib) => {
556
- const version = this.#resolveProductionDependencyVersion(lib);
557
- if (!version) throw new Error(`Dependency ${lib} not found in package.json`);
558
- return `${lib}@${version}`;
559
- });
500
+ .map((lib) => `${lib}@${this.#resolveProductionDependencyVersion(lib)}`);
501
+ }
502
+ get akanVersion() {
503
+ return getAkanPackageJson().version;
560
504
  }
561
505
  getProductionPackageJson(data: Partial<PackageJson> = {}): PackageJson {
562
506
  return {
@@ -565,11 +509,10 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
565
509
  version: "1.0.0",
566
510
  main: "./main.js",
567
511
  dependencies: Object.fromEntries(
568
- [...new Set(this.#getProductionRuntimePackages())].map((lib) => {
569
- const version = this.#resolveProductionDependencyVersion(lib);
570
- if (!version) throw new Error(`Dependency ${lib} not found in package.json`);
571
- return [lib, version];
572
- }),
512
+ [...new Set(this.#getProductionRuntimePackages())].map((lib) => [
513
+ lib,
514
+ this.#resolveProductionDependencyVersion(lib),
515
+ ]),
573
516
  ),
574
517
  ...data,
575
518
  };
@@ -643,41 +586,14 @@ export class AkanLibConfig implements LibConfigResult {
643
586
  }
644
587
  }
645
588
 
646
- // export const getCapacitorConfig = (configImp: AppConfig, appInfo: AppScanResult, tsconfig: TsConfigJson) => {
647
- // const props: RunnerProps = {
648
- // type: "app",
649
- // name: appInfo.name,
650
- // repoName: appInfo.repoName,
651
- // serveDomain: appInfo.serveDomain,
652
- // env: (process.env.AKAN_PUBLIC_ENV ?? "debug") as "testing" | "local" | "debug" | "develop" | "main",
653
- // libs: appInfo.libDeps,
654
- // tsconfig,
655
- // };
656
- // const config = typeof configImp === "function" ? configImp(props) : configImp;
657
- // const akanConfig = makeAppConfig(config, props);
658
- // return akanConfig;
659
- // };
660
-
661
589
  //! need to refactor
662
- export const increaseBuildNum = async (app: App) => {
663
- const appConfig = await AkanAppConfig.from(app);
590
+ const shiftBuildNum = async (app: App, delta: number) => {
591
+ const { buildNum } = (await AkanAppConfig.from(app)).mobile;
664
592
  const akanConfigPath = path.join(app.cwdPath, "akan.config.ts");
665
593
  const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
666
- const akanConfigContent = akanConfig.replace(
667
- `buildNum: ${appConfig.mobile.buildNum}`,
668
- `buildNum: ${appConfig.mobile.buildNum + 1}`,
669
- );
670
- //? 개선할 여지가 있는지 확인
671
- fs.writeFileSync(akanConfigPath, akanConfigContent);
594
+ fs.writeFileSync(akanConfigPath, akanConfig.replace(`buildNum: ${buildNum}`, `buildNum: ${buildNum + delta}`));
672
595
  };
673
596
 
674
- export const decreaseBuildNum = async (app: App) => {
675
- const appConfig = await AkanAppConfig.from(app);
676
- const akanConfigPath = path.join(app.cwdPath, "akan.config.ts");
677
- const akanConfig = fs.readFileSync(akanConfigPath, "utf8");
678
- const akanConfigContent = akanConfig.replace(
679
- `buildNum: ${appConfig.mobile.buildNum}`,
680
- `buildNum: ${appConfig.mobile.buildNum - 1}`,
681
- );
682
- fs.writeFileSync(akanConfigPath, akanConfigContent);
683
- };
597
+ export const increaseBuildNum = async (app: App) => await shiftBuildNum(app, 1);
598
+
599
+ export const decreaseBuildNum = async (app: App) => await shiftBuildNum(app, -1);
@@ -2,6 +2,7 @@ export type {
2
2
  AkanApiConfig,
3
3
  AkanAssetsConfig,
4
4
  AkanConfigFile,
5
+ AkanDatabaseConfig,
5
6
  AkanExecutor,
6
7
  AkanMobileConfig,
7
8
  AkanMobileTargetConfig,