@akanjs/devkit 3.2.0-rc.1 → 3.2.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +233 -11
  2. package/agentAssets.ts +1 -1
  3. package/akanApp/akanApp.host.test.ts +127 -0
  4. package/akanApp/akanApp.host.ts +50 -57
  5. package/akanApp/devHostPolicy.test.ts +11 -11
  6. package/akanApp/devHostPolicy.ts +2 -4
  7. package/akanContext.ts +2 -5
  8. package/akanMcpContract.ts +21 -10
  9. package/akanMcpToolError.ts +16 -0
  10. package/apiReport/apiDeclarationPrinter.ts +120 -0
  11. package/apiReport/apiReport.test.ts +497 -0
  12. package/apiReport/apiReport.ts +232 -0
  13. package/apiReport/apiReportDiff.ts +126 -0
  14. package/apiReport/apiReportPolicy.ts +65 -0
  15. package/apiReport/apiTargets.ts +311 -0
  16. package/apiReport/changesetBump.ts +19 -0
  17. package/applicationBuildReporter.test.ts +21 -0
  18. package/applicationBuildReporter.ts +8 -0
  19. package/applicationBuildRunner.test.ts +67 -0
  20. package/applicationBuildRunner.ts +6 -1
  21. package/artifact/implicitRootLayout.test.ts +4 -0
  22. package/artifact/implicitRootLayout.ts +13 -1
  23. package/artifact/storeBootstrap.ts +3 -2
  24. package/bareErrorScanner.ts +126 -0
  25. package/biome.base.json +2 -1
  26. package/codeAgent/skills/akan-debug/SKILL.md +7 -3
  27. package/codeAgent/skills/akan-err/SKILL.md +7 -0
  28. package/codeAgent/skills/akan-mcp/SKILL.md +7 -1
  29. package/codeAgent/skills/akan-scaffold/SKILL.md +3 -2
  30. package/codeAgent/skills/akan-transport/SKILL.md +2 -0
  31. package/codeAgent/skills/akan-validate/SKILL.md +1 -0
  32. package/codeAgent/tools/McpClient.ts +28 -6
  33. package/codeAgent/tools/McpToolPack.ts +1 -1
  34. package/codeAgent/tools/akanCodePlugins.test.ts +13 -1
  35. package/commandDecorators/argMeta.ts +4 -0
  36. package/commandDecorators/cliError.ts +72 -0
  37. package/commandDecorators/command.ts +186 -96
  38. package/commandDecorators/commandDecorators.test.ts +240 -17
  39. package/commandDecorators/exitCodeError.ts +6 -7
  40. package/commandDecorators/index.ts +1 -0
  41. package/commandDecorators/targetMeta.ts +3 -0
  42. package/conventionSet.test.ts +5 -2
  43. package/csrE2e/csrE2eHarness.fixture.ts +1 -1
  44. package/errorCodeReference.test.ts +282 -0
  45. package/errorCodeReference.ts +71 -0
  46. package/errorCodeScan.ts +551 -0
  47. package/frontendBuild/allRoutesBuilder.test.ts +16 -4
  48. package/frontendBuild/allRoutesBuilder.ts +24 -6
  49. package/frontendBuild/buildRouteClient.test.ts +43 -0
  50. package/frontendBuild/bundleDefine.ts +2 -0
  51. package/frontendBuild/clientBuildTypes.ts +5 -2
  52. package/frontendBuild/clientEntryDiscovery.test.ts +80 -0
  53. package/frontendBuild/clientEntryDiscovery.ts +53 -9
  54. package/frontendBuild/csrArtifactBuilder.test.ts +48 -0
  55. package/frontendBuild/csrArtifactBuilder.ts +9 -3
  56. package/frontendBuild/csrDevBundler.ts +12 -2
  57. package/frontendBuild/csrDevModuleCompiler.test.ts +97 -0
  58. package/frontendBuild/csrDevModuleCompiler.ts +37 -8
  59. package/frontendBuild/csrDevPatcher.test.ts +89 -2
  60. package/frontendBuild/csrDevPatcher.ts +47 -7
  61. package/frontendBuild/csrDevTypes.ts +16 -0
  62. package/frontendBuild/cssCompiler.test.ts +63 -1
  63. package/frontendBuild/devBarrelImports.ts +132 -0
  64. package/frontendBuild/devRegistryBundler.ts +19 -4
  65. package/frontendBuild/mcpAppViewBuilder.test.ts +67 -0
  66. package/frontendBuild/nativeCore.test.ts +148 -0
  67. package/frontendBuild/precompressArtifacts.ts +1 -1
  68. package/frontendBuild/ssrBaseArtifactBuilder.test.ts +26 -0
  69. package/frontendBuild/ssrBaseArtifactBuilder.ts +23 -19
  70. package/frontendBuild/ssrDevBundler.ts +13 -2
  71. package/frontendBuild/vendorImportScanner.test.ts +24 -3
  72. package/frontendBuild/vendorImportScanner.ts +25 -10
  73. package/incrementalBuilder/incrementalBuilder.host.test.ts +30 -1
  74. package/incrementalBuilder/incrementalBuilder.host.ts +10 -22
  75. package/lint/__fixtures__/no-import-common-err/bad.tsx +13 -0
  76. package/lint/__fixtures__/no-import-common-err/good.tsx +12 -0
  77. package/lint/no-import-common-err.grit +31 -0
  78. package/lint/no-init-fetch-in-client.grit +1 -1
  79. package/lint/no-web-only-api-outside-webkit.grit +3 -3
  80. package/maintainabilityBaseline.test.ts +49 -0
  81. package/maintainabilityBaseline.ts +73 -8
  82. package/maintainabilityContract.test.ts +302 -1
  83. package/maintainabilityContract.ts +87 -22
  84. package/maintainabilityExceptionScanner.test.ts +14 -0
  85. package/maintainabilityExceptionScanner.ts +5 -1
  86. package/maintainabilityLimits.ts +12 -0
  87. package/maintainabilityReport.ts +20 -3
  88. package/moduleFieldNames.test.ts +56 -0
  89. package/moduleFieldNames.ts +25 -0
  90. package/package.json +3 -3
  91. package/preview/PreviewView.ts +32 -11
  92. package/preview/previewView.test.ts +76 -9
  93. package/routeJsBudget.test.ts +47 -11
  94. package/routeJsBudget.ts +88 -25
  95. package/transforms/barrelAnalyzer.ts +46 -1
  96. package/transforms/barrelImportsPlugin.ts +61 -45
  97. package/transforms/transforms.test.ts +67 -1
  98. package/typecheck/projectTypecheck.ts +5 -1
  99. package/typecheck/typecheck.proc.ts +3 -2
  100. package/typecheck/typecheckFailure.ts +6 -1
  101. package/workflow/artifacts.ts +25 -7
  102. package/workflow/fieldType.ts +92 -0
  103. package/workflow/index.ts +2 -0
  104. package/workflow/plainName.ts +28 -0
  105. package/workflow/plan.ts +67 -22
  106. package/workflow/source.ts +0 -5
  107. package/workspaceGraph.test.ts +200 -3
  108. package/workspaceGraph.ts +46 -8
