@akanjs/devkit 3.0.0-beta.9 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/CHANGELOG.md +1819 -0
  2. package/DEV_RUNTIME_KNOBS.md +44 -26
  3. package/README.ko.md +21 -7
  4. package/README.md +17 -8
  5. package/abstractDoc.test.ts +6 -15
  6. package/abstractDoc.ts +6 -8
  7. package/agentsIndex.test.ts +4 -7
  8. package/agentsIndex.ts +6 -22
  9. package/akanApp/BackendImportGraph.test.ts +8 -22
  10. package/akanApp/BackendImportGraph.ts +1 -20
  11. package/akanApp/akanApp.host.test.ts +56 -3
  12. package/akanApp/akanApp.host.ts +218 -278
  13. package/akanApp/devBootLatch.test.ts +39 -0
  14. package/akanApp/devBootLatch.ts +39 -0
  15. package/akanApp/devHostPolicy.test.ts +81 -35
  16. package/akanApp/devHostPolicy.ts +41 -102
  17. package/akanConfig/akanBin.test.ts +62 -0
  18. package/akanConfig/akanBin.ts +78 -0
  19. package/akanConfig/akanConfig.test.ts +439 -141
  20. package/akanConfig/akanConfig.ts +359 -275
  21. package/akanConfig/index.ts +1 -0
  22. package/akanConfig/types.ts +22 -8
  23. package/akanContext.ts +110 -160
  24. package/akanMcpContract.ts +28 -73
  25. package/appSelectionMemory.ts +1 -4
  26. package/applicationBuildReporter.ts +37 -5
  27. package/applicationBuildRunner.test.ts +41 -5
  28. package/applicationBuildRunner.ts +71 -63
  29. package/applicationTestPreload.ts +3 -12
  30. package/artifact/implicitRootLayout.test.ts +34 -31
  31. package/artifact/implicitRootLayout.ts +60 -107
  32. package/artifact/routeSeedIndex.test.ts +5 -18
  33. package/artifact/routeSeedIndex.ts +11 -44
  34. package/biome.base.json +39 -2
  35. package/biomeBase.ts +2 -5
  36. package/cloud/cloudApi.test.ts +202 -0
  37. package/cloud/cloudApi.ts +77 -39
  38. package/cloud/configLock.test.ts +64 -0
  39. package/cloud/configLock.ts +96 -0
  40. package/cloud/constants.ts +26 -5
  41. package/cloud/globalConfig.ts +71 -32
  42. package/codeAgent/agent/AkanCodeServices.ts +97 -0
  43. package/codeAgent/agent/AkanContextFiles.ts +94 -0
  44. package/codeAgent/agent/CodeAgent.ts +551 -0
  45. package/codeAgent/agent/CodeAgentAsks.ts +66 -0
  46. package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
  47. package/codeAgent/agent/CodeAgentGate.ts +93 -0
  48. package/codeAgent/agent/CodeAgentProxy.ts +82 -0
  49. package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
  50. package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
  51. package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
  52. package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
  53. package/codeAgent/agent/CodeAgentUi.ts +47 -0
  54. package/codeAgent/agent/CodeMailbox.ts +148 -0
  55. package/codeAgent/agent/CodeSessionFork.ts +56 -0
  56. package/codeAgent/agent/CodeSessionIndex.ts +78 -0
  57. package/codeAgent/agent/SubagentPool.ts +214 -0
  58. package/codeAgent/agent/akanCodeModel.ts +47 -0
  59. package/codeAgent/agent/akanCodePaths.ts +31 -0
  60. package/codeAgent/agent/akanContextFiles.test.ts +114 -0
  61. package/codeAgent/agent/akanSystemPrompt.ts +46 -0
  62. package/codeAgent/agent/codeAgent.test.ts +178 -0
  63. package/codeAgent/agent/codeAgentContract.test.ts +523 -0
  64. package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
  65. package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
  66. package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
  67. package/codeAgent/agent/codeMailbox.test.ts +76 -0
  68. package/codeAgent/agent/codeSessionFork.test.ts +58 -0
  69. package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
  70. package/codeAgent/agent/consoleToStderr.ts +8 -0
  71. package/codeAgent/agent/jsonLines.ts +29 -0
  72. package/codeAgent/agent/stringArg.ts +5 -0
  73. package/codeAgent/feedback/DevLogFeedback.ts +82 -0
  74. package/codeAgent/feedback/PreviewChrome.ts +82 -0
  75. package/codeAgent/feedback/PreviewFeedback.ts +46 -0
  76. package/codeAgent/feedback/PreviewView.ts +95 -0
  77. package/codeAgent/feedback/TurnFeedback.ts +98 -0
  78. package/codeAgent/feedback/VerifyFeedback.ts +40 -0
  79. package/codeAgent/feedback/previewChrome.test.ts +73 -0
  80. package/codeAgent/index.ts +34 -0
  81. package/codeAgent/skills/akan-code/SKILL.md +130 -0
  82. package/codeAgent/skills/akan-debug/SKILL.md +42 -0
  83. package/codeAgent/skills/akan-deps/SKILL.md +50 -0
  84. package/codeAgent/skills/akan-err/SKILL.md +39 -0
  85. package/codeAgent/skills/akan-module/SKILL.md +45 -0
  86. package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
  87. package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
  88. package/codeAgent/skills/akan-service/SKILL.md +34 -0
  89. package/codeAgent/skills/akan-store/SKILL.md +67 -0
  90. package/codeAgent/skills/akan-ui/SKILL.md +63 -0
  91. package/codeAgent/skills/akan-validate/SKILL.md +44 -0
  92. package/codeAgent/tools/AkanCli.ts +50 -0
  93. package/codeAgent/tools/AkanCodePlugins.ts +136 -0
  94. package/codeAgent/tools/AkanEditScope.ts +48 -0
  95. package/codeAgent/tools/AkanEnvKeys.ts +50 -0
  96. package/codeAgent/tools/AkanToolPack.ts +92 -0
  97. package/codeAgent/tools/AkanVerifier.ts +48 -0
  98. package/codeAgent/tools/AskToolPack.ts +99 -0
  99. package/codeAgent/tools/MailToolPack.ts +76 -0
  100. package/codeAgent/tools/McpClient.ts +187 -0
  101. package/codeAgent/tools/McpOAuth.ts +233 -0
  102. package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
  103. package/codeAgent/tools/McpServerConfig.ts +129 -0
  104. package/codeAgent/tools/McpSignIn.ts +158 -0
  105. package/codeAgent/tools/McpTokenStore.ts +81 -0
  106. package/codeAgent/tools/McpToolPack.ts +199 -0
  107. package/codeAgent/tools/SessionToolPack.ts +91 -0
  108. package/codeAgent/tools/WebToolPack.ts +147 -0
  109. package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
  110. package/codeAgent/tools/askToolPack.test.ts +116 -0
  111. package/codeAgent/tools/mcpOAuth.test.ts +612 -0
  112. package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
  113. package/codegenLock.test.ts +4 -13
  114. package/codegenLock.ts +3 -17
  115. package/commandDecorators/argMeta.ts +1 -5
  116. package/commandDecorators/camelToKebabCase.ts +1 -0
  117. package/commandDecorators/command.ts +46 -102
  118. package/commandDecorators/commandBuilder.ts +30 -61
  119. package/commandDecorators/commandDecorators.test.ts +2 -10
  120. package/commandDecorators/dependencyBuilder.ts +10 -1
  121. package/commandDecorators/helpFormatter.ts +68 -145
  122. package/commandDecorators/index.ts +0 -2
  123. package/commandDecorators/targetMeta.ts +9 -7
  124. package/createTunnel.ts +7 -14
  125. package/csrE2e/csrE2eHarness.fixture.ts +506 -0
  126. package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
  127. package/csrE2e/csrPageStack.e2e.test.ts +265 -0
  128. package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
  129. package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
  130. package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
  131. package/csrE2e/registryVendor.e2e.test.ts +221 -0
  132. package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
  133. package/dependencyScanner.test.ts +4 -16
  134. package/dependencyScanner.ts +31 -80
  135. package/devkitUtils.test.ts +48 -20
  136. package/executors.linkFallback.test.ts +103 -0
  137. package/executors.test.ts +176 -106
  138. package/executors.ts +212 -498
  139. package/fileEditor.ts +9 -37
  140. package/{getDirname.test.ts → fileSys.test.ts} +5 -3
  141. package/fileSys.ts +22 -9
  142. package/formSetterScanner.ts +3 -16
  143. package/frontendBuild/allRoutesBuilder.ts +7 -21
  144. package/frontendBuild/autoImportSync.test.ts +27 -38
  145. package/frontendBuild/autoImportSync.ts +31 -65
  146. package/frontendBuild/buildRouteClient.test.ts +123 -37
  147. package/frontendBuild/bundleDefine.test.ts +12 -0
  148. package/frontendBuild/bundleDefine.ts +16 -0
  149. package/frontendBuild/clientBuildTypes.ts +5 -36
  150. package/frontendBuild/clientEntriesBundler.ts +9 -40
  151. package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
  152. package/frontendBuild/clientEntryDiscovery.ts +122 -110
  153. package/frontendBuild/csrArtifactBuilder.ts +9 -31
  154. package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
  155. package/frontendBuild/csrDevArtifactWriter.ts +307 -0
  156. package/frontendBuild/csrDevBundler.test.ts +66 -0
  157. package/frontendBuild/csrDevBundler.ts +95 -0
  158. package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
  159. package/frontendBuild/csrDevFactoryCheck.ts +39 -0
  160. package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
  161. package/frontendBuild/csrDevModuleCompiler.ts +422 -0
  162. package/frontendBuild/csrDevPatcher.test.ts +572 -0
  163. package/frontendBuild/csrDevPatcher.ts +548 -0
  164. package/frontendBuild/csrDevPaths.ts +145 -0
  165. package/frontendBuild/csrDevResolver.test.ts +239 -0
  166. package/frontendBuild/csrDevResolver.ts +363 -0
  167. package/frontendBuild/csrDevTypes.ts +61 -0
  168. package/frontendBuild/csrEntryFiles.ts +24 -0
  169. package/frontendBuild/cssCandidateCache.test.ts +0 -6
  170. package/frontendBuild/cssCandidateCache.ts +6 -41
  171. package/frontendBuild/cssCompiler.ts +31 -82
  172. package/frontendBuild/cssImportResolver.ts +6 -19
  173. package/frontendBuild/devChangePlanner.ts +7 -10
  174. package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
  175. package/frontendBuild/devGeneratedIndexSync.ts +1 -4
  176. package/frontendBuild/devRegistryBundler.ts +192 -0
  177. package/frontendBuild/fontOptimizer.test.ts +6 -14
  178. package/frontendBuild/fontOptimizer.ts +15 -72
  179. package/frontendBuild/fontPruner.test.ts +5 -10
  180. package/frontendBuild/fontPruner.ts +5 -20
  181. package/frontendBuild/frontendBuild.test.ts +57 -70
  182. package/frontendBuild/hmrChangeClassifier.ts +2 -2
  183. package/frontendBuild/hmrWatcher.test.ts +59 -32
  184. package/frontendBuild/hmrWatcher.ts +37 -74
  185. package/frontendBuild/index.ts +4 -0
  186. package/frontendBuild/pagesBundleBuilder.ts +28 -30
  187. package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
  188. package/frontendBuild/precompressArtifacts.ts +5 -18
  189. package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
  190. package/frontendBuild/registryMetadataFingerprint.ts +28 -0
  191. package/frontendBuild/routeClientBuilder.ts +100 -62
  192. package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
  193. package/frontendBuild/serverGraphFile.test.ts +77 -0
  194. package/frontendBuild/serverGraphFile.ts +93 -0
  195. package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
  196. package/frontendBuild/sourceMtimeIndex.ts +19 -130
  197. package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
  198. package/frontendBuild/ssrDevBundler.ts +85 -0
  199. package/frontendBuild/styleContract.ts +1 -8
  200. package/frontendBuild/themeValidator.ts +4 -26
  201. package/frontendBuild/watchRootResolver.test.ts +23 -42
  202. package/frontendBuild/watchRootResolver.ts +5 -27
  203. package/getRelatedCnsts.ts +12 -92
  204. package/incrementalBuilder/buildBatch.proc.ts +147 -63
  205. package/incrementalBuilder/buildBatchProtocol.ts +13 -38
  206. package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
  207. package/incrementalBuilder/buildBatchRunner.ts +26 -38
  208. package/incrementalBuilder/builderChannel.test.ts +24 -49
  209. package/incrementalBuilder/builderChannel.ts +6 -37
  210. package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
  211. package/incrementalBuilder/builderRequestRouter.ts +5 -24
  212. package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
  213. package/incrementalBuilder/builderWorkQueue.ts +126 -0
  214. package/incrementalBuilder/devWatchBatch.test.ts +21 -24
  215. package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
  216. package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
  217. package/incrementalBuilder/incrementalBuilder.proc.test.ts +270 -0
  218. package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
  219. package/index.ts +5 -19
  220. package/integration/devResourceProbe.ts +50 -56
  221. package/integration/devStability.integration.test.ts +111 -202
  222. package/integration/devStabilityHarness.test.ts +13 -28
  223. package/integration/devStabilityHarness.ts +67 -330
  224. package/integration/ssrMemoryProbe.ts +41 -167
  225. package/libSource.test.ts +4 -15
  226. package/libSource.ts +19 -31
  227. package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
  228. package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
  229. package/lint/__fixtures__/no-static-in-object-light-model/bad.ts +43 -0
  230. package/lint/__fixtures__/no-static-in-object-light-model/fixture.json +1 -0
  231. package/lint/__fixtures__/no-static-in-object-light-model/good.ts +42 -0
  232. package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
  233. package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
  234. package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
  235. package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
  236. package/lint/gritRules.test.ts +8 -24
  237. package/lint/no-deep-internal-import.grit +2 -2
  238. package/lint/no-document-cookie.grit +29 -0
  239. package/lint/no-redeclare-predefined-endpoint.grit +2 -1
  240. package/lint/no-static-in-object-light-model.grit +27 -0
  241. package/lint/no-web-only-api-outside-webkit.grit +44 -0
  242. package/lint/no-web-storage.grit +30 -0
  243. package/linter.test.ts +1 -2
  244. package/linter.ts +26 -116
  245. package/mobile/desktopBin.test.ts +182 -0
  246. package/mobile/desktopBin.ts +144 -0
  247. package/mobile/desktopServerStage.test.ts +252 -0
  248. package/mobile/desktopServerStage.ts +178 -0
  249. package/mobile/index.ts +9 -0
  250. package/mobile/mobileTarget.test.ts +57 -8
  251. package/mobile/mobileTarget.ts +45 -26
  252. package/mobile/nativeApi.test.ts +30 -0
  253. package/mobile/nativeApi.ts +40 -0
  254. package/mobile/nativeApp.test.ts +184 -0
  255. package/mobile/nativeApp.ts +454 -0
  256. package/mobile/nativeAppLine.test.ts +69 -0
  257. package/mobile/nativeAppLine.ts +71 -0
  258. package/mobile/nativeConfig.test.ts +438 -0
  259. package/mobile/nativeConfig.ts +294 -0
  260. package/mobile/nativePluginFolders.test.ts +81 -0
  261. package/mobile/nativePluginFolders.ts +60 -0
  262. package/mobile/nativeWebDir.test.ts +80 -0
  263. package/mobile/nativeWebDir.ts +66 -0
  264. package/mobile/placeholderAppId.test.ts +13 -0
  265. package/mobile/placeholderAppId.ts +16 -0
  266. package/mobile/usageDescriptions.test.ts +27 -0
  267. package/mobile/usageDescriptions.ts +24 -0
  268. package/package.json +11 -11
  269. package/packageExports.test.ts +3 -18
  270. package/packageExportsMap.ts +3 -19
  271. package/piDependencyPins.test.ts +46 -0
  272. package/platformTest/BunTestReport.test.ts +60 -0
  273. package/platformTest/BunTestReport.ts +65 -0
  274. package/platformTest/IdleWatchedProcess.test.ts +56 -0
  275. package/platformTest/IdleWatchedProcess.ts +73 -0
  276. package/platformTest/LinuxDockerTarget.ts +105 -0
  277. package/platformTest/PlatformTestRun.test.ts +64 -0
  278. package/platformTest/PlatformTestRun.ts +141 -0
  279. package/platformTest/PlatformTestTarget.ts +142 -0
  280. package/platformTest/TestSnapshot.test.ts +72 -0
  281. package/platformTest/TestSnapshot.ts +103 -0
  282. package/platformTest/WindowsSshTarget.ts +151 -0
  283. package/prompter.ts +3 -13
  284. package/qualityScanner.test.ts +9 -26
  285. package/qualityScanner.ts +200 -286
  286. package/recipeScanner.test.ts +7 -12
  287. package/recipeScanner.ts +9 -38
  288. package/repoIdentity.ts +2 -9
  289. package/routeSourceValidator.test.ts +0 -1
  290. package/routeSourceValidator.ts +7 -42
  291. package/scanInfo.ts +91 -225
  292. package/selectModel.ts +1 -2
  293. package/semver.ts +3 -5
  294. package/slicePlanner.test.ts +6 -24
  295. package/slicePlanner.ts +11 -32
  296. package/spinner.test.ts +3 -8
  297. package/spinner.ts +4 -16
  298. package/sshTunnel.ts +1 -6
  299. package/ssrScanner.test.ts +1 -6
  300. package/ssrScanner.ts +103 -113
  301. package/stripAnsi.ts +3 -0
  302. package/subspace.test.ts +19 -26
  303. package/subspace.ts +48 -115
  304. package/subspaceConfig.ts +3 -11
  305. package/testHelpers.ts +194 -0
  306. package/transforms/asyncDefaultExportDetector.ts +3 -11
  307. package/transforms/barrelAnalyzer.ts +43 -135
  308. package/transforms/barrelImportsPlugin.ts +38 -175
  309. package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
  310. package/transforms/externalizeFrameworkPlugin.ts +13 -87
  311. package/transforms/moduleSyntax.ts +32 -0
  312. package/transforms/rscUseClientTransform.ts +42 -20
  313. package/transforms/serverEnvPlugin.test.ts +103 -0
  314. package/transforms/serverEnvPlugin.ts +41 -0
  315. package/transforms/transforms.test.ts +166 -44
  316. package/transforms/tsconfigPackageResolver.test.ts +6 -24
  317. package/transforms/useClientBundlePlugin.ts +6 -23
  318. package/typeChecker.ts +7 -63
  319. package/types.ts +2 -0
  320. package/uploadRelease.ts +10 -25
  321. package/workflow/artifacts.ts +18 -0
  322. package/workflow/executor.test.ts +1 -12
  323. package/workflow/executor.ts +101 -110
  324. package/workflow/index.ts +0 -1
  325. package/workflow/moduleIndex.test.ts +4 -15
  326. package/workflow/moduleIndex.ts +9 -49
  327. package/workflow/plan.ts +18 -25
  328. package/workflow/render.ts +80 -96
  329. package/workflow/rolloutGate.test.ts +1 -1
  330. package/workflow/source.ts +52 -69
  331. package/workflow/sourceAst.ts +50 -0
  332. package/workspaceLayout.test.ts +18 -3
  333. package/workspaceLayout.ts +22 -23
  334. package/abstractCompactor.ts +0 -112
  335. package/aiEditor.test.ts +0 -60
  336. package/aiEditor.ts +0 -387
  337. package/applicationReleasePackager.ts +0 -209
  338. package/capacitorApp.test.ts +0 -372
  339. package/capacitorApp.ts +0 -1648
  340. package/commandDecorators/commandMeta.ts +0 -7
  341. package/commandDecorators/types.ts +0 -10
  342. package/getDirname.ts +0 -4
  343. package/guideline.ts +0 -19
  344. package/local/optimize-resource/ipcprobe/child.ts +0 -1
  345. package/local/optimize-resource/ipcprobe/parent.ts +0 -11
  346. 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
+ });