@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
package/workflow/render.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
PrimitiveFormat,
|
|
3
|
+
PrimitiveWriteReport,
|
|
2
4
|
RepairReport,
|
|
3
5
|
WorkflowApplyReport,
|
|
6
|
+
WorkflowDiagnostic,
|
|
4
7
|
WorkflowFormat,
|
|
5
8
|
WorkflowPlan,
|
|
6
9
|
WorkflowRunArtifact,
|
|
@@ -9,6 +12,23 @@ import type {
|
|
|
9
12
|
} from "./types";
|
|
10
13
|
import { jsonText } from "./utils";
|
|
11
14
|
|
|
15
|
+
const listOr = <T>(items: readonly T[] | undefined, render: (item: T) => string, empty = "- none") =>
|
|
16
|
+
items?.length ? items.map(render) : [empty];
|
|
17
|
+
|
|
18
|
+
const renderDiagnostic = (diagnostic: WorkflowDiagnostic) =>
|
|
19
|
+
`- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
|
|
20
|
+
|
|
21
|
+
const renderCommand = (entry: { command: string; reason: string }) => `- \`${entry.command}\`: ${entry.reason}`;
|
|
22
|
+
|
|
23
|
+
const renderFile = (file: { action: string; path: string; reason: string }) =>
|
|
24
|
+
`- \`${file.action}\` ${file.path}: ${file.reason}`;
|
|
25
|
+
|
|
26
|
+
const renderRecommendation = (recommendation: WorkflowApplyReport["recommendations"][number]) => {
|
|
27
|
+
const target = recommendation.target ? ` ${recommendation.target}` : "";
|
|
28
|
+
const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
|
|
29
|
+
return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
|
|
30
|
+
};
|
|
31
|
+
|
|
12
32
|
export const renderWorkflowList = (specs: readonly WorkflowSpec[]) =>
|
|
13
33
|
[
|
|
14
34
|
"# Akan Workflows",
|
|
@@ -41,7 +61,7 @@ export const renderWorkflowExplain = (spec: WorkflowSpec) =>
|
|
|
41
61
|
...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
42
62
|
"",
|
|
43
63
|
"## Validation",
|
|
44
|
-
...spec.validation.map(
|
|
64
|
+
...spec.validation.map(renderCommand),
|
|
45
65
|
"",
|
|
46
66
|
].join("\n");
|
|
47
67
|
|
|
@@ -71,43 +91,16 @@ export const renderWorkflowPlan = (plan: WorkflowPlan) =>
|
|
|
71
91
|
}),
|
|
72
92
|
"",
|
|
73
93
|
"## Validation",
|
|
74
|
-
...plan.validation.map(
|
|
94
|
+
...plan.validation.map(renderCommand),
|
|
75
95
|
"",
|
|
76
96
|
"## Diagnostics",
|
|
77
|
-
...(plan.diagnostics
|
|
78
|
-
? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`)
|
|
79
|
-
: ["- none"]),
|
|
97
|
+
...listOr(plan.diagnostics, renderDiagnostic),
|
|
80
98
|
"",
|
|
81
99
|
"## Recommendations",
|
|
82
|
-
...(plan.recommendations.
|
|
83
|
-
? plan.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`)
|
|
84
|
-
: ["- none"]),
|
|
100
|
+
...listOr(plan.recommendations, (recommendation) => `- [${recommendation.kind}] ${recommendation.message}`),
|
|
85
101
|
"",
|
|
86
102
|
].join("\n");
|
|
87
103
|
|
|
88
|
-
const renderRecommendation = (recommendation: WorkflowApplyReport["recommendations"][number]) => {
|
|
89
|
-
const target = recommendation.target ? ` ${recommendation.target}` : "";
|
|
90
|
-
const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
|
|
91
|
-
return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const renderDiagnostic = (diagnostic: WorkflowApplyReport["diagnostics"][number]) =>
|
|
95
|
-
`- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
|
|
96
|
-
|
|
97
|
-
const renderNextAction = (action: WorkflowApplyReport["nextActions"][number]) =>
|
|
98
|
-
`- \`${action.command}\`: ${action.reason}`;
|
|
99
|
-
|
|
100
|
-
const applySourceStatus = (report: WorkflowApplyReport) =>
|
|
101
|
-
report.diagnostics.some(
|
|
102
|
-
(diagnostic) =>
|
|
103
|
-
diagnostic.severity === "error" &&
|
|
104
|
-
(diagnostic.failureScope === "source-change" ||
|
|
105
|
-
!diagnostic.failureScope ||
|
|
106
|
-
diagnostic.failureScope === "unknown"),
|
|
107
|
-
)
|
|
108
|
-
? "failed"
|
|
109
|
-
: "passed";
|
|
110
|
-
|
|
111
104
|
export const renderWorkflowApplyReport = (report: WorkflowApplyReport) => {
|
|
112
105
|
const applySummary = {
|
|
113
106
|
sourceFilesChanged: report.summary?.sourceFilesChanged ?? report.changedFiles,
|
|
@@ -140,7 +133,7 @@ export const renderWorkflowApplyReport = (report: WorkflowApplyReport) => {
|
|
|
140
133
|
"",
|
|
141
134
|
`- Mode: ${report.mode}`,
|
|
142
135
|
`- Status: ${report.status}`,
|
|
143
|
-
`- Source-change status: ${
|
|
136
|
+
`- Source-change status: ${sourceBlockers.length ? "failed" : "passed"}`,
|
|
144
137
|
`- Workspace status: ${validationBlockers.length ? "blocked" : "not blocked during apply"}`,
|
|
145
138
|
`- Source files changed: ${applySummary.sourceFilesChanged.length}`,
|
|
146
139
|
`- Generated files queued for sync: ${applySummary.generatedFilesSynced.length}`,
|
|
@@ -155,24 +148,16 @@ export const renderWorkflowApplyReport = (report: WorkflowApplyReport) => {
|
|
|
155
148
|
...(sourceBlockers.length ? sourceBlockers : ["- none"]),
|
|
156
149
|
"",
|
|
157
150
|
"## Automatically Modified",
|
|
158
|
-
...(applySummary.sourceFilesChanged
|
|
159
|
-
? applySummary.sourceFilesChanged.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`)
|
|
160
|
-
: ["- none"]),
|
|
151
|
+
...listOr(applySummary.sourceFilesChanged, renderFile),
|
|
161
152
|
"",
|
|
162
153
|
"## Generated Sync",
|
|
163
|
-
...(applySummary.generatedFilesSynced
|
|
164
|
-
? applySummary.generatedFilesSynced.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`)
|
|
165
|
-
: ["- none"]),
|
|
154
|
+
...listOr(applySummary.generatedFilesSynced, renderFile),
|
|
166
155
|
"",
|
|
167
156
|
"## Applied Commands",
|
|
168
|
-
...(report.appliedCommands
|
|
169
|
-
? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`)
|
|
170
|
-
: ["- none"]),
|
|
157
|
+
...listOr(report.appliedCommands, renderCommand),
|
|
171
158
|
"",
|
|
172
159
|
"## Recommended Validation Commands",
|
|
173
|
-
...(report.recommendedValidationCommands
|
|
174
|
-
? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`)
|
|
175
|
-
: ["- none"]),
|
|
160
|
+
...listOr(report.recommendedValidationCommands, renderCommand),
|
|
176
161
|
"",
|
|
177
162
|
"## User Review Required",
|
|
178
163
|
...(manualReviewItems.length ? manualReviewItems : ["- none"]),
|
|
@@ -185,13 +170,13 @@ export const renderWorkflowApplyReport = (report: WorkflowApplyReport) => {
|
|
|
185
170
|
: ["- none"]),
|
|
186
171
|
"",
|
|
187
172
|
"## Diagnostics",
|
|
188
|
-
...(report.diagnostics
|
|
173
|
+
...listOr(report.diagnostics, renderDiagnostic),
|
|
189
174
|
"",
|
|
190
175
|
"## Recommendations",
|
|
191
|
-
...(report.recommendations.
|
|
176
|
+
...listOr(report.recommendations.slice(0, 3), renderRecommendation),
|
|
192
177
|
"",
|
|
193
178
|
"## Next Actions",
|
|
194
|
-
...(report.nextActions.
|
|
179
|
+
...listOr(report.nextActions.slice(0, 3), renderCommand),
|
|
195
180
|
"",
|
|
196
181
|
].join("\n");
|
|
197
182
|
};
|
|
@@ -229,21 +214,16 @@ export const renderWorkflowValidationRunReport = (report: WorkflowValidationRunR
|
|
|
229
214
|
`- Environment: ${report.summary.environment}`,
|
|
230
215
|
"",
|
|
231
216
|
"## Source Change Diagnostics",
|
|
232
|
-
...(report.workflowDiagnostics
|
|
233
|
-
? report.workflowDiagnostics.map(
|
|
234
|
-
(diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`,
|
|
235
|
-
)
|
|
236
|
-
: ["- none"]),
|
|
217
|
+
...listOr(report.workflowDiagnostics, renderDiagnostic),
|
|
237
218
|
"",
|
|
238
219
|
"## Existing Workspace Blockers",
|
|
239
220
|
`- Status: ${baselineSummary.status}`,
|
|
240
221
|
`- Errors: ${baselineSummary.totalErrors}`,
|
|
241
222
|
`- Warnings: ${baselineSummary.totalWarnings}`,
|
|
242
|
-
...(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
: ["- none"]),
|
|
223
|
+
...listOr(
|
|
224
|
+
baselineSummary.byCode,
|
|
225
|
+
(item) => `- [${item.severity}] ${item.code} (${item.count}x): ${item.sampleMessage}`,
|
|
226
|
+
),
|
|
247
227
|
...(baselineSummary.detailsIncluded
|
|
248
228
|
? []
|
|
249
229
|
: [
|
|
@@ -251,46 +231,30 @@ export const renderWorkflowValidationRunReport = (report: WorkflowValidationRunR
|
|
|
251
231
|
]),
|
|
252
232
|
"",
|
|
253
233
|
"## Existing Workspace Blocker Details",
|
|
254
|
-
...(report.baselineDiagnostics
|
|
255
|
-
? report.baselineDiagnostics.map(
|
|
256
|
-
(diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`,
|
|
257
|
-
)
|
|
258
|
-
: baselineSummary.detailsIncluded
|
|
259
|
-
? ["- none"]
|
|
260
|
-
: ["- omitted"]),
|
|
234
|
+
...listOr(report.baselineDiagnostics, renderDiagnostic, baselineSummary.detailsIncluded ? "- none" : "- omitted"),
|
|
261
235
|
"",
|
|
262
236
|
"## Known Blockers",
|
|
263
|
-
...(report.knownBlockers
|
|
264
|
-
?
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
})
|
|
270
|
-
: ["- none"]),
|
|
237
|
+
...listOr(report.knownBlockers, (blocker) => {
|
|
238
|
+
const command = blocker.command ? ` \`${blocker.command}\`` : "";
|
|
239
|
+
const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
|
|
240
|
+
const known = blocker.known ? " known" : "";
|
|
241
|
+
return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
|
|
242
|
+
}),
|
|
271
243
|
"",
|
|
272
244
|
"## Commands",
|
|
273
|
-
...(report.commands
|
|
274
|
-
?
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
})
|
|
278
|
-
: ["- none"]),
|
|
245
|
+
...listOr(report.commands, (command) => {
|
|
246
|
+
const scope = command.failureScope ? ` (${command.failureScope})` : "";
|
|
247
|
+
return `- [${command.status}] \`${command.command}\`${scope}: ${command.reason}`;
|
|
248
|
+
}),
|
|
279
249
|
"",
|
|
280
250
|
"## Diagnostics",
|
|
281
|
-
...(report.diagnostics
|
|
282
|
-
? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`)
|
|
283
|
-
: ["- none"]),
|
|
251
|
+
...listOr(report.diagnostics, renderDiagnostic),
|
|
284
252
|
"",
|
|
285
253
|
"## Repair Actions",
|
|
286
|
-
...(report.repairActions
|
|
287
|
-
? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`)
|
|
288
|
-
: ["- none"]),
|
|
254
|
+
...listOr(report.repairActions, renderCommand),
|
|
289
255
|
"",
|
|
290
256
|
"## Next Actions",
|
|
291
|
-
...(report.nextActions
|
|
292
|
-
? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`)
|
|
293
|
-
: ["- none"]),
|
|
257
|
+
...listOr(report.nextActions, renderCommand),
|
|
294
258
|
"",
|
|
295
259
|
].join("\n");
|
|
296
260
|
};
|
|
@@ -306,19 +270,13 @@ export const renderRepairReportMarkdown = (report: RepairReport) =>
|
|
|
306
270
|
`- Target: ${report.target ?? "none"}`,
|
|
307
271
|
"",
|
|
308
272
|
"## Commands",
|
|
309
|
-
...(report.commands.
|
|
310
|
-
? report.commands.map((command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`)
|
|
311
|
-
: ["- none"]),
|
|
273
|
+
...listOr(report.commands, (command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`),
|
|
312
274
|
"",
|
|
313
275
|
"## Diagnostics",
|
|
314
|
-
...(report.diagnostics
|
|
315
|
-
? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`)
|
|
316
|
-
: ["- none"]),
|
|
276
|
+
...listOr(report.diagnostics, renderDiagnostic),
|
|
317
277
|
"",
|
|
318
278
|
"## Next Actions",
|
|
319
|
-
...(report.nextActions
|
|
320
|
-
? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`)
|
|
321
|
-
: ["- none"]),
|
|
279
|
+
...listOr(report.nextActions, renderCommand),
|
|
322
280
|
"",
|
|
323
281
|
].join("\n");
|
|
324
282
|
|
|
@@ -333,3 +291,29 @@ export const renderWorkflowRunArtifact = (artifact: WorkflowRunArtifact, format:
|
|
|
333
291
|
}
|
|
334
292
|
return jsonText(artifact);
|
|
335
293
|
};
|
|
294
|
+
|
|
295
|
+
export const renderPrimitiveWriteReport = (report: PrimitiveWriteReport) =>
|
|
296
|
+
[
|
|
297
|
+
`# Primitive Write: ${report.command}`,
|
|
298
|
+
"",
|
|
299
|
+
`- Status: ${report.status}`,
|
|
300
|
+
"",
|
|
301
|
+
"## Changed Files",
|
|
302
|
+
...listOr(report.changedFiles, renderFile),
|
|
303
|
+
"",
|
|
304
|
+
"## Generated Files",
|
|
305
|
+
...listOr(report.generatedFiles, renderFile),
|
|
306
|
+
"",
|
|
307
|
+
"## Validation Commands",
|
|
308
|
+
...listOr(report.validationCommands, renderCommand),
|
|
309
|
+
"",
|
|
310
|
+
"## Diagnostics",
|
|
311
|
+
...listOr(report.diagnostics, renderDiagnostic),
|
|
312
|
+
"",
|
|
313
|
+
"## Next Actions",
|
|
314
|
+
...listOr(report.nextActions, renderCommand),
|
|
315
|
+
"",
|
|
316
|
+
].join("\n");
|
|
317
|
+
|
|
318
|
+
export const renderPrimitiveReport = (report: PrimitiveWriteReport, format: PrimitiveFormat = "markdown") =>
|
|
319
|
+
format === "json" ? jsonText(report) : renderPrimitiveWriteReport(report);
|
|
@@ -24,7 +24,7 @@ const workspacePackageManifestPaths = async () => {
|
|
|
24
24
|
for (const workspace of workspaces) {
|
|
25
25
|
const glob = new Bun.Glob(`${workspace}/package.json`);
|
|
26
26
|
for await (const relativePath of glob.scan({ cwd: workspaceRoot, onlyFiles: true })) {
|
|
27
|
-
paths.add(relativePath);
|
|
27
|
+
paths.add(relativePath.split(path.sep).join("/"));
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
return [...paths].sort();
|
package/workflow/source.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
+
import { lowerlize } from "akanjs/common";
|
|
1
2
|
import ts from "typescript";
|
|
2
3
|
import type { Sys } from "../commandDecorators";
|
|
3
4
|
import { generatedFilePathsForTarget } from "./artifacts";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
callExpressionName,
|
|
7
|
+
expressionName,
|
|
8
|
+
firstObjectReturnedByArrow,
|
|
9
|
+
heritageCall,
|
|
10
|
+
nodeName,
|
|
11
|
+
propertyName,
|
|
12
|
+
sourceFileFor,
|
|
13
|
+
} from "./sourceAst";
|
|
5
14
|
import type {
|
|
6
15
|
PrimitiveChangedFile,
|
|
7
16
|
PrimitiveFileMap,
|
|
8
17
|
PrimitiveGeneratedFile,
|
|
9
18
|
PrimitiveNextAction,
|
|
10
19
|
PrimitiveValidationCommand,
|
|
20
|
+
PrimitiveWriteReport,
|
|
11
21
|
WorkflowDiagnostic,
|
|
12
22
|
} from "./types";
|
|
13
23
|
|
|
@@ -57,6 +67,27 @@ export const nextActionsForTarget = (target: string) =>
|
|
|
57
67
|
{ command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." },
|
|
58
68
|
] satisfies PrimitiveNextAction[];
|
|
59
69
|
|
|
70
|
+
export const createPrimitiveWriteReport = ({
|
|
71
|
+
command,
|
|
72
|
+
status,
|
|
73
|
+
changedFiles = [],
|
|
74
|
+
generatedFiles = [],
|
|
75
|
+
validationCommands = [],
|
|
76
|
+
diagnostics = [],
|
|
77
|
+
nextActions = [],
|
|
78
|
+
}: Omit<PrimitiveWriteReport, "schemaVersion" | "status"> & {
|
|
79
|
+
status?: PrimitiveWriteReport["status"];
|
|
80
|
+
}): PrimitiveWriteReport => ({
|
|
81
|
+
schemaVersion: 1,
|
|
82
|
+
command,
|
|
83
|
+
status: status ?? (diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed"),
|
|
84
|
+
changedFiles,
|
|
85
|
+
generatedFiles,
|
|
86
|
+
validationCommands,
|
|
87
|
+
diagnostics,
|
|
88
|
+
nextActions,
|
|
89
|
+
});
|
|
90
|
+
|
|
60
91
|
export const createPassedPrimitiveReport = ({
|
|
61
92
|
command,
|
|
62
93
|
changedFiles,
|
|
@@ -90,8 +121,6 @@ export const titleize = (value: string) =>
|
|
|
90
121
|
.replace(/[-_]+/g, " ")
|
|
91
122
|
.replace(/\b\w/g, (char) => char.toUpperCase());
|
|
92
123
|
|
|
93
|
-
export const lowerlize = (value: string) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
|
|
94
|
-
|
|
95
124
|
const koLabels: Record<string, string> = {
|
|
96
125
|
amount: "금액",
|
|
97
126
|
budget: "예산",
|
|
@@ -160,21 +189,27 @@ export const normalizeFieldType = (typeName: string) => {
|
|
|
160
189
|
return normalizedTypes[typeName.toLowerCase()] ?? typeName;
|
|
161
190
|
};
|
|
162
191
|
|
|
163
|
-
export const
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const nextNames = [...baseImport.names, typeName].sort();
|
|
192
|
+
export const ensureNamedImport = (content: string, moduleSpecifier: string, name: string) => {
|
|
193
|
+
const source = sourceFileFor("source.ts", content);
|
|
194
|
+
const namedImport = findNamedImport(source, moduleSpecifier);
|
|
195
|
+
if (namedImport) {
|
|
196
|
+
if (namedImport.names.includes(name)) return content;
|
|
197
|
+
const nextNames = [...namedImport.names, name].sort();
|
|
170
198
|
return spliceText(
|
|
171
199
|
content,
|
|
172
|
-
|
|
173
|
-
|
|
200
|
+
namedImport.namedBindingsStart,
|
|
201
|
+
namedImport.namedBindingsEnd,
|
|
174
202
|
`{ ${nextNames.join(", ")} }`,
|
|
175
203
|
);
|
|
176
204
|
}
|
|
177
|
-
return `import { ${
|
|
205
|
+
return `import { ${name} } from "${moduleSpecifier}";\n${content}`;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
export const ensureBaseImport = (content: string, name: string) => ensureNamedImport(content, "akanjs/base", name);
|
|
209
|
+
|
|
210
|
+
export const ensureBaseTypeImport = (content: string, typeName: string) => {
|
|
211
|
+
if (typeName !== "Int" && typeName !== "Float") return content;
|
|
212
|
+
return ensureBaseImport(content, typeName);
|
|
178
213
|
};
|
|
179
214
|
|
|
180
215
|
export type FieldDefaultValue = string | number | boolean | null;
|
|
@@ -200,12 +235,12 @@ const booleanDefault = (rawDefault: FieldDefaultValue): string | null => {
|
|
|
200
235
|
};
|
|
201
236
|
|
|
202
237
|
const dateDefault = (rawDefault: FieldDefaultValue): string | null => {
|
|
203
|
-
if (typeof rawDefault === "number" && Number.isFinite(rawDefault)) return `
|
|
238
|
+
if (typeof rawDefault === "number" && Number.isFinite(rawDefault)) return `() => dayjs(${rawDefault})`;
|
|
204
239
|
if (typeof rawDefault !== "string") return null;
|
|
205
240
|
const trimmed = rawDefault.trim();
|
|
206
241
|
if (!trimmed) return null;
|
|
207
|
-
if (trimmed === "now") return "
|
|
208
|
-
if (!Number.isNaN(Date.parse(trimmed))) return `
|
|
242
|
+
if (trimmed === "now") return "() => dayjs()";
|
|
243
|
+
if (!Number.isNaN(Date.parse(trimmed))) return `() => dayjs(${JSON.stringify(trimmed)})`;
|
|
209
244
|
return null;
|
|
210
245
|
};
|
|
211
246
|
|
|
@@ -373,9 +408,6 @@ export interface AkanDictionaryStructure {
|
|
|
373
408
|
fields: string[];
|
|
374
409
|
}
|
|
375
410
|
|
|
376
|
-
const sourceFileFor = (fileName: string, content: string, scriptKind = ts.ScriptKind.TS) =>
|
|
377
|
-
ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, scriptKind);
|
|
378
|
-
|
|
379
411
|
const hasParseDiagnostics = (source: ts.SourceFile) =>
|
|
380
412
|
((source as ts.SourceFile & { parseDiagnostics?: readonly ts.Diagnostic[] }).parseDiagnostics ?? []).length > 0;
|
|
381
413
|
|
|
@@ -392,52 +424,6 @@ const lineEndAt = (content: string, position: number) => {
|
|
|
392
424
|
const lineIndentAt = (content: string, position: number) =>
|
|
393
425
|
/^[ \t]*/.exec(content.slice(lineStartAt(content, position)))?.[0] ?? "";
|
|
394
426
|
|
|
395
|
-
const nodeName = (node: ts.PropertyName | ts.BindingName | undefined) => {
|
|
396
|
-
if (!node) return null;
|
|
397
|
-
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return node.text;
|
|
398
|
-
return null;
|
|
399
|
-
};
|
|
400
|
-
|
|
401
|
-
const propertyName = (node: ts.ObjectLiteralElementLike) =>
|
|
402
|
-
ts.isPropertyAssignment(node) || ts.isShorthandPropertyAssignment(node) || ts.isMethodDeclaration(node)
|
|
403
|
-
? nodeName(node.name)
|
|
404
|
-
: null;
|
|
405
|
-
|
|
406
|
-
const expressionName = (expression: ts.Expression): string | null => {
|
|
407
|
-
if (ts.isIdentifier(expression)) return expression.text;
|
|
408
|
-
if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
|
|
409
|
-
if (ts.isCallExpression(expression)) return expressionName(expression.expression);
|
|
410
|
-
if (ts.isAsExpression(expression)) return expressionName(expression.expression);
|
|
411
|
-
return null;
|
|
412
|
-
};
|
|
413
|
-
|
|
414
|
-
const firstObjectReturnedByArrow = (node: ts.Node): ts.ObjectLiteralExpression | null => {
|
|
415
|
-
if (!ts.isArrowFunction(node) && !ts.isFunctionExpression(node)) return null;
|
|
416
|
-
if (ts.isObjectLiteralExpression(node.body)) return node.body;
|
|
417
|
-
if (ts.isParenthesizedExpression(node.body) && ts.isObjectLiteralExpression(node.body.expression)) {
|
|
418
|
-
return node.body.expression;
|
|
419
|
-
}
|
|
420
|
-
if (!ts.isBlock(node.body)) return null;
|
|
421
|
-
for (const statement of node.body.statements) {
|
|
422
|
-
if (ts.isReturnStatement(statement) && statement.expression && ts.isObjectLiteralExpression(statement.expression)) {
|
|
423
|
-
return statement.expression;
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
return null;
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
const isViaCall = (expression: ts.Expression) =>
|
|
430
|
-
ts.isCallExpression(expression) && expressionName(expression.expression) === "via";
|
|
431
|
-
|
|
432
|
-
const heritageCall = (node: ts.ClassDeclaration) => {
|
|
433
|
-
const heritage = node.heritageClauses?.flatMap((clause) => [...clause.types]) ?? [];
|
|
434
|
-
const expression = heritage.find((clause) => isViaCall(clause.expression))?.expression;
|
|
435
|
-
return expression && ts.isCallExpression(expression) ? expression : null;
|
|
436
|
-
};
|
|
437
|
-
|
|
438
|
-
const callExpressionName = (node: ts.CallExpression) =>
|
|
439
|
-
ts.isPropertyAccessExpression(node.expression) ? node.expression.name.text : expressionName(node.expression);
|
|
440
|
-
|
|
441
427
|
const locatedObject = (source: ts.SourceFile, objectLiteral: ts.ObjectLiteralExpression): ObjectInsertionLocator => ({
|
|
442
428
|
objectStart: objectLiteral.getStart(source),
|
|
443
429
|
objectEnd: objectLiteral.getEnd(),
|
|
@@ -863,8 +849,6 @@ export const parseValues = (value: string | null) =>
|
|
|
863
849
|
.map((item) => item.trim())
|
|
864
850
|
.filter(Boolean) ?? [];
|
|
865
851
|
|
|
866
|
-
// ---- Service / signal insertion (add-mutation, add-slice) ----
|
|
867
|
-
|
|
868
852
|
export const hasSourceParseErrors = (content: string, fileName = "source.ts") =>
|
|
869
853
|
hasParseDiagnostics(sourceFileFor(fileName, content));
|
|
870
854
|
|
|
@@ -982,8 +966,7 @@ export const insertSignalFactoryEntry = (
|
|
|
982
966
|
if (!located) return null;
|
|
983
967
|
const locator = locatedObject(source, located.object);
|
|
984
968
|
if (locator.fields.some((field) => field.name === entryName)) return content;
|
|
985
|
-
//
|
|
986
|
-
// object splice (at a higher offset) does not invalidate the param region positions.
|
|
969
|
+
// Apply the param edit last: it sits before the object, so applying it first would shift the object's offsets.
|
|
987
970
|
const paramEdit = factoryParamEdit(content, source, located.arrow, param);
|
|
988
971
|
if (paramEdit === null) return null;
|
|
989
972
|
const withEntry = insertOrderedFieldLine(content, locator, entryName, entryLine, {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
export const sourceFileFor = (fileName: string, content: string) =>
|
|
4
|
+
ts.createSourceFile(fileName, content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
5
|
+
|
|
6
|
+
export const nodeName = (node: ts.PropertyName | ts.BindingName | undefined) => {
|
|
7
|
+
if (!node) return null;
|
|
8
|
+
if (ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node)) return node.text;
|
|
9
|
+
return null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const propertyName = (node: ts.ObjectLiteralElementLike) =>
|
|
13
|
+
ts.isPropertyAssignment(node) || ts.isShorthandPropertyAssignment(node) || ts.isMethodDeclaration(node)
|
|
14
|
+
? nodeName(node.name)
|
|
15
|
+
: null;
|
|
16
|
+
|
|
17
|
+
export const expressionName = (expression: ts.Expression): string | null => {
|
|
18
|
+
if (ts.isIdentifier(expression)) return expression.text;
|
|
19
|
+
if (ts.isPropertyAccessExpression(expression)) return expression.name.text;
|
|
20
|
+
if (ts.isCallExpression(expression)) return expressionName(expression.expression);
|
|
21
|
+
if (ts.isAsExpression(expression)) return expressionName(expression.expression);
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const callExpressionName = (node: ts.CallExpression) =>
|
|
26
|
+
ts.isPropertyAccessExpression(node.expression) ? node.expression.name.text : expressionName(node.expression);
|
|
27
|
+
|
|
28
|
+
export const firstObjectReturnedByArrow = (node: ts.Node): ts.ObjectLiteralExpression | null => {
|
|
29
|
+
if (!ts.isArrowFunction(node) && !ts.isFunctionExpression(node)) return null;
|
|
30
|
+
if (ts.isObjectLiteralExpression(node.body)) return node.body;
|
|
31
|
+
if (ts.isParenthesizedExpression(node.body) && ts.isObjectLiteralExpression(node.body.expression)) {
|
|
32
|
+
return node.body.expression;
|
|
33
|
+
}
|
|
34
|
+
if (!ts.isBlock(node.body)) return null;
|
|
35
|
+
for (const statement of node.body.statements) {
|
|
36
|
+
if (ts.isReturnStatement(statement) && statement.expression && ts.isObjectLiteralExpression(statement.expression)) {
|
|
37
|
+
return statement.expression;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const isViaCall = (expression: ts.Expression) =>
|
|
44
|
+
ts.isCallExpression(expression) && expressionName(expression.expression) === "via";
|
|
45
|
+
|
|
46
|
+
export const heritageCall = (node: ts.ClassDeclaration) => {
|
|
47
|
+
const heritage = node.heritageClauses?.flatMap((clause) => [...clause.types]) ?? [];
|
|
48
|
+
const expression = heritage.find((clause) => isViaCall(clause.expression))?.expression;
|
|
49
|
+
return expression && ts.isCallExpression(expression) ? expression : null;
|
|
50
|
+
};
|
package/workspaceLayout.test.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
isScannedRootEntry,
|
|
7
7
|
libRootAllowedDirs,
|
|
8
8
|
libRootAllowedFiles,
|
|
9
|
+
rootEntryHintOf,
|
|
9
10
|
} from "./workspaceLayout";
|
|
10
11
|
|
|
11
12
|
describe("app root layout allowlist", () => {
|
|
@@ -15,16 +16,30 @@ describe("app root layout allowlist", () => {
|
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
test("admits every documented app root folder", () => {
|
|
18
|
-
for (const dirname of ["
|
|
19
|
+
for (const dirname of [".akan", "plugin", "script", "secrets", "srvkit", "webkit"]) {
|
|
19
20
|
expect(appRootAllowedDirs.has(dirname)).toBe(true);
|
|
20
21
|
}
|
|
21
22
|
});
|
|
22
23
|
|
|
24
|
+
test("admits the native plugins an app owns, and a lib too", () => {
|
|
25
|
+
expect(appRootAllowedDirs.has("native")).toBe(true);
|
|
26
|
+
expect(libRootAllowedDirs.has("native")).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
|
|
23
29
|
test("rejects an app root entry no facet owns", () => {
|
|
24
30
|
expect(appRootAllowedFiles.has("helper.ts")).toBe(false);
|
|
25
31
|
expect(appRootAllowedDirs.has("base")).toBe(false);
|
|
26
32
|
});
|
|
27
33
|
|
|
34
|
+
test("names what a Capacitor app kept in its root as a leftover, not an unknown entry", () => {
|
|
35
|
+
for (const name of ["android", "ios", "mobile", "capacitor.config.ts", "capacitor.config.json"]) {
|
|
36
|
+
expect(appRootAllowedFiles.has(name) || appRootAllowedDirs.has(name)).toBe(false);
|
|
37
|
+
expect(rootEntryHintOf("app", name)).toContain(".akan/native/<target>");
|
|
38
|
+
}
|
|
39
|
+
expect(rootEntryHintOf("app", "base")).toBeNull();
|
|
40
|
+
expect(rootEntryHintOf("lib", "ios")).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
|
|
28
43
|
test("skips dotfile artifacts the sync glob never sees, but keeps .akan", () => {
|
|
29
44
|
expect(isScannedRootEntry("app", ".DS_Store")).toBe(false);
|
|
30
45
|
expect(isScannedRootEntry("app", ".akan")).toBe(true);
|
|
@@ -48,10 +63,10 @@ describe("lib root layout allowlist", () => {
|
|
|
48
63
|
});
|
|
49
64
|
|
|
50
65
|
test("rejects the app-only run and mobile entries", () => {
|
|
51
|
-
for (const filename of ["main.ts", "
|
|
66
|
+
for (const filename of ["main.ts", "akan.app.json"]) {
|
|
52
67
|
expect(libRootAllowedFiles.has(filename)).toBe(false);
|
|
53
68
|
}
|
|
54
|
-
for (const dirname of [".akan", "
|
|
69
|
+
for (const dirname of [".akan", "script", "secrets"]) {
|
|
55
70
|
expect(libRootAllowedDirs.has(dirname)).toBe(false);
|
|
56
71
|
}
|
|
57
72
|
});
|