@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,438 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import { mkdir, writeFile } from "node:fs/promises";
3
+ import path from "node:path";
4
+ import type { AkanNativeTarget } from "../akanConfig";
5
+ import { tempDirs } from "../testHelpers";
6
+ import { NativeApi } from "./nativeApi";
7
+ import { NativeConfig } from "./nativeConfig";
8
+
9
+ const makeTempRoot = tempDirs("akan-native-config-");
10
+ const repoApp = path.resolve(import.meta.dir, "../../../../apps/minimal");
11
+
12
+ const minimalTarget: AkanNativeTarget = {
13
+ name: "default",
14
+ indexPath: "/explore",
15
+ appName: "minimal",
16
+ appId: "com.minimal.dev.app",
17
+ version: "0.0.1",
18
+ buildNum: 1,
19
+ permissions: ["push"],
20
+ deepLinks: { schemes: ["minimal"], domains: ["example.com"] },
21
+ ios: { teamId: "TEAMID" },
22
+ android: { googleServices: "secrets/google-services.json", sha256CertFingerprints: ["00:11"] },
23
+ };
24
+
25
+ const adminTarget: AkanNativeTarget = {
26
+ name: "admin",
27
+ basePath: "admin",
28
+ appName: "Portal Admin",
29
+ appId: "com.portal.admin",
30
+ fileName: "portal-admin",
31
+ version: "2.1.0",
32
+ buildNum: 42,
33
+ icon: "assets/icon.png",
34
+ splash: "assets/splash.png",
35
+ permissions: ["camera", "location", "contacts", "speech"],
36
+ deepLinks: { domains: ["portal.example"] },
37
+ plugins: ["iap"],
38
+ ios: { infoPlist: { ITSAppUsesNonExemptEncryption: false } },
39
+ android: {
40
+ manifest: ['<queries><package android:name="com.kakao.talk" /></queries>'],
41
+ files: { "res/raw/chime.mp3": "assets/chime.mp3" },
42
+ },
43
+ };
44
+
45
+ const updatesKey = Buffer.alloc(32, 7).toString("base64");
46
+
47
+ describe("NativeConfig.build", () => {
48
+ test("turns the minimal app's push target into the runtime's config, single-instance carrying its link scheme", () => {
49
+ const { config, warnings } = NativeConfig.build({
50
+ appPath: "/repo/apps/minimal",
51
+ target: minimalTarget,
52
+ webDir: "/repo/apps/minimal/.akan/native/default/web",
53
+ contributions: [{ permission: "push", plugins: ["push"] }],
54
+ locales: ["en", "ko"],
55
+ platform: "android",
56
+ });
57
+
58
+ expect(warnings).toEqual([]);
59
+ expect(config).toEqual({
60
+ app: { id: "com.minimal.dev.app", name: "minimal", fileName: "minimal", version: "0.0.1", build: 1 },
61
+ web: { dir: "/repo/apps/minimal/.akan/native/default/web" },
62
+ plugins: [...NativeConfig.basePlugins, "single-instance", "push"],
63
+ capabilities: [
64
+ {
65
+ identifier: "app",
66
+ description: "The plugins minimal ships, each with its default permissions",
67
+ permissions: [...NativeConfig.basePlugins, "single-instance", "push"].map((plugin) => `${plugin}:default`),
68
+ },
69
+ ],
70
+ deepLinks: { schemes: ["minimal"], domains: ["example.com"] },
71
+ android: {
72
+ debugAppIdSuffix: ".debug",
73
+ googleServices: path.resolve("/repo/apps/minimal", "secrets/google-services.json"),
74
+ },
75
+ keyboard: { resize: "none" },
76
+ });
77
+ });
78
+
79
+ test("falls back to the builtin permission plugins, names the app in their texts, and lists basePath links per locale", () => {
80
+ const { config, warnings } = NativeConfig.build({
81
+ appPath: "/repo/apps/portal",
82
+ target: adminTarget,
83
+ webDir: "/repo/apps/portal/.akan/native/admin/web",
84
+ contributions: [],
85
+ locales: ["en", "ko"],
86
+ platform: "android",
87
+ });
88
+
89
+ expect(warnings).toEqual(["Permission 'speech' has no native plugin yet; the app ships without it."]);
90
+ expect(config).toEqual({
91
+ app: { id: "com.portal.admin", name: "Portal Admin", fileName: "portal-admin", version: "2.1.0", build: 42 },
92
+ web: { dir: "/repo/apps/portal/.akan/native/admin/web" },
93
+ plugins: [...NativeConfig.basePlugins, "camera", "geolocation", "contacts", "iap"],
94
+ capabilities: [
95
+ {
96
+ identifier: "app",
97
+ description: "The plugins Portal Admin ships, each with its default permissions",
98
+ permissions: [...NativeConfig.basePlugins, "camera", "geolocation", "contacts", "iap"].map(
99
+ (plugin) => `${plugin}:default`,
100
+ ),
101
+ },
102
+ ],
103
+ usageDescriptions: {
104
+ NSCameraUsageDescription: "Portal Admin requires access to the camera to take photos.",
105
+ NSPhotoLibraryAddUsageDescription: "Portal Admin requires access to the photo library to take photos.",
106
+ NSPhotoLibraryUsageDescription: "Portal Admin requires access to the photo library to take photos.",
107
+ NSLocationAlwaysAndWhenInUseUsageDescription:
108
+ "Portal Admin requires access to the location to get the user's location.",
109
+ NSLocationAlwaysUsageDescription: "Portal Admin requires access to the location to get the user's location.",
110
+ NSLocationWhenInUseUsageDescription: "Portal Admin requires access to the location to get the user's location.",
111
+ NSContactsUsageDescription: "Portal Admin requires access to the contacts to find people you know.",
112
+ },
113
+ deepLinks: { domains: [{ host: "portal.example", pathPrefixes: ["/en/admin", "/ko/admin"] }] },
114
+ native: {
115
+ ios: { infoPlist: { ITSAppUsesNonExemptEncryption: false } },
116
+ android: { manifest: ['<queries><package android:name="com.kakao.talk" /></queries>'] },
117
+ resources: [{ from: path.resolve("/repo/apps/portal", "assets/chime.mp3"), to: "android/res/raw/chime.mp3" }],
118
+ },
119
+ android: { debugAppIdSuffix: ".debug" },
120
+ keyboard: { resize: "none" },
121
+ icon: path.resolve("/repo/apps/portal", "assets/icon.png"),
122
+ splash: { image: path.resolve("/repo/apps/portal", "assets/splash.png") },
123
+ });
124
+ });
125
+
126
+ test("a plugin that claims a permission replaces the builtin one and adds its Android entries", () => {
127
+ const { config } = NativeConfig.build({
128
+ appPath: "/repo/apps/portal",
129
+ target: {
130
+ ...adminTarget,
131
+ permissions: ["camera"],
132
+ plugins: undefined,
133
+ ios: undefined,
134
+ android: undefined,
135
+ icon: undefined,
136
+ splash: undefined,
137
+ },
138
+ webDir: "/web",
139
+ contributions: [
140
+ {
141
+ permission: "camera",
142
+ plugins: ["camera"],
143
+ usageDescriptions: { cameraUsageDescription: "Scan receipts with $(PRODUCT_NAME)." },
144
+ androidPermissions: ["CAMERA"],
145
+ androidFeatures: ["android.hardware.camera"],
146
+ },
147
+ { permission: "push", plugins: ["push"] },
148
+ ],
149
+ locales: ["en"],
150
+ platform: "android",
151
+ });
152
+
153
+ expect(config.plugins).toEqual([...NativeConfig.basePlugins, "camera"]);
154
+ expect(config.usageDescriptions).toEqual({ NSCameraUsageDescription: "Scan receipts with Portal Admin." });
155
+ expect(config.native).toEqual({
156
+ android: {
157
+ manifest: [
158
+ '<uses-permission android:name="android.permission.CAMERA" />',
159
+ '<uses-feature android:name="android.hardware.camera" android:required="false" />',
160
+ ],
161
+ },
162
+ });
163
+ });
164
+
165
+ test("updates bring their plugin and follow the binary's backend unless a channel is named", () => {
166
+ const build = (updates: AkanNativeTarget["updates"], env?: "main" | "develop") =>
167
+ NativeConfig.build({
168
+ appPath: "/repo/apps/portal",
169
+ target: { ...adminTarget, updates },
170
+ webDir: "/web",
171
+ contributions: [],
172
+ locales: ["en"],
173
+ platform: "android",
174
+ env,
175
+ }).config;
176
+ const url = "https://updates.example.com/portal";
177
+ expect(build({ url, publicKey: updatesKey }, "develop").updates).toEqual({
178
+ url,
179
+ publicKey: updatesKey,
180
+ channel: "develop",
181
+ });
182
+ expect(build({ url, publicKey: updatesKey, channel: "beta", readyTimeout: 15_000 }, "main").updates).toEqual({
183
+ url,
184
+ publicKey: updatesKey,
185
+ channel: "beta",
186
+ readyTimeout: 15_000,
187
+ });
188
+ expect(build({ url, publicKey: updatesKey }).updates).toEqual({ url, publicKey: updatesKey });
189
+ expect(build({ url, publicKey: updatesKey }, "main").plugins).toContain("updates");
190
+ expect(build(undefined, "main").plugins).not.toContain("updates");
191
+ });
192
+
193
+ test("push, privacy and the icon and splash objects reach the runtime, paths from the app folder", () => {
194
+ const { config } = NativeConfig.build({
195
+ appPath: "/repo/apps/portal",
196
+ target: {
197
+ ...adminTarget,
198
+ icon: { image: "assets/icon.png", backgroundColor: "#ffffff" },
199
+ splash: { backgroundColor: { light: "#ffffff", dark: "#000000" }, autoHide: false },
200
+ android: { push: { smallIcon: "assets/noti.png", color: "#ff5a5f", channel: { id: "chat", name: "Chat" } } },
201
+ ios: { privacy: { tracking: false, accessedApis: { DiskSpace: ["E174.1"] } } },
202
+ },
203
+ webDir: "/web",
204
+ contributions: [],
205
+ locales: ["en"],
206
+ platform: "android",
207
+ });
208
+ const abs = (relative: string) => path.resolve("/repo/apps/portal", relative);
209
+ expect(config.icon).toEqual({ image: abs("assets/icon.png"), backgroundColor: "#ffffff" });
210
+ expect(config.splash).toEqual({ backgroundColor: { light: "#ffffff", dark: "#000000" }, autoHide: false });
211
+ expect(config.push).toEqual({
212
+ android: { smallIcon: abs("assets/noti.png"), color: "#ff5a5f", channel: { id: "chat", name: "Chat" } },
213
+ });
214
+ expect(config.privacy).toEqual({ tracking: false, accessedApis: { DiskSpace: ["E174.1"] } });
215
+ });
216
+
217
+ test("an app id per platform gives each build its own", () => {
218
+ const idOn = (platform: "ios" | "android") =>
219
+ NativeConfig.build({
220
+ appPath: "/repo/apps/portal",
221
+ target: { ...adminTarget, appId: { ios: "com.puffinplanet.admin", android: "com.portal.admin" } },
222
+ webDir: "/web",
223
+ contributions: [],
224
+ locales: ["en"],
225
+ platform,
226
+ }).config.app.id;
227
+ expect([idOn("ios"), idOn("android")]).toEqual(["com.puffinplanet.admin", "com.portal.admin"]);
228
+ });
229
+
230
+ test("the runtime accepts both configs as they are", async () => {
231
+ const root = await makeTempRoot();
232
+ for (const file of ["secrets/google-services.json", "assets/chime.mp3"])
233
+ await mkdir(path.dirname(path.join(root, file)), { recursive: true }).then(() =>
234
+ writeFile(path.join(root, file), file.endsWith(".json") ? googleServicesJson : "x"),
235
+ );
236
+ for (const image of ["assets/icon.png", "assets/splash.png"])
237
+ await writeFile(path.join(root, image), Buffer.from(onePixelPng, "base64"));
238
+ await mkdir(path.join(root, "web"), { recursive: true });
239
+ await writeFile(path.join(root, "web/index.html"), "<html><head></head><body></body></html>");
240
+ const api = await NativeApi.load(repoApp);
241
+
242
+ const adminWithEverything: AkanNativeTarget = {
243
+ ...adminTarget,
244
+ updates: { url: "https://updates.example.com/portal", publicKey: updatesKey },
245
+ ios: { ...adminTarget.ios, privacy: { tracking: false } },
246
+ android: { ...adminTarget.android, push: { smallIcon: "assets/icon.png", color: "#ff5a5f" } },
247
+ };
248
+ for (const target of [minimalTarget, adminWithEverything]) {
249
+ const { config } = NativeConfig.build({
250
+ appPath: root,
251
+ env: "main",
252
+ target,
253
+ webDir: path.join(root, "web"),
254
+ contributions: [],
255
+ locales: ["en", "ko"],
256
+ platform: "android",
257
+ });
258
+ expect(api.validateConfig(config, { appDir: root })).toEqual([]);
259
+ }
260
+ });
261
+
262
+ test("ships the app's own native plugins by folder and names each by its id in the capability", async () => {
263
+ const root = await makeTempRoot();
264
+ const kiosk = await writeManifest(path.join(root, "native", "kiosk"), "kiosk");
265
+ await writeManifest(path.join(root, "vendor", "led-panel"), "led-panel");
266
+ const { config } = NativeConfig.build({
267
+ appPath: root,
268
+ target: {
269
+ ...minimalTarget,
270
+ permissions: [],
271
+ plugins: ["./vendor/led-panel"],
272
+ deepLinks: undefined,
273
+ android: undefined,
274
+ },
275
+ webDir: path.join(root, "web"),
276
+ contributions: [],
277
+ locales: ["en"],
278
+ platform: "macos",
279
+ nativePlugins: [{ id: "kiosk", dir: kiosk, owner: "apps/board" }],
280
+ });
281
+
282
+ expect(config.plugins).toEqual([...NativeConfig.basePlugins, "./vendor/led-panel", kiosk]);
283
+ expect(config.capabilities?.[0]?.permissions).toEqual(
284
+ [...NativeConfig.basePlugins, "led-panel", "kiosk"].map((id) => `${id}:default`),
285
+ );
286
+ await mkdir(path.join(root, "web"), { recursive: true });
287
+ await writeFile(path.join(root, "web/index.html"), "<html><head></head><body></body></html>");
288
+ const api = await NativeApi.load(repoApp);
289
+ expect(api.validateConfig(config, { appDir: root })).toEqual([]);
290
+ });
291
+
292
+ test("ships a native/ folder once when the target also lists it by path or by id", async () => {
293
+ const root = await makeTempRoot();
294
+ const kiosk = await writeManifest(path.join(root, "native", "kiosk"), "kiosk");
295
+ const { config } = NativeConfig.build({
296
+ appPath: root,
297
+ target: {
298
+ ...minimalTarget,
299
+ permissions: [],
300
+ plugins: ["./native/kiosk/", "kiosk", "haptics"],
301
+ deepLinks: undefined,
302
+ android: undefined,
303
+ },
304
+ webDir: path.join(root, "web"),
305
+ contributions: [],
306
+ locales: ["en"],
307
+ platform: "macos",
308
+ nativePlugins: [{ id: "kiosk", dir: kiosk, owner: "apps/board" }],
309
+ });
310
+
311
+ expect(config.plugins).toEqual([...NativeConfig.basePlugins, kiosk]);
312
+ });
313
+
314
+ test("hands an unattended app's settings on: page recovery, a kiosk window, screen capture and Android autoplay", async () => {
315
+ const root = await makeTempRoot();
316
+ const { config } = NativeConfig.build({
317
+ appPath: root,
318
+ target: {
319
+ ...minimalTarget,
320
+ permissions: [],
321
+ deepLinks: undefined,
322
+ android: { autoplay: true },
323
+ desktop: { recovery: "reload", window: { fullscreen: true, skipTaskbar: true }, screenCapture: "auto" },
324
+ },
325
+ webDir: path.join(root, "web"),
326
+ contributions: [],
327
+ locales: ["en"],
328
+ platform: "macos",
329
+ desktopBin: root,
330
+ });
331
+
332
+ expect(config.desktop).toEqual({
333
+ recovery: "reload",
334
+ window: { fullscreen: true, skipTaskbar: true },
335
+ screenCapture: "auto",
336
+ bin: root,
337
+ });
338
+ expect(config.android).toEqual({ debugAppIdSuffix: ".debug", autoplay: true });
339
+ await mkdir(path.join(root, "web"), { recursive: true });
340
+ await writeFile(path.join(root, "web/index.html"), "<html><head></head><body></body></html>");
341
+ expect((await NativeApi.load(repoApp)).validateConfig(config, { appDir: root })).toEqual([]);
342
+ });
343
+
344
+ test("a desktop app's own entitlements reach the macOS build, and the runtime accepts them", async () => {
345
+ const root = await makeTempRoot();
346
+ const entitlements = { "com.apple.security.cs.disable-library-validation": true };
347
+ const { config } = NativeConfig.build({
348
+ appPath: root,
349
+ target: {
350
+ ...minimalTarget,
351
+ permissions: [],
352
+ deepLinks: undefined,
353
+ android: undefined,
354
+ desktop: { entitlements },
355
+ },
356
+ webDir: path.join(root, "web"),
357
+ contributions: [],
358
+ locales: ["en"],
359
+ platform: "macos",
360
+ });
361
+
362
+ expect(config.native?.macos).toEqual({ entitlements });
363
+ await mkdir(path.join(root, "web"), { recursive: true });
364
+ await writeFile(path.join(root, "web/index.html"), "<html><head></head><body></body></html>");
365
+ expect((await NativeApi.load(repoApp)).validateConfig(config, { appDir: root })).toEqual([]);
366
+ });
367
+
368
+ test("an app with updates ships the updates plugin and its settings", () => {
369
+ const updates = { url: "https://releases.example.com/board", publicKey: `${"a".repeat(43)}=`, channel: "pilot" };
370
+ const { config } = NativeConfig.build({
371
+ appPath: "/repo/apps/board",
372
+ target: { ...minimalTarget, permissions: [], deepLinks: undefined, android: undefined, updates },
373
+ webDir: "/web",
374
+ contributions: [],
375
+ locales: ["en"],
376
+ platform: "macos",
377
+ });
378
+
379
+ expect(config.plugins).toEqual([...NativeConfig.basePlugins, "updates"]);
380
+ expect(config.updates).toEqual(updates);
381
+ });
382
+
383
+ test("an updates channel left unnamed is the backend env the binary is built for", () => {
384
+ const updates = { url: "https://releases.example.com/board", publicKey: `${"a".repeat(43)}=` };
385
+ const build = (env?: "debug" | "main", channel?: string) =>
386
+ NativeConfig.build({
387
+ appPath: "/repo/apps/board",
388
+ target: {
389
+ ...minimalTarget,
390
+ permissions: [],
391
+ deepLinks: undefined,
392
+ android: undefined,
393
+ updates: { ...updates, ...(channel ? { channel } : {}) },
394
+ },
395
+ webDir: "/web",
396
+ contributions: [],
397
+ locales: ["en"],
398
+ platform: "macos",
399
+ ...(env ? { env } : {}),
400
+ }).config.updates;
401
+
402
+ expect(build("debug")).toEqual({ ...updates, channel: "debug" });
403
+ expect(build("main")).toEqual({ ...updates, channel: "main" });
404
+ expect(build("debug", "pilot")).toEqual({ ...updates, channel: "pilot" });
405
+ expect(build()).toEqual(updates);
406
+ });
407
+
408
+ test("keeps a file name the runtime accepts", () => {
409
+ expect(NativeConfig.fileNameOf("minimal")).toBe("minimal");
410
+ expect(NativeConfig.fileNameOf("my app!")).toBe("my-app");
411
+ expect(NativeConfig.fileNameOf("...")).toBe("app");
412
+ });
413
+ });
414
+
415
+ const writeManifest = async (dir: string, id: string) => {
416
+ await mkdir(dir, { recursive: true });
417
+ await writeFile(
418
+ path.join(dir, "native-plugin.json"),
419
+ JSON.stringify({ id, apiVersion: 1, methods: [], events: [], web: null, desktop: null, ios: null, android: null }),
420
+ );
421
+ return dir;
422
+ };
423
+
424
+ const onePixelPng = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==";
425
+
426
+ const googleServicesJson = JSON.stringify({
427
+ project_info: { project_number: "1", project_id: "demo", storage_bucket: "demo.appspot.com" },
428
+ client: [
429
+ {
430
+ client_info: { mobilesdk_app_id: "1:1:android:1", android_client_info: { package_name: "com.minimal.dev.app" } },
431
+ api_key: [{ current_key: "demo-key" }],
432
+ },
433
+ {
434
+ client_info: { mobilesdk_app_id: "1:1:android:2", android_client_info: { package_name: "com.portal.admin" } },
435
+ api_key: [{ current_key: "demo-key" }],
436
+ },
437
+ ],
438
+ });