@blzsky/variant 0.2.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.
Files changed (42) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +177 -0
  3. package/dist/build-2LO7GD2S.js +63 -0
  4. package/dist/check-ZTKGGF4M.js +44 -0
  5. package/dist/chunk-3E5LBU3O.js +180 -0
  6. package/dist/chunk-4LLK2S4R.js +630 -0
  7. package/dist/chunk-6J4XDG44.js +14 -0
  8. package/dist/chunk-CXNXZLDG.js +13 -0
  9. package/dist/chunk-DRMASVYN.js +25 -0
  10. package/dist/chunk-GDSZFIHM.js +29 -0
  11. package/dist/chunk-ISRF5F3Q.js +68 -0
  12. package/dist/chunk-KSHWC2XN.js +78 -0
  13. package/dist/chunk-LJDDS4RR.js +91 -0
  14. package/dist/chunk-LXKLCLL6.js +91 -0
  15. package/dist/chunk-MZZOE7L4.js +26 -0
  16. package/dist/chunk-NSNZOH5P.js +31 -0
  17. package/dist/chunk-O4LZKXNZ.js +301 -0
  18. package/dist/chunk-RGD422OM.js +108 -0
  19. package/dist/chunk-RVXUCJYA.js +81 -0
  20. package/dist/chunk-SWPVR2DF.js +468 -0
  21. package/dist/chunk-V22IPJ7A.js +64 -0
  22. package/dist/chunk-V5CTGXMM.js +256 -0
  23. package/dist/chunk-VTJ67TT5.js +238 -0
  24. package/dist/chunk-XTYVVUWW.js +279 -0
  25. package/dist/chunk-XUBWCTJZ.js +100 -0
  26. package/dist/chunk-YYGCSKIQ.js +29 -0
  27. package/dist/cli.d.ts +1 -0
  28. package/dist/cli.js +225 -0
  29. package/dist/diff-GFI6RAEA.js +60 -0
  30. package/dist/doctor-GH4RZVEH.js +127 -0
  31. package/dist/env-QORDD6FQ.js +24 -0
  32. package/dist/impact-AF453PLK.js +702 -0
  33. package/dist/index.d.ts +56 -0
  34. package/dist/index.js +7 -0
  35. package/dist/init-KJIUNGAN.js +158 -0
  36. package/dist/insight-DZ5NQY6D.js +29 -0
  37. package/dist/pr-UFZRZX6S.js +152 -0
  38. package/dist/run-6PNH4QRA.js +40 -0
  39. package/dist/symbol-graph-YYUEMZGR.js +22 -0
  40. package/dist/tsconfig-paths-E3UEDGXZ.js +10 -0
  41. package/dist/workspace-X5FUBTY3.js +186 -0
  42. package/package.json +86 -0
