@akanjs/devkit 3.0.0-beta.3 → 3.0.0-beta.30
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 +2 -2
- package/README.md +3 -2
- 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 +404 -141
- package/akanConfig/akanConfig.ts +317 -274
- package/akanConfig/index.ts +1 -0
- package/akanConfig/types.ts +21 -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 +269 -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 +177 -0
- package/mobile/desktopBin.ts +143 -0
- package/mobile/desktopServerStage.test.ts +204 -0
- package/mobile/desktopServerStage.ts +138 -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 +151 -0
- package/mobile/nativeApp.ts +405 -0
- package/mobile/nativeAppLine.test.ts +69 -0
- package/mobile/nativeAppLine.ts +71 -0
- package/mobile/nativeConfig.test.ts +414 -0
- package/mobile/nativeConfig.ts +296 -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 +30 -25
- package/subspace.ts +81 -115
- package/subspaceConfig.ts +11 -9
- 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,193 @@ 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("an app without basePaths has one target, default, with no basePath and the section's settings", () => {
|
|
481
|
+
const config = new AkanAppConfig(
|
|
482
|
+
app,
|
|
483
|
+
[],
|
|
484
|
+
packageJson,
|
|
485
|
+
{ native: { indexPath: "/explore", desktop: { server: true } } },
|
|
486
|
+
baseDevEnv,
|
|
487
|
+
);
|
|
488
|
+
|
|
489
|
+
expect(Object.keys(config.native.targets)).toEqual(["default"]);
|
|
490
|
+
expect(config.native.targets.default.basePath).toBeUndefined();
|
|
491
|
+
expect(config.native.targets.default).toMatchObject({ indexPath: "/explore", desktop: { server: true } });
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
test("a target takes the native section with its own fields over it: objects merge, lists and values replace", () => {
|
|
495
|
+
const config = new AkanAppConfig(
|
|
496
|
+
app,
|
|
497
|
+
[],
|
|
498
|
+
packageJson,
|
|
499
|
+
{
|
|
500
|
+
native: {
|
|
501
|
+
fileName: "portal",
|
|
502
|
+
plugins: ["iap"],
|
|
503
|
+
ios: { infoPlist: { ITSAppUsesNonExemptEncryption: false }, files: { "sound.caf": "assets/sound.caf" } },
|
|
504
|
+
android: {
|
|
505
|
+
manifest: ["<queries/>"],
|
|
506
|
+
googleServices: "secrets/google-services.json",
|
|
507
|
+
files: { "res/raw/chime.mp3": "assets/chime.mp3" },
|
|
508
|
+
},
|
|
509
|
+
targets: {
|
|
510
|
+
default: {
|
|
511
|
+
plugins: ["share"],
|
|
512
|
+
ios: { files: { "extra.caf": "assets/extra.caf" } },
|
|
513
|
+
android: { manifest: ["<uses-feature/>"] },
|
|
514
|
+
},
|
|
515
|
+
},
|
|
516
|
+
},
|
|
517
|
+
},
|
|
518
|
+
baseDevEnv,
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
expect(config.native.targets.default).toMatchObject({
|
|
522
|
+
fileName: "portal",
|
|
523
|
+
plugins: ["share"],
|
|
524
|
+
ios: {
|
|
525
|
+
infoPlist: { ITSAppUsesNonExemptEncryption: false },
|
|
526
|
+
files: { "sound.caf": "assets/sound.caf", "extra.caf": "assets/extra.caf" },
|
|
527
|
+
},
|
|
528
|
+
android: {
|
|
529
|
+
manifest: ["<uses-feature/>"],
|
|
530
|
+
googleServices: "secrets/google-services.json",
|
|
531
|
+
files: { "res/raw/chime.mp3": "assets/chime.mp3" },
|
|
405
532
|
},
|
|
406
533
|
});
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
test("gives every target the section's updates, a target overriding a field", () => {
|
|
537
|
+
const updates = { url: "https://releases.example.com/portal", publicKey: "key=" };
|
|
538
|
+
const config = new AkanAppConfig(
|
|
539
|
+
app,
|
|
540
|
+
[],
|
|
541
|
+
packageJson,
|
|
542
|
+
{ native: { updates, targets: { default: {}, pilot: { updates: { channel: "pilot" } } } } },
|
|
543
|
+
baseDevEnv,
|
|
544
|
+
);
|
|
407
545
|
|
|
546
|
+
expect(config.native.targets.default.updates).toEqual(updates);
|
|
547
|
+
expect(config.native.targets.pilot.updates).toEqual({ ...updates, channel: "pilot" });
|
|
408
548
|
expect(
|
|
409
549
|
() =>
|
|
410
550
|
new AkanAppConfig(
|
|
411
551
|
app,
|
|
412
552
|
[],
|
|
413
553
|
packageJson,
|
|
414
|
-
{
|
|
415
|
-
mobile: { targets: { bad: { basePath: "missing" } } },
|
|
416
|
-
},
|
|
554
|
+
{ native: { targets: { pilot: { updates: { channel: "pilot" } } } } },
|
|
417
555
|
baseDevEnv,
|
|
418
556
|
),
|
|
419
|
-
).toThrow("
|
|
557
|
+
).toThrow("native.targets.pilot.updates in apps/portal/akan.config.ts has no url or publicKey");
|
|
558
|
+
expect(
|
|
559
|
+
() => new AkanAppConfig(app, [], packageJson, { native: { updates: { channel: "pilot" } } }, baseDevEnv),
|
|
560
|
+
).toThrow("native.updates in apps/portal/akan.config.ts has no url or publicKey.");
|
|
420
561
|
});
|
|
421
562
|
|
|
422
|
-
test("
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
563
|
+
test("merges the desktop settings field by field, the target winning", () => {
|
|
564
|
+
const config = new AkanAppConfig(
|
|
565
|
+
app,
|
|
566
|
+
[],
|
|
567
|
+
packageJson,
|
|
568
|
+
{
|
|
569
|
+
native: {
|
|
570
|
+
desktop: { recovery: "reload", window: { fullscreen: true } },
|
|
571
|
+
targets: { default: { desktop: { window: { skipTaskbar: true } } } },
|
|
572
|
+
},
|
|
573
|
+
},
|
|
574
|
+
baseDevEnv,
|
|
575
|
+
);
|
|
576
|
+
|
|
577
|
+
expect(config.native.targets.default.desktop).toEqual({
|
|
578
|
+
recovery: "reload",
|
|
579
|
+
window: { fullscreen: true, skipTaskbar: true },
|
|
580
|
+
});
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
test("merges the section's push and privacy into each target field by field; an icon object is one value", () => {
|
|
584
|
+
const publicKey = Buffer.alloc(32, 7).toString("base64");
|
|
585
|
+
const config = new AkanAppConfig(
|
|
586
|
+
app,
|
|
587
|
+
[],
|
|
588
|
+
packageJson,
|
|
589
|
+
{
|
|
590
|
+
native: {
|
|
591
|
+
updates: { url: "https://updates.example.com/portal", publicKey },
|
|
592
|
+
icon: { image: "assets/icon.png", backgroundColor: "#000000" },
|
|
593
|
+
android: { push: { color: "#ff5a5f" } },
|
|
594
|
+
ios: { privacy: { tracking: false } },
|
|
595
|
+
targets: {
|
|
596
|
+
default: {},
|
|
597
|
+
beta: {
|
|
598
|
+
updates: { channel: "beta" },
|
|
599
|
+
icon: { image: "assets/beta.png" },
|
|
600
|
+
android: { push: { smallIcon: "assets/noti.png" } },
|
|
601
|
+
},
|
|
602
|
+
},
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
baseDevEnv,
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
expect(config.native.targets.default.updates).toEqual({ url: "https://updates.example.com/portal", publicKey });
|
|
609
|
+
expect(config.native.targets.beta).toMatchObject({
|
|
610
|
+
updates: { url: "https://updates.example.com/portal", publicKey, channel: "beta" },
|
|
611
|
+
android: { push: { color: "#ff5a5f", smallIcon: "assets/noti.png" } },
|
|
612
|
+
ios: { privacy: { tracking: false } },
|
|
613
|
+
});
|
|
614
|
+
expect(config.native.targets.beta.icon).toEqual({ image: "assets/beta.png" });
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
test("refuses `mobile` and every setting that moved, naming where it went", () => {
|
|
618
|
+
const make = (config: Record<string, unknown>) => () =>
|
|
619
|
+
new AkanAppConfig(app, [], packageJson, config as never, baseDevEnv);
|
|
620
|
+
|
|
621
|
+
expect(make({ mobile: { appName: "portal" } })).toThrow("declares `mobile`, which is now `native`");
|
|
622
|
+
expect(make({ native: { assets: { icon: "assets/icon.png" } } })).toThrow(
|
|
623
|
+
"native.assets in apps/portal/akan.config.ts has moved: icon and splash sit directly in the native section.",
|
|
624
|
+
);
|
|
625
|
+
expect(make({ native: { targets: { default: { native: { desktop: { server: true } } } } } })).toThrow(
|
|
626
|
+
"native.targets.default.native in apps/portal/akan.config.ts has moved",
|
|
627
|
+
);
|
|
628
|
+
expect(make({ native: { files: { "ios/sound.caf": "assets/sound.caf" } } })).toThrow(
|
|
629
|
+
"native.files in apps/portal/akan.config.ts has moved: ios.files",
|
|
630
|
+
);
|
|
631
|
+
expect(make({ native: { deepLinks: { ios: { teamId: "TEAMID" } } } })).toThrow(
|
|
632
|
+
"native.deepLinks.ios in apps/portal/akan.config.ts has moved: ios.teamId.",
|
|
633
|
+
);
|
|
634
|
+
expect(make({ native: { android: { files: { "App/x.json": "x.json" } } } })).toThrow(
|
|
635
|
+
'native.android.files["App/x.json"] in apps/portal/akan.config.ts must land at res/<type>/<file> or assets/<path>.',
|
|
636
|
+
);
|
|
637
|
+
expect(make({ native: { targets: { kiosk: { ios: { files: { "../x.caf": "x.caf" } } } } } })).toThrow(
|
|
638
|
+
'native.targets.kiosk.ios.files["../x.caf"] in apps/portal/akan.config.ts must land at <path in the app bundle>.',
|
|
639
|
+
);
|
|
640
|
+
expect(make({ native: { ios: { scheme: "App" } } })).toThrow(
|
|
641
|
+
"native.ios.scheme in apps/portal/akan.config.ts is not a native setting",
|
|
642
|
+
);
|
|
643
|
+
expect(make({ native: { server: { url: "http://x" } } })).toThrow(
|
|
644
|
+
"native.server in apps/portal/akan.config.ts is not a native setting",
|
|
645
|
+
);
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
test("derives a repo-scoped default appId and records an explicit native section", () => {
|
|
649
|
+
const withoutNative = new AkanAppConfig(app, [], packageJson, {}, baseDevEnv);
|
|
650
|
+
expect(withoutNative.native.appId).toBe("com.akanjs.portal");
|
|
651
|
+
expect(withoutNative.hasNativeConfig).toBe(false);
|
|
427
652
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
expect(
|
|
431
|
-
expect(withMobile.hasMobileConfig).toBe(true);
|
|
653
|
+
const withNative = new AkanAppConfig(app, [], packageJson, { native: { version: "2.0.0" } }, baseDevEnv);
|
|
654
|
+
expect(withNative.native.appId).toBe("com.akanjs.portal");
|
|
655
|
+
expect(withNative.hasNativeConfig).toBe(true);
|
|
432
656
|
});
|
|
433
657
|
});
|
|
434
658
|
|
|
@@ -466,25 +690,13 @@ describe("AkanAppConfig lib externalLibs", () => {
|
|
|
466
690
|
});
|
|
467
691
|
|
|
468
692
|
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();
|
|
693
|
+
const config = await loadExtAppConfig(
|
|
694
|
+
"akan-config-libext-",
|
|
695
|
+
"export default { externalLibs: ['shiki'] };\n",
|
|
696
|
+
"export default { externalLibs: ['puppeteer'] };\n",
|
|
697
|
+
);
|
|
483
698
|
|
|
484
|
-
|
|
485
|
-
} finally {
|
|
486
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
487
|
-
}
|
|
699
|
+
expect(config.externalLibs).toEqual(["shiki", "puppeteer"]);
|
|
488
700
|
});
|
|
489
701
|
});
|
|
490
702
|
|
|
@@ -528,26 +740,77 @@ describe("AkanAppConfig lib docker runs", () => {
|
|
|
528
740
|
});
|
|
529
741
|
|
|
530
742
|
test("reads them off every workspace lib config on load", async () => {
|
|
531
|
-
const
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
fs.writeFileSync(path.join(root, ".env"), "AKAN_PUBLIC_REPO_NAME=extrepo\nAKAN_PUBLIC_SERVE_DOMAIN=ext.test\n");
|
|
743
|
+
const config = await loadExtAppConfig(
|
|
744
|
+
"akan-config-libdocker-",
|
|
745
|
+
"export default {};\n",
|
|
746
|
+
"export default { docker: { preRuns: ['echo from-lib'], postRuns: [{ arm64: 'echo arm-only' }] } };\n",
|
|
747
|
+
);
|
|
748
|
+
|
|
749
|
+
expect(config.dockerfile).toContain("RUN echo from-lib");
|
|
750
|
+
expect(config.dockerfile).toContain('RUN if [ "$TARGETARCH" = "arm64"');
|
|
751
|
+
});
|
|
752
|
+
});
|
|
542
753
|
|
|
543
|
-
|
|
544
|
-
|
|
754
|
+
describe("AkanAppConfig trustedDependencies and bin", () => {
|
|
755
|
+
const sha256 = "b".repeat(64);
|
|
756
|
+
const libBin = { ffmpeg: { "linux-x64": { url: "https://files.test/ffmpeg.tar.xz", sha256, file: "bin/ffmpeg" } } };
|
|
545
757
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
758
|
+
test("the production package.json trusts the app's and its libs' packages, and nothing when none are named", () => {
|
|
759
|
+
const withTrusted = new AkanAppConfig(
|
|
760
|
+
app,
|
|
761
|
+
[],
|
|
762
|
+
packageJson,
|
|
763
|
+
{ trustedDependencies: [" rclnodejs ", "sharp"] },
|
|
764
|
+
baseDevEnv,
|
|
765
|
+
[],
|
|
766
|
+
{
|
|
767
|
+
externalLibs: [],
|
|
768
|
+
trustedDependencies: ["sharp", "@serialport/bindings-cpp"],
|
|
769
|
+
docker: { preRuns: [], postRuns: [] },
|
|
770
|
+
},
|
|
771
|
+
);
|
|
772
|
+
expect(withTrusted.getProductionPackageJson().trustedDependencies).toEqual([
|
|
773
|
+
"rclnodejs",
|
|
774
|
+
"sharp",
|
|
775
|
+
"@serialport/bindings-cpp",
|
|
776
|
+
]);
|
|
777
|
+
expect(new AkanAppConfig(app, [], packageJson, {}, baseDevEnv).getProductionPackageJson()).not.toHaveProperty(
|
|
778
|
+
"trustedDependencies",
|
|
779
|
+
);
|
|
780
|
+
expect(() => new AkanAppConfig(app, [], packageJson, { trustedDependencies: [""] }, baseDevEnv)).toThrow(
|
|
781
|
+
"apps/portal/akan.config.ts: trustedDependencies lists package names",
|
|
782
|
+
);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
test("keeps the app's bin apart from each lib's, with paths made absolute where they were declared", () => {
|
|
786
|
+
const config = new AkanAppConfig(
|
|
787
|
+
{ name: "portal", cwdPath: "/repo/apps/portal" } as never,
|
|
788
|
+
[],
|
|
789
|
+
packageJson,
|
|
790
|
+
{ bin: { ffmpeg: { "darwin-arm64": { path: "tools/ffmpeg" } } } },
|
|
791
|
+
baseDevEnv,
|
|
792
|
+
[],
|
|
793
|
+
{ externalLibs: [], docker: { preRuns: [], postRuns: [] }, bin: [{ lib: "media", bin: libBin }] },
|
|
794
|
+
);
|
|
795
|
+
expect(config.bin).toEqual({
|
|
796
|
+
ffmpeg: { "darwin-arm64": { path: path.resolve("/repo/apps/portal/tools/ffmpeg") } },
|
|
797
|
+
});
|
|
798
|
+
expect(config.libBins).toEqual([{ lib: "media", bin: libBin }]);
|
|
799
|
+
expect(
|
|
800
|
+
new AkanLibConfig({ name: "media", cwdPath: "/repo/libs/media" } as never, {
|
|
801
|
+
bin: { ffprobe: { "linux-x64": { path: "../../tools/ffprobe" } } },
|
|
802
|
+
}).bin,
|
|
803
|
+
).toEqual({ ffprobe: { "linux-x64": { path: path.resolve("/repo/tools/ffprobe") } } });
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
test("reads them off every workspace lib config on load", async () => {
|
|
807
|
+
const config = await loadExtAppConfig(
|
|
808
|
+
"akan-config-libbin-",
|
|
809
|
+
"export default { trustedDependencies: ['sharp'] };\n",
|
|
810
|
+
`export default { trustedDependencies: ['rclnodejs'], bin: ${JSON.stringify(libBin)} };\n`,
|
|
811
|
+
);
|
|
812
|
+
expect(config.trustedDependencies).toEqual(["sharp", "rclnodejs"]);
|
|
813
|
+
expect(config.libBins).toEqual([{ lib: "extlib", bin: libBin }]);
|
|
551
814
|
});
|
|
552
815
|
});
|
|
553
816
|
|
|
@@ -556,7 +819,7 @@ describe("AkanLibConfig", () => {
|
|
|
556
819
|
const lib = { name: "shared" } as never;
|
|
557
820
|
expect(new AkanLibConfig(lib, {}).externalLibs).toEqual([]);
|
|
558
821
|
|
|
559
|
-
const config:
|
|
822
|
+
const config: LibConfigInput = {
|
|
560
823
|
externalLibs: ["firebase-admin"],
|
|
561
824
|
};
|
|
562
825
|
expect(new AkanLibConfig(lib, config).externalLibs).toEqual(["firebase-admin"]);
|