package/workspaceGraph.ts CHANGED
@@ -1,6 +1,13 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { type AkanChangeMap, type AkanGraph, AkanGraphDiff, type AkanGraphNode, AkanIndex } from "@akanjs/language";
3
+ import {
4
+ type AkanChangeMap,
5
+ type AkanGraph,
6
+ AkanGraphDiff,
7
+ type AkanGraphEdge,
8
+ type AkanGraphNode,
9
+ AkanIndex,
10
+ } from "@akanjs/language";
4
11
  import ts from "typescript";
5
12
  import type { Workspace } from "./commandDecorators";
6
13
  import { GitRevision } from "./gitRevision";
@@ -64,12 +71,14 @@ export class WorkspaceGraph {
64
71
  .filter((node) => node.change)
65
72
  .flatMap((node) => [
66
73
  node.at?.file,
74
+ ...(node.sources ?? []),
67
75
  ...(node.fields ?? []).filter((field) => field.change).map(({ at }) => at.file),
68
76
  ]),
69
77
  ...changes.edges.filter((edge) => edge.change).map((edge) => edge.at?.file),
70
78
  ]);
71
79
  const unmapped = files.filter(({ file }) => !mapped.has(file) && !hiddenPath.test(file));
72
- return { ...changes, since: sha.slice(0, 12), unmapped };
80
+ const nodes = changes.nodes.map(({ sources: _sources, ...node }) => node);
81
+ return { ...changes, nodes, since: sha.slice(0, 12), unmapped };
73
82
  }
74
83
 
75
84
  static #baseAt(root: string, sha: string) {
