@akanjs/devkit 2.3.9-rc.6 → 2.3.9-rc.8
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/akanMcpContract.test.ts +37 -0
- package/akanMcpContract.ts +670 -0
- package/index.ts +1 -0
- package/package.json +2 -2
- package/workflow/artifacts.ts +58 -6
- package/workflow/executor.ts +208 -4
- package/workflow/index.ts +2 -0
- package/workflow/moduleIndex.test.ts +296 -0
- package/workflow/moduleIndex.ts +504 -0
- package/workflow/render.ts +4 -3
- package/workflow/rolloutGate.test.ts +109 -0
- package/workflow/rolloutGate.ts +138 -0
- package/workflow/source.test.ts +62 -0
- package/workflow/source.ts +463 -75
- package/workflow/types.ts +130 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type { PackageJson } from "../types";
|
|
2
|
+
|
|
3
|
+
export type ToolingRolloutDependencySection =
|
|
4
|
+
| "dependencies"
|
|
5
|
+
| "devDependencies"
|
|
6
|
+
| "peerDependencies"
|
|
7
|
+
| "optionalDependencies";
|
|
8
|
+
|
|
9
|
+
export type ToolingRolloutCandidateStatus = "allowed" | "reference-only" | "experiment-only" | "blocked";
|
|
10
|
+
|
|
11
|
+
export interface ToolingRolloutCandidate {
|
|
12
|
+
packageName: string;
|
|
13
|
+
status: ToolingRolloutCandidateStatus;
|
|
14
|
+
role: string;
|
|
15
|
+
adoptionGate: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface ToolingRolloutViolation {
|
|
19
|
+
packageName: string;
|
|
20
|
+
version: string;
|
|
21
|
+
section: ToolingRolloutDependencySection;
|
|
22
|
+
status: Exclude<ToolingRolloutCandidateStatus, "allowed">;
|
|
23
|
+
reason: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const dependencySections = [
|
|
27
|
+
"dependencies",
|
|
28
|
+
"devDependencies",
|
|
29
|
+
"peerDependencies",
|
|
30
|
+
"optionalDependencies",
|
|
31
|
+
] as const satisfies readonly ToolingRolloutDependencySection[];
|
|
32
|
+
|
|
33
|
+
export const toolingRolloutCandidates = [
|
|
34
|
+
{
|
|
35
|
+
packageName: "typescript",
|
|
36
|
+
status: "allowed",
|
|
37
|
+
role: "Primary TypeScript Compiler API baseline for AST locator, text splice, and reparse checks.",
|
|
38
|
+
adoptionGate: "Already present; keep using stable Bun-compatible package builds.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
packageName: "@ttsc/graph",
|
|
42
|
+
status: "reference-only",
|
|
43
|
+
role: "Source-free graph shape and lazy resident model reference.",
|
|
44
|
+
adoptionGate: "Do not add as an Akan runtime dependency without a separate proposal or milestone update.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
packageName: "ts-morph",
|
|
48
|
+
status: "experiment-only",
|
|
49
|
+
role: "Prototype candidate for complex object literal insertion, import update, and class traversal.",
|
|
50
|
+
adoptionGate: "Use only in isolated prototypes until package size and formatting churn are measured.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
packageName: "ast-grep",
|
|
54
|
+
status: "experiment-only",
|
|
55
|
+
role: "Prototype candidate for shape detection, CI rules, and migration rules.",
|
|
56
|
+
adoptionGate: "Use as a local rule/check experiment, not as the edit engine.",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
packageName: "recast",
|
|
60
|
+
status: "experiment-only",
|
|
61
|
+
role: "Prototype candidate for formatting preservation.",
|
|
62
|
+
adoptionGate: "Low priority; require proof that formatting churn is lower than the baseline.",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
packageName: "typescript-go",
|
|
66
|
+
status: "blocked",
|
|
67
|
+
role: "TypeScript-Go toolchain transition placeholder.",
|
|
68
|
+
adoptionGate: "Out of scope for Season 2 rollout.",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
packageName: "@typescript/native-preview",
|
|
72
|
+
status: "blocked",
|
|
73
|
+
role: "TypeScript-Go native preview package placeholder.",
|
|
74
|
+
adoptionGate: "Out of scope for Season 2 rollout.",
|
|
75
|
+
},
|
|
76
|
+
] as const satisfies readonly ToolingRolloutCandidate[];
|
|
77
|
+
|
|
78
|
+
export const toolingRolloutGate = {
|
|
79
|
+
schemaVersion: 1,
|
|
80
|
+
strategy: "reference-first-dependency-later",
|
|
81
|
+
dependencyPolicy:
|
|
82
|
+
"Season 2 keeps new AST and graph tooling as references or isolated experiments until a separate proposal or milestone update approves adoption.",
|
|
83
|
+
gateConditions: [
|
|
84
|
+
"Works in Bun runtime and published package artifacts.",
|
|
85
|
+
"Does not break dist/pkgs package verification.",
|
|
86
|
+
"Keeps generated source formatting churn limited.",
|
|
87
|
+
"Reduces add-field regression fixture failures compared with the current TypeScript API baseline.",
|
|
88
|
+
"Does not move MCP responses toward returning source bodies.",
|
|
89
|
+
],
|
|
90
|
+
candidates: toolingRolloutCandidates,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const isRestrictedCandidate = (
|
|
94
|
+
candidate: ToolingRolloutCandidate,
|
|
95
|
+
): candidate is ToolingRolloutCandidate & { status: Exclude<ToolingRolloutCandidateStatus, "allowed"> } =>
|
|
96
|
+
candidate.status !== "allowed";
|
|
97
|
+
|
|
98
|
+
const restrictedCandidates = new Map(
|
|
99
|
+
toolingRolloutCandidates.filter(isRestrictedCandidate).map((candidate) => [candidate.packageName, candidate]),
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
const blockedTypeScriptVersion = (version: string) =>
|
|
103
|
+
/(?:^|[^\w])(?:rc|next|beta|canary|insiders)(?:[^\w]|$)/i.test(version) || /typescript@rc/i.test(version);
|
|
104
|
+
|
|
105
|
+
export const findToolingRolloutViolations = (packageJson: PackageJson): ToolingRolloutViolation[] => {
|
|
106
|
+
const violations: ToolingRolloutViolation[] = [];
|
|
107
|
+
|
|
108
|
+
for (const section of dependencySections) {
|
|
109
|
+
const dependencies = packageJson[section];
|
|
110
|
+
if (!dependencies) continue;
|
|
111
|
+
|
|
112
|
+
for (const [packageName, version] of Object.entries(dependencies)) {
|
|
113
|
+
const candidate = restrictedCandidates.get(packageName);
|
|
114
|
+
if (candidate) {
|
|
115
|
+
violations.push({
|
|
116
|
+
packageName,
|
|
117
|
+
version,
|
|
118
|
+
section,
|
|
119
|
+
status: candidate.status,
|
|
120
|
+
reason: candidate.adoptionGate,
|
|
121
|
+
});
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (packageName === "typescript" && blockedTypeScriptVersion(version)) {
|
|
126
|
+
violations.push({
|
|
127
|
+
packageName,
|
|
128
|
+
version,
|
|
129
|
+
section,
|
|
130
|
+
status: "blocked",
|
|
131
|
+
reason: "TypeScript-Go or typescript@rc toolchain transitions are out of scope for Season 2 rollout.",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return violations;
|
|
138
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { inspectDictionaryStructure } from "./source";
|
|
3
|
+
|
|
4
|
+
describe("inspectDictionaryStructure", () => {
|
|
5
|
+
test("preserves protected dictionary chain order around the model object", () => {
|
|
6
|
+
const structure = inspectDictionaryStructure(
|
|
7
|
+
`
|
|
8
|
+
import { modelDictionary } from "akanjs/dictionary";
|
|
9
|
+
import type { Article, ArticleSlice } from "./article.constant";
|
|
10
|
+
|
|
11
|
+
export const dictionary = modelDictionary(["en", "ko"])
|
|
12
|
+
.of((t) => t(["Article", "Article"]))
|
|
13
|
+
.model<Article>((t) => ({
|
|
14
|
+
title: t(["Title", "제목"]),
|
|
15
|
+
}))
|
|
16
|
+
.slice<ArticleSlice>((fn) => ({
|
|
17
|
+
inPublic: fn(["Article In Public", "Article 공개"]),
|
|
18
|
+
}))
|
|
19
|
+
.enum<ArticleStatus>("articleStatus", (t) => ({}))
|
|
20
|
+
.error({})
|
|
21
|
+
.translate({});
|
|
22
|
+
`,
|
|
23
|
+
"Article",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
expect(structure).toMatchObject({
|
|
27
|
+
parseValid: true,
|
|
28
|
+
modelObjectFound: true,
|
|
29
|
+
chainOrderValid: true,
|
|
30
|
+
fields: ["title"],
|
|
31
|
+
});
|
|
32
|
+
expect(structure.chainMethods).toEqual(["modelDictionary", "of", "model", "slice", "enum", "error", "translate"]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("reports broken dictionary chain order even when the field remains inside model", () => {
|
|
36
|
+
const structure = inspectDictionaryStructure(
|
|
37
|
+
`
|
|
38
|
+
import { modelDictionary } from "akanjs/dictionary";
|
|
39
|
+
import type { Article, ArticleSlice } from "./article.constant";
|
|
40
|
+
|
|
41
|
+
export const dictionary = modelDictionary(["en", "ko"])
|
|
42
|
+
.slice<ArticleSlice>((fn) => ({
|
|
43
|
+
inPublic: fn(["Article In Public", "Article 공개"]),
|
|
44
|
+
}))
|
|
45
|
+
.model<Article>((t) => ({
|
|
46
|
+
title: t(["Title", "제목"]),
|
|
47
|
+
}))
|
|
48
|
+
.translate({})
|
|
49
|
+
.error({});
|
|
50
|
+
`,
|
|
51
|
+
"Article",
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
expect(structure).toMatchObject({
|
|
55
|
+
parseValid: true,
|
|
56
|
+
modelObjectFound: true,
|
|
57
|
+
chainOrderValid: false,
|
|
58
|
+
fields: ["title"],
|
|
59
|
+
});
|
|
60
|
+
expect(structure.chainMethods).toEqual(["modelDictionary", "slice", "model", "translate", "error"]);
|
|
61
|
+
});
|
|
62
|
+
});
|