@akanjs/devkit 3.0.0-beta.9 → 3.0.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 (341) hide show
  1. package/DEV_RUNTIME_KNOBS.md +44 -26
  2. package/README.ko.md +21 -7
  3. package/README.md +17 -8
  4. package/abstractDoc.test.ts +6 -15
  5. package/abstractDoc.ts +6 -8
  6. package/agentsIndex.test.ts +4 -7
  7. package/agentsIndex.ts +6 -22
  8. package/akanApp/BackendImportGraph.test.ts +8 -22
  9. package/akanApp/BackendImportGraph.ts +1 -20
  10. package/akanApp/akanApp.host.test.ts +56 -3
  11. package/akanApp/akanApp.host.ts +218 -278
  12. package/akanApp/devBootLatch.test.ts +39 -0
  13. package/akanApp/devBootLatch.ts +39 -0
  14. package/akanApp/devHostPolicy.test.ts +81 -35
  15. package/akanApp/devHostPolicy.ts +41 -102
  16. package/akanConfig/akanBin.test.ts +62 -0
  17. package/akanConfig/akanBin.ts +78 -0
  18. package/akanConfig/akanConfig.test.ts +439 -141
  19. package/akanConfig/akanConfig.ts +359 -275
  20. package/akanConfig/index.ts +1 -0
  21. package/akanConfig/types.ts +22 -8
  22. package/akanContext.ts +110 -160
  23. package/akanMcpContract.ts +28 -73
  24. package/appSelectionMemory.ts +1 -4
  25. package/applicationBuildReporter.ts +37 -5
  26. package/applicationBuildRunner.test.ts +41 -5
  27. package/applicationBuildRunner.ts +71 -63
  28. package/applicationTestPreload.ts +3 -12
  29. package/artifact/implicitRootLayout.test.ts +32 -30
  30. package/artifact/implicitRootLayout.ts +58 -106
  31. package/artifact/routeSeedIndex.test.ts +7 -6
  32. package/artifact/routeSeedIndex.ts +11 -44
  33. package/biome.base.json +35 -2
  34. package/biomeBase.ts +2 -5
  35. package/cloud/cloudApi.test.ts +202 -0
  36. package/cloud/cloudApi.ts +77 -39
  37. package/cloud/configLock.test.ts +64 -0
  38. package/cloud/configLock.ts +96 -0
  39. package/cloud/constants.ts +26 -5
  40. package/cloud/globalConfig.ts +71 -32
  41. package/codeAgent/agent/AkanCodeServices.ts +97 -0
  42. package/codeAgent/agent/AkanContextFiles.ts +94 -0
  43. package/codeAgent/agent/CodeAgent.ts +551 -0
  44. package/codeAgent/agent/CodeAgentAsks.ts +66 -0
  45. package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
  46. package/codeAgent/agent/CodeAgentGate.ts +93 -0
  47. package/codeAgent/agent/CodeAgentProxy.ts +82 -0
  48. package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
  49. package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
  50. package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
  51. package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
  52. package/codeAgent/agent/CodeAgentUi.ts +47 -0
  53. package/codeAgent/agent/CodeMailbox.ts +148 -0
  54. package/codeAgent/agent/CodeSessionFork.ts +56 -0
  55. package/codeAgent/agent/CodeSessionIndex.ts +78 -0
  56. package/codeAgent/agent/SubagentPool.ts +214 -0
  57. package/codeAgent/agent/akanCodeModel.ts +47 -0
  58. package/codeAgent/agent/akanCodePaths.ts +31 -0
  59. package/codeAgent/agent/akanContextFiles.test.ts +114 -0
  60. package/codeAgent/agent/akanSystemPrompt.ts +46 -0
  61. package/codeAgent/agent/codeAgent.test.ts +178 -0
  62. package/codeAgent/agent/codeAgentContract.test.ts +523 -0
  63. package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
  64. package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
  65. package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
  66. package/codeAgent/agent/codeMailbox.test.ts +76 -0
  67. package/codeAgent/agent/codeSessionFork.test.ts +58 -0
  68. package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
  69. package/codeAgent/agent/consoleToStderr.ts +8 -0
  70. package/codeAgent/agent/jsonLines.ts +29 -0
  71. package/codeAgent/agent/stringArg.ts +5 -0
  72. package/codeAgent/feedback/DevLogFeedback.ts +82 -0
  73. package/codeAgent/feedback/PreviewChrome.ts +82 -0
  74. package/codeAgent/feedback/PreviewFeedback.ts +46 -0
  75. package/codeAgent/feedback/PreviewView.ts +95 -0
  76. package/codeAgent/feedback/TurnFeedback.ts +98 -0
  77. package/codeAgent/feedback/VerifyFeedback.ts +40 -0
  78. package/codeAgent/feedback/previewChrome.test.ts +73 -0
  79. package/codeAgent/index.ts +34 -0
  80. package/codeAgent/skills/akan-code/SKILL.md +130 -0
  81. package/codeAgent/skills/akan-debug/SKILL.md +42 -0
  82. package/codeAgent/skills/akan-deps/SKILL.md +50 -0
  83. package/codeAgent/skills/akan-err/SKILL.md +39 -0
  84. package/codeAgent/skills/akan-module/SKILL.md +45 -0
  85. package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
  86. package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
  87. package/codeAgent/skills/akan-service/SKILL.md +34 -0
  88. package/codeAgent/skills/akan-store/SKILL.md +67 -0
  89. package/codeAgent/skills/akan-ui/SKILL.md +63 -0
  90. package/codeAgent/skills/akan-validate/SKILL.md +44 -0
  91. package/codeAgent/tools/AkanCli.ts +50 -0
  92. package/codeAgent/tools/AkanCodePlugins.ts +136 -0
  93. package/codeAgent/tools/AkanEditScope.ts +48 -0
  94. package/codeAgent/tools/AkanEnvKeys.ts +50 -0
  95. package/codeAgent/tools/AkanToolPack.ts +92 -0
  96. package/codeAgent/tools/AkanVerifier.ts +48 -0
  97. package/codeAgent/tools/AskToolPack.ts +99 -0
  98. package/codeAgent/tools/MailToolPack.ts +76 -0
  99. package/codeAgent/tools/McpClient.ts +187 -0
  100. package/codeAgent/tools/McpOAuth.ts +233 -0
  101. package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
  102. package/codeAgent/tools/McpServerConfig.ts +129 -0
  103. package/codeAgent/tools/McpSignIn.ts +158 -0
  104. package/codeAgent/tools/McpTokenStore.ts +81 -0
  105. package/codeAgent/tools/McpToolPack.ts +199 -0
  106. package/codeAgent/tools/SessionToolPack.ts +91 -0
  107. package/codeAgent/tools/WebToolPack.ts +147 -0
  108. package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
  109. package/codeAgent/tools/askToolPack.test.ts +116 -0
  110. package/codeAgent/tools/mcpOAuth.test.ts +612 -0
  111. package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
  112. package/codegenLock.test.ts +4 -13
  113. package/codegenLock.ts +3 -17
  114. package/commandDecorators/argMeta.ts +1 -5
  115. package/commandDecorators/camelToKebabCase.ts +1 -0
  116. package/commandDecorators/command.ts +46 -102
  117. package/commandDecorators/commandBuilder.ts +30 -61
  118. package/commandDecorators/commandDecorators.test.ts +2 -10
  119. package/commandDecorators/dependencyBuilder.ts +10 -1
  120. package/commandDecorators/helpFormatter.ts +68 -145
  121. package/commandDecorators/index.ts +0 -2
  122. package/commandDecorators/targetMeta.ts +9 -7
  123. package/createTunnel.ts +7 -14
  124. package/csrE2e/csrE2eHarness.fixture.ts +505 -0
  125. package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
  126. package/csrE2e/csrPageStack.e2e.test.ts +252 -0
  127. package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
  128. package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
  129. package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
  130. package/csrE2e/registryVendor.e2e.test.ts +221 -0
  131. package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
  132. package/dependencyScanner.test.ts +4 -16
  133. package/dependencyScanner.ts +31 -80
  134. package/devkitUtils.test.ts +48 -20
  135. package/executors.linkFallback.test.ts +103 -0
  136. package/executors.test.ts +176 -106
  137. package/executors.ts +212 -498
  138. package/fileEditor.ts +9 -37
  139. package/{getDirname.test.ts → fileSys.test.ts} +5 -3
  140. package/fileSys.ts +22 -9
  141. package/formSetterScanner.ts +3 -16
  142. package/frontendBuild/allRoutesBuilder.ts +7 -21
  143. package/frontendBuild/autoImportSync.test.ts +27 -38
  144. package/frontendBuild/autoImportSync.ts +31 -65
  145. package/frontendBuild/buildRouteClient.test.ts +123 -37
  146. package/frontendBuild/bundleDefine.test.ts +12 -0
  147. package/frontendBuild/bundleDefine.ts +16 -0
  148. package/frontendBuild/clientBuildTypes.ts +5 -36
  149. package/frontendBuild/clientEntriesBundler.ts +9 -40
  150. package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
  151. package/frontendBuild/clientEntryDiscovery.ts +122 -110
  152. package/frontendBuild/csrArtifactBuilder.ts +9 -31
  153. package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
  154. package/frontendBuild/csrDevArtifactWriter.ts +307 -0
  155. package/frontendBuild/csrDevBundler.test.ts +66 -0
  156. package/frontendBuild/csrDevBundler.ts +95 -0
  157. package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
  158. package/frontendBuild/csrDevFactoryCheck.ts +39 -0
  159. package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
  160. package/frontendBuild/csrDevModuleCompiler.ts +422 -0
  161. package/frontendBuild/csrDevPatcher.test.ts +572 -0
  162. package/frontendBuild/csrDevPatcher.ts +548 -0
  163. package/frontendBuild/csrDevPaths.ts +145 -0
  164. package/frontendBuild/csrDevResolver.test.ts +239 -0
  165. package/frontendBuild/csrDevResolver.ts +363 -0
  166. package/frontendBuild/csrDevTypes.ts +61 -0
  167. package/frontendBuild/csrEntryFiles.ts +24 -0
  168. package/frontendBuild/cssCandidateCache.test.ts +0 -6
  169. package/frontendBuild/cssCandidateCache.ts +6 -41
  170. package/frontendBuild/cssCompiler.ts +31 -82
  171. package/frontendBuild/cssImportResolver.ts +6 -19
  172. package/frontendBuild/devChangePlanner.ts +7 -10
  173. package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
  174. package/frontendBuild/devGeneratedIndexSync.ts +1 -4
  175. package/frontendBuild/devRegistryBundler.ts +192 -0
  176. package/frontendBuild/fontOptimizer.test.ts +6 -14
  177. package/frontendBuild/fontOptimizer.ts +15 -72
  178. package/frontendBuild/fontPruner.test.ts +5 -10
  179. package/frontendBuild/fontPruner.ts +5 -20
  180. package/frontendBuild/frontendBuild.test.ts +57 -70
  181. package/frontendBuild/hmrChangeClassifier.ts +2 -2
  182. package/frontendBuild/hmrWatcher.test.ts +59 -32
  183. package/frontendBuild/hmrWatcher.ts +31 -74
  184. package/frontendBuild/index.ts +4 -0
  185. package/frontendBuild/pagesBundleBuilder.ts +28 -30
  186. package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
  187. package/frontendBuild/precompressArtifacts.ts +5 -18
  188. package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
  189. package/frontendBuild/registryMetadataFingerprint.ts +28 -0
  190. package/frontendBuild/routeClientBuilder.ts +100 -62
  191. package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
  192. package/frontendBuild/serverGraphFile.test.ts +77 -0
  193. package/frontendBuild/serverGraphFile.ts +93 -0
  194. package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
  195. package/frontendBuild/sourceMtimeIndex.ts +19 -130
  196. package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
  197. package/frontendBuild/ssrDevBundler.ts +85 -0
  198. package/frontendBuild/styleContract.ts +1 -8
  199. package/frontendBuild/themeValidator.ts +4 -26
  200. package/frontendBuild/watchRootResolver.test.ts +23 -42
  201. package/frontendBuild/watchRootResolver.ts +5 -27
  202. package/getRelatedCnsts.ts +12 -92
  203. package/incrementalBuilder/buildBatch.proc.ts +147 -63
  204. package/incrementalBuilder/buildBatchProtocol.ts +13 -38
  205. package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
  206. package/incrementalBuilder/buildBatchRunner.ts +26 -38
  207. package/incrementalBuilder/builderChannel.test.ts +24 -49
  208. package/incrementalBuilder/builderChannel.ts +6 -37
  209. package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
  210. package/incrementalBuilder/builderRequestRouter.ts +5 -24
  211. package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
  212. package/incrementalBuilder/builderWorkQueue.ts +126 -0
  213. package/incrementalBuilder/devWatchBatch.test.ts +21 -24
  214. package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
  215. package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
  216. package/incrementalBuilder/incrementalBuilder.proc.test.ts +270 -0
  217. package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
  218. package/index.ts +5 -19
  219. package/integration/devResourceProbe.ts +50 -56
  220. package/integration/devStability.integration.test.ts +105 -199
  221. package/integration/devStabilityHarness.test.ts +13 -28
  222. package/integration/devStabilityHarness.ts +65 -330
  223. package/integration/ssrMemoryProbe.ts +41 -167
  224. package/libSource.test.ts +4 -15
  225. package/libSource.ts +19 -31
  226. package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
  227. package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
  228. package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
  229. package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
  230. package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
  231. package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
  232. package/lint/gritRules.test.ts +8 -24
  233. package/lint/no-deep-internal-import.grit +2 -2
  234. package/lint/no-document-cookie.grit +29 -0
  235. package/lint/no-redeclare-predefined-endpoint.grit +2 -1
  236. package/lint/no-web-only-api-outside-webkit.grit +44 -0
  237. package/lint/no-web-storage.grit +30 -0
  238. package/linter.test.ts +1 -2
  239. package/linter.ts +26 -116
  240. package/mobile/desktopBin.test.ts +182 -0
  241. package/mobile/desktopBin.ts +144 -0
  242. package/mobile/desktopServerStage.test.ts +252 -0
  243. package/mobile/desktopServerStage.ts +178 -0
  244. package/mobile/index.ts +9 -0
  245. package/mobile/mobileTarget.test.ts +57 -8
  246. package/mobile/mobileTarget.ts +45 -26
  247. package/mobile/nativeApi.test.ts +30 -0
  248. package/mobile/nativeApi.ts +40 -0
  249. package/mobile/nativeApp.test.ts +184 -0
  250. package/mobile/nativeApp.ts +454 -0
  251. package/mobile/nativeAppLine.test.ts +69 -0
  252. package/mobile/nativeAppLine.ts +71 -0
  253. package/mobile/nativeConfig.test.ts +438 -0
  254. package/mobile/nativeConfig.ts +294 -0
  255. package/mobile/nativePluginFolders.test.ts +81 -0
  256. package/mobile/nativePluginFolders.ts +60 -0
  257. package/mobile/nativeWebDir.test.ts +80 -0
  258. package/mobile/nativeWebDir.ts +66 -0
  259. package/mobile/placeholderAppId.test.ts +13 -0
  260. package/mobile/placeholderAppId.ts +16 -0
  261. package/mobile/usageDescriptions.test.ts +27 -0
  262. package/mobile/usageDescriptions.ts +24 -0
  263. package/package.json +11 -11
  264. package/packageExports.test.ts +3 -18
  265. package/packageExportsMap.ts +3 -19
  266. package/piDependencyPins.test.ts +46 -0
  267. package/platformTest/BunTestReport.test.ts +60 -0
  268. package/platformTest/BunTestReport.ts +65 -0
  269. package/platformTest/IdleWatchedProcess.test.ts +56 -0
  270. package/platformTest/IdleWatchedProcess.ts +73 -0
  271. package/platformTest/LinuxDockerTarget.ts +105 -0
  272. package/platformTest/PlatformTestRun.test.ts +64 -0
  273. package/platformTest/PlatformTestRun.ts +141 -0
  274. package/platformTest/PlatformTestTarget.ts +142 -0
  275. package/platformTest/TestSnapshot.test.ts +72 -0
  276. package/platformTest/TestSnapshot.ts +103 -0
  277. package/platformTest/WindowsSshTarget.ts +151 -0
  278. package/prompter.ts +3 -13
  279. package/qualityScanner.test.ts +9 -26
  280. package/qualityScanner.ts +200 -286
  281. package/recipeScanner.test.ts +7 -12
  282. package/recipeScanner.ts +9 -38
  283. package/repoIdentity.ts +2 -9
  284. package/routeSourceValidator.test.ts +0 -1
  285. package/routeSourceValidator.ts +7 -42
  286. package/scanInfo.ts +91 -225
  287. package/selectModel.ts +1 -2
  288. package/semver.ts +3 -5
  289. package/slicePlanner.test.ts +6 -24
  290. package/slicePlanner.ts +11 -32
  291. package/spinner.test.ts +3 -8
  292. package/spinner.ts +4 -16
  293. package/sshTunnel.ts +1 -6
  294. package/ssrScanner.test.ts +1 -6
  295. package/ssrScanner.ts +103 -113
  296. package/stripAnsi.ts +3 -0
  297. package/subspace.test.ts +9 -25
  298. package/subspace.ts +48 -115
  299. package/subspaceConfig.ts +3 -11
  300. package/testHelpers.ts +194 -0
  301. package/transforms/asyncDefaultExportDetector.ts +3 -11
  302. package/transforms/barrelAnalyzer.ts +43 -135
  303. package/transforms/barrelImportsPlugin.ts +38 -175
  304. package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
  305. package/transforms/externalizeFrameworkPlugin.ts +13 -87
  306. package/transforms/moduleSyntax.ts +32 -0
  307. package/transforms/rscUseClientTransform.ts +42 -20
  308. package/transforms/serverEnvPlugin.test.ts +103 -0
  309. package/transforms/serverEnvPlugin.ts +41 -0
  310. package/transforms/transforms.test.ts +166 -44
  311. package/transforms/tsconfigPackageResolver.test.ts +6 -24
  312. package/transforms/useClientBundlePlugin.ts +6 -23
  313. package/typeChecker.ts +7 -63
  314. package/types.ts +2 -0
  315. package/uploadRelease.ts +10 -25
  316. package/workflow/artifacts.ts +18 -0
  317. package/workflow/executor.test.ts +1 -12
  318. package/workflow/executor.ts +101 -110
  319. package/workflow/index.ts +0 -1
  320. package/workflow/moduleIndex.test.ts +4 -15
  321. package/workflow/moduleIndex.ts +9 -49
  322. package/workflow/plan.ts +18 -25
  323. package/workflow/render.ts +80 -96
  324. package/workflow/rolloutGate.test.ts +1 -1
  325. package/workflow/source.ts +52 -69
  326. package/workflow/sourceAst.ts +50 -0
  327. package/workspaceLayout.test.ts +18 -3
  328. package/workspaceLayout.ts +22 -23
  329. package/abstractCompactor.ts +0 -112
  330. package/aiEditor.test.ts +0 -60
  331. package/aiEditor.ts +0 -387
  332. package/applicationReleasePackager.ts +0 -209
  333. package/capacitorApp.test.ts +0 -372
  334. package/capacitorApp.ts +0 -1648
  335. package/commandDecorators/commandMeta.ts +0 -7
  336. package/commandDecorators/types.ts +0 -10
  337. package/getDirname.ts +0 -4
  338. package/guideline.ts +0 -19
  339. package/local/optimize-resource/ipcprobe/child.ts +0 -1
  340. package/local/optimize-resource/ipcprobe/parent.ts +0 -11
  341. package/workflow/primitive.ts +0 -59
