@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.
- package/DEV_RUNTIME_KNOBS.md +44 -26
- package/README.ko.md +21 -7
- package/README.md +17 -8
- package/abstractDoc.test.ts +6 -15
- package/abstractDoc.ts +6 -8
- package/agentsIndex.test.ts +4 -7
- package/agentsIndex.ts +6 -22
- package/akanApp/BackendImportGraph.test.ts +8 -22
- package/akanApp/BackendImportGraph.ts +1 -20
- package/akanApp/akanApp.host.test.ts +56 -3
- package/akanApp/akanApp.host.ts +218 -278
- package/akanApp/devBootLatch.test.ts +39 -0
- package/akanApp/devBootLatch.ts +39 -0
- package/akanApp/devHostPolicy.test.ts +81 -35
- package/akanApp/devHostPolicy.ts +41 -102
- package/akanConfig/akanBin.test.ts +62 -0
- package/akanConfig/akanBin.ts +78 -0
- package/akanConfig/akanConfig.test.ts +439 -141
- package/akanConfig/akanConfig.ts +359 -275
- package/akanConfig/index.ts +1 -0
- package/akanConfig/types.ts +22 -8
- package/akanContext.ts +110 -160
- package/akanMcpContract.ts +28 -73
- package/appSelectionMemory.ts +1 -4
- package/applicationBuildReporter.ts +37 -5
- package/applicationBuildRunner.test.ts +41 -5
- package/applicationBuildRunner.ts +71 -63
- package/applicationTestPreload.ts +3 -12
- package/artifact/implicitRootLayout.test.ts +32 -30
- package/artifact/implicitRootLayout.ts +58 -106
- package/artifact/routeSeedIndex.test.ts +7 -6
- package/artifact/routeSeedIndex.ts +11 -44
- package/biome.base.json +35 -2
- package/biomeBase.ts +2 -5
- package/cloud/cloudApi.test.ts +202 -0
- package/cloud/cloudApi.ts +77 -39
- package/cloud/configLock.test.ts +64 -0
- package/cloud/configLock.ts +96 -0
- package/cloud/constants.ts +26 -5
- package/cloud/globalConfig.ts +71 -32
- package/codeAgent/agent/AkanCodeServices.ts +97 -0
- package/codeAgent/agent/AkanContextFiles.ts +94 -0
- package/codeAgent/agent/CodeAgent.ts +551 -0
- package/codeAgent/agent/CodeAgentAsks.ts +66 -0
- package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
- package/codeAgent/agent/CodeAgentGate.ts +93 -0
- package/codeAgent/agent/CodeAgentProxy.ts +82 -0
- package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
- package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
- package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
- package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
- package/codeAgent/agent/CodeAgentUi.ts +47 -0
- package/codeAgent/agent/CodeMailbox.ts +148 -0
- package/codeAgent/agent/CodeSessionFork.ts +56 -0
- package/codeAgent/agent/CodeSessionIndex.ts +78 -0
- package/codeAgent/agent/SubagentPool.ts +214 -0
- package/codeAgent/agent/akanCodeModel.ts +47 -0
- package/codeAgent/agent/akanCodePaths.ts +31 -0
- package/codeAgent/agent/akanContextFiles.test.ts +114 -0
- package/codeAgent/agent/akanSystemPrompt.ts +46 -0
- package/codeAgent/agent/codeAgent.test.ts +178 -0
- package/codeAgent/agent/codeAgentContract.test.ts +523 -0
- package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
- package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
- package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
- package/codeAgent/agent/codeMailbox.test.ts +76 -0
- package/codeAgent/agent/codeSessionFork.test.ts +58 -0
- package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
- package/codeAgent/agent/consoleToStderr.ts +8 -0
- package/codeAgent/agent/jsonLines.ts +29 -0
- package/codeAgent/agent/stringArg.ts +5 -0
- package/codeAgent/feedback/DevLogFeedback.ts +82 -0
- package/codeAgent/feedback/PreviewChrome.ts +82 -0
- package/codeAgent/feedback/PreviewFeedback.ts +46 -0
- package/codeAgent/feedback/PreviewView.ts +95 -0
- package/codeAgent/feedback/TurnFeedback.ts +98 -0
- package/codeAgent/feedback/VerifyFeedback.ts +40 -0
- package/codeAgent/feedback/previewChrome.test.ts +73 -0
- package/codeAgent/index.ts +34 -0
- package/codeAgent/skills/akan-code/SKILL.md +130 -0
- package/codeAgent/skills/akan-debug/SKILL.md +42 -0
- package/codeAgent/skills/akan-deps/SKILL.md +50 -0
- package/codeAgent/skills/akan-err/SKILL.md +39 -0
- package/codeAgent/skills/akan-module/SKILL.md +45 -0
- package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
- package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
- package/codeAgent/skills/akan-service/SKILL.md +34 -0
- package/codeAgent/skills/akan-store/SKILL.md +67 -0
- package/codeAgent/skills/akan-ui/SKILL.md +63 -0
- package/codeAgent/skills/akan-validate/SKILL.md +44 -0
- package/codeAgent/tools/AkanCli.ts +50 -0
- package/codeAgent/tools/AkanCodePlugins.ts +136 -0
- package/codeAgent/tools/AkanEditScope.ts +48 -0
- package/codeAgent/tools/AkanEnvKeys.ts +50 -0
- package/codeAgent/tools/AkanToolPack.ts +92 -0
- package/codeAgent/tools/AkanVerifier.ts +48 -0
- package/codeAgent/tools/AskToolPack.ts +99 -0
- package/codeAgent/tools/MailToolPack.ts +76 -0
- package/codeAgent/tools/McpClient.ts +187 -0
- package/codeAgent/tools/McpOAuth.ts +233 -0
- package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
- package/codeAgent/tools/McpServerConfig.ts +129 -0
- package/codeAgent/tools/McpSignIn.ts +158 -0
- package/codeAgent/tools/McpTokenStore.ts +81 -0
- package/codeAgent/tools/McpToolPack.ts +199 -0
- package/codeAgent/tools/SessionToolPack.ts +91 -0
- package/codeAgent/tools/WebToolPack.ts +147 -0
- package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
- package/codeAgent/tools/askToolPack.test.ts +116 -0
- package/codeAgent/tools/mcpOAuth.test.ts +612 -0
- package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
- package/codegenLock.test.ts +4 -13
- package/codegenLock.ts +3 -17
- package/commandDecorators/argMeta.ts +1 -5
- package/commandDecorators/camelToKebabCase.ts +1 -0
- package/commandDecorators/command.ts +46 -102
- package/commandDecorators/commandBuilder.ts +30 -61
- package/commandDecorators/commandDecorators.test.ts +2 -10
- package/commandDecorators/dependencyBuilder.ts +10 -1
- package/commandDecorators/helpFormatter.ts +68 -145
- package/commandDecorators/index.ts +0 -2
- package/commandDecorators/targetMeta.ts +9 -7
- package/createTunnel.ts +7 -14
- package/csrE2e/csrE2eHarness.fixture.ts +505 -0
- package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
- package/csrE2e/csrPageStack.e2e.test.ts +252 -0
- package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
- package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
- package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
- package/csrE2e/registryVendor.e2e.test.ts +221 -0
- package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
- package/dependencyScanner.test.ts +4 -16
- package/dependencyScanner.ts +31 -80
- package/devkitUtils.test.ts +48 -20
- package/executors.linkFallback.test.ts +103 -0
- package/executors.test.ts +176 -106
- package/executors.ts +212 -498
- package/fileEditor.ts +9 -37
- package/{getDirname.test.ts → fileSys.test.ts} +5 -3
- package/fileSys.ts +22 -9
- package/formSetterScanner.ts +3 -16
- package/frontendBuild/allRoutesBuilder.ts +7 -21
- package/frontendBuild/autoImportSync.test.ts +27 -38
- package/frontendBuild/autoImportSync.ts +31 -65
- package/frontendBuild/buildRouteClient.test.ts +123 -37
- package/frontendBuild/bundleDefine.test.ts +12 -0
- package/frontendBuild/bundleDefine.ts +16 -0
- package/frontendBuild/clientBuildTypes.ts +5 -36
- package/frontendBuild/clientEntriesBundler.ts +9 -40
- package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
- package/frontendBuild/clientEntryDiscovery.ts +122 -110
- package/frontendBuild/csrArtifactBuilder.ts +9 -31
- package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
- package/frontendBuild/csrDevArtifactWriter.ts +307 -0
- package/frontendBuild/csrDevBundler.test.ts +66 -0
- package/frontendBuild/csrDevBundler.ts +95 -0
- package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
- package/frontendBuild/csrDevFactoryCheck.ts +39 -0
- package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
- package/frontendBuild/csrDevModuleCompiler.ts +422 -0
- package/frontendBuild/csrDevPatcher.test.ts +572 -0
- package/frontendBuild/csrDevPatcher.ts +548 -0
- package/frontendBuild/csrDevPaths.ts +145 -0
- package/frontendBuild/csrDevResolver.test.ts +239 -0
- package/frontendBuild/csrDevResolver.ts +363 -0
- package/frontendBuild/csrDevTypes.ts +61 -0
- package/frontendBuild/csrEntryFiles.ts +24 -0
- package/frontendBuild/cssCandidateCache.test.ts +0 -6
- package/frontendBuild/cssCandidateCache.ts +6 -41
- package/frontendBuild/cssCompiler.ts +31 -82
- package/frontendBuild/cssImportResolver.ts +6 -19
- package/frontendBuild/devChangePlanner.ts +7 -10
- package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
- package/frontendBuild/devGeneratedIndexSync.ts +1 -4
- package/frontendBuild/devRegistryBundler.ts +192 -0
- package/frontendBuild/fontOptimizer.test.ts +6 -14
- package/frontendBuild/fontOptimizer.ts +15 -72
- package/frontendBuild/fontPruner.test.ts +5 -10
- package/frontendBuild/fontPruner.ts +5 -20
- package/frontendBuild/frontendBuild.test.ts +57 -70
- package/frontendBuild/hmrChangeClassifier.ts +2 -2
- package/frontendBuild/hmrWatcher.test.ts +59 -32
- package/frontendBuild/hmrWatcher.ts +31 -74
- package/frontendBuild/index.ts +4 -0
- package/frontendBuild/pagesBundleBuilder.ts +28 -30
- package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
- package/frontendBuild/precompressArtifacts.ts +5 -18
- package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
- package/frontendBuild/registryMetadataFingerprint.ts +28 -0
- package/frontendBuild/routeClientBuilder.ts +100 -62
- package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
- package/frontendBuild/serverGraphFile.test.ts +77 -0
- package/frontendBuild/serverGraphFile.ts +93 -0
- package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
- package/frontendBuild/sourceMtimeIndex.ts +19 -130
- package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
- package/frontendBuild/ssrDevBundler.ts +85 -0
- package/frontendBuild/styleContract.ts +1 -8
- package/frontendBuild/themeValidator.ts +4 -26
- package/frontendBuild/watchRootResolver.test.ts +23 -42
- package/frontendBuild/watchRootResolver.ts +5 -27
- package/getRelatedCnsts.ts +12 -92
- package/incrementalBuilder/buildBatch.proc.ts +147 -63
- package/incrementalBuilder/buildBatchProtocol.ts +13 -38
- package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
- package/incrementalBuilder/buildBatchRunner.ts +26 -38
- package/incrementalBuilder/builderChannel.test.ts +24 -49
- package/incrementalBuilder/builderChannel.ts +6 -37
- package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
- package/incrementalBuilder/builderRequestRouter.ts +5 -24
- package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
- package/incrementalBuilder/builderWorkQueue.ts +126 -0
- package/incrementalBuilder/devWatchBatch.test.ts +21 -24
- package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
- package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
- package/incrementalBuilder/incrementalBuilder.proc.test.ts +270 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
- package/index.ts +5 -19
- package/integration/devResourceProbe.ts +50 -56
- package/integration/devStability.integration.test.ts +105 -199
- package/integration/devStabilityHarness.test.ts +13 -28
- package/integration/devStabilityHarness.ts +65 -330
- package/integration/ssrMemoryProbe.ts +41 -167
- package/libSource.test.ts +4 -15
- package/libSource.ts +19 -31
- package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
- package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
- package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
- package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
- package/lint/gritRules.test.ts +8 -24
- package/lint/no-deep-internal-import.grit +2 -2
- package/lint/no-document-cookie.grit +29 -0
- package/lint/no-redeclare-predefined-endpoint.grit +2 -1
- package/lint/no-web-only-api-outside-webkit.grit +44 -0
- package/lint/no-web-storage.grit +30 -0
- package/linter.test.ts +1 -2
- package/linter.ts +26 -116
- package/mobile/desktopBin.test.ts +182 -0
- package/mobile/desktopBin.ts +144 -0
- package/mobile/desktopServerStage.test.ts +252 -0
- package/mobile/desktopServerStage.ts +178 -0
- package/mobile/index.ts +9 -0
- package/mobile/mobileTarget.test.ts +57 -8
- package/mobile/mobileTarget.ts +45 -26
- package/mobile/nativeApi.test.ts +30 -0
- package/mobile/nativeApi.ts +40 -0
- package/mobile/nativeApp.test.ts +184 -0
- package/mobile/nativeApp.ts +454 -0
- package/mobile/nativeAppLine.test.ts +69 -0
- package/mobile/nativeAppLine.ts +71 -0
- package/mobile/nativeConfig.test.ts +438 -0
- package/mobile/nativeConfig.ts +294 -0
- package/mobile/nativePluginFolders.test.ts +81 -0
- package/mobile/nativePluginFolders.ts +60 -0
- package/mobile/nativeWebDir.test.ts +80 -0
- package/mobile/nativeWebDir.ts +66 -0
- package/mobile/placeholderAppId.test.ts +13 -0
- package/mobile/placeholderAppId.ts +16 -0
- package/mobile/usageDescriptions.test.ts +27 -0
- package/mobile/usageDescriptions.ts +24 -0
- package/package.json +11 -11
- package/packageExports.test.ts +3 -18
- package/packageExportsMap.ts +3 -19
- package/piDependencyPins.test.ts +46 -0
- package/platformTest/BunTestReport.test.ts +60 -0
- package/platformTest/BunTestReport.ts +65 -0
- package/platformTest/IdleWatchedProcess.test.ts +56 -0
- package/platformTest/IdleWatchedProcess.ts +73 -0
- package/platformTest/LinuxDockerTarget.ts +105 -0
- package/platformTest/PlatformTestRun.test.ts +64 -0
- package/platformTest/PlatformTestRun.ts +141 -0
- package/platformTest/PlatformTestTarget.ts +142 -0
- package/platformTest/TestSnapshot.test.ts +72 -0
- package/platformTest/TestSnapshot.ts +103 -0
- package/platformTest/WindowsSshTarget.ts +151 -0
- package/prompter.ts +3 -13
- package/qualityScanner.test.ts +9 -26
- package/qualityScanner.ts +200 -286
- package/recipeScanner.test.ts +7 -12
- package/recipeScanner.ts +9 -38
- package/repoIdentity.ts +2 -9
- package/routeSourceValidator.test.ts +0 -1
- package/routeSourceValidator.ts +7 -42
- package/scanInfo.ts +91 -225
- package/selectModel.ts +1 -2
- package/semver.ts +3 -5
- package/slicePlanner.test.ts +6 -24
- package/slicePlanner.ts +11 -32
- package/spinner.test.ts +3 -8
- package/spinner.ts +4 -16
- package/sshTunnel.ts +1 -6
- package/ssrScanner.test.ts +1 -6
- package/ssrScanner.ts +103 -113
- package/stripAnsi.ts +3 -0
- package/subspace.test.ts +9 -25
- package/subspace.ts +48 -115
- package/subspaceConfig.ts +3 -11
- package/testHelpers.ts +194 -0
- package/transforms/asyncDefaultExportDetector.ts +3 -11
- package/transforms/barrelAnalyzer.ts +43 -135
- package/transforms/barrelImportsPlugin.ts +38 -175
- package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
- package/transforms/externalizeFrameworkPlugin.ts +13 -87
- package/transforms/moduleSyntax.ts +32 -0
- package/transforms/rscUseClientTransform.ts +42 -20
- package/transforms/serverEnvPlugin.test.ts +103 -0
- package/transforms/serverEnvPlugin.ts +41 -0
- package/transforms/transforms.test.ts +166 -44
- package/transforms/tsconfigPackageResolver.test.ts +6 -24
- package/transforms/useClientBundlePlugin.ts +6 -23
- package/typeChecker.ts +7 -63
- package/types.ts +2 -0
- package/uploadRelease.ts +10 -25
- package/workflow/artifacts.ts +18 -0
- package/workflow/executor.test.ts +1 -12
- package/workflow/executor.ts +101 -110
- package/workflow/index.ts +0 -1
- package/workflow/moduleIndex.test.ts +4 -15
- package/workflow/moduleIndex.ts +9 -49
- package/workflow/plan.ts +18 -25
- package/workflow/render.ts +80 -96
- package/workflow/rolloutGate.test.ts +1 -1
- package/workflow/source.ts +52 -69
- package/workflow/sourceAst.ts +50 -0
- package/workspaceLayout.test.ts +18 -3
- package/workspaceLayout.ts +22 -23
- package/abstractCompactor.ts +0 -112
- package/aiEditor.test.ts +0 -60
- package/aiEditor.ts +0 -387
- package/applicationReleasePackager.ts +0 -209
- package/capacitorApp.test.ts +0 -372
- package/capacitorApp.ts +0 -1648
- package/commandDecorators/commandMeta.ts +0 -7
- package/commandDecorators/types.ts +0 -10
- package/getDirname.ts +0 -4
- package/guideline.ts +0 -19
- package/local/optimize-resource/ipcprobe/child.ts +0 -1
- package/local/optimize-resource/ipcprobe/parent.ts +0 -11
- package/workflow/primitive.ts +0 -59
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from "node:url";
|
|
|
6
6
|
import { AppExecutor, WorkspaceExecutor } from "../executors";
|
|
7
7
|
import type { PackageJson } from "../types";
|
|
8
8
|
import { AkanAppConfig, AkanLibConfig, deriveDefaultAppId } from "./akanConfig";
|
|
9
|
-
import type {
|
|
9
|
+
import type { LibConfigInput } from "./types";
|
|
10
10
|
|
|
11
11
|
const akanPackageJson = JSON.parse(
|
|
12
12
|
fs.readFileSync(path.join(path.dirname(fileURLToPath(import.meta.url)), "../../../akanjs/package.json"), "utf8"),
|
|
@@ -33,8 +33,24 @@ const baseDevEnv = {
|
|
|
33
33
|
workspaceRoot: "/workspace",
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
const loadExtAppConfig = async (tmpPrefix: string, appConfig: string, libConfig: string) => {
|
|
37
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), tmpPrefix));
|
|
38
|
+
try {
|
|
39
|
+
fs.mkdirSync(path.join(root, "apps/extapp"), { recursive: true });
|
|
40
|
+
fs.mkdirSync(path.join(root, "libs/extlib"), { recursive: true });
|
|
41
|
+
fs.writeFileSync(path.join(root, "apps/extapp/akan.config.ts"), appConfig);
|
|
42
|
+
fs.writeFileSync(path.join(root, "libs/extlib/akan.config.ts"), libConfig);
|
|
43
|
+
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "extrepo", version: "1.0.0" }));
|
|
44
|
+
fs.writeFileSync(path.join(root, ".env"), "AKAN_PUBLIC_REPO_NAME=extrepo\nAKAN_PUBLIC_SERVE_DOMAIN=ext.test\n");
|
|
45
|
+
const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot: root, repoName: "extrepo" });
|
|
46
|
+
return await AppExecutor.from(workspace, "extapp").getConfig();
|
|
47
|
+
} finally {
|
|
48
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
36
52
|
describe("AkanAppConfig", () => {
|
|
37
|
-
test("applies defaults for route domains, i18n, image,
|
|
53
|
+
test("applies defaults for route domains, i18n, image, native, and imports", () => {
|
|
38
54
|
const config = new AkanAppConfig(app, ["shared"], packageJson, {}, baseDevEnv);
|
|
39
55
|
|
|
40
56
|
expect([...config.domains].sort()).toEqual([
|
|
@@ -46,7 +62,7 @@ describe("AkanAppConfig", () => {
|
|
|
46
62
|
expect(config.i18n.defaultLocale).toBe("en");
|
|
47
63
|
expect(config.i18n.locales).toContain("en");
|
|
48
64
|
expect(config.images.formats).toEqual(["image/webp"]);
|
|
49
|
-
expect(config.
|
|
65
|
+
expect(config.native).toMatchObject({
|
|
50
66
|
appName: "portal",
|
|
51
67
|
appId: "com.akanjs.portal",
|
|
52
68
|
version: "0.0.1",
|
|
@@ -86,7 +102,7 @@ describe("AkanAppConfig", () => {
|
|
|
86
102
|
},
|
|
87
103
|
],
|
|
88
104
|
i18n: { locales: ["ko", "en"], defaultLocale: "ko" },
|
|
89
|
-
|
|
105
|
+
native: {
|
|
90
106
|
appName: "Portal App",
|
|
91
107
|
appId: "com.portal.mobile",
|
|
92
108
|
version: "1.2.3",
|
|
@@ -119,8 +135,8 @@ describe("AkanAppConfig", () => {
|
|
|
119
135
|
expect(config.i18n.defaultLocale).toBe("ko");
|
|
120
136
|
expect(config.images.qualities).toEqual([80, 90]);
|
|
121
137
|
expect(config.images.dangerouslyAllowSVG).toBe(true);
|
|
122
|
-
expect(config.
|
|
123
|
-
expect(config.
|
|
138
|
+
expect(config.native.buildNum).toBe(7);
|
|
139
|
+
expect(config.native.targets.default).toMatchObject({
|
|
124
140
|
name: "default",
|
|
125
141
|
appName: "Portal App",
|
|
126
142
|
appId: "com.portal.mobile",
|
|
@@ -156,10 +172,79 @@ describe("AkanAppConfig", () => {
|
|
|
156
172
|
expect(new AkanAppConfig(app, [], packageJson, { web: true }, baseDevEnv).web).toEqual({ ssr: true, csr: true });
|
|
157
173
|
});
|
|
158
174
|
|
|
159
|
-
test("
|
|
175
|
+
test("writes the image env from getProductionEnv, one ENV line per key in its order", () => {
|
|
176
|
+
const config = new AkanAppConfig(
|
|
177
|
+
app,
|
|
178
|
+
[],
|
|
179
|
+
packageJson,
|
|
180
|
+
{ routes: [{ basePath: "admin", domains: {} }], web: false, database: { modes: ["single", "cluster"] } },
|
|
181
|
+
baseDevEnv,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
expect(config.getProductionEnv()).toEqual({
|
|
185
|
+
PORT: "8282",
|
|
186
|
+
NODE_ENV: "production",
|
|
187
|
+
AKAN_PUBLIC_REPO_NAME: "akanjs",
|
|
188
|
+
AKAN_PUBLIC_SERVE_DOMAIN: "akanjs.com",
|
|
189
|
+
AKAN_PUBLIC_APP_NAME: "portal",
|
|
190
|
+
AKAN_PUBLIC_ENV: "debug",
|
|
191
|
+
AKAN_PUBLIC_BASE_PATHS: "admin",
|
|
192
|
+
AKAN_PUBLIC_DEFAULT_LOCALE: "en",
|
|
193
|
+
AKAN_PUBLIC_LOCALES: config.i18n.locales.join(","),
|
|
194
|
+
AKAN_PUBLIC_API_PREFIX: "/api",
|
|
195
|
+
AKAN_PUBLIC_WS_PREFIX: "/ws",
|
|
196
|
+
AKAN_PUBLIC_OPERATION_MODE: "cloud",
|
|
197
|
+
AKAN_DATABASE_MODES: "single,cluster",
|
|
198
|
+
AKAN_LOG_TO_FILE: "0",
|
|
199
|
+
AKAN_SSR: "false",
|
|
200
|
+
AKAN_CSR: "false",
|
|
201
|
+
});
|
|
202
|
+
const envBlock = Object.entries(config.getProductionEnv())
|
|
203
|
+
.map(([key, value]) => `ENV ${key}=${value}`)
|
|
204
|
+
.join("\n");
|
|
205
|
+
expect(config.dockerfile).toContain(`COPY . .\n${envBlock}\nCMD ["bun","main.js"]`);
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test("writes the Dockerfile instructions it wrote before its env came from getProductionEnv", () => {
|
|
209
|
+
//? 9adfb95c's output for this config; only the blank lines its empty interpolations left are gone since.
|
|
210
|
+
const before = [
|
|
211
|
+
"FROM oven/bun:1-slim",
|
|
212
|
+
"RUN apt-get update && apt-get upgrade -y && apt-get install -y --no-install-recommends ca-certificates tzdata && rm -rf /var/lib/apt/lists/*",
|
|
213
|
+
"RUN ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime",
|
|
214
|
+
"ARG TARGETARCH",
|
|
215
|
+
"",
|
|
216
|
+
"RUN mkdir -p /workspace",
|
|
217
|
+
"WORKDIR /workspace",
|
|
218
|
+
"COPY ./package.json ./package.json",
|
|
219
|
+
"RUN bun install --production",
|
|
220
|
+
"",
|
|
221
|
+
"COPY . .",
|
|
222
|
+
"ENV PORT=8282",
|
|
223
|
+
"ENV NODE_ENV=production",
|
|
224
|
+
"ENV AKAN_PUBLIC_REPO_NAME=akanjs",
|
|
225
|
+
"ENV AKAN_PUBLIC_SERVE_DOMAIN=akanjs.com",
|
|
226
|
+
"ENV AKAN_PUBLIC_APP_NAME=portal",
|
|
227
|
+
"ENV AKAN_PUBLIC_ENV=debug",
|
|
228
|
+
"",
|
|
229
|
+
"ENV AKAN_PUBLIC_DEFAULT_LOCALE=en",
|
|
230
|
+
"ENV AKAN_PUBLIC_LOCALES=en,ko",
|
|
231
|
+
"ENV AKAN_PUBLIC_API_PREFIX=/api",
|
|
232
|
+
"ENV AKAN_PUBLIC_WS_PREFIX=/ws",
|
|
233
|
+
"ENV AKAN_PUBLIC_OPERATION_MODE=cloud",
|
|
234
|
+
"ENV AKAN_DATABASE_MODES=single",
|
|
235
|
+
"ENV AKAN_LOG_TO_FILE=0",
|
|
236
|
+
"",
|
|
237
|
+
'CMD ["bun","main.js"]',
|
|
238
|
+
];
|
|
239
|
+
const lines = (text: string) => text.split("\n").filter((line) => line !== "");
|
|
240
|
+
|
|
241
|
+
expect(lines(new AkanAppConfig(app, [], packageJson, {}, baseDevEnv).dockerfile)).toEqual(lines(before.join("\n")));
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("refuses a csr-less build that ships a native app", () => {
|
|
160
245
|
expect(
|
|
161
|
-
() => new AkanAppConfig(app, [], packageJson, { web: { csr: false },
|
|
162
|
-
).toThrow("the
|
|
246
|
+
() => new AkanAppConfig(app, [], packageJson, { web: { csr: false }, native: { appName: "portal" } }, baseDevEnv),
|
|
247
|
+
).toThrow("the native apps ship that bundle");
|
|
163
248
|
});
|
|
164
249
|
|
|
165
250
|
test("installs only ca-certificates and tzdata in the default image", () => {
|
|
@@ -249,31 +334,65 @@ describe("AkanAppConfig", () => {
|
|
|
249
334
|
...akanPackageJson.dependencies,
|
|
250
335
|
...akanPackageJson.peerDependencies,
|
|
251
336
|
};
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
expect(multipleConfig
|
|
263
|
-
"@libsql/client": runtimeDependencies["@libsql/client"],
|
|
337
|
+
const configOf = (database: { modes: ("single" | "multiple" | "cluster")[] }) =>
|
|
338
|
+
new AkanAppConfig(app, [], packageJson, { database }, baseDevEnv);
|
|
339
|
+
const dependenciesOf = (config: AkanAppConfig) => config.getProductionPackageJson().dependencies ?? {};
|
|
340
|
+
const singleConfig = configOf({ modes: ["single"] });
|
|
341
|
+
const multipleConfig = configOf({ modes: ["multiple"] });
|
|
342
|
+
const clusterConfig = configOf({ modes: ["cluster"] });
|
|
343
|
+
const edgeAndCloud = configOf({ modes: ["single", "cluster"] });
|
|
344
|
+
|
|
345
|
+
for (const driver of ["ioredis", "bullmq", "postgres", "@libsql/client", "protobufjs"])
|
|
346
|
+
expect(dependenciesOf(singleConfig)).not.toHaveProperty(driver);
|
|
347
|
+
expect(dependenciesOf(multipleConfig)).toMatchObject({
|
|
264
348
|
bullmq: runtimeDependencies.bullmq,
|
|
265
349
|
ioredis: runtimeDependencies.ioredis,
|
|
266
|
-
protobufjs: runtimeDependencies.protobufjs,
|
|
267
350
|
});
|
|
268
|
-
expect(
|
|
269
|
-
|
|
270
|
-
expect(clusterConfig.getProductionPackageJson().dependencies).toMatchObject({
|
|
351
|
+
expect(dependenciesOf(clusterConfig)).toMatchObject({
|
|
271
352
|
bullmq: runtimeDependencies.bullmq,
|
|
272
353
|
ioredis: runtimeDependencies.ioredis,
|
|
273
354
|
postgres: runtimeDependencies.postgres,
|
|
274
|
-
protobufjs: runtimeDependencies.protobufjs,
|
|
275
355
|
});
|
|
276
|
-
|
|
356
|
+
// multiple opens the same SQLite as single and Redis replaced protobuf on the wire, so neither ships by default.
|
|
357
|
+
for (const config of [multipleConfig, clusterConfig])
|
|
358
|
+
for (const driver of ["@libsql/client", "protobufjs"]) expect(dependenciesOf(config)).not.toHaveProperty(driver);
|
|
359
|
+
expect(dependenciesOf(multipleConfig)).not.toHaveProperty("postgres");
|
|
360
|
+
// One image for an edge site and a cloud cluster carries both modes' drivers, and says which it carries.
|
|
361
|
+
expect(dependenciesOf(edgeAndCloud)).toMatchObject({ postgres: runtimeDependencies.postgres });
|
|
362
|
+
expect(edgeAndCloud.dockerfile).toContain("ENV AKAN_DATABASE_MODES=single,cluster");
|
|
363
|
+
expect(singleConfig.dockerfile).toContain("ENV AKAN_DATABASE_MODES=single");
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test("runs single when the app declares no mode", () => {
|
|
367
|
+
expect(new AkanAppConfig(app, [], packageJson, {}, baseDevEnv).database.modes).toEqual(["single"]);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
test("refuses a mode name that is not one of the three, naming the file", () => {
|
|
371
|
+
expect(
|
|
372
|
+
() => new AkanAppConfig(app, [], packageJson, { database: { modes: ["clsuter" as "cluster"] } }, baseDevEnv),
|
|
373
|
+
).toThrow("database.modes in apps/portal/akan.config.ts");
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("resolves a command's mode within the declared ones", () => {
|
|
377
|
+
const previous = process.env.AKAN_DATABASE_MODE;
|
|
378
|
+
try {
|
|
379
|
+
const config = new AkanAppConfig(
|
|
380
|
+
app,
|
|
381
|
+
[],
|
|
382
|
+
packageJson,
|
|
383
|
+
{ database: { modes: ["single", "cluster"] } },
|
|
384
|
+
baseDevEnv,
|
|
385
|
+
);
|
|
386
|
+
delete process.env.AKAN_DATABASE_MODE;
|
|
387
|
+
expect(config.resolveDatabaseMode()).toBe("single");
|
|
388
|
+
process.env.AKAN_DATABASE_MODE = "cluster";
|
|
389
|
+
expect(config.resolveDatabaseMode()).toBe("cluster");
|
|
390
|
+
process.env.AKAN_DATABASE_MODE = "multiple";
|
|
391
|
+
expect(() => config.resolveDatabaseMode()).toThrow('Add "multiple" to database.modes');
|
|
392
|
+
} finally {
|
|
393
|
+
if (previous === undefined) delete process.env.AKAN_DATABASE_MODE;
|
|
394
|
+
else process.env.AKAN_DATABASE_MODE = previous;
|
|
395
|
+
}
|
|
277
396
|
});
|
|
278
397
|
|
|
279
398
|
test("resolves database mode runtime packages and missing install specs", () => {
|
|
@@ -300,69 +419,22 @@ describe("AkanAppConfig", () => {
|
|
|
300
419
|
);
|
|
301
420
|
|
|
302
421
|
expect(config.getDatabaseModeRuntimePackages("single")).toEqual([]);
|
|
303
|
-
expect(config.getDatabaseModeRuntimePackages("multiple")).toEqual([
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
"ioredis",
|
|
307
|
-
"protobufjs",
|
|
308
|
-
]);
|
|
309
|
-
expect(config.getDatabaseModeRuntimePackages("cluster")).toEqual(["bullmq", "ioredis", "postgres", "protobufjs"]);
|
|
310
|
-
expect(config.getMissingDatabaseModeDependencySpecs("multiple")).toEqual([
|
|
311
|
-
`@libsql/client@${runtimeDependencies["@libsql/client"]}`,
|
|
312
|
-
`protobufjs@${runtimeDependencies.protobufjs}`,
|
|
313
|
-
]);
|
|
422
|
+
expect(config.getDatabaseModeRuntimePackages("multiple")).toEqual(["bullmq", "ioredis"]);
|
|
423
|
+
expect(config.getDatabaseModeRuntimePackages("cluster")).toEqual(["bullmq", "ioredis", "postgres"]);
|
|
424
|
+
expect(config.getMissingDatabaseModeDependencySpecs("multiple")).toEqual([]);
|
|
314
425
|
expect(config.getMissingDatabaseModeDependencySpecs("cluster")).toEqual([
|
|
315
426
|
`postgres@${runtimeDependencies.postgres}`,
|
|
316
|
-
`protobufjs@${runtimeDependencies.protobufjs}`,
|
|
317
|
-
]);
|
|
318
|
-
// The workspace-root install covers the toolchain/runtime plus every app Capacitor plugin
|
|
319
|
-
// (deduped — "@capacitor/core" appears in both source lists).
|
|
320
|
-
const expectedMobilePackages: string[] = [
|
|
321
|
-
"@capacitor/cli",
|
|
322
|
-
"@capacitor/core",
|
|
323
|
-
"@capacitor/ios",
|
|
324
|
-
"@capacitor/android",
|
|
325
|
-
"@capacitor/assets",
|
|
326
|
-
"@capacitor/app",
|
|
327
|
-
"@capacitor/browser",
|
|
328
|
-
"@capacitor/camera",
|
|
329
|
-
"@capacitor/device",
|
|
330
|
-
"@capacitor/geolocation",
|
|
331
|
-
"@capacitor/haptics",
|
|
332
|
-
"@capacitor/inappbrowser",
|
|
333
|
-
"@capacitor/keyboard",
|
|
334
|
-
"@capacitor/preferences",
|
|
335
|
-
"@capacitor/push-notifications",
|
|
336
|
-
"capacitor-plugin-safe-area",
|
|
337
|
-
];
|
|
338
|
-
expect(config.getMobileRuntimePackages() as string[]).toEqual(expectedMobilePackages);
|
|
339
|
-
expect(config.getMissingMobileDependencySpecs()).toEqual(
|
|
340
|
-
expectedMobilePackages.map((lib) => `${lib}@${runtimeDependencies[lib as keyof typeof runtimeDependencies]}`),
|
|
341
|
-
);
|
|
342
|
-
expect(config.getMobileAppCapacitorPlugins()).toEqual([
|
|
343
|
-
"@capacitor/app",
|
|
344
|
-
"@capacitor/browser",
|
|
345
|
-
"@capacitor/camera",
|
|
346
|
-
"@capacitor/core",
|
|
347
|
-
"@capacitor/device",
|
|
348
|
-
"@capacitor/geolocation",
|
|
349
|
-
"@capacitor/haptics",
|
|
350
|
-
"@capacitor/inappbrowser",
|
|
351
|
-
"@capacitor/keyboard",
|
|
352
|
-
"@capacitor/preferences",
|
|
353
|
-
"@capacitor/push-notifications",
|
|
354
|
-
"capacitor-plugin-safe-area",
|
|
355
427
|
]);
|
|
356
428
|
});
|
|
357
429
|
|
|
358
|
-
test("normalizes
|
|
430
|
+
test("normalizes the native targets and validates base paths", () => {
|
|
359
431
|
const config = new AkanAppConfig(
|
|
360
432
|
app,
|
|
361
433
|
[],
|
|
362
434
|
packageJson,
|
|
363
435
|
{
|
|
364
436
|
routes: [{ basePath: "admin", domains: {} }],
|
|
365
|
-
|
|
437
|
+
native: {
|
|
366
438
|
appName: "Portal",
|
|
367
439
|
appId: "com.portal.app",
|
|
368
440
|
version: "1.0.0",
|
|
@@ -375,12 +447,9 @@ describe("AkanAppConfig", () => {
|
|
|
375
447
|
appId: "com.portal.admin",
|
|
376
448
|
buildNum: 8,
|
|
377
449
|
permissions: ["camera"],
|
|
378
|
-
deepLinks: {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
ios: { teamId: " TEAMID " },
|
|
382
|
-
android: { sha256CertFingerprints: ["AA:BB", "AA:BB"] },
|
|
383
|
-
},
|
|
450
|
+
deepLinks: { schemes: ["portal-admin", "portal-admin"], domains: ["https://Portal.Admin/"] },
|
|
451
|
+
ios: { teamId: " TEAMID " },
|
|
452
|
+
android: { sha256CertFingerprints: ["AA:BB", "AA:BB"] },
|
|
384
453
|
},
|
|
385
454
|
},
|
|
386
455
|
},
|
|
@@ -388,7 +457,7 @@ describe("AkanAppConfig", () => {
|
|
|
388
457
|
baseDevEnv,
|
|
389
458
|
);
|
|
390
459
|
|
|
391
|
-
expect(config.
|
|
460
|
+
expect(config.native.targets.admin).toMatchObject({
|
|
392
461
|
name: "admin",
|
|
393
462
|
basePath: "admin",
|
|
394
463
|
indexPath: "/admin/home",
|
|
@@ -397,38 +466,228 @@ describe("AkanAppConfig", () => {
|
|
|
397
466
|
version: "1.0.0",
|
|
398
467
|
buildNum: 8,
|
|
399
468
|
permissions: ["camera"],
|
|
400
|
-
deepLinks: {
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
469
|
+
deepLinks: { schemes: ["portal-admin"], domains: ["portal.admin"] },
|
|
470
|
+
ios: { teamId: "TEAMID" },
|
|
471
|
+
android: { sha256CertFingerprints: ["AA:BB"] },
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
expect(
|
|
475
|
+
() =>
|
|
476
|
+
new AkanAppConfig(app, [], packageJson, { native: { targets: { bad: { basePath: "missing" } } } }, baseDevEnv),
|
|
477
|
+
).toThrow("unknown basePath");
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
test("a desktop server is true or { omit }, its names trimmed, deduplicated and sorted", () => {
|
|
481
|
+
const resolve = (server: unknown) =>
|
|
482
|
+
new AkanAppConfig(app, [], packageJson, { native: { desktop: { server } } } as never, baseDevEnv).native.targets
|
|
483
|
+
.default.desktop?.server;
|
|
484
|
+
|
|
485
|
+
expect(resolve(true)).toBe(true);
|
|
486
|
+
expect(resolve({ omit: [" rclnodejs", "protobufjs", "rclnodejs"] })).toEqual({ omit: ["protobufjs", "rclnodejs"] });
|
|
487
|
+
expect(resolve({})).toEqual({ omit: [] });
|
|
488
|
+
expect(() => resolve({ omit: "rclnodejs" })).toThrow("native.desktop.server.omit in apps/");
|
|
489
|
+
expect(() => resolve({ exclude: ["rclnodejs"] })).toThrow("native.desktop.server.exclude in apps/");
|
|
490
|
+
});
|
|
491
|
+
|
|
492
|
+
test("an app without basePaths has one target, default, with no basePath and the section's settings", () => {
|
|
493
|
+
const config = new AkanAppConfig(
|
|
494
|
+
app,
|
|
495
|
+
[],
|
|
496
|
+
packageJson,
|
|
497
|
+
{ native: { indexPath: "/explore", desktop: { server: true } } },
|
|
498
|
+
baseDevEnv,
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
expect(Object.keys(config.native.targets)).toEqual(["default"]);
|
|
502
|
+
expect(config.native.targets.default.basePath).toBeUndefined();
|
|
503
|
+
expect(config.native.targets.default).toMatchObject({ indexPath: "/explore", desktop: { server: true } });
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
test("a platform section may name its own indexPath, normalized like the section's", () => {
|
|
507
|
+
const config = new AkanAppConfig(
|
|
508
|
+
app,
|
|
509
|
+
[],
|
|
510
|
+
packageJson,
|
|
511
|
+
{
|
|
512
|
+
native: {
|
|
513
|
+
indexPath: "/mobile",
|
|
514
|
+
ios: { indexPath: "cockpit/" },
|
|
515
|
+
desktop: { indexPath: " / ", server: true },
|
|
516
|
+
},
|
|
405
517
|
},
|
|
518
|
+
baseDevEnv,
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
expect(config.native.targets.default).toMatchObject({
|
|
522
|
+
indexPath: "/mobile",
|
|
523
|
+
ios: { indexPath: "/cockpit" },
|
|
524
|
+
desktop: { indexPath: "/", server: true },
|
|
406
525
|
});
|
|
526
|
+
expect(config.native.targets.default.android).toBeUndefined();
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
test("a target takes the native section with its own fields over it: objects merge, lists and values replace", () => {
|
|
530
|
+
const config = new AkanAppConfig(
|
|
531
|
+
app,
|
|
532
|
+
[],
|
|
533
|
+
packageJson,
|
|
534
|
+
{
|
|
535
|
+
native: {
|
|
536
|
+
fileName: "portal",
|
|
537
|
+
plugins: ["iap"],
|
|
538
|
+
ios: { infoPlist: { ITSAppUsesNonExemptEncryption: false }, files: { "sound.caf": "assets/sound.caf" } },
|
|
539
|
+
android: {
|
|
540
|
+
manifest: ["<queries/>"],
|
|
541
|
+
googleServices: "secrets/google-services.json",
|
|
542
|
+
files: { "res/raw/chime.mp3": "assets/chime.mp3" },
|
|
543
|
+
},
|
|
544
|
+
targets: {
|
|
545
|
+
default: {
|
|
546
|
+
plugins: ["share"],
|
|
547
|
+
ios: { files: { "extra.caf": "assets/extra.caf" } },
|
|
548
|
+
android: { manifest: ["<uses-feature/>"] },
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
baseDevEnv,
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
expect(config.native.targets.default).toMatchObject({
|
|
557
|
+
fileName: "portal",
|
|
558
|
+
plugins: ["share"],
|
|
559
|
+
ios: {
|
|
560
|
+
infoPlist: { ITSAppUsesNonExemptEncryption: false },
|
|
561
|
+
files: { "sound.caf": "assets/sound.caf", "extra.caf": "assets/extra.caf" },
|
|
562
|
+
},
|
|
563
|
+
android: {
|
|
564
|
+
manifest: ["<uses-feature/>"],
|
|
565
|
+
googleServices: "secrets/google-services.json",
|
|
566
|
+
files: { "res/raw/chime.mp3": "assets/chime.mp3" },
|
|
567
|
+
},
|
|
568
|
+
});
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
test("gives every target the section's updates, a target overriding a field", () => {
|
|
572
|
+
const updates = { url: "https://releases.example.com/portal", publicKey: "key=" };
|
|
573
|
+
const config = new AkanAppConfig(
|
|
574
|
+
app,
|
|
575
|
+
[],
|
|
576
|
+
packageJson,
|
|
577
|
+
{ native: { updates, targets: { default: {}, pilot: { updates: { channel: "pilot" } } } } },
|
|
578
|
+
baseDevEnv,
|
|
579
|
+
);
|
|
407
580
|
|
|
581
|
+
expect(config.native.targets.default.updates).toEqual(updates);
|
|
582
|
+
expect(config.native.targets.pilot.updates).toEqual({ ...updates, channel: "pilot" });
|
|
408
583
|
expect(
|
|
409
584
|
() =>
|
|
410
585
|
new AkanAppConfig(
|
|
411
586
|
app,
|
|
412
587
|
[],
|
|
413
588
|
packageJson,
|
|
414
|
-
{
|
|
415
|
-
mobile: { targets: { bad: { basePath: "missing" } } },
|
|
416
|
-
},
|
|
589
|
+
{ native: { targets: { pilot: { updates: { channel: "pilot" } } } } },
|
|
417
590
|
baseDevEnv,
|
|
418
591
|
),
|
|
419
|
-
).toThrow("
|
|
592
|
+
).toThrow("native.targets.pilot.updates in apps/portal/akan.config.ts has no url or publicKey");
|
|
593
|
+
expect(
|
|
594
|
+
() => new AkanAppConfig(app, [], packageJson, { native: { updates: { channel: "pilot" } } }, baseDevEnv),
|
|
595
|
+
).toThrow("native.updates in apps/portal/akan.config.ts has no url or publicKey.");
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
test("merges the desktop settings field by field, the target winning", () => {
|
|
599
|
+
const config = new AkanAppConfig(
|
|
600
|
+
app,
|
|
601
|
+
[],
|
|
602
|
+
packageJson,
|
|
603
|
+
{
|
|
604
|
+
native: {
|
|
605
|
+
desktop: { recovery: "reload", window: { fullscreen: true } },
|
|
606
|
+
targets: { default: { desktop: { window: { skipTaskbar: true } } } },
|
|
607
|
+
},
|
|
608
|
+
},
|
|
609
|
+
baseDevEnv,
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
expect(config.native.targets.default.desktop).toEqual({
|
|
613
|
+
recovery: "reload",
|
|
614
|
+
window: { fullscreen: true, skipTaskbar: true },
|
|
615
|
+
});
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
test("merges the section's push and privacy into each target field by field; an icon object is one value", () => {
|
|
619
|
+
const publicKey = Buffer.alloc(32, 7).toString("base64");
|
|
620
|
+
const config = new AkanAppConfig(
|
|
621
|
+
app,
|
|
622
|
+
[],
|
|
623
|
+
packageJson,
|
|
624
|
+
{
|
|
625
|
+
native: {
|
|
626
|
+
updates: { url: "https://updates.example.com/portal", publicKey },
|
|
627
|
+
icon: { image: "assets/icon.png", backgroundColor: "#000000" },
|
|
628
|
+
android: { push: { color: "#ff5a5f" } },
|
|
629
|
+
ios: { privacy: { tracking: false } },
|
|
630
|
+
targets: {
|
|
631
|
+
default: {},
|
|
632
|
+
beta: {
|
|
633
|
+
updates: { channel: "beta" },
|
|
634
|
+
icon: { image: "assets/beta.png" },
|
|
635
|
+
android: { push: { smallIcon: "assets/noti.png" } },
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
},
|
|
640
|
+
baseDevEnv,
|
|
641
|
+
);
|
|
642
|
+
|
|
643
|
+
expect(config.native.targets.default.updates).toEqual({ url: "https://updates.example.com/portal", publicKey });
|
|
644
|
+
expect(config.native.targets.beta).toMatchObject({
|
|
645
|
+
updates: { url: "https://updates.example.com/portal", publicKey, channel: "beta" },
|
|
646
|
+
android: { push: { color: "#ff5a5f", smallIcon: "assets/noti.png" } },
|
|
647
|
+
ios: { privacy: { tracking: false } },
|
|
648
|
+
});
|
|
649
|
+
expect(config.native.targets.beta.icon).toEqual({ image: "assets/beta.png" });
|
|
420
650
|
});
|
|
421
651
|
|
|
422
|
-
test("
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
expect(withoutMobile.mobile.appId).toBe("com.akanjs.portal");
|
|
426
|
-
expect(withoutMobile.hasMobileConfig).toBe(false);
|
|
652
|
+
test("refuses `mobile` and every setting that moved, naming where it went", () => {
|
|
653
|
+
const make = (config: Record<string, unknown>) => () =>
|
|
654
|
+
new AkanAppConfig(app, [], packageJson, config as never, baseDevEnv);
|
|
427
655
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
656
|
+
expect(make({ mobile: { appName: "portal" } })).toThrow("declares `mobile`, which is now `native`");
|
|
657
|
+
expect(make({ native: { assets: { icon: "assets/icon.png" } } })).toThrow(
|
|
658
|
+
"native.assets in apps/portal/akan.config.ts has moved: icon and splash sit directly in the native section.",
|
|
659
|
+
);
|
|
660
|
+
expect(make({ native: { targets: { default: { native: { desktop: { server: true } } } } } })).toThrow(
|
|
661
|
+
"native.targets.default.native in apps/portal/akan.config.ts has moved",
|
|
662
|
+
);
|
|
663
|
+
expect(make({ native: { files: { "ios/sound.caf": "assets/sound.caf" } } })).toThrow(
|
|
664
|
+
"native.files in apps/portal/akan.config.ts has moved: ios.files",
|
|
665
|
+
);
|
|
666
|
+
expect(make({ native: { deepLinks: { ios: { teamId: "TEAMID" } } } })).toThrow(
|
|
667
|
+
"native.deepLinks.ios in apps/portal/akan.config.ts has moved: ios.teamId.",
|
|
668
|
+
);
|
|
669
|
+
expect(make({ native: { android: { files: { "App/x.json": "x.json" } } } })).toThrow(
|
|
670
|
+
'native.android.files["App/x.json"] in apps/portal/akan.config.ts must land at res/<type>/<file> or assets/<path>.',
|
|
671
|
+
);
|
|
672
|
+
expect(make({ native: { targets: { kiosk: { ios: { files: { "../x.caf": "x.caf" } } } } } })).toThrow(
|
|
673
|
+
'native.targets.kiosk.ios.files["../x.caf"] in apps/portal/akan.config.ts must land at <path in the app bundle>.',
|
|
674
|
+
);
|
|
675
|
+
expect(make({ native: { ios: { scheme: "App" } } })).toThrow(
|
|
676
|
+
"native.ios.scheme in apps/portal/akan.config.ts is not a native setting",
|
|
677
|
+
);
|
|
678
|
+
expect(make({ native: { server: { url: "http://x" } } })).toThrow(
|
|
679
|
+
"native.server in apps/portal/akan.config.ts is not a native setting",
|
|
680
|
+
);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
test("derives a repo-scoped default appId and records an explicit native section", () => {
|
|
684
|
+
const withoutNative = new AkanAppConfig(app, [], packageJson, {}, baseDevEnv);
|
|
685
|
+
expect(withoutNative.native.appId).toBe("com.akanjs.portal");
|
|
686
|
+
expect(withoutNative.hasNativeConfig).toBe(false);
|
|
687
|
+
|
|
688
|
+
const withNative = new AkanAppConfig(app, [], packageJson, { native: { version: "2.0.0" } }, baseDevEnv);
|
|
689
|
+
expect(withNative.native.appId).toBe("com.akanjs.portal");
|
|
690
|
+
expect(withNative.hasNativeConfig).toBe(true);
|
|
432
691
|
});
|
|
433
692
|
});
|
|
434
693
|
|
|
@@ -466,25 +725,13 @@ describe("AkanAppConfig lib externalLibs", () => {
|
|
|
466
725
|
});
|
|
467
726
|
|
|
468
727
|
test("reads them off every workspace lib config on load", async () => {
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
fs.writeFileSync(
|
|
475
|
-
path.join(root, "libs/extlib/akan.config.ts"),
|
|
476
|
-
"export default { externalLibs: ['puppeteer'] };\n",
|
|
477
|
-
);
|
|
478
|
-
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "extrepo", version: "1.0.0" }));
|
|
479
|
-
fs.writeFileSync(path.join(root, ".env"), "AKAN_PUBLIC_REPO_NAME=extrepo\nAKAN_PUBLIC_SERVE_DOMAIN=ext.test\n");
|
|
480
|
-
|
|
481
|
-
const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot: root, repoName: "extrepo" });
|
|
482
|
-
const config = await AppExecutor.from(workspace, "extapp").getConfig();
|
|
728
|
+
const config = await loadExtAppConfig(
|
|
729
|
+
"akan-config-libext-",
|
|
730
|
+
"export default { externalLibs: ['shiki'] };\n",
|
|
731
|
+
"export default { externalLibs: ['puppeteer'] };\n",
|
|
732
|
+
);
|
|
483
733
|
|
|
484
|
-
|
|
485
|
-
} finally {
|
|
486
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
487
|
-
}
|
|
734
|
+
expect(config.externalLibs).toEqual(["shiki", "puppeteer"]);
|
|
488
735
|
});
|
|
489
736
|
});
|
|
490
737
|
|
|
@@ -528,26 +775,77 @@ describe("AkanAppConfig lib docker runs", () => {
|
|
|
528
775
|
});
|
|
529
776
|
|
|
530
777
|
test("reads them off every workspace lib config on load", async () => {
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
fs.writeFileSync(
|
|
537
|
-
path.join(root, "libs/extlib/akan.config.ts"),
|
|
538
|
-
"export default { docker: { preRuns: ['echo from-lib'], postRuns: [{ arm64: 'echo arm-only' }] } };\n",
|
|
539
|
-
);
|
|
540
|
-
fs.writeFileSync(path.join(root, "package.json"), JSON.stringify({ name: "extrepo", version: "1.0.0" }));
|
|
541
|
-
fs.writeFileSync(path.join(root, ".env"), "AKAN_PUBLIC_REPO_NAME=extrepo\nAKAN_PUBLIC_SERVE_DOMAIN=ext.test\n");
|
|
778
|
+
const config = await loadExtAppConfig(
|
|
779
|
+
"akan-config-libdocker-",
|
|
780
|
+
"export default {};\n",
|
|
781
|
+
"export default { docker: { preRuns: ['echo from-lib'], postRuns: [{ arm64: 'echo arm-only' }] } };\n",
|
|
782
|
+
);
|
|
542
783
|
|
|
543
|
-
|
|
544
|
-
|
|
784
|
+
expect(config.dockerfile).toContain("RUN echo from-lib");
|
|
785
|
+
expect(config.dockerfile).toContain('RUN if [ "$TARGETARCH" = "arm64"');
|
|
786
|
+
});
|
|
787
|
+
});
|
|
545
788
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
789
|
+
describe("AkanAppConfig trustedDependencies and bin", () => {
|
|
790
|
+
const sha256 = "b".repeat(64);
|
|
791
|
+
const libBin = { ffmpeg: { "linux-x64": { url: "https://files.test/ffmpeg.tar.xz", sha256, file: "bin/ffmpeg" } } };
|
|
792
|
+
|
|
793
|
+
test("the production package.json trusts the app's and its libs' packages, and nothing when none are named", () => {
|
|
794
|
+
const withTrusted = new AkanAppConfig(
|
|
795
|
+
app,
|
|
796
|
+
[],
|
|
797
|
+
packageJson,
|
|
798
|
+
{ trustedDependencies: [" rclnodejs ", "sharp"] },
|
|
799
|
+
baseDevEnv,
|
|
800
|
+
[],
|
|
801
|
+
{
|
|
802
|
+
externalLibs: [],
|
|
803
|
+
trustedDependencies: ["sharp", "@serialport/bindings-cpp"],
|
|
804
|
+
docker: { preRuns: [], postRuns: [] },
|
|
805
|
+
},
|
|
806
|
+
);
|
|
807
|
+
expect(withTrusted.getProductionPackageJson().trustedDependencies).toEqual([
|
|
808
|
+
"rclnodejs",
|
|
809
|
+
"sharp",
|
|
810
|
+
"@serialport/bindings-cpp",
|
|
811
|
+
]);
|
|
812
|
+
expect(new AkanAppConfig(app, [], packageJson, {}, baseDevEnv).getProductionPackageJson()).not.toHaveProperty(
|
|
813
|
+
"trustedDependencies",
|
|
814
|
+
);
|
|
815
|
+
expect(() => new AkanAppConfig(app, [], packageJson, { trustedDependencies: [""] }, baseDevEnv)).toThrow(
|
|
816
|
+
"apps/portal/akan.config.ts: trustedDependencies lists package names",
|
|
817
|
+
);
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
test("keeps the app's bin apart from each lib's, with paths made absolute where they were declared", () => {
|
|
821
|
+
const config = new AkanAppConfig(
|
|
822
|
+
{ name: "portal", cwdPath: "/repo/apps/portal" } as never,
|
|
823
|
+
[],
|
|
824
|
+
packageJson,
|
|
825
|
+
{ bin: { ffmpeg: { "darwin-arm64": { path: "tools/ffmpeg" } } } },
|
|
826
|
+
baseDevEnv,
|
|
827
|
+
[],
|
|
828
|
+
{ externalLibs: [], docker: { preRuns: [], postRuns: [] }, bin: [{ lib: "media", bin: libBin }] },
|
|
829
|
+
);
|
|
830
|
+
expect(config.bin).toEqual({
|
|
831
|
+
ffmpeg: { "darwin-arm64": { path: path.resolve("/repo/apps/portal/tools/ffmpeg") } },
|
|
832
|
+
});
|
|
833
|
+
expect(config.libBins).toEqual([{ lib: "media", bin: libBin }]);
|
|
834
|
+
expect(
|
|
835
|
+
new AkanLibConfig({ name: "media", cwdPath: "/repo/libs/media" } as never, {
|
|
836
|
+
bin: { ffprobe: { "linux-x64": { path: "../../tools/ffprobe" } } },
|
|
837
|
+
}).bin,
|
|
838
|
+
).toEqual({ ffprobe: { "linux-x64": { path: path.resolve("/repo/tools/ffprobe") } } });
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
test("reads them off every workspace lib config on load", async () => {
|
|
842
|
+
const config = await loadExtAppConfig(
|
|
843
|
+
"akan-config-libbin-",
|
|
844
|
+
"export default { trustedDependencies: ['sharp'] };\n",
|
|
845
|
+
`export default { trustedDependencies: ['rclnodejs'], bin: ${JSON.stringify(libBin)} };\n`,
|
|
846
|
+
);
|
|
847
|
+
expect(config.trustedDependencies).toEqual(["sharp", "rclnodejs"]);
|
|
848
|
+
expect(config.libBins).toEqual([{ lib: "extlib", bin: libBin }]);
|
|
551
849
|
});
|
|
552
850
|
});
|
|
553
851
|
|
|
@@ -556,7 +854,7 @@ describe("AkanLibConfig", () => {
|
|
|
556
854
|
const lib = { name: "shared" } as never;
|
|
557
855
|
expect(new AkanLibConfig(lib, {}).externalLibs).toEqual([]);
|
|
558
856
|
|
|
559
|
-
const config:
|
|
857
|
+
const config: LibConfigInput = {
|
|
560
858
|
externalLibs: ["firebase-admin"],
|
|
561
859
|
};
|
|
562
860
|
expect(new AkanLibConfig(lib, config).externalLibs).toEqual(["firebase-admin"]);
|