@@ -107,9 +116,30 @@ export class WorkspaceGraph {
107
116
  //? A lib's guard is linked from every app's endpoints; through it, a scope would reach all of them.
108
117
  const guards = new Set(graph.nodes.filter((node) => node.kind === "guard").map((node) => node.id));
109
118
  const reaches = (id: string) => seeds.has(id) && !guards.has(id);
110
- const edges = graph.edges.filter((edge) => reaches(edge.from) || reaches(edge.to));
111
- const ids = new Set([...seeds, ...edges.flatMap((edge) => [edge.from, edge.to])]);
112
- return { version: 1, nodes: graph.nodes.filter((node) => ids.has(node.id)), edges };
119
+ const edges = graph.edges.filter((edge) => edge.kind !== "binds" && (reaches(edge.from) || reaches(edge.to)));
120
+ const kept = new Set([...seeds, ...edges.flatMap((edge) => [edge.from, edge.to])]);
121
+ const isLib =
122
+ !!app &&
123
+ graph.nodes.some((node) => node.owner === `libs/${app}`) &&
124
+ !graph.nodes.some((node) => node.owner === `apps/${app}`);
125
+ const applicable = WorkspaceGraph.#bindingsOf(graph, app && !isLib ? `apps/${app}` : null);
126
+ const isOwn = (edge: AkanGraphEdge) => !module && !!owners?.has(edge.label ?? "");
127
+ const roles = new Set(
128
+ applicable.filter((edge) => isOwn(edge) || kept.has(edge.from) || reaches(edge.to)).map((edge) => edge.from),
129
+ );
130
+ const bindings = applicable.filter((edge) => roles.has(edge.from));
131
+ const ids = new Set([...kept, ...bindings.flatMap((edge) => [edge.from, edge.to])]);
132
+ return { version: 1, nodes: graph.nodes.filter((node) => ids.has(node.id)), edges: [...edges, ...bindings] };
133
+ }
134
+
135
+ //? An app runs a role as its own `applyAdaptor` binds it, else as a lib's does; a lib's code runs under every app's.
136
+ static #bindingsOf(graph: AkanGraph, appOwner: string | null) {
137
+ const binds = graph.edges.filter((edge) => edge.kind === "binds");
138
+ if (!appOwner) return binds;
139
+ const own = new Set(
140
+ binds.filter((edge) => edge.label === appOwner && edge.change !== "removed").map((edge) => edge.from),
141
+ );
142
+ return binds.filter((edge) => edge.label === appOwner || (!edge.label?.startsWith("apps/") && !own.has(edge.from)));
113
143
  }
114
144
 
115
145
  //? Pages are many and most only wrap one another, so a graph too large to answer whole drops the pages that neither
@@ -139,7 +169,14 @@ export class WorkspaceGraph {
139
169
  const isVisible = ({ owner }: AkanGraphNode) => !owner || owner === appOwner || owner.startsWith("libs/");
140
170
  const nodes = graph.nodes.filter(isVisible);
141
171
  const ids = new Set(nodes.map((node) => node.id));
142
- return { version: 1, nodes, edges: graph.edges.filter((edge) => ids.has(edge.from) && ids.has(edge.to)) };
172
+ const bindings = new Set(WorkspaceGraph.#bindingsOf(graph, appOwner));
173
+ return {
174
+ version: 1,
175
+ nodes,
176
+ edges: graph.edges.filter(
177
+ (edge) => ids.has(edge.from) && ids.has(edge.to) && (edge.kind !== "binds" || bindings.has(edge)),
178
+ ),
179
+ };
143
180
  }
144
181
 
145
182
  static changesVisibleTo(changes: AkanChangeMap, app: string): AkanChangeMap {
@@ -188,15 +225,16 @@ export class WorkspaceGraph {
188
225
  if (scope.app || scope.module || graph.nodes.length <= unscopedNodeLimit) return WorkspaceGraph.scope(graph, scope);
189
226
  const unscoped = WorkspaceGraph.#withoutPages(graph);
190
227
  if (unscoped.nodes.length <= unscopedNodeLimit) return unscoped;
228
+ const kept = new Set(unscoped.nodes.map((node) => node.id));
191
229
  const owners = new Map<string, number>();
192
230
  for (const node of counted)
193
- if (node.owner && !pageKinds.has(node.kind)) owners.set(node.owner, (owners.get(node.owner) ?? 0) + 1);
231
+ if (node.owner && kept.has(node.id)) owners.set(node.owner, (owners.get(node.owner) ?? 0) + 1);
194
232
  return {
195
233
  diagnostics: [
196
234
  {
197
235
  severity: "error",
198
236
  code: "graph-scope-required",
199
- message: `The graph has ${unscoped.nodes.length} nodes besides its pages. Pass app, module or both to scope it.`,
237
+ message: `The graph has ${unscoped.nodes.length} nodes once unchanged pages are set aside. Pass app, module or both to scope it.`,
200
238
  input: "app",
201
239
  },
202
240
  ],