@h-rig/validator-kit 0.0.6-alpha.157 → 0.0.6-alpha.158
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/dist/src/checks.d.ts +8 -0
- package/dist/src/checks.js +49 -0
- package/dist/src/exec.d.ts +23 -0
- package/dist/src/exec.js +19 -0
- package/dist/src/index.d.ts +5 -1
- package/dist/src/index.js +113 -0
- package/dist/src/validator-registry.d.ts +27 -0
- package/dist/src/validator-registry.js +64 -0
- package/package.json +2 -2
package/dist/src/checks.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Checker } from "./result";
|
|
2
|
+
import { type RunCommand } from "./exec";
|
|
2
3
|
/** Check a file exists. */
|
|
3
4
|
export declare function requireFileCheck(checker: Checker, path: string, checkName: string): void;
|
|
4
5
|
/** Check a directory exists. */
|
|
@@ -7,3 +8,10 @@ export declare function requireDirCheck(checker: Checker, path: string, checkNam
|
|
|
7
8
|
export declare function requirePackageStructure(checker: Checker, serviceDir: string, prefix: string): void;
|
|
8
9
|
/** Check test files exist in a project directory. */
|
|
9
10
|
export declare function checkTestsExist(checker: Checker, projectDir: string, checkName: string): void;
|
|
11
|
+
/**
|
|
12
|
+
* Check that a TypeScript project compiles cleanly (`tsc --noEmit`). Runs the
|
|
13
|
+
* compile through the injectable {@link RunCommand} primitive — the default is
|
|
14
|
+
* exec.ts's minimal Bun.spawn runner; callers may inject a richer runtime
|
|
15
|
+
* runner. Records a pass/fail on the {@link Checker}; never throws.
|
|
16
|
+
*/
|
|
17
|
+
export declare function checkTypescriptCompiles(checker: Checker, projectDir: string, checkName: string, run?: RunCommand): Promise<void>;
|
package/dist/src/checks.js
CHANGED
|
@@ -30,6 +30,22 @@ function countTestFiles(dir) {
|
|
|
30
30
|
return count;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
// packages/validator-kit/src/exec.ts
|
|
34
|
+
var runCommand = async (cmd, cwd) => {
|
|
35
|
+
const proc = Bun.spawn([...cmd], {
|
|
36
|
+
cwd,
|
|
37
|
+
stdout: "pipe",
|
|
38
|
+
stderr: "pipe",
|
|
39
|
+
env: { ...process.env, PWD: cwd, INIT_CWD: cwd }
|
|
40
|
+
});
|
|
41
|
+
const exitCode = await proc.exited;
|
|
42
|
+
return {
|
|
43
|
+
exitCode,
|
|
44
|
+
stdout: await new Response(proc.stdout).text(),
|
|
45
|
+
stderr: await new Response(proc.stderr).text()
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
|
|
33
49
|
// packages/validator-kit/src/checks.ts
|
|
34
50
|
function requireFileCheck(checker, path, checkName) {
|
|
35
51
|
if (existsSync2(path)) {
|
|
@@ -58,9 +74,42 @@ function checkTestsExist(checker, projectDir, checkName) {
|
|
|
58
74
|
checker.fail(checkName, "no test files found");
|
|
59
75
|
}
|
|
60
76
|
}
|
|
77
|
+
function resolveTypescriptCommand(projectDir) {
|
|
78
|
+
const tsconfigPath = resolve2(projectDir, "tsconfig.json");
|
|
79
|
+
const candidates = [
|
|
80
|
+
resolve2(projectDir, "node_modules", "typescript", "lib", "tsc.js"),
|
|
81
|
+
resolve2(projectDir, "..", "node_modules", "typescript", "lib", "tsc.js"),
|
|
82
|
+
resolve2(projectDir, "..", "..", "node_modules", "typescript", "lib", "tsc.js"),
|
|
83
|
+
resolve2(projectDir, "..", "..", "..", "node_modules", "typescript", "lib", "tsc.js")
|
|
84
|
+
];
|
|
85
|
+
for (const candidate of candidates) {
|
|
86
|
+
if (existsSync2(candidate)) {
|
|
87
|
+
return [process.execPath, candidate, "--noEmit", "--project", tsconfigPath];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return ["bunx", "tsc", "--noEmit", "--project", tsconfigPath];
|
|
91
|
+
}
|
|
92
|
+
async function checkTypescriptCompiles(checker, projectDir, checkName, run = runCommand) {
|
|
93
|
+
if (!existsSync2(resolve2(projectDir, "tsconfig.json"))) {
|
|
94
|
+
checker.fail(checkName, "no tsconfig.json found");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const command = resolveTypescriptCommand(projectDir);
|
|
98
|
+
const result = await run(command, projectDir);
|
|
99
|
+
if (result.exitCode === 0) {
|
|
100
|
+
checker.pass(checkName);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const combined = result.stdout + result.stderr;
|
|
104
|
+
const errorCount = combined.split(`
|
|
105
|
+
`).filter((line) => line.includes("error TS")).length;
|
|
106
|
+
const detail = errorCount > 0 ? `${errorCount} TypeScript errors` : `TypeScript command exited ${result.exitCode} without TS diagnostics`;
|
|
107
|
+
checker.fail(checkName, detail);
|
|
108
|
+
}
|
|
61
109
|
export {
|
|
62
110
|
requirePackageStructure,
|
|
63
111
|
requireFileCheck,
|
|
64
112
|
requireDirCheck,
|
|
113
|
+
checkTypescriptCompiles,
|
|
65
114
|
checkTestsExist
|
|
66
115
|
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal honest command-runner for validator checks.
|
|
3
|
+
*
|
|
4
|
+
* @rig/validator-kit is floor-neutral (contracts-only deps), so it does NOT
|
|
5
|
+
* depend on @rig/runtime's `resolveBunCli`/build-macro machinery. There is no
|
|
6
|
+
* shared `runCommand` PRIMITIVE for this floor to wire to yet, so this is the
|
|
7
|
+
* minimal honest implementation: a thin Bun.spawn wrapper that captures exit
|
|
8
|
+
* code + stdout + stderr. It is INJECTABLE — every check that needs to run a
|
|
9
|
+
* command accepts a `RunCommand` so a caller (or test) can substitute the
|
|
10
|
+
* runtime's richer runner. When a real shared primitive lands, swap the default.
|
|
11
|
+
*/
|
|
12
|
+
export type RunCommandResult = {
|
|
13
|
+
readonly exitCode: number;
|
|
14
|
+
readonly stdout: string;
|
|
15
|
+
readonly stderr: string;
|
|
16
|
+
};
|
|
17
|
+
export type RunCommand = (cmd: readonly string[], cwd: string) => Promise<RunCommandResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Default runner: spawn the command verbatim and capture output. Honest about
|
|
20
|
+
* its limits — it does not resolve a baked bun path or inject build env; it runs
|
|
21
|
+
* exactly what it is handed. Callers needing that resolution inject their own.
|
|
22
|
+
*/
|
|
23
|
+
export declare const runCommand: RunCommand;
|
package/dist/src/exec.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/validator-kit/src/exec.ts
|
|
3
|
+
var runCommand = async (cmd, cwd) => {
|
|
4
|
+
const proc = Bun.spawn([...cmd], {
|
|
5
|
+
cwd,
|
|
6
|
+
stdout: "pipe",
|
|
7
|
+
stderr: "pipe",
|
|
8
|
+
env: { ...process.env, PWD: cwd, INIT_CWD: cwd }
|
|
9
|
+
});
|
|
10
|
+
const exitCode = await proc.exited;
|
|
11
|
+
return {
|
|
12
|
+
exitCode,
|
|
13
|
+
stdout: await new Response(proc.stdout).text(),
|
|
14
|
+
stderr: await new Response(proc.stderr).text()
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
export {
|
|
18
|
+
runCommand
|
|
19
|
+
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -3,4 +3,8 @@ export { pass, fail, error, Checker } from "./result";
|
|
|
3
3
|
export { findFirstFile, readFileSafe, requireFile, fileContains, walkDir, listSubdirs, countTsFiles, countTestFiles, findFirstDir, findFilesByName, } from "./fs";
|
|
4
4
|
export { grepFiles, grepCount, grepLines } from "./grep";
|
|
5
5
|
export { requireMarkdownSections, requireTerms, requireJsonKeys, } from "./content";
|
|
6
|
-
export { requireFileCheck, requireDirCheck, requirePackageStructure, checkTestsExist, } from "./checks";
|
|
6
|
+
export { requireFileCheck, requireDirCheck, requirePackageStructure, checkTestsExist, checkTypescriptCompiles, } from "./checks";
|
|
7
|
+
export { runCommand } from "./exec";
|
|
8
|
+
export type { RunCommand, RunCommandResult } from "./exec";
|
|
9
|
+
export { createValidatorRegistry } from "./validator-registry";
|
|
10
|
+
export type { ValidatorResult, ValidatorContext, RegisteredValidator, ValidatorRegistry, } from "./validator-registry";
|
package/dist/src/index.js
CHANGED
|
@@ -233,6 +233,24 @@ function requireJsonKeys(checker, filePath, keys) {
|
|
|
233
233
|
// packages/validator-kit/src/checks.ts
|
|
234
234
|
import { existsSync as existsSync2, statSync as statSync2 } from "fs";
|
|
235
235
|
import { resolve as resolve2 } from "path";
|
|
236
|
+
|
|
237
|
+
// packages/validator-kit/src/exec.ts
|
|
238
|
+
var runCommand = async (cmd, cwd) => {
|
|
239
|
+
const proc = Bun.spawn([...cmd], {
|
|
240
|
+
cwd,
|
|
241
|
+
stdout: "pipe",
|
|
242
|
+
stderr: "pipe",
|
|
243
|
+
env: { ...process.env, PWD: cwd, INIT_CWD: cwd }
|
|
244
|
+
});
|
|
245
|
+
const exitCode = await proc.exited;
|
|
246
|
+
return {
|
|
247
|
+
exitCode,
|
|
248
|
+
stdout: await new Response(proc.stdout).text(),
|
|
249
|
+
stderr: await new Response(proc.stderr).text()
|
|
250
|
+
};
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
// packages/validator-kit/src/checks.ts
|
|
236
254
|
function requireFileCheck(checker, path, checkName) {
|
|
237
255
|
if (existsSync2(path)) {
|
|
238
256
|
checker.pass(checkName);
|
|
@@ -260,8 +278,101 @@ function checkTestsExist(checker, projectDir, checkName) {
|
|
|
260
278
|
checker.fail(checkName, "no test files found");
|
|
261
279
|
}
|
|
262
280
|
}
|
|
281
|
+
function resolveTypescriptCommand(projectDir) {
|
|
282
|
+
const tsconfigPath = resolve2(projectDir, "tsconfig.json");
|
|
283
|
+
const candidates = [
|
|
284
|
+
resolve2(projectDir, "node_modules", "typescript", "lib", "tsc.js"),
|
|
285
|
+
resolve2(projectDir, "..", "node_modules", "typescript", "lib", "tsc.js"),
|
|
286
|
+
resolve2(projectDir, "..", "..", "node_modules", "typescript", "lib", "tsc.js"),
|
|
287
|
+
resolve2(projectDir, "..", "..", "..", "node_modules", "typescript", "lib", "tsc.js")
|
|
288
|
+
];
|
|
289
|
+
for (const candidate of candidates) {
|
|
290
|
+
if (existsSync2(candidate)) {
|
|
291
|
+
return [process.execPath, candidate, "--noEmit", "--project", tsconfigPath];
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return ["bunx", "tsc", "--noEmit", "--project", tsconfigPath];
|
|
295
|
+
}
|
|
296
|
+
async function checkTypescriptCompiles(checker, projectDir, checkName, run = runCommand) {
|
|
297
|
+
if (!existsSync2(resolve2(projectDir, "tsconfig.json"))) {
|
|
298
|
+
checker.fail(checkName, "no tsconfig.json found");
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const command = resolveTypescriptCommand(projectDir);
|
|
302
|
+
const result = await run(command, projectDir);
|
|
303
|
+
if (result.exitCode === 0) {
|
|
304
|
+
checker.pass(checkName);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const combined = result.stdout + result.stderr;
|
|
308
|
+
const errorCount = combined.split(`
|
|
309
|
+
`).filter((line) => line.includes("error TS")).length;
|
|
310
|
+
const detail = errorCount > 0 ? `${errorCount} TypeScript errors` : `TypeScript command exited ${result.exitCode} without TS diagnostics`;
|
|
311
|
+
checker.fail(checkName, detail);
|
|
312
|
+
}
|
|
313
|
+
// packages/validator-kit/src/validator-registry.ts
|
|
314
|
+
import { existsSync as existsSync3 } from "fs";
|
|
315
|
+
import { join } from "path";
|
|
316
|
+
function createValidatorRegistry() {
|
|
317
|
+
const map = new Map;
|
|
318
|
+
const order = [];
|
|
319
|
+
const registry = {
|
|
320
|
+
register(v) {
|
|
321
|
+
if (map.has(v.id))
|
|
322
|
+
throw new Error(`validator already registered: ${v.id}`);
|
|
323
|
+
map.set(v.id, v);
|
|
324
|
+
order.push(v);
|
|
325
|
+
},
|
|
326
|
+
resolve(id) {
|
|
327
|
+
const v = map.get(id);
|
|
328
|
+
if (!v)
|
|
329
|
+
throw new Error(`validator not registered: ${id}`);
|
|
330
|
+
return v;
|
|
331
|
+
},
|
|
332
|
+
list: () => order
|
|
333
|
+
};
|
|
334
|
+
registerBuiltInValidators(registry);
|
|
335
|
+
return registry;
|
|
336
|
+
}
|
|
337
|
+
function registerBuiltInValidators(registry) {
|
|
338
|
+
registry.register({
|
|
339
|
+
id: "std:typecheck",
|
|
340
|
+
category: "custom",
|
|
341
|
+
description: "Runs the package typecheck script when present.",
|
|
342
|
+
run: async (ctx) => runStdTypecheck(ctx)
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
async function runStdTypecheck(ctx) {
|
|
346
|
+
const packageJsonPath = join(ctx.workspaceRoot, "package.json");
|
|
347
|
+
if (!existsSync3(packageJsonPath)) {
|
|
348
|
+
return {
|
|
349
|
+
id: "std:typecheck",
|
|
350
|
+
passed: false,
|
|
351
|
+
summary: `package.json not found at ${packageJsonPath}`
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const proc = Bun.spawn(["bun", "run", "typecheck"], {
|
|
355
|
+
cwd: ctx.workspaceRoot,
|
|
356
|
+
env: process.env,
|
|
357
|
+
stdout: "pipe",
|
|
358
|
+
stderr: "pipe"
|
|
359
|
+
});
|
|
360
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
361
|
+
proc.exited,
|
|
362
|
+
new Response(proc.stdout).text(),
|
|
363
|
+
new Response(proc.stderr).text()
|
|
364
|
+
]);
|
|
365
|
+
const output = `${stdout}${stderr}`.trim();
|
|
366
|
+
return {
|
|
367
|
+
id: "std:typecheck",
|
|
368
|
+
passed: exitCode === 0,
|
|
369
|
+
summary: exitCode === 0 ? "typecheck passed" : "typecheck failed",
|
|
370
|
+
...output ? { details: output.slice(0, 4000) } : {}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
263
373
|
export {
|
|
264
374
|
walkDir,
|
|
375
|
+
runCommand,
|
|
265
376
|
requireTerms,
|
|
266
377
|
requirePackageStructure,
|
|
267
378
|
requireMarkdownSections,
|
|
@@ -281,8 +392,10 @@ export {
|
|
|
281
392
|
fileContains,
|
|
282
393
|
fail,
|
|
283
394
|
error,
|
|
395
|
+
createValidatorRegistry,
|
|
284
396
|
countTsFiles,
|
|
285
397
|
countTestFiles,
|
|
398
|
+
checkTypescriptCompiles,
|
|
286
399
|
checkTestsExist,
|
|
287
400
|
Checker
|
|
288
401
|
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ValidatorRegistration } from "@rig/contracts";
|
|
2
|
+
export interface ValidatorResult {
|
|
3
|
+
id: string;
|
|
4
|
+
passed: boolean;
|
|
5
|
+
summary: string;
|
|
6
|
+
details?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ValidatorContext {
|
|
9
|
+
taskId: string;
|
|
10
|
+
workspaceRoot: string;
|
|
11
|
+
scope: readonly string[];
|
|
12
|
+
/** Absolute path to the monorepo checkout root (MONOREPO_ROOT / MONOREPO_MAIN_ROOT env equiv). */
|
|
13
|
+
monorepoRoot?: string;
|
|
14
|
+
/** Absolute path to the task's artifacts output directory (ARTIFACTS_DIR env equiv). */
|
|
15
|
+
artifactsDir?: string;
|
|
16
|
+
/** The full task config entry for this task — opaque to the registry, but available for advanced validators. */
|
|
17
|
+
taskConfig?: unknown;
|
|
18
|
+
}
|
|
19
|
+
export interface RegisteredValidator extends ValidatorRegistration {
|
|
20
|
+
run(ctx: ValidatorContext): Promise<ValidatorResult>;
|
|
21
|
+
}
|
|
22
|
+
export interface ValidatorRegistry {
|
|
23
|
+
register(v: RegisteredValidator): void;
|
|
24
|
+
resolve(id: string): RegisteredValidator;
|
|
25
|
+
list(): readonly RegisteredValidator[];
|
|
26
|
+
}
|
|
27
|
+
export declare function createValidatorRegistry(): ValidatorRegistry;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// packages/validator-kit/src/validator-registry.ts
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { join } from "path";
|
|
5
|
+
function createValidatorRegistry() {
|
|
6
|
+
const map = new Map;
|
|
7
|
+
const order = [];
|
|
8
|
+
const registry = {
|
|
9
|
+
register(v) {
|
|
10
|
+
if (map.has(v.id))
|
|
11
|
+
throw new Error(`validator already registered: ${v.id}`);
|
|
12
|
+
map.set(v.id, v);
|
|
13
|
+
order.push(v);
|
|
14
|
+
},
|
|
15
|
+
resolve(id) {
|
|
16
|
+
const v = map.get(id);
|
|
17
|
+
if (!v)
|
|
18
|
+
throw new Error(`validator not registered: ${id}`);
|
|
19
|
+
return v;
|
|
20
|
+
},
|
|
21
|
+
list: () => order
|
|
22
|
+
};
|
|
23
|
+
registerBuiltInValidators(registry);
|
|
24
|
+
return registry;
|
|
25
|
+
}
|
|
26
|
+
function registerBuiltInValidators(registry) {
|
|
27
|
+
registry.register({
|
|
28
|
+
id: "std:typecheck",
|
|
29
|
+
category: "custom",
|
|
30
|
+
description: "Runs the package typecheck script when present.",
|
|
31
|
+
run: async (ctx) => runStdTypecheck(ctx)
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function runStdTypecheck(ctx) {
|
|
35
|
+
const packageJsonPath = join(ctx.workspaceRoot, "package.json");
|
|
36
|
+
if (!existsSync(packageJsonPath)) {
|
|
37
|
+
return {
|
|
38
|
+
id: "std:typecheck",
|
|
39
|
+
passed: false,
|
|
40
|
+
summary: `package.json not found at ${packageJsonPath}`
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const proc = Bun.spawn(["bun", "run", "typecheck"], {
|
|
44
|
+
cwd: ctx.workspaceRoot,
|
|
45
|
+
env: process.env,
|
|
46
|
+
stdout: "pipe",
|
|
47
|
+
stderr: "pipe"
|
|
48
|
+
});
|
|
49
|
+
const [exitCode, stdout, stderr] = await Promise.all([
|
|
50
|
+
proc.exited,
|
|
51
|
+
new Response(proc.stdout).text(),
|
|
52
|
+
new Response(proc.stderr).text()
|
|
53
|
+
]);
|
|
54
|
+
const output = `${stdout}${stderr}`.trim();
|
|
55
|
+
return {
|
|
56
|
+
id: "std:typecheck",
|
|
57
|
+
passed: exitCode === 0,
|
|
58
|
+
summary: exitCode === 0 ? "typecheck passed" : "typecheck failed",
|
|
59
|
+
...output ? { details: output.slice(0, 4000) } : {}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export {
|
|
63
|
+
createValidatorRegistry
|
|
64
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-rig/validator-kit",
|
|
3
|
-
"version": "0.0.6-alpha.
|
|
3
|
+
"version": "0.0.6-alpha.158",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Validator schema helpers for Rig plugin contributions; not a validation runtime.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"module": "./dist/src/index.js",
|
|
22
22
|
"types": "./dist/src/index.d.ts",
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.
|
|
24
|
+
"@rig/contracts": "npm:@h-rig/contracts@0.0.6-alpha.158",
|
|
25
25
|
"effect": "4.0.0-beta.90"
|
|
26
26
|
}
|
|
27
27
|
}
|