@akanjs/devkit 3.0.0-beta.19 → 3.0.0-beta.20
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/abstractDoc.test.ts +6 -15
- package/abstractDoc.ts +1 -4
- 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 +1 -3
- package/akanApp/akanApp.host.ts +64 -250
- package/akanApp/devHostPolicy.test.ts +10 -34
- package/akanApp/devHostPolicy.ts +12 -98
- package/akanConfig/akanConfig.test.ts +30 -39
- package/akanConfig/akanConfig.ts +40 -136
- package/akanContext.ts +106 -159
- package/akanMcpContract.ts +28 -73
- package/appSelectionMemory.ts +1 -4
- package/applicationBuildReporter.ts +37 -5
- package/applicationBuildRunner.ts +25 -59
- package/applicationReleasePackager.ts +24 -44
- package/applicationTestPreload.ts +3 -12
- package/artifact/implicitRootLayout.test.ts +3 -19
- package/artifact/implicitRootLayout.ts +34 -91
- package/artifact/routeSeedIndex.ts +11 -44
- package/biomeBase.ts +2 -5
- package/capacitorApp.test.ts +4 -14
- package/capacitorApp.ts +86 -235
- package/cloud/cloudApi.test.ts +202 -0
- package/cloud/cloudApi.ts +33 -57
- package/cloud/configLock.test.ts +64 -0
- package/cloud/configLock.ts +96 -0
- package/cloud/constants.ts +3 -2
- package/cloud/globalConfig.ts +64 -31
- package/codeAgent/agent/AkanCodeServices.ts +15 -57
- package/codeAgent/agent/AkanContextFiles.ts +5 -28
- package/codeAgent/agent/CodeAgent.ts +33 -108
- package/codeAgent/agent/CodeAgentAsks.ts +7 -30
- package/codeAgent/agent/CodeAgentEventMapper.ts +9 -52
- package/codeAgent/agent/CodeAgentGate.ts +8 -34
- package/codeAgent/agent/CodeAgentProxy.ts +4 -13
- package/codeAgent/agent/CodeAgentRpcHost.ts +3 -16
- package/codeAgent/agent/CodeAgentRpcListener.ts +2 -10
- package/codeAgent/agent/CodeAgentStreamPrinter.ts +4 -15
- package/codeAgent/agent/CodeAgentSuspended.ts +1 -8
- package/codeAgent/agent/CodeAgentUi.ts +2 -7
- package/codeAgent/agent/CodeMailbox.ts +7 -34
- package/codeAgent/agent/CodeSessionFork.ts +4 -19
- package/codeAgent/agent/CodeSessionIndex.ts +5 -31
- package/codeAgent/agent/SubagentPool.ts +48 -58
- package/codeAgent/agent/akanCodeModel.ts +5 -39
- package/codeAgent/agent/akanCodePaths.ts +4 -24
- package/codeAgent/agent/akanContextFiles.test.ts +0 -2
- package/codeAgent/agent/akanSystemPrompt.ts +1 -9
- package/codeAgent/agent/codeAgent.test.ts +178 -0
- package/codeAgent/agent/codeAgentContract.test.ts +5 -28
- package/codeAgent/agent/codeMailbox.test.ts +0 -4
- package/codeAgent/agent/codeSessionFork.test.ts +0 -2
- package/codeAgent/agent/codeSessionIndex.test.ts +9 -4
- package/codeAgent/agent/consoleToStderr.ts +2 -12
- package/codeAgent/agent/jsonLines.ts +29 -0
- package/codeAgent/agent/stringArg.ts +5 -0
- package/codeAgent/feedback/DevLogFeedback.ts +3 -18
- package/codeAgent/feedback/PreviewChrome.ts +5 -29
- package/codeAgent/feedback/PreviewFeedback.ts +2 -9
- package/codeAgent/feedback/PreviewView.ts +3 -15
- package/codeAgent/feedback/TurnFeedback.ts +2 -17
- package/codeAgent/feedback/VerifyFeedback.ts +1 -7
- package/codeAgent/feedback/previewChrome.test.ts +1 -1
- package/codeAgent/index.ts +2 -10
- package/codeAgent/skills/akan-code/SKILL.md +12 -4
- package/codeAgent/tools/AkanCli.ts +4 -16
- package/codeAgent/tools/AkanCodePlugins.ts +21 -43
- package/codeAgent/tools/AkanEditScope.ts +2 -12
- package/codeAgent/tools/AkanEnvKeys.ts +3 -11
- package/codeAgent/tools/AkanToolPack.ts +6 -28
- package/codeAgent/tools/AkanVerifier.ts +1 -8
- package/codeAgent/tools/AskToolPack.ts +3 -15
- package/codeAgent/tools/MailToolPack.ts +0 -9
- package/codeAgent/tools/McpClient.ts +3 -24
- package/codeAgent/tools/McpOAuth.ts +6 -41
- package/codeAgent/tools/McpOAuthLoopback.ts +2 -15
- package/codeAgent/tools/McpServerConfig.ts +7 -26
- package/codeAgent/tools/McpSignIn.ts +46 -49
- package/codeAgent/tools/McpTokenStore.ts +6 -17
- package/codeAgent/tools/McpToolPack.ts +107 -35
- package/codeAgent/tools/SessionToolPack.ts +1 -11
- package/codeAgent/tools/WebToolPack.ts +1 -7
- package/codeAgent/tools/akanCodePlugins.test.ts +231 -63
- package/codeAgent/tools/askToolPack.test.ts +0 -3
- package/codeAgent/tools/mcpOAuth.test.ts +315 -27
- package/codeAgent/tools/mcpServerConfig.test.ts +21 -14
- 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 +44 -102
- package/commandDecorators/commandBuilder.ts +30 -61
- package/commandDecorators/commandDecorators.test.ts +1 -9
- package/commandDecorators/dependencyBuilder.ts +10 -1
- package/commandDecorators/helpFormatter.ts +66 -144
- package/commandDecorators/index.ts +0 -2
- package/commandDecorators/targetMeta.ts +9 -7
- package/createTunnel.ts +7 -14
- package/dependencyScanner.test.ts +4 -16
- package/dependencyScanner.ts +27 -78
- package/devkitUtils.test.ts +47 -19
- package/executors.test.ts +114 -60
- package/executors.ts +165 -484
- package/fileEditor.ts +9 -37
- package/{getDirname.test.ts → fileSys.test.ts} +1 -1
- package/fileSys.ts +6 -8
- package/formSetterScanner.ts +3 -16
- package/frontendBuild/allRoutesBuilder.ts +7 -21
- package/frontendBuild/autoImportSync.test.ts +14 -38
- package/frontendBuild/autoImportSync.ts +15 -67
- package/frontendBuild/buildRouteClient.test.ts +10 -36
- package/frontendBuild/bundleDefine.ts +12 -0
- package/frontendBuild/clientBuildTypes.ts +5 -36
- package/frontendBuild/clientEntriesBundler.ts +9 -40
- package/frontendBuild/clientEntryDiscovery.ts +6 -41
- package/frontendBuild/csrArtifactBuilder.ts +6 -25
- package/frontendBuild/cssCandidateCache.test.ts +0 -6
- package/frontendBuild/cssCandidateCache.ts +6 -41
- package/frontendBuild/cssCompiler.ts +29 -78
- package/frontendBuild/cssImportResolver.ts +5 -18
- package/frontendBuild/devChangePlanner.ts +7 -10
- package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
- package/frontendBuild/devGeneratedIndexSync.ts +1 -4
- package/frontendBuild/fontOptimizer.test.ts +5 -13
- package/frontendBuild/fontOptimizer.ts +15 -72
- package/frontendBuild/fontPruner.test.ts +5 -10
- package/frontendBuild/fontPruner.ts +5 -20
- package/frontendBuild/frontendBuild.test.ts +27 -52
- package/frontendBuild/hmrChangeClassifier.ts +2 -2
- package/frontendBuild/hmrWatcher.test.ts +7 -31
- package/frontendBuild/hmrWatcher.ts +14 -71
- package/frontendBuild/pagesBundleBuilder.ts +4 -29
- package/frontendBuild/precompressArtifacts.ts +5 -18
- package/frontendBuild/routeClientBuilder.ts +7 -47
- package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
- package/frontendBuild/sourceMtimeIndex.test.ts +37 -94
- package/frontendBuild/sourceMtimeIndex.ts +19 -130
- package/frontendBuild/ssrBaseArtifactBuilder.ts +2 -8
- 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 +22 -57
- package/incrementalBuilder/buildBatchProtocol.ts +8 -36
- package/incrementalBuilder/buildBatchRunner.test.ts +34 -0
- package/incrementalBuilder/buildBatchRunner.ts +10 -31
- 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/devWatchBatch.test.ts +14 -18
- package/incrementalBuilder/incrementalBuilder.host.test.ts +18 -55
- package/incrementalBuilder/incrementalBuilder.host.ts +18 -76
- package/incrementalBuilder/incrementalBuilder.proc.test.ts +81 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +48 -161
- package/index.ts +3 -15
- package/integration/devResourceProbe.ts +50 -56
- package/integration/devStability.integration.test.ts +47 -194
- package/integration/devStabilityHarness.test.ts +9 -29
- 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/gritRules.test.ts +6 -23
- package/linter.test.ts +1 -2
- package/linter.ts +22 -115
- package/mobile/mobileTarget.ts +14 -13
- package/package.json +2 -2
- package/packageExports.test.ts +3 -18
- package/packageExportsMap.ts +3 -19
- package/piDependencyPins.test.ts +3 -14
- package/platformTest/IdleWatchedProcess.test.ts +5 -13
- package/platformTest/IdleWatchedProcess.ts +1 -5
- package/platformTest/LinuxDockerTarget.ts +20 -35
- package/platformTest/PlatformTestRun.test.ts +5 -9
- package/platformTest/PlatformTestTarget.ts +24 -2
- package/platformTest/TestSnapshot.test.ts +6 -14
- package/platformTest/TestSnapshot.ts +1 -5
- package/platformTest/WindowsSshTarget.ts +6 -22
- package/prompter.ts +3 -6
- package/qualityScanner.test.ts +6 -24
- package/qualityScanner.ts +196 -284
- package/recipeScanner.test.ts +7 -12
- package/recipeScanner.ts +9 -38
- package/repoIdentity.ts +2 -9
- package/routeSourceValidator.ts +7 -30
- package/scanInfo.ts +82 -214
- 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/subspace.test.ts +6 -25
- package/subspace.ts +31 -109
- package/subspaceConfig.ts +3 -11
- package/testHelpers.ts +49 -34
- 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 +35 -20
- package/transforms/transforms.test.ts +153 -44
- package/transforms/tsconfigPackageResolver.test.ts +6 -24
- package/transforms/useClientBundlePlugin.ts +2 -22
- package/typeChecker.ts +7 -63
- package/uploadRelease.ts +10 -25
- package/workflow/artifacts.ts +18 -0
- package/workflow/executor.test.ts +1 -12
- package/workflow/executor.ts +85 -146
- 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/source.ts +33 -56
- package/workflow/sourceAst.ts +50 -0
- package/workspaceLayout.ts +4 -19
- package/commandDecorators/commandMeta.ts +0 -7
- package/commandDecorators/types.ts +0 -10
- package/getDirname.ts +0 -4
- package/local/optimize-resource/ipcprobe/child.ts +0 -1
- package/local/optimize-resource/ipcprobe/parent.ts +0 -11
- package/workflow/primitive.ts +0 -59
|
@@ -10,11 +10,8 @@ import chalk from "chalk";
|
|
|
10
10
|
import { type Command, program } from "commander";
|
|
11
11
|
import { AppSelectionMemory } from "../appSelectionMemory";
|
|
12
12
|
import { AppExecutor, Executor, LibExecutor, ModuleExecutor, PkgExecutor, WorkspaceExecutor } from "../executors";
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
// every process that registers a command (measured: 236MB vs 3MB).
|
|
16
|
-
import { FileSys } from "../fileSys";
|
|
17
|
-
import { getDirname } from "../getDirname";
|
|
13
|
+
// Never the root barrel: it drags ink, @trapezedev/project, ssh2 and the cloud stack into every command process.
|
|
14
|
+
import { FileSys, getDirname } from "../fileSys";
|
|
18
15
|
import type { PackageJson } from "../types";
|
|
19
16
|
import {
|
|
20
17
|
type ArgMeta,
|
|
@@ -24,11 +21,11 @@ import {
|
|
|
24
21
|
getArgMetas,
|
|
25
22
|
type InternalArgMeta,
|
|
26
23
|
} from "./argMeta";
|
|
24
|
+
import { camelToKebabCase } from "./camelToKebabCase";
|
|
27
25
|
import { CommandContainer } from "./dependencyBuilder";
|
|
28
26
|
import { formatCommandHelp, formatHelp } from "./helpFormatter";
|
|
29
27
|
import { type CommandCls, getTargetCommandNames, getTargetMetas } from "./targetMeta";
|
|
30
28
|
|
|
31
|
-
const camelToKebabCase = (str: string) => str.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
32
29
|
const loggedCliErrorObjects = new WeakSet<object>();
|
|
33
30
|
const loggedCliErrorMessages = new Set<string>();
|
|
34
31
|
|
|
@@ -97,30 +94,16 @@ const normalizeEnumChoices = (enumChoices: EnumChoices) =>
|
|
|
97
94
|
: { value: choice, name: choice.toString() },
|
|
98
95
|
);
|
|
99
96
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
*
|
|
103
|
-
* `runCommands` lives in this module, so a static import put `@inquirer/prompts` (~24MB) in the CLI
|
|
104
|
-
* entry's chunk closure — and `akan start` holds that process for the whole dev session while never
|
|
105
|
-
* asking a question, because every argument it needs is already on the command line.
|
|
106
|
-
*
|
|
107
|
-
* The wrappers are typed from the real prompts so no call site changes, and `import type` leaves no
|
|
108
|
-
* runtime edge for the bundler to follow (`entryModuleGraph.test.ts` asserts that).
|
|
109
|
-
*/
|
|
97
|
+
// Loaded on the first prompt: a static import keeps @inquirer/prompts (~24MB) resident for a whole `akan start`.
|
|
98
|
+
// The types come through `import type`, which leaves no runtime edge (entryModuleGraph.test.ts asserts that).
|
|
110
99
|
const prompts = async () => await import("@inquirer/prompts");
|
|
111
100
|
const select = ((config, context) => prompts().then((m) => m.select(config, context))) as typeof inquirerSelect;
|
|
112
101
|
const confirm = ((config, context) => prompts().then((m) => m.confirm(config, context))) as typeof inquirerConfirm;
|
|
113
102
|
const input = ((config, context) => prompts().then((m) => m.input(config, context))) as typeof inquirerInput;
|
|
114
103
|
const checkbox = ((config, context) => prompts().then((m) => m.checkbox(config, context))) as typeof inquirerCheckbox;
|
|
115
104
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
*
|
|
119
|
-
* Only a static choice list can be checked: a `DynamicEnum` resolves against the command context, which
|
|
120
|
-
* is not populated until the internal args are resolved, and it is the interactive `select` that consumes
|
|
121
|
-
* it. Comparison is stringly on purpose — the value still carries commander's raw string here, while a
|
|
122
|
-
* numeric choice list holds numbers.
|
|
123
|
-
*/
|
|
105
|
+
// A DynamicEnum is not checked: it resolves against a context the internal args have not populated yet.
|
|
106
|
+
// Compared as strings: the value is still commander's raw string while a numeric choice list holds numbers.
|
|
124
107
|
const assertEnumChoice = (argMeta: ArgMeta, value: unknown) => {
|
|
125
108
|
const enumChoices = argMeta.argsOption.enum;
|
|
126
109
|
if (!enumChoices || typeof enumChoices === "function") return;
|
|
@@ -133,17 +116,13 @@ const assertEnumChoice = (argMeta: ArgMeta, value: unknown) => {
|
|
|
133
116
|
);
|
|
134
117
|
};
|
|
135
118
|
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
if (!enumChoices) return null;
|
|
139
|
-
if (typeof enumChoices === "function") return await enumChoices(context);
|
|
140
|
-
return enumChoices;
|
|
141
|
-
};
|
|
119
|
+
const inputMessageOf = ({ name, argsOption: { desc, example, ask } }: ArgMeta) =>
|
|
120
|
+
ask ? `${ask}: ` : desc ? `${desc}: ` : `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
|
|
142
121
|
|
|
143
122
|
export const getOptionValue = async (argMeta: ArgMeta, opt: Record<string, unknown>, context: CommandContext) => {
|
|
144
123
|
const {
|
|
145
124
|
name,
|
|
146
|
-
argsOption: { enum: enumChoices, default: defaultValue, type, desc, nullable,
|
|
125
|
+
argsOption: { enum: enumChoices, default: defaultValue, type, desc, nullable, ask },
|
|
147
126
|
} = argMeta;
|
|
148
127
|
if (opt[argMeta.name] !== undefined) {
|
|
149
128
|
assertEnumChoice(argMeta, opt[argMeta.name]);
|
|
@@ -151,42 +130,26 @@ export const getOptionValue = async (argMeta: ArgMeta, opt: Record<string, unkno
|
|
|
151
130
|
} else if (defaultValue !== undefined) return defaultValue;
|
|
152
131
|
|
|
153
132
|
if (enumChoices) {
|
|
154
|
-
const choices = normalizeEnumChoices(
|
|
133
|
+
const choices = normalizeEnumChoices(
|
|
134
|
+
(typeof enumChoices === "function" ? await enumChoices(context) : enumChoices) ?? [],
|
|
135
|
+
);
|
|
155
136
|
if (choices.length === 1) return choices[0]?.value;
|
|
156
|
-
|
|
157
|
-
return choice;
|
|
137
|
+
return await select({ message: ask ?? desc ?? `Select the ${name} value`, choices });
|
|
158
138
|
} else if (nullable) return null;
|
|
159
139
|
else if (type === "boolean") {
|
|
160
140
|
const message = ask ?? desc ?? `Do you want to set ${name}? ${desc ? ` (${desc})` : ""}: `;
|
|
161
141
|
return await confirm({ message });
|
|
162
|
-
} else {
|
|
163
|
-
const message = ask
|
|
164
|
-
? `${ask}: `
|
|
165
|
-
: desc
|
|
166
|
-
? `${desc}: `
|
|
167
|
-
: `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
|
|
168
|
-
if (argMeta.argsOption.nullable) return await input({ message });
|
|
169
|
-
else return convertArgValue(await input({ message }), type ?? "string");
|
|
170
|
-
}
|
|
142
|
+
} else return convertArgValue(await input({ message: inputMessageOf(argMeta) }), type ?? "string");
|
|
171
143
|
};
|
|
172
144
|
|
|
173
145
|
export const getArgumentValue = async (argMeta: ArgMeta, value: string | undefined) => {
|
|
174
|
-
const {
|
|
175
|
-
name,
|
|
176
|
-
argsOption: { default: defaultValue, type, desc, nullable, example, ask },
|
|
177
|
-
} = argMeta;
|
|
146
|
+
const { default: defaultValue, type, nullable } = argMeta.argsOption;
|
|
178
147
|
if (value !== undefined) {
|
|
179
148
|
assertEnumChoice(argMeta, value);
|
|
180
149
|
return convertArgValue(value, type ?? "string");
|
|
181
150
|
} else if (defaultValue !== undefined) return defaultValue;
|
|
182
151
|
else if (nullable) return null;
|
|
183
|
-
|
|
184
|
-
const message = ask
|
|
185
|
-
? `${ask}: `
|
|
186
|
-
: desc
|
|
187
|
-
? `${desc}: `
|
|
188
|
-
: `Enter the ${name} value${example ? ` (example: ${example})` : ""}: `;
|
|
189
|
-
return convertArgValue(await input({ message }), type ?? "string");
|
|
152
|
+
return convertArgValue(await input({ message: inputMessageOf(argMeta) }), type ?? "string");
|
|
190
153
|
};
|
|
191
154
|
|
|
192
155
|
const assignCommandContext = (context: CommandContext, argMeta: ArgMeta | InternalArgMeta, value: unknown) => {
|
|
@@ -217,18 +180,13 @@ const assertCurrentDirectoryIsWorkspaceRoot = async () => {
|
|
|
217
180
|
);
|
|
218
181
|
};
|
|
219
182
|
|
|
220
|
-
/** A variadic positional arrives as an array, and each entry may itself be a comma-separated list. */
|
|
221
183
|
const parseAppNameList = (value: string | string[] | undefined): string[] =>
|
|
222
184
|
(Array.isArray(value) ? value : value === undefined ? [] : [value])
|
|
223
185
|
.flatMap((entry) => entry.split(","))
|
|
224
186
|
.map((entry) => entry.trim())
|
|
225
187
|
.filter(Boolean);
|
|
226
188
|
|
|
227
|
-
|
|
228
|
-
* The `Apps` token: named apps, `all`, or a checkbox when the command line names none. Kept out of
|
|
229
|
-
* `getInternalArgumentValue` because it is the only internal arg whose positional is variadic, so it is
|
|
230
|
-
* the only one whose raw value is an array.
|
|
231
|
-
*/
|
|
189
|
+
// Kept out of getInternalArgumentValue: Apps is the only internal arg whose positional is variadic (an array).
|
|
232
190
|
export const getAppsArgumentValue = async (
|
|
233
191
|
value: string | string[] | undefined,
|
|
234
192
|
workspace: WorkspaceExecutor,
|
|
@@ -265,32 +223,23 @@ export const getInternalArgumentValue = async (
|
|
|
265
223
|
if (argMeta.type === "Workspace") return workspace;
|
|
266
224
|
const sysType = argMeta.type.toLowerCase();
|
|
267
225
|
const [appNames, libNames, pkgNames] = await workspace.getExecs();
|
|
268
|
-
if (sysType === "sys") {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
const execName = await select<string>({
|
|
286
|
-
message: `Select the App or Lib or Pkg name`,
|
|
287
|
-
choices: [...appNames, ...libNames, ...pkgNames],
|
|
288
|
-
});
|
|
289
|
-
if (appNames.includes(execName)) return AppExecutor.from(workspace, execName);
|
|
290
|
-
else if (libNames.includes(execName)) return LibExecutor.from(workspace, execName);
|
|
291
|
-
else if (pkgNames.includes(execName)) return PkgExecutor.from(workspace, execName);
|
|
292
|
-
else throw new Error(`Invalid system name: ${execName}`);
|
|
293
|
-
}
|
|
226
|
+
if (sysType === "sys" || sysType === "exec") {
|
|
227
|
+
const pkgChoices = sysType === "exec" ? pkgNames : [];
|
|
228
|
+
const execOf = (name: string) => {
|
|
229
|
+
if (appNames.includes(name)) return AppExecutor.from(workspace, name);
|
|
230
|
+
if (libNames.includes(name)) return LibExecutor.from(workspace, name);
|
|
231
|
+
if (pkgChoices.includes(name)) return PkgExecutor.from(workspace, name);
|
|
232
|
+
return null;
|
|
233
|
+
};
|
|
234
|
+
const found = value ? execOf(value) : null;
|
|
235
|
+
if (found) return found;
|
|
236
|
+
const name = await select<string>({
|
|
237
|
+
message: sysType === "exec" ? `Select the App or Lib or Pkg name` : `Select the App or Lib name`,
|
|
238
|
+
choices: [...appNames, ...libNames, ...pkgChoices],
|
|
239
|
+
});
|
|
240
|
+
const picked = execOf(name);
|
|
241
|
+
if (!picked) throw new Error(`Invalid system name: ${name}`);
|
|
242
|
+
return picked;
|
|
294
243
|
} else if (sysType === "app") {
|
|
295
244
|
if (value && appNames.includes(value)) return AppExecutor.from(workspace, value);
|
|
296
245
|
if (!value && appNames.length === 1 && appNames[0]) return AppExecutor.from(workspace, appNames[0]);
|
|
@@ -355,17 +304,10 @@ export const runCommands = async (...commands: CommandCls[]) => {
|
|
|
355
304
|
}
|
|
356
305
|
process.env.AKAN_VERSION = cliPackageJson?.version ?? "0.0.1";
|
|
357
306
|
|
|
358
|
-
// Custom help handling
|
|
359
307
|
const hasHelpFlag = process.argv.includes("--help") || process.argv.includes("-h");
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
if (hasHelpFlag || !hasCommand) {
|
|
364
|
-
if (process.argv.length === 2 || (process.argv.length === 3 && hasHelpFlag)) {
|
|
365
|
-
// Global help (no specific command)
|
|
366
|
-
Logger.rawLog(formatHelp(commands, process.env.AKAN_VERSION));
|
|
367
|
-
process.exit(0);
|
|
368
|
-
}
|
|
308
|
+
if (process.argv.length === 2 || (process.argv.length === 3 && hasHelpFlag)) {
|
|
309
|
+
Logger.rawLog(formatHelp(commands, process.env.AKAN_VERSION));
|
|
310
|
+
process.exit(0);
|
|
369
311
|
}
|
|
370
312
|
|
|
371
313
|
program.version(process.env.AKAN_VERSION).description("Akan CLI").configureHelp({
|
|
@@ -416,8 +358,6 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`,
|
|
|
416
358
|
}
|
|
417
359
|
}
|
|
418
360
|
programCommand = programCommand.option(`-v, --verbose [boolean]`, `verbose output`);
|
|
419
|
-
|
|
420
|
-
// Override help completely for each command
|
|
421
361
|
programCommand.helpInformation = () => {
|
|
422
362
|
return formatCommandHelp(command, targetMeta.key);
|
|
423
363
|
};
|
|
@@ -446,11 +386,9 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`,
|
|
|
446
386
|
cmdArgs[argMeta.idx] as string,
|
|
447
387
|
workspace,
|
|
448
388
|
);
|
|
449
|
-
// set app name to env
|
|
450
389
|
if (commandArgs[argMeta.idx] instanceof AppExecutor)
|
|
451
390
|
process.env.AKAN_PUBLIC_APP_NAME = (commandArgs[argMeta.idx] as AppExecutor).name;
|
|
452
|
-
//? Only
|
|
453
|
-
//? every env-derived answer in this process belong to whichever app happened to be last.
|
|
391
|
+
//? Only for a single app: with several, every env-derived answer would belong to whichever came last.
|
|
454
392
|
else if (Array.isArray(commandArgs[argMeta.idx])) {
|
|
455
393
|
const apps = commandArgs[argMeta.idx] as AppExecutor[];
|
|
456
394
|
if (apps.length === 1 && apps[0]) process.env.AKAN_PUBLIC_APP_NAME = apps[0].name;
|
|
@@ -471,5 +409,9 @@ It may cause unexpected behavior. Run \`akan update\` to update latest akanjs.`,
|
|
|
471
409
|
}
|
|
472
410
|
}
|
|
473
411
|
}
|
|
474
|
-
await
|
|
412
|
+
// Handled here, not by the handler above: a rejection reaching the entry's top-level await prints Bun's own trace.
|
|
413
|
+
await program.parseAsync(process.argv).catch((error: unknown) => {
|
|
414
|
+
printCliError(error);
|
|
415
|
+
process.exit(1);
|
|
416
|
+
});
|
|
475
417
|
};
|
|
@@ -2,6 +2,7 @@ import type {
|
|
|
2
2
|
App,
|
|
3
3
|
ArgMeta,
|
|
4
4
|
ArgsOption,
|
|
5
|
+
ArgType,
|
|
5
6
|
CommandContext,
|
|
6
7
|
Exec,
|
|
7
8
|
InternalArgMeta,
|
|
@@ -13,9 +14,14 @@ import type {
|
|
|
13
14
|
Sys,
|
|
14
15
|
} from "./argMeta";
|
|
15
16
|
import { normalizePrimitiveArgType } from "./argMeta";
|
|
16
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
assertUniqueDependencies,
|
|
19
|
+
type DependencyCls,
|
|
20
|
+
type DependencyInstanceMap,
|
|
21
|
+
type DependencyKey,
|
|
22
|
+
injectDependencies,
|
|
23
|
+
} from "./dependencyBuilder";
|
|
17
24
|
import { COMMAND_META, type CommandCls, type TargetMeta, type TargetOption } from "./targetMeta";
|
|
18
|
-
import type { DependencyCls, DependencyKey } from "./types";
|
|
19
25
|
|
|
20
26
|
type PrimitiveValue<T extends PrimitiveArgType> = T extends StringConstructor
|
|
21
27
|
? string
|
|
@@ -23,11 +29,7 @@ type PrimitiveValue<T extends PrimitiveArgType> = T extends StringConstructor
|
|
|
23
29
|
? number
|
|
24
30
|
: boolean;
|
|
25
31
|
type MaybeNullable<Value, Option> = Option extends { nullable: true } ? Value | null : Value;
|
|
26
|
-
|
|
27
|
-
* The literal union a static `enum` declares, or `never` for a `DynamicEnum` — a function does not extend
|
|
28
|
-
* a readonly array, so a runtime-resolved choice list falls back to the primitive type. `{ label, value }`
|
|
29
|
-
* choices contribute their `value`.
|
|
30
|
-
*/
|
|
32
|
+
// A DynamicEnum yields never (a function is no readonly array), so ArgValue falls back to the primitive type.
|
|
31
33
|
type EnumValue<Option> = Option extends { enum: infer Choices }
|
|
32
34
|
? Choices extends readonly (infer Choice)[]
|
|
33
35
|
? Choice extends { value: infer Value }
|
|
@@ -73,6 +75,14 @@ type CommandHandler<Deps extends readonly DependencyCls[], Params extends unknow
|
|
|
73
75
|
...args: Params
|
|
74
76
|
) => unknown | Promise<unknown>;
|
|
75
77
|
|
|
78
|
+
const argMetaOf = (
|
|
79
|
+
type: ArgType,
|
|
80
|
+
name: string,
|
|
81
|
+
primitive: PrimitiveArgType,
|
|
82
|
+
argsOption: object | undefined,
|
|
83
|
+
idx: number,
|
|
84
|
+
): ArgMeta => ({ name, argsOption: { ...argsOption, type: normalizePrimitiveArgType(primitive) }, key: "", idx, type });
|
|
85
|
+
|
|
76
86
|
class TargetBuilder<Deps extends readonly DependencyCls[], Params extends unknown[] = [], Context = object> {
|
|
77
87
|
readonly #args: (ArgMeta | InternalArgMeta)[];
|
|
78
88
|
|
|
@@ -83,56 +93,29 @@ class TargetBuilder<Deps extends readonly DependencyCls[], Params extends unknow
|
|
|
83
93
|
this.#args = args;
|
|
84
94
|
}
|
|
85
95
|
|
|
96
|
+
#add<NextParams extends unknown[], NextContext = Context>(...argMetas: (ArgMeta | InternalArgMeta)[]) {
|
|
97
|
+
return new TargetBuilder<Deps, NextParams, NextContext>(this.targetOption, [...this.#args, ...argMetas]);
|
|
98
|
+
}
|
|
99
|
+
|
|
86
100
|
arg<Type extends PrimitiveArgType, const Option extends ArgsOption<Context> = ArgsOption<Context>>(
|
|
87
101
|
name: string,
|
|
88
102
|
type: Type,
|
|
89
103
|
argsOption: Option = {} as Option,
|
|
90
|
-
)
|
|
91
|
-
return
|
|
92
|
-
...this.#args,
|
|
93
|
-
{
|
|
94
|
-
name,
|
|
95
|
-
argsOption: { ...argsOption, type: normalizePrimitiveArgType(type) },
|
|
96
|
-
key: "",
|
|
97
|
-
idx: this.#args.length,
|
|
98
|
-
type: "Argument",
|
|
99
|
-
} as ArgMeta<CommandContext>,
|
|
100
|
-
]);
|
|
104
|
+
) {
|
|
105
|
+
return this.#add<AddArg<Params, Type, Option>>(argMetaOf("Argument", name, type, argsOption, this.#args.length));
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
option<Type extends PrimitiveArgType, const Option extends ArgsOption<Context> = ArgsOption<Context>>(
|
|
104
109
|
name: string,
|
|
105
110
|
type: Type,
|
|
106
111
|
argsOption: Option = {} as Option,
|
|
107
|
-
)
|
|
108
|
-
return
|
|
109
|
-
...this.#args,
|
|
110
|
-
{
|
|
111
|
-
name,
|
|
112
|
-
argsOption: { ...argsOption, type: normalizePrimitiveArgType(type) },
|
|
113
|
-
key: "",
|
|
114
|
-
idx: this.#args.length,
|
|
115
|
-
type: "Option",
|
|
116
|
-
} as ArgMeta<CommandContext>,
|
|
117
|
-
]);
|
|
112
|
+
) {
|
|
113
|
+
return this.#add<AddArg<Params, Type, Option>>(argMetaOf("Option", name, type, argsOption, this.#args.length));
|
|
118
114
|
}
|
|
119
115
|
|
|
120
|
-
with<const Tokens extends readonly InternalArgToken[]>(
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return new TargetBuilder<Deps, AddInternalArgs<Params, Tokens>, AddInternalContext<Context, Tokens>>(
|
|
124
|
-
this.targetOption,
|
|
125
|
-
[
|
|
126
|
-
...this.#args,
|
|
127
|
-
...tokens.map(
|
|
128
|
-
(token, offset) =>
|
|
129
|
-
({
|
|
130
|
-
key: "",
|
|
131
|
-
idx: this.#args.length + offset,
|
|
132
|
-
type: token.type,
|
|
133
|
-
}) satisfies InternalArgMeta,
|
|
134
|
-
),
|
|
135
|
-
],
|
|
116
|
+
with<const Tokens extends readonly InternalArgToken[]>(...tokens: Tokens) {
|
|
117
|
+
return this.#add<AddInternalArgs<Params, Tokens>, AddInternalContext<Context, Tokens>>(
|
|
118
|
+
...tokens.map((token, offset) => ({ key: "", idx: this.#args.length + offset, type: token.type })),
|
|
136
119
|
);
|
|
137
120
|
}
|
|
138
121
|
|
|
@@ -174,22 +157,8 @@ const createContext = <Deps extends readonly DependencyCls[]>(): CommandBuilderC
|
|
|
174
157
|
public: createTarget<Deps>("public"),
|
|
175
158
|
cloud: createTarget<Deps>("cloud"),
|
|
176
159
|
dev: createTarget<Deps>("dev"),
|
|
177
|
-
arg: (name, type, argsOption) =>
|
|
178
|
-
|
|
179
|
-
name,
|
|
180
|
-
argsOption: { ...(argsOption ?? {}), type: normalizePrimitiveArgType(type) },
|
|
181
|
-
key: "",
|
|
182
|
-
idx: -1,
|
|
183
|
-
type: "Argument",
|
|
184
|
-
}) as ArgMeta<CommandContext>,
|
|
185
|
-
option: (name, type, argsOption) =>
|
|
186
|
-
({
|
|
187
|
-
name,
|
|
188
|
-
argsOption: { ...(argsOption ?? {}), type: normalizePrimitiveArgType(type) },
|
|
189
|
-
key: "",
|
|
190
|
-
idx: -1,
|
|
191
|
-
type: "Option",
|
|
192
|
-
}) as ArgMeta<CommandContext>,
|
|
160
|
+
arg: (name, type, argsOption) => argMetaOf("Argument", name, type, argsOption, -1),
|
|
161
|
+
option: (name, type, argsOption) => argMetaOf("Option", name, type, argsOption, -1),
|
|
193
162
|
});
|
|
194
163
|
|
|
195
164
|
const buildCommandMeta = (definitions: Record<string, TargetDefinition>) => {
|
|
@@ -243,8 +243,6 @@ describe("enum arg choices", () => {
|
|
|
243
243
|
expect(await getOptionValue(optionMeta(["text", "json"]), { format: "json" }, context)).toBe("json");
|
|
244
244
|
});
|
|
245
245
|
|
|
246
|
-
// Before this check `akan quality --format yaml` reached the script as "yaml" while the declaration
|
|
247
|
-
// claimed the parameter was "text" | "json".
|
|
248
246
|
test("rejects an undeclared value in commander's wording", async () => {
|
|
249
247
|
await expect(getOptionValue(optionMeta(["text", "json"]), { format: "yaml" }, context)).rejects.toThrow(
|
|
250
248
|
"option '--format' argument 'yaml' is invalid. Allowed choices are text, json.",
|
|
@@ -269,8 +267,6 @@ describe("enum arg choices", () => {
|
|
|
269
267
|
await expect(getOptionValue(labelled, { format: "JSON output" }, context)).rejects.toThrow("is invalid");
|
|
270
268
|
});
|
|
271
269
|
|
|
272
|
-
// A DynamicEnum resolves against a context that is not populated yet, and the interactive select is its
|
|
273
|
-
// only consumer, so an explicit value passes through unchecked rather than being rejected wrongly.
|
|
274
270
|
test("leaves a dynamic choice list unchecked", async () => {
|
|
275
271
|
expect(
|
|
276
272
|
await getOptionValue(
|
|
@@ -291,11 +287,7 @@ describe("enum arg type inference", () => {
|
|
|
291
287
|
CommandContainer.clear();
|
|
292
288
|
});
|
|
293
289
|
|
|
294
|
-
|
|
295
|
-
* Compile-time only. Nothing here can fail at runtime: if `enum` stops narrowing, the parameter widens
|
|
296
|
-
* to the primitive and these calls stop typechecking, so the regression surfaces in `akan typecheck`
|
|
297
|
-
* rather than in fifteen `as "a" | "b"` casts creeping back into the command files.
|
|
298
|
-
*/
|
|
290
|
+
//? Compile-time only: if `enum` stops narrowing, the parameter widens and these calls stop typechecking.
|
|
299
291
|
const expectLiteral = <Expected>(_value: Expected) => undefined;
|
|
300
292
|
|
|
301
293
|
test("a static enum narrows the exec parameter to its declared choices", () => {
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
export type Cls<T = unknown> = new (...args: unknown[]) => T;
|
|
2
|
+
|
|
3
|
+
export type DependencyKind = "command" | "script" | "runner";
|
|
4
|
+
export type DependencyKey<RefName extends string, Kind extends DependencyKind> = `${RefName}${Capitalize<Kind>}`;
|
|
5
|
+
|
|
6
|
+
export type DependencyCls<T = unknown, Key extends string = string> = (new () => T) & {
|
|
7
|
+
readonly dependencyKey: Key;
|
|
8
|
+
readonly dependencyKind: DependencyKind;
|
|
9
|
+
readonly refName: string;
|
|
10
|
+
};
|
|
2
11
|
|
|
3
12
|
type UnionToIntersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (
|
|
4
13
|
value: infer Intersection,
|