@@ -0,0 +1,301 @@
1
+ // src/core/semantic/surface.ts
2
+ function collectExportedSurface(file, tsm) {
3
+ const symbols = /* @__PURE__ */ new Map();
4
+ const notes = [];
5
+ for (const decl of file.getExportDeclarations()) {
6
+ const moduleSpec = decl.getModuleSpecifierValue();
7
+ const declTypeOnly = decl.isTypeOnly();
8
+ const named = decl.getNamedExports();
9
+ if (moduleSpec === void 0) {
10
+ for (const spec of named) {
11
+ const source = findImportSource(file, spec.getName());
12
+ if (source === void 0) continue;
13
+ const typeOnly = declTypeOnly || spec.isTypeOnly() || source.typeOnly;
14
+ symbols.set(spec.getAliasNode()?.getText() ?? spec.getName(), {
15
+ signature: reexportSignature(spec.getName(), source.module, typeOnly),
16
+ body: "",
17
+ typeSpace: typeOnly,
18
+ kind: "reexport"
19
+ });
20
+ }
21
+ continue;
22
+ }
23
+ if (named.length > 0) {
24
+ for (const spec of named) {
25
+ const typeOnly = declTypeOnly || spec.isTypeOnly();
26
+ symbols.set(spec.getAliasNode()?.getText() ?? spec.getName(), {
27
+ signature: reexportSignature(spec.getName(), moduleSpec, typeOnly),
28
+ body: "",
29
+ typeSpace: typeOnly,
30
+ kind: "reexport"
31
+ });
32
+ }
33
+ continue;
34
+ }
35
+ const namespaceExport = decl.getNamespaceExport();
36
+ if (namespaceExport !== void 0) {
37
+ symbols.set(namespaceExport.getName(), {
38
+ signature: reexportSignature("*", moduleSpec, declTypeOnly),
39
+ body: "",
40
+ typeSpace: declTypeOnly,
41
+ kind: "reexport"
42
+ });
43
+ continue;
44
+ }
45
+ symbols.set(`* from ${moduleSpec}`, {
46
+ signature: reexportSignature("*", moduleSpec, declTypeOnly),
47
+ body: "",
48
+ typeSpace: declTypeOnly,
49
+ kind: "reexport"
50
+ });
51
+ notes.push(`export * from "${moduleSpec}" hides which names are exported`);
52
+ }
53
+ for (const [name, decls] of file.getExportedDeclarations()) {
54
+ if (symbols.has(name) || decls.length === 0) continue;
55
+ if (decls.some((d) => tsm.Node.isModuleDeclaration(d))) {
56
+ notes.push(`"${name}" uses namespace/declaration merging`);
57
+ } else if (new Set(decls.map((d) => d.getKind())).size > 1) {
58
+ notes.push(`"${name}" merges declarations of different kinds`);
59
+ }
60
+ const surfaces = decls.map((d) => declarationSurface(d, tsm));
61
+ const first = surfaces[0];
62
+ symbols.set(name, {
63
+ signature: surfaces.map((s) => s.signature).join(" & "),
64
+ body: surfaces.map((s) => s.body).filter((b) => b !== "").join(" "),
65
+ typeSpace: surfaces.every((s) => s.typeSpace),
66
+ kind: first === void 0 ? "unknown" : first.kind
67
+ });
68
+ }
69
+ if (hasDynamicImport(file, tsm)) {
70
+ notes.push("dynamic import() prevents full static resolution");
71
+ }
72
+ return { symbols, notes };
73
+ }
74
+ function reexportSignature(name, module, typeOnly) {
75
+ return `reexport ${name} from ${module}${typeOnly ? " (type-only)" : ""}`;
76
+ }
77
+ function findImportSource(file, localName) {
78
+ for (const imp of file.getImportDeclarations()) {
79
+ const module = imp.getModuleSpecifierValue();
80
+ const typeOnly = imp.isTypeOnly();
81
+ if (imp.getDefaultImport()?.getText() === localName) {
82
+ return { module, typeOnly };
83
+ }
84
+ if (imp.getNamespaceImport()?.getText() === localName) {
85
+ return { module, typeOnly };
86
+ }
87
+ for (const named of imp.getNamedImports()) {
88
+ const local = named.getAliasNode()?.getText() ?? named.getName();
89
+ if (local === localName) {
90
+ return { module, typeOnly: typeOnly || named.isTypeOnly() };
91
+ }
92
+ }
93
+ }
94
+ return void 0;
95
+ }
96
+ function declarationSurface(decl, tsm) {
97
+ if (tsm.Node.isFunctionDeclaration(decl)) {
98
+ return {
99
+ signature: functionSignature(decl, tsm),
100
+ body: significantText(
101
+ `${decl.getBody()?.getText() ?? ""} ${parameterDefaults(decl)}`,
102
+ tsm
103
+ ),
104
+ typeSpace: false,
105
+ kind: "function"
106
+ };
107
+ }
108
+ if (tsm.Node.isVariableDeclaration(decl)) {
109
+ const init = decl.getInitializer();
110
+ if (init !== void 0 && (tsm.Node.isArrowFunction(init) || tsm.Node.isFunctionExpression(init))) {
111
+ return {
112
+ signature: normalize(decl.getTypeNode()?.getText() ?? "") || functionSignature(init, tsm),
113
+ body: significantText(
114
+ `${init.getBody().getText()} ${parameterDefaults(init)}`,
115
+ tsm
116
+ ),
117
+ typeSpace: false,
118
+ kind: "function"
119
+ };
120
+ }
121
+ const typeText = decl.getTypeNode()?.getText() ?? inferTypeText(
122
+ () => decl.getType().getBaseTypeOfLiteralType().getText(void 0, tsm.TypeFormatFlags.NoTruncation)
123
+ );
124
+ return {
125
+ signature: normalize(typeText),
126
+ body: significantText(init?.getText() ?? "", tsm),
127
+ typeSpace: false,
128
+ kind: "variable"
129
+ };
130
+ }
131
+ if (tsm.Node.isClassDeclaration(decl)) {
132
+ return classSurface(decl, tsm);
133
+ }
134
+ if (tsm.Node.isInterfaceDeclaration(decl)) {
135
+ return {
136
+ signature: significantText(decl.getText(), tsm),
137
+ body: "",
138
+ typeSpace: true,
139
+ kind: "interface"
140
+ };
141
+ }
142
+ if (tsm.Node.isTypeAliasDeclaration(decl)) {
143
+ return {
144
+ signature: significantText(decl.getText(), tsm),
145
+ body: "",
146
+ typeSpace: true,
147
+ kind: "type"
148
+ };
149
+ }
150
+ if (tsm.Node.isEnumDeclaration(decl)) {
151
+ return {
152
+ signature: significantText(decl.getText(), tsm),
153
+ body: "",
154
+ typeSpace: false,
155
+ kind: "enum"
156
+ };
157
+ }
158
+ if (tsm.Node.isModuleDeclaration(decl)) {
159
+ return {
160
+ signature: significantText(decl.getText(), tsm),
161
+ body: "",
162
+ typeSpace: false,
163
+ kind: "namespace"
164
+ };
165
+ }
166
+ return {
167
+ signature: significantText(decl.getText(), tsm),
168
+ body: "",
169
+ typeSpace: false,
170
+ kind: "unknown"
171
+ };
172
+ }
173
+ function functionSignature(fn, tsm) {
174
+ const typeParams = fn.getTypeParameters().map((t) => t.getText()).join(", ");
175
+ const params = fn.getParameters().map((p) => parameterSignature(p, tsm)).join(", ");
176
+ const returnType = fn.getReturnTypeNode()?.getText() ?? inferTypeText(
177
+ () => fn.getReturnType().getText(void 0, tsm.TypeFormatFlags.NoTruncation)
178
+ );
179
+ return normalize(`<${typeParams}>(${params}) => ${returnType}`);
180
+ }
181
+ function parameterSignature(p, tsm) {
182
+ const rest = p.isRestParameter() ? "..." : "";
183
+ const optional = p.hasQuestionToken() || p.hasInitializer() ? "?" : "";
184
+ const type = p.getTypeNode()?.getText() ?? inferTypeText(
185
+ () => p.getType().getText(void 0, tsm.TypeFormatFlags.NoTruncation)
186
+ );
187
+ return `${rest}${type}${optional}`;
188
+ }
189
+ function parameterDefaults(fn) {
190
+ return fn.getParameters().map((p) => p.getInitializer()?.getText() ?? "").filter((t) => t !== "").join(" ");
191
+ }
192
+ function classSurface(cls, tsm) {
193
+ const signatureParts = [];
194
+ const bodyParts = [];
195
+ const typeParams = cls.getTypeParameters().map((t) => t.getText()).join(", ");
196
+ const heritage = cls.getHeritageClauses().map((h) => h.getText()).join(" ");
197
+ const decorators = cls.getDecorators().map((d) => d.getText()).join(" ");
198
+ signatureParts.push(`class<${typeParams}> ${heritage} ${decorators}`);
199
+ for (const member of cls.getMembers()) {
200
+ if (tsm.Node.isConstructorDeclaration(member)) {
201
+ const params = member.getParameters().map((p) => {
202
+ const modifiers = p.getModifiers().map((m) => m.getText()).join(" ");
203
+ return `${modifiers} ${parameterSignature(p, tsm)}`.trim();
204
+ }).join(", ");
205
+ signatureParts.push(`constructor(${params})`);
206
+ bodyParts.push(member.getBody()?.getText() ?? "");
207
+ bodyParts.push(parameterDefaults(member));
208
+ continue;
209
+ }
210
+ if (isPrivateMember(member, tsm)) {
211
+ bodyParts.push(member.getText());
212
+ continue;
213
+ }
214
+ if (tsm.Node.isMethodDeclaration(member)) {
215
+ const staticMod = member.isStatic() ? "static " : "";
216
+ const optional = member.hasQuestionToken() ? "?" : "";
217
+ signatureParts.push(
218
+ `${staticMod}${member.getName()}${optional}${functionSignature(member, tsm)}`
219
+ );
220
+ bodyParts.push(member.getBody()?.getText() ?? "");
221
+ bodyParts.push(parameterDefaults(member));
222
+ continue;
223
+ }
224
+ if (tsm.Node.isPropertyDeclaration(member)) {
225
+ const staticMod = member.isStatic() ? "static " : "";
226
+ const readonlyMod = member.isReadonly() ? "readonly " : "";
227
+ const optional = member.hasQuestionToken() ? "?" : "";
228
+ const type = member.getTypeNode()?.getText() ?? inferTypeText(
229
+ () => member.getType().getBaseTypeOfLiteralType().getText(void 0, tsm.TypeFormatFlags.NoTruncation)
230
+ );
231
+ signatureParts.push(
232
+ `${staticMod}${readonlyMod}${member.getName()}${optional}: ${type}`
233
+ );
234
+ bodyParts.push(member.getInitializer()?.getText() ?? "");
235
+ continue;
236
+ }
237
+ if (tsm.Node.isGetAccessorDeclaration(member)) {
238
+ const type = member.getReturnTypeNode()?.getText() ?? inferTypeText(
239
+ () => member.getReturnType().getText(void 0, tsm.TypeFormatFlags.NoTruncation)
240
+ );
241
+ signatureParts.push(`get ${member.getName()}: ${type}`);
242
+ bodyParts.push(member.getBody()?.getText() ?? "");
243
+ continue;
244
+ }
245
+ if (tsm.Node.isSetAccessorDeclaration(member)) {
246
+ const param = member.getParameters()[0];
247
+ signatureParts.push(
248
+ `set ${member.getName()}(${param === void 0 ? "" : parameterSignature(param, tsm)})`
249
+ );
250
+ bodyParts.push(member.getBody()?.getText() ?? "");
251
+ continue;
252
+ }
253
+ bodyParts.push(member.getText());
254
+ }
255
+ return {
256
+ signature: normalize(signatureParts.join(" | ")),
257
+ body: significantText(bodyParts.filter((b) => b !== "").join(" "), tsm),
258
+ typeSpace: false,
259
+ kind: "class"
260
+ };
261
+ }
262
+ function isPrivateMember(member, tsm) {
263
+ const name = tsm.Node.hasName(member) ? member.getName() : "";
264
+ if (name.startsWith("#")) return true;
265
+ return tsm.Node.isScoped(member) && member.getScope() === tsm.Scope.Private;
266
+ }
267
+ function hasDynamicImport(file, tsm) {
268
+ return file.getDescendantsOfKind(tsm.SyntaxKind.CallExpression).some((c) => c.getExpression().getKind() === tsm.SyntaxKind.ImportKeyword);
269
+ }
270
+ function inferTypeText(get) {
271
+ try {
272
+ return get();
273
+ } catch {
274
+ return "<unresolved>";
275
+ }
276
+ }
277
+ function significantText(src, tsm) {
278
+ if (src.trim() === "") return "";
279
+ const scanner = tsm.ts.createScanner(
280
+ tsm.ts.ScriptTarget.Latest,
281
+ /* skipTrivia */
282
+ true,
283
+ tsm.ts.LanguageVariant.Standard,
284
+ src
285
+ );
286
+ const parts = [];
287
+ let kind = scanner.scan();
288
+ while (kind !== tsm.ts.SyntaxKind.EndOfFileToken) {
289
+ parts.push(scanner.getTokenText());
290
+ kind = scanner.scan();
291
+ }
292
+ return parts.join(" ");
293
+ }
294
+ function normalize(text) {
295
+ return text.replace(/\s+/g, " ").trim();
296
+ }
297
+
298
+ export {
299
+ collectExportedSurface,
300
+ significantText
301
+ };
@@ -0,0 +1,108 @@
1
+ import {
2
+ ConfigError
3
+ } from "./chunk-RVXUCJYA.js";
4
+
5
+ // src/core/config/loader.ts
6
+ import { existsSync } from "fs";
7
+ import { readFile } from "fs/promises";
8
+ import path from "path";
9
+
10
+ // src/core/config/schema.ts
11
+ import * as z from "zod";
12
+ var defaultCache = {
13
+ mode: "content",
14
+ directory: ".variant/cache"
15
+ };
16
+ var taskConfigSchema = z.object({
17
+ inputs: z.array(z.string()).optional(),
18
+ dependsOn: z.array(z.string()).optional(),
19
+ command: z.string().optional(),
20
+ /** Phase 4: scheduler hint. */
21
+ cpuHeavy: z.boolean().optional()
22
+ });
23
+ var variantConfigSchema = z.object({
24
+ strategy: z.enum(["adaptive", "strict"]).default("adaptive"),
25
+ cache: z.object({
26
+ mode: z.literal("content").default(defaultCache.mode),
27
+ directory: z.string().default(defaultCache.directory),
28
+ /** Evict local cache entries older than this many days. */
29
+ ttlDays: z.number().optional()
30
+ }).default(defaultCache),
31
+ tasks: z.record(z.string(), taskConfigSchema).default({}),
32
+ workspace: z.object({
33
+ enabled: z.boolean().default(false),
34
+ scripts: z.array(z.string()).default(["build", "test", "lint"])
35
+ }).optional(),
36
+ git: z.object({
37
+ baseRef: z.string().default("HEAD~1"),
38
+ enabled: z.boolean().default(true)
39
+ }).optional(),
40
+ semanticDiff: z.object({
41
+ enabled: z.boolean().default(false)
42
+ }).optional(),
43
+ scheduler: z.object({
44
+ policy: z.enum(["auto", "light-first", "pack-heavy", "critical-path"]).default("auto")
45
+ }).optional()
46
+ }).superRefine((val, ctx) => {
47
+ const taskNames = new Set(Object.keys(val.tasks));
48
+ for (const [name, cfg] of Object.entries(val.tasks)) {
49
+ for (const dep of cfg.dependsOn ?? []) {
50
+ if (!taskNames.has(dep)) {
51
+ ctx.addIssue({
52
+ code: "custom",
53
+ path: ["tasks", name, "dependsOn"],
54
+ message: `references unknown task "${dep}" \u2014 add it to config.tasks or remove the reference`
55
+ });
56
+ }
57
+ }
58
+ }
59
+ });
60
+
61
+ // src/core/config/loader.ts
62
+ function defineConfig(config) {
63
+ return config;
64
+ }
65
+ var CANDIDATES = [
66
+ "variant.config.ts",
67
+ "variant.config.mjs",
68
+ "variant.config.js",
69
+ "variant.config.json"
70
+ ];
71
+ function findVariantConfigPath(cwd) {
72
+ return CANDIDATES.map((f) => path.join(cwd, f)).find(existsSync);
73
+ }
74
+ async function loadConfig(cwd = process.cwd()) {
75
+ const configPath = findVariantConfigPath(cwd);
76
+ let raw = {};
77
+ if (configPath) {
78
+ if (path.extname(configPath) === ".json") {
79
+ try {
80
+ const text = await readFile(configPath, "utf8");
81
+ raw = JSON.parse(text);
82
+ } catch (err) {
83
+ const hint = err instanceof Error ? err.message : String(err);
84
+ throw new ConfigError(
85
+ `Invalid JSON config (${path.basename(configPath)}): ${hint}`
86
+ );
87
+ }
88
+ } else {
89
+ const { createJiti } = await import("jiti");
90
+ const jiti = createJiti(import.meta.url);
91
+ const mod = await jiti.import(configPath);
92
+ raw = mod.default ?? mod;
93
+ }
94
+ }
95
+ const result = variantConfigSchema.safeParse(raw);
96
+ if (!result.success) {
97
+ const messages = result.error.issues.map((e) => ` ${e.path.map(String).join(".")}: ${e.message}`).join("\n");
98
+ throw new ConfigError(`Invalid variant config:
99
+ ${messages}`);
100
+ }
101
+ return result.data;
102
+ }
103
+
104
+ export {
105
+ defineConfig,
106
+ findVariantConfigPath,
107
+ loadConfig
108
+ };
@@ -0,0 +1,81 @@
1
+ // src/core/errors.ts
2
+ var VariantError = class extends Error {
3
+ constructor(code, message, options) {
4
+ super(message, options);
5
+ this.code = code;
6
+ this.name = "VariantError";
7
+ }
8
+ code;
9
+ hint;
10
+ /**
11
+ * Why the task that produced this failure was running. Attached by the
12
+ * runner so the failure and its reason travel together; absent on errors
13
+ * belonging to no task (config, CLI usage) and on successful runs.
14
+ *
15
+ * This says nothing about why the task FAILED — see `RunReason`.
16
+ */
17
+ provenance;
18
+ };
19
+ var ConfigError = class extends VariantError {
20
+ constructor(message, options) {
21
+ super("CONFIG_ERROR", message, options);
22
+ this.hint = "Run `variant doctor` to diagnose configuration issues.";
23
+ }
24
+ };
25
+ var CycleError = class extends VariantError {
26
+ constructor(cycle) {
27
+ super("CYCLE_ERROR", `Circular dependency detected: ${cycle.join(" -> ")}`);
28
+ this.cycle = cycle;
29
+ this.hint = "Remove or reorder `dependsOn` entries to break the cycle.";
30
+ }
31
+ cycle;
32
+ };
33
+ var TaskExecutionError = class extends VariantError {
34
+ constructor(task, exitCode, message, options) {
35
+ super(
36
+ "TASK_EXECUTION_ERROR",
37
+ message ?? `Task "${task}" failed with exit code ${exitCode}`,
38
+ options
39
+ );
40
+ this.task = task;
41
+ this.exitCode = exitCode;
42
+ this.hint = `Check the output above, fix the failing command in task "${task}", then re-run.`;
43
+ }
44
+ task;
45
+ exitCode;
46
+ };
47
+ var CacheError = class extends VariantError {
48
+ constructor(message, options) {
49
+ super("CACHE_ERROR", message, options);
50
+ this.hint = "Delete `.variant/cache/` and retry.";
51
+ }
52
+ };
53
+ var GraphError = class extends VariantError {
54
+ constructor(message, options) {
55
+ super("GRAPH_ERROR", message, options);
56
+ this.hint = "Delete `.variant/graph/` and retry.";
57
+ }
58
+ };
59
+ var ImpactReportError = class extends VariantError {
60
+ constructor(message) {
61
+ super("IMPACT_REPORT_ERROR", `Could not read the test report: ${message}`);
62
+ this.hint = "Pass a Vitest `--reporter=json --outputFile=<path>` or Jest `--json --outputFile=<path>` report.";
63
+ }
64
+ };
65
+ var CliUsageError = class extends VariantError {
66
+ constructor(message) {
67
+ super("CLI_USAGE", message);
68
+ this.hint = "Run `variant --help` for usage information.";
69
+ }
70
+ };
71
+
72
+ export {
73
+ VariantError,
74
+ ConfigError,
75
+ CycleError,
76
+ TaskExecutionError,
77
+ CacheError,
78
+ GraphError,
79
+ ImpactReportError,
80
+ CliUsageError
81
+ };