@@ -0,0 +1,270 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import path from "node:path";
3
+ import type { BuilderMessage, BuilderReq } from "akanjs/server";
4
+ import { createTempApp, tempRoots, writeText } from "../testHelpers";
5
+
6
+ const track = tempRoots();
7
+
8
+ const wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
9
+
10
+ const until = async (proc: Bun.Subprocess, what: string, condition: () => boolean, timeoutMs = 20_000) => {
11
+ const deadline = Date.now() + timeoutMs;
12
+ while (!condition()) {
13
+ if (proc.exitCode !== null || Date.now() > deadline) throw new Error(`the builder never sent ${what}`);
14
+ await wait(20);
15
+ }
16
+ };
17
+
18
+ describe("incremental builder process", () => {
19
+ test("says why and where a route's client bundle failed, in its answer, its build status and its log, at the builder's generation", async () => {
20
+ const { root } = track(await createTempApp("demo"));
21
+ const appDir = path.join(root, "apps/demo");
22
+ // The batch runner looks under the workspace root first: a stand-in for the boot build, which needs a whole app.
23
+ await writeText(
24
+ path.join(root, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
25
+ `const { generation } = JSON.parse(process.argv[2]);
26
+ process.send({ type: "build-batch-result", data: { generation, errors: {}, artifact: {}, optimizedFonts: { css: "", files: [] } } });
27
+ `,
28
+ );
29
+ const page = path.join(appDir, "page/_index.tsx");
30
+ await writeText(page, 'import { Broken } from "../ui/Broken";\nexport default Broken;\n');
31
+ await writeText(
32
+ path.join(appDir, "ui/Broken.tsx"),
33
+ '"use client";\nimport { gone } from "./not-there";\nexport const Broken = () => gone;\n',
34
+ );
35
+
36
+ const messages: BuilderMessage[] = [];
37
+ const proc = Bun.spawn(["bun", path.join(import.meta.dir, "incrementalBuilder.proc.ts")], {
38
+ cwd: appDir,
39
+ env: {
40
+ ...process.env,
41
+ AKAN_WORKSPACE_ROOT: root,
42
+ AKAN_PUBLIC_APP_NAME: "demo",
43
+ AKAN_PUBLIC_REPO_NAME: "repo",
44
+ AKAN_PUBLIC_SERVE_DOMAIN: "localhost",
45
+ AKAN_PUBLIC_ENV: "local",
46
+ AKAN_WATCH: "0",
47
+ AKAN_BUILDER_INITIAL_GENERATION: "5",
48
+ },
49
+ stdio: ["ignore", "ignore", "pipe"],
50
+ serialization: "advanced",
51
+ ipc: (message: BuilderMessage) => {
52
+ messages.push(message);
53
+ },
54
+ });
55
+ try {
56
+ await until(proc, "builder-ready", () => messages.some((message) => message.type === "builder-ready"));
57
+ proc.send({
58
+ type: "build-route",
59
+ id: 1,
60
+ routeId: "page:/",
61
+ seeds: [page],
62
+ knownEntries: [],
63
+ generation: 3,
64
+ } satisfies BuilderReq);
65
+ await until(proc, "build-route-res", () => messages.some((message) => message.type === "build-route-res"));
66
+ } finally {
67
+ proc.kill();
68
+ //? Windows refuses to remove a directory a live process has as its cwd.
69
+ await proc.exited;
70
+ }
71
+ const log = await new Response(proc.stderr).text();
72
+
73
+ const reason = '"./not-there" (apps/demo/ui/Broken.tsx:2:22)';
74
+ expect(messages.find((message) => message.type === "build-route-res")).toMatchObject({
75
+ id: 1,
76
+ ok: false,
77
+ error: expect.stringContaining(reason),
78
+ });
79
+ expect(messages.find((message) => message.type === "build-status")).toMatchObject({
80
+ data: { generation: 6, phase: "route", ok: false, message: expect.stringContaining(reason), scope: "page:/" },
81
+ });
82
+ expect(log).toContain(reason);
83
+ }, 30_000);
84
+
85
+ test("a killed boot build is tried again after the crash window, before boot-armed; a route build's check waits likewise", async () => {
86
+ // Inside the checkout so the route's client bundle resolves the framework from its source.
87
+ const { root } = track(await createTempApp("demo", path.join(import.meta.dir, "..", "local")));
88
+ const appDir = path.join(root, "apps/demo");
89
+ const runs = path.join(root, "ssr-runs.log");
90
+ // The first two registry builds are killed (the boot build and its retry), the third succeeds.
91
+ await writeText(
92
+ path.join(root, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
93
+ `import fs from "node:fs";
94
+ const { generation, needs } = JSON.parse(process.argv[2]);
95
+ if (needs.includes("ssr")) {
96
+ fs.appendFileSync(${JSON.stringify(runs)}, Date.now() + "\\n");
97
+ if (fs.readFileSync(${JSON.stringify(runs)}, "utf8").trim().split("\\n").length <= 2) process.exit(1);
98
+ }
99
+ process.send({ type: "build-batch-result", data: { generation, errors: {}, artifact: {}, optimizedFonts: { css: "", files: [] } } });
100
+ `,
101
+ );
102
+ const page = path.join(appDir, "page/_index.tsx");
103
+ await writeText(page, 'import { Card } from "../ui/Card";\nexport default Card;\n');
104
+ await writeText(path.join(appDir, "ui/Card.tsx"), '"use client";\nexport const Card = () => null;\n');
105
+
106
+ const messages: BuilderMessage[] = [];
107
+ const proc = Bun.spawn(["bun", path.join(import.meta.dir, "incrementalBuilder.proc.ts")], {
108
+ cwd: appDir,
109
+ env: {
110
+ ...process.env,
111
+ AKAN_WORKSPACE_ROOT: root,
112
+ AKAN_PUBLIC_APP_NAME: "demo",
113
+ AKAN_PUBLIC_REPO_NAME: "repo",
114
+ AKAN_PUBLIC_SERVE_DOMAIN: "localhost",
115
+ AKAN_PUBLIC_ENV: "local",
116
+ AKAN_WATCH: "0",
117
+ },
118
+ stdio: ["ignore", "ignore", "ignore"],
119
+ serialization: "advanced",
120
+ ipc: (message: BuilderMessage) => {
121
+ messages.push(message);
122
+ },
123
+ });
124
+ const ssrRuns = async () =>
125
+ (await Bun.file(runs).exists()) ? (await Bun.file(runs).text()).trim().split("\n").map(Number) : [];
126
+ try {
127
+ //? boot-armed waits out the 10s crash window first, so 20s left a loaded machine too little for the rest.
128
+ await until(proc, "boot-armed", () => messages.some((message) => message.type === "boot-armed"), 30_000);
129
+ const [first = 0, second = 0] = await ssrRuns();
130
+ expect(await ssrRuns()).toHaveLength(2);
131
+ expect(second - first).toBeGreaterThanOrEqual(9_500);
132
+ proc.send({
133
+ type: "build-route",
134
+ id: 1,
135
+ routeId: "page:/",
136
+ seeds: [page],
137
+ knownEntries: [],
138
+ generation: 3,
139
+ } satisfies BuilderReq);
140
+ await until(proc, "build-route-res", () => messages.some((message) => message.type === "build-route-res"));
141
+ expect(messages.find((message) => message.type === "build-route-res")).toMatchObject({ ok: true });
142
+ const deadline = Date.now() + 15_000;
143
+ while ((await ssrRuns()).length < 3 && Date.now() < deadline) await wait(100);
144
+ const [, retried = 0, third = 0] = await ssrRuns();
145
+ expect(third - retried).toBeGreaterThanOrEqual(9_500);
146
+ } finally {
147
+ proc.kill();
148
+ await proc.exited;
149
+ }
150
+ }, 60_000);
151
+
152
+ test("a route build's check whose own worker is killed is tried again after the crash window", async () => {
153
+ const { root } = track(await createTempApp("demo", path.join(import.meta.dir, "..", "local")));
154
+ const appDir = path.join(root, "apps/demo");
155
+ const runs = path.join(root, "ssr-runs.log");
156
+ // The boot build succeeds, the route check's worker is killed, and the retry after the window succeeds.
157
+ await writeText(
158
+ path.join(root, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
159
+ `import fs from "node:fs";
160
+ const { generation, needs } = JSON.parse(process.argv[2]);
161
+ if (needs.includes("ssr")) {
162
+ fs.appendFileSync(${JSON.stringify(runs)}, Date.now() + "\\n");
163
+ if (fs.readFileSync(${JSON.stringify(runs)}, "utf8").trim().split("\\n").length === 2) process.exit(1);
164
+ }
165
+ process.send({ type: "build-batch-result", data: { generation, errors: {}, artifact: {}, optimizedFonts: { css: "", files: [] } } });
166
+ `,
167
+ );
168
+ const page = path.join(appDir, "page/_index.tsx");
169
+ await writeText(page, 'import { Card } from "../ui/Card";\nexport default Card;\n');
170
+ await writeText(path.join(appDir, "ui/Card.tsx"), '"use client";\nexport const Card = () => null;\n');
171
+
172
+ const messages: BuilderMessage[] = [];
173
+ const proc = Bun.spawn(["bun", path.join(import.meta.dir, "incrementalBuilder.proc.ts")], {
174
+ cwd: appDir,
175
+ env: {
176
+ ...process.env,
177
+ AKAN_WORKSPACE_ROOT: root,
178
+ AKAN_PUBLIC_APP_NAME: "demo",
179
+ AKAN_PUBLIC_REPO_NAME: "repo",
180
+ AKAN_PUBLIC_SERVE_DOMAIN: "localhost",
181
+ AKAN_PUBLIC_ENV: "local",
182
+ AKAN_WATCH: "0",
183
+ },
184
+ stdio: ["ignore", "ignore", "ignore"],
185
+ serialization: "advanced",
186
+ ipc: (message: BuilderMessage) => {
187
+ messages.push(message);
188
+ },
189
+ });
190
+ const ssrRuns = async () =>
191
+ (await Bun.file(runs).exists()) ? (await Bun.file(runs).text()).trim().split("\n").map(Number) : [];
192
+ try {
193
+ await until(proc, "boot-armed", () => messages.some((message) => message.type === "boot-armed"));
194
+ proc.send({
195
+ type: "build-route",
196
+ id: 1,
197
+ routeId: "page:/",
198
+ seeds: [page],
199
+ knownEntries: [],
200
+ generation: 3,
201
+ } satisfies BuilderReq);
202
+ await until(proc, "build-route-res", () => messages.some((message) => message.type === "build-route-res"));
203
+ const deadline = Date.now() + 20_000;
204
+ while ((await ssrRuns()).length < 3 && Date.now() < deadline) await wait(100);
205
+ const [, killed = 0, retried = 0] = await ssrRuns();
206
+ expect(retried - killed).toBeGreaterThanOrEqual(9_500);
207
+ } finally {
208
+ proc.kill();
209
+ await proc.exited;
210
+ }
211
+ }, 60_000);
212
+
213
+ test("a route build's registry check that has nothing to add reports no ssr status, so it cannot hide a failure", async () => {
214
+ const { root } = track(await createTempApp("demo", path.join(import.meta.dir, "..", "local")));
215
+ const appDir = path.join(root, "apps/demo");
216
+ // The registry's own build is the real worker's; the rest is stood in for, as a boot build needs a whole app.
217
+ await writeText(
218
+ path.join(root, "pkgs/@akanjs/devkit/incrementalBuilder/buildBatch.proc.ts"),
219
+ `const { generation, needs } = JSON.parse(process.argv[2]);
220
+ if (needs.includes("ssr")) await import(${JSON.stringify(path.join(import.meta.dir, "buildBatch.proc.ts"))});
221
+ else process.send({ type: "build-batch-result", data: { generation, errors: {}, artifact: {}, optimizedFonts: { css: "", files: [] } } });
222
+ `,
223
+ );
224
+ await writeText(path.join(appDir, "env/env.client.ts"), "export const env = {} as const;\n");
225
+ const page = path.join(appDir, "page/_index.tsx");
226
+ await writeText(page, 'import { Card } from "../ui/Card";\nexport default Card;\n');
227
+ await writeText(path.join(appDir, "ui/Card.tsx"), '"use client";\nexport const Card = () => null;\n');
228
+
229
+ const messages: BuilderMessage[] = [];
230
+ const proc = Bun.spawn(["bun", path.join(import.meta.dir, "incrementalBuilder.proc.ts")], {
231
+ cwd: appDir,
232
+ env: {
233
+ ...process.env,
234
+ AKAN_WORKSPACE_ROOT: root,
235
+ AKAN_PUBLIC_APP_NAME: "demo",
236
+ AKAN_PUBLIC_REPO_NAME: "repo",
237
+ AKAN_PUBLIC_SERVE_DOMAIN: "localhost",
238
+ AKAN_PUBLIC_ENV: "local",
239
+ AKAN_WATCH: "0",
240
+ },
241
+ stdio: ["ignore", "ignore", "ignore"],
242
+ serialization: "advanced",
243
+ ipc: (message: BuilderMessage) => {
244
+ messages.push(message);
245
+ },
246
+ });
247
+ const ssrStatuses = () =>
248
+ messages.filter((message) => message.type === "build-status" && message.data.phase === "ssr");
249
+ try {
250
+ await until(proc, "boot-armed", () => messages.some((message) => message.type === "boot-armed"));
251
+ expect(messages.some((message) => message.type === "ssr-updated")).toBe(true);
252
+ const before = ssrStatuses().length;
253
+ proc.send({
254
+ type: "build-route",
255
+ id: 1,
256
+ routeId: "page:/",
257
+ seeds: [page],
258
+ knownEntries: [],
259
+ generation: 3,
260
+ } satisfies BuilderReq);
261
+ await until(proc, "build-route-res", () => messages.some((message) => message.type === "build-route-res"));
262
+ expect(messages.find((message) => message.type === "build-route-res")).toMatchObject({ ok: true });
263
+ await wait(200);
264
+ expect(ssrStatuses().length).toBe(before);
265
+ } finally {
266
+ proc.kill();
267
+ await proc.exited;
268
+ }
269
+ }, 60_000);
270
+ });