@outfitter/tooling 0.3.4 → 0.3.5

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 (50) hide show
  1. package/README.md +12 -3
  2. package/dist/cli/check-changeset.d.ts +28 -12
  3. package/dist/cli/check-changeset.js +5 -1
  4. package/dist/cli/check-exports.d.ts +2 -1
  5. package/dist/cli/check-exports.js +6 -3
  6. package/dist/cli/check-home-paths.d.ts +31 -0
  7. package/dist/cli/check-home-paths.js +12 -0
  8. package/dist/cli/check-readme-imports.d.ts +2 -1
  9. package/dist/cli/check-tsdoc.d.ts +4 -1
  10. package/dist/cli/check-tsdoc.js +16 -10
  11. package/dist/cli/index.js +16 -4
  12. package/dist/cli/internal/exports-analysis.d.ts +2 -0
  13. package/dist/cli/internal/exports-analysis.js +10 -0
  14. package/dist/cli/internal/exports-fs.d.ts +17 -0
  15. package/dist/cli/internal/exports-fs.js +9 -0
  16. package/dist/cli/internal/pre-push-checks.d.ts +2 -0
  17. package/dist/cli/internal/pre-push-checks.js +37 -0
  18. package/dist/cli/internal/tsdoc-analysis.d.ts +3 -0
  19. package/dist/cli/internal/tsdoc-analysis.js +26 -0
  20. package/dist/cli/internal/tsdoc-formatting.d.ts +3 -0
  21. package/dist/cli/internal/tsdoc-formatting.js +10 -0
  22. package/dist/cli/internal/tsdoc-types.d.ts +2 -0
  23. package/dist/cli/internal/tsdoc-types.js +16 -0
  24. package/dist/cli/pre-push.d.ts +2 -55
  25. package/dist/cli/pre-push.js +6 -4
  26. package/dist/index.d.ts +4 -1
  27. package/dist/shared/@outfitter/tooling-0zjz8eg9.js +106 -0
  28. package/dist/shared/@outfitter/tooling-2vv5y3s4.js +145 -0
  29. package/dist/shared/@outfitter/{tooling-875svjnz.js → tooling-5xxctk9b.js} +2 -113
  30. package/dist/shared/@outfitter/tooling-5ynz680q.js +59 -0
  31. package/dist/shared/@outfitter/tooling-7437rmy6.js +39 -0
  32. package/dist/shared/@outfitter/tooling-8qcwr06t.d.ts +74 -0
  33. package/dist/shared/@outfitter/tooling-a59br34g.js +32 -0
  34. package/dist/shared/@outfitter/tooling-a6q3zh7t.js +86 -0
  35. package/dist/shared/@outfitter/tooling-ayps7c4x.js +58 -0
  36. package/dist/shared/@outfitter/{tooling-d363b88r.js → tooling-c8q6mj8z.js} +27 -148
  37. package/dist/shared/@outfitter/{tooling-wesswf21.d.ts → tooling-cb0b8wsx.d.ts} +9 -11
  38. package/dist/shared/@outfitter/tooling-f8q38e9z.d.ts +16 -0
  39. package/dist/shared/@outfitter/tooling-h5dnevjw.js +139 -0
  40. package/dist/shared/@outfitter/tooling-j8d1h2zd.d.ts +10 -0
  41. package/dist/shared/@outfitter/tooling-mq2xvz96.js +285 -0
  42. package/dist/shared/@outfitter/tooling-stgnc2zx.d.ts +85 -0
  43. package/dist/shared/@outfitter/tooling-tj9p41vj.d.ts +55 -0
  44. package/dist/shared/@outfitter/tooling-y43b117h.d.ts +13 -0
  45. package/lefthook.yml +5 -1
  46. package/package.json +10 -4
  47. package/registry/registry.json +5 -5
  48. package/dist/shared/@outfitter/tooling-6cxfdx0q.js +0 -187
  49. package/dist/shared/@outfitter/tooling-h04te11c.js +0 -231
  50. package/dist/shared/@outfitter/tooling-njw4z34x.d.ts +0 -140
@@ -0,0 +1,106 @@
1
+ // @bun
2
+ import {
3
+ computeExpectedExports
4
+ } from "./tooling-a6q3zh7t.js";
5
+ import {
6
+ compareExports
7
+ } from "./tooling-ayps7c4x.js";
8
+
9
+ // packages/tooling/src/cli/check-exports.ts
10
+ import { resolve } from "path";
11
+ var COLORS = {
12
+ reset: "\x1B[0m",
13
+ red: "\x1B[31m",
14
+ green: "\x1B[32m",
15
+ yellow: "\x1B[33m",
16
+ blue: "\x1B[34m",
17
+ dim: "\x1B[2m"
18
+ };
19
+ function resolveJsonMode(options = {}) {
20
+ return options.json ?? process.env["OUTFITTER_JSON"] === "1";
21
+ }
22
+ async function runCheckExports(options = {}) {
23
+ const cwd = process.cwd();
24
+ const configPath = resolve(cwd, "bunup.config.ts");
25
+ let workspaces;
26
+ try {
27
+ const configModule = await import(configPath);
28
+ const rawConfig = configModule.default;
29
+ if (!Array.isArray(rawConfig)) {
30
+ process.stderr.write(`bunup.config.ts must export a workspace array
31
+ `);
32
+ process.exitCode = 1;
33
+ return;
34
+ }
35
+ workspaces = rawConfig;
36
+ } catch {
37
+ process.stderr.write(`Could not load bunup.config.ts from ${cwd}
38
+ `);
39
+ process.exitCode = 1;
40
+ return;
41
+ }
42
+ const results = [];
43
+ for (const workspace of workspaces) {
44
+ const packageRoot = resolve(cwd, workspace.root);
45
+ const pkgPath = resolve(packageRoot, "package.json");
46
+ let pkg;
47
+ try {
48
+ pkg = await Bun.file(pkgPath).json();
49
+ } catch {
50
+ results.push({ name: workspace.name, status: "ok" });
51
+ continue;
52
+ }
53
+ const actual = typeof pkg.exports === "object" && pkg.exports !== null ? pkg.exports : {};
54
+ const expected = computeExpectedExports(packageRoot, workspace, pkg);
55
+ results.push(compareExports({
56
+ name: workspace.name,
57
+ actual,
58
+ expected,
59
+ path: workspace.root
60
+ }));
61
+ }
62
+ const checkResult = {
63
+ ok: results.every((r) => r.status === "ok"),
64
+ packages: results
65
+ };
66
+ if (resolveJsonMode(options)) {
67
+ process.stdout.write(`${JSON.stringify(checkResult, null, 2)}
68
+ `);
69
+ } else {
70
+ const drifted = results.filter((r) => r.status === "drift");
71
+ if (drifted.length === 0) {
72
+ process.stdout.write(`${COLORS.green}All ${results.length} packages have exports in sync.${COLORS.reset}
73
+ `);
74
+ } else {
75
+ process.stderr.write(`${COLORS.red}Export drift detected in ${drifted.length} package(s):${COLORS.reset}
76
+
77
+ `);
78
+ for (const result of drifted) {
79
+ const drift = result.drift;
80
+ if (!drift)
81
+ continue;
82
+ process.stderr.write(` ${COLORS.yellow}${result.name}${COLORS.reset} ${COLORS.dim}(${drift.path})${COLORS.reset}
83
+ `);
84
+ for (const key of drift.added) {
85
+ process.stderr.write(` ${COLORS.green}+ ${key}${COLORS.reset} ${COLORS.dim}(missing from package.json)${COLORS.reset}
86
+ `);
87
+ }
88
+ for (const key of drift.removed) {
89
+ process.stderr.write(` ${COLORS.red}- ${key}${COLORS.reset} ${COLORS.dim}(not in source)${COLORS.reset}
90
+ `);
91
+ }
92
+ for (const entry of drift.changed) {
93
+ process.stderr.write(` ${COLORS.yellow}~ ${entry.key}${COLORS.reset} ${COLORS.dim}(value mismatch)${COLORS.reset}
94
+ `);
95
+ }
96
+ process.stderr.write(`
97
+ `);
98
+ }
99
+ process.stderr.write(`Run ${COLORS.blue}bun run build${COLORS.reset} to regenerate exports.
100
+ `);
101
+ }
102
+ }
103
+ process.exitCode = checkResult.ok ? 0 : 1;
104
+ }
105
+
106
+ export { resolveJsonMode, runCheckExports };
@@ -0,0 +1,145 @@
1
+ // @bun
2
+ import {
3
+ canBypassRedPhaseByChangedFiles,
4
+ checkBunVersion,
5
+ createVerificationPlan,
6
+ getChangedFilesForPush,
7
+ getCurrentBranch,
8
+ hasPackageSourceChanges,
9
+ hasRedPhaseBranchInContext,
10
+ isRedPhaseBranch,
11
+ isReleaseBranch,
12
+ isScaffoldBranch,
13
+ printTsdocSummary,
14
+ readPackageScripts
15
+ } from "./tooling-c8q6mj8z.js";
16
+ // packages/tooling/src/cli/pre-push.ts
17
+ var COLORS = {
18
+ reset: "\x1B[0m",
19
+ red: "\x1B[31m",
20
+ green: "\x1B[32m",
21
+ yellow: "\x1B[33m",
22
+ blue: "\x1B[34m"
23
+ };
24
+ function log(msg) {
25
+ process.stdout.write(`${msg}
26
+ `);
27
+ }
28
+ function runScript(scriptName) {
29
+ log("");
30
+ log(`Running: ${COLORS.blue}bun run ${scriptName}${COLORS.reset}`);
31
+ const result = Bun.spawnSync(["bun", "run", scriptName], {
32
+ stdio: ["inherit", "inherit", "inherit"]
33
+ });
34
+ return result.exitCode === 0;
35
+ }
36
+ function maybeSkipForRedPhase(reason, branch) {
37
+ const changedFiles = getChangedFilesForPush();
38
+ if (!changedFiles.deterministic) {
39
+ log(`${COLORS.yellow}RED-phase bypass denied${COLORS.reset}: could not determine full push diff range`);
40
+ log("Running strict verification.");
41
+ log("");
42
+ return false;
43
+ }
44
+ if (!canBypassRedPhaseByChangedFiles(changedFiles)) {
45
+ log(`${COLORS.yellow}RED-phase bypass denied${COLORS.reset}: changed files are not test-only`);
46
+ if (changedFiles.files.length > 0) {
47
+ log(`Changed files (${changedFiles.source}): ${changedFiles.files.join(", ")}`);
48
+ } else {
49
+ log(`No changed files detected in ${changedFiles.source} range. Running strict verification.`);
50
+ }
51
+ log("");
52
+ return false;
53
+ }
54
+ if (reason === "branch") {
55
+ log(`${COLORS.yellow}TDD RED phase${COLORS.reset} detected: ${COLORS.blue}${branch}${COLORS.reset}`);
56
+ } else {
57
+ log(`${COLORS.yellow}Scaffold branch${COLORS.reset} with RED phase branch in context: ${COLORS.blue}${branch}${COLORS.reset}`);
58
+ }
59
+ log(`${COLORS.yellow}Skipping strict verification${COLORS.reset} - changed files are test-only`);
60
+ log(`Diff source: ${changedFiles.source}`);
61
+ log("");
62
+ log("Remember: GREEN phase (implementation) must make these tests pass!");
63
+ return true;
64
+ }
65
+ async function runPrePush(options = {}) {
66
+ log(`${COLORS.blue}Pre-push verify${COLORS.reset} (TDD-aware)`);
67
+ log("");
68
+ if (options.force) {
69
+ log(`${COLORS.yellow}Force flag set${COLORS.reset} - skipping strict verification`);
70
+ process.exitCode = 0;
71
+ return;
72
+ }
73
+ const versionCheck = checkBunVersion();
74
+ if (!versionCheck.matches) {
75
+ log(`${COLORS.red}Bun version mismatch${COLORS.reset}: running ${versionCheck.actual}, pinned ${versionCheck.expected}`);
76
+ log("Fix: bunx @outfitter/tooling upgrade-bun");
77
+ log("");
78
+ process.exitCode = 1;
79
+ return;
80
+ }
81
+ const branch = getCurrentBranch();
82
+ if (isReleaseBranch(branch)) {
83
+ log(`${COLORS.yellow}Release branch detected${COLORS.reset}: ${COLORS.blue}${branch}${COLORS.reset}`);
84
+ log(`${COLORS.yellow}Skipping strict verification${COLORS.reset} for automated changeset release push`);
85
+ process.exitCode = 0;
86
+ return;
87
+ }
88
+ if (isRedPhaseBranch(branch)) {
89
+ if (maybeSkipForRedPhase("branch", branch)) {
90
+ process.exitCode = 0;
91
+ return;
92
+ }
93
+ }
94
+ if (isScaffoldBranch(branch)) {
95
+ if (hasRedPhaseBranchInContext(branch)) {
96
+ if (maybeSkipForRedPhase("context", branch)) {
97
+ process.exitCode = 0;
98
+ return;
99
+ }
100
+ }
101
+ }
102
+ const plan = createVerificationPlan(readPackageScripts());
103
+ if (!plan.ok) {
104
+ log(`${COLORS.red}Strict pre-push verification is not configured${COLORS.reset}`);
105
+ log(plan.error);
106
+ log("");
107
+ log("Add one of:");
108
+ log(" - verify:push");
109
+ log(" - verify:ci");
110
+ log(" - typecheck + (check or lint) + build + test");
111
+ process.exitCode = 1;
112
+ return;
113
+ }
114
+ log(`Running strict verification for branch: ${COLORS.blue}${branch}${COLORS.reset}`);
115
+ if (plan.source === "verify:push" || plan.source === "verify:ci") {
116
+ log(`Using \`${plan.source}\` script.`);
117
+ } else {
118
+ log(`Using fallback scripts: ${plan.scripts.join(" -> ")}`);
119
+ }
120
+ for (const scriptName of plan.scripts) {
121
+ if (runScript(scriptName)) {
122
+ continue;
123
+ }
124
+ log("");
125
+ log(`${COLORS.red}Verification failed${COLORS.reset} on script: ${scriptName}`);
126
+ log("");
127
+ log("If this is intentional TDD RED phase work, name your branch:");
128
+ log(" - feature-tests");
129
+ log(" - feature/tests");
130
+ log(" - feature_tests");
131
+ process.exitCode = 1;
132
+ return;
133
+ }
134
+ const changedFiles = getChangedFilesForPush();
135
+ if (hasPackageSourceChanges(changedFiles)) {
136
+ try {
137
+ await printTsdocSummary(log);
138
+ } catch {}
139
+ }
140
+ log("");
141
+ log(`${COLORS.green}Strict verification passed${COLORS.reset}`);
142
+ process.exitCode = 0;
143
+ }
144
+
145
+ export { runPrePush };
@@ -3,44 +3,9 @@ import {
3
3
  __require
4
4
  } from "./tooling-jnrs9rqd.js";
5
5
 
6
- // packages/tooling/src/cli/check-tsdoc.ts
6
+ // packages/tooling/src/cli/internal/tsdoc-analysis.ts
7
7
  import { resolve } from "path";
8
8
  import ts from "typescript";
9
- import { z } from "zod";
10
- var coverageLevelSchema = z.enum([
11
- "documented",
12
- "partial",
13
- "undocumented"
14
- ]);
15
- var declarationCoverageSchema = z.object({
16
- name: z.string(),
17
- kind: z.string(),
18
- level: coverageLevelSchema,
19
- file: z.string(),
20
- line: z.number()
21
- });
22
- var coverageSummarySchema = z.object({
23
- documented: z.number(),
24
- partial: z.number(),
25
- undocumented: z.number(),
26
- total: z.number(),
27
- percentage: z.number()
28
- });
29
- var packageCoverageSchema = z.object({
30
- name: z.string(),
31
- path: z.string(),
32
- declarations: z.array(declarationCoverageSchema),
33
- documented: z.number(),
34
- partial: z.number(),
35
- undocumented: z.number(),
36
- total: z.number(),
37
- percentage: z.number()
38
- });
39
- var tsDocCheckResultSchema = z.object({
40
- ok: z.boolean(),
41
- packages: z.array(packageCoverageSchema),
42
- summary: coverageSummarySchema
43
- });
44
9
  function isExportedDeclaration(node) {
45
10
  if (ts.isExportDeclaration(node))
46
11
  return false;
@@ -159,24 +124,6 @@ function calculateCoverage(declarations) {
159
124
  const percentage = Math.round(score / total * 100);
160
125
  return { documented, partial, undocumented, total, percentage };
161
126
  }
162
- var COLORS = {
163
- reset: "\x1B[0m",
164
- red: "\x1B[31m",
165
- green: "\x1B[32m",
166
- yellow: "\x1B[33m",
167
- blue: "\x1B[34m",
168
- dim: "\x1B[2m",
169
- bold: "\x1B[1m"
170
- };
171
- function resolveJsonMode(options = {}) {
172
- return options.json ?? process.env["OUTFITTER_JSON"] === "1";
173
- }
174
- function bar(percentage, width = 20) {
175
- const filled = Math.round(percentage / 100 * width);
176
- const empty = width - filled;
177
- const color = percentage >= 80 ? COLORS.green : percentage >= 50 ? COLORS.yellow : COLORS.red;
178
- return `${color}${"\u2588".repeat(filled)}${COLORS.dim}${"\u2591".repeat(empty)}${COLORS.reset}`;
179
- }
180
127
  function discoverPackages(cwd) {
181
128
  const packages = [];
182
129
  const seenEntryPoints = new Set;
@@ -344,63 +291,5 @@ function analyzeCheckTsdoc(options = {}) {
344
291
  summary
345
292
  };
346
293
  }
347
- function printCheckTsdocHuman(result, options) {
348
- process.stdout.write(`
349
- ${COLORS.bold}TSDoc Coverage Report${COLORS.reset}
350
-
351
- `);
352
- for (const pkg of result.packages) {
353
- const color = pkg.percentage >= 80 ? COLORS.green : pkg.percentage >= 50 ? COLORS.yellow : COLORS.red;
354
- process.stdout.write(` ${color}${pkg.percentage.toString().padStart(3)}%${COLORS.reset} ${bar(pkg.percentage)} ${pkg.name}
355
- `);
356
- if (pkg.total > 0) {
357
- const parts = [];
358
- if (pkg.documented > 0)
359
- parts.push(`${COLORS.green}${pkg.documented} documented${COLORS.reset}`);
360
- if (pkg.partial > 0)
361
- parts.push(`${COLORS.yellow}${pkg.partial} partial${COLORS.reset}`);
362
- if (pkg.undocumented > 0)
363
- parts.push(`${COLORS.red}${pkg.undocumented} undocumented${COLORS.reset}`);
364
- process.stdout.write(` ${COLORS.dim}${pkg.total} declarations:${COLORS.reset} ${parts.join(", ")}
365
- `);
366
- } else {
367
- process.stdout.write(` ${COLORS.dim}no exported declarations${COLORS.reset}
368
- `);
369
- }
370
- }
371
- const { summary } = result;
372
- process.stdout.write(`
373
- ${COLORS.bold}Summary:${COLORS.reset} ${summary.percentage}% coverage (${summary.documented} documented, ${summary.partial} partial, ${summary.undocumented} undocumented of ${summary.total} total)
374
- `);
375
- const minCoverage = options?.minCoverage ?? 0;
376
- if (options?.strict && summary.percentage < minCoverage) {
377
- process.stderr.write(`
378
- ${COLORS.red}Coverage ${summary.percentage}% is below minimum threshold of ${minCoverage}%${COLORS.reset}
379
- `);
380
- }
381
- process.stdout.write(`
382
- `);
383
- }
384
- async function runCheckTsdoc(options = {}) {
385
- const result = analyzeCheckTsdoc(options);
386
- if (!result) {
387
- process.stderr.write(`No packages found with src/index.ts entry points.
388
- ` + `Searched: packages/*/src/index.ts, apps/*/src/index.ts, src/index.ts
389
- ` + `Use --package <path> to specify a package path explicitly.
390
- `);
391
- process.exitCode = 1;
392
- return;
393
- }
394
- if (resolveJsonMode(options)) {
395
- process.stdout.write(`${JSON.stringify(result, null, 2)}
396
- `);
397
- } else {
398
- printCheckTsdocHuman(result, {
399
- strict: options.strict,
400
- minCoverage: options.minCoverage
401
- });
402
- }
403
- process.exitCode = result.ok ? 0 : 1;
404
- }
405
294
 
406
- export { coverageLevelSchema, declarationCoverageSchema, coverageSummarySchema, packageCoverageSchema, tsDocCheckResultSchema, isExportedDeclaration, getDeclarationName, getDeclarationKind, classifyDeclaration, analyzeSourceFile, calculateCoverage, resolveJsonMode, analyzeCheckTsdoc, printCheckTsdocHuman, runCheckTsdoc };
295
+ export { isExportedDeclaration, getDeclarationName, getDeclarationKind, classifyDeclaration, analyzeSourceFile, calculateCoverage, discoverPackages, collectReExportedSourceFiles, analyzePackage, analyzeCheckTsdoc };
@@ -0,0 +1,59 @@
1
+ // @bun
2
+ // packages/tooling/src/cli/internal/tsdoc-formatting.ts
3
+ var COLORS = {
4
+ reset: "\x1B[0m",
5
+ red: "\x1B[31m",
6
+ green: "\x1B[32m",
7
+ yellow: "\x1B[33m",
8
+ blue: "\x1B[34m",
9
+ dim: "\x1B[2m",
10
+ bold: "\x1B[1m"
11
+ };
12
+ function resolveJsonMode(options = {}) {
13
+ return options.json ?? process.env["OUTFITTER_JSON"] === "1";
14
+ }
15
+ function bar(percentage, width = 20) {
16
+ const filled = Math.round(percentage / 100 * width);
17
+ const empty = width - filled;
18
+ const color = percentage >= 80 ? COLORS.green : percentage >= 50 ? COLORS.yellow : COLORS.red;
19
+ return `${color}${"\u2588".repeat(filled)}${COLORS.dim}${"\u2591".repeat(empty)}${COLORS.reset}`;
20
+ }
21
+ function printCheckTsdocHuman(result, options) {
22
+ process.stdout.write(`
23
+ ${COLORS.bold}TSDoc Coverage Report${COLORS.reset}
24
+
25
+ `);
26
+ for (const pkg of result.packages) {
27
+ const color = pkg.percentage >= 80 ? COLORS.green : pkg.percentage >= 50 ? COLORS.yellow : COLORS.red;
28
+ process.stdout.write(` ${color}${pkg.percentage.toString().padStart(3)}%${COLORS.reset} ${bar(pkg.percentage)} ${pkg.name}
29
+ `);
30
+ if (pkg.total > 0) {
31
+ const parts = [];
32
+ if (pkg.documented > 0)
33
+ parts.push(`${COLORS.green}${pkg.documented} documented${COLORS.reset}`);
34
+ if (pkg.partial > 0)
35
+ parts.push(`${COLORS.yellow}${pkg.partial} partial${COLORS.reset}`);
36
+ if (pkg.undocumented > 0)
37
+ parts.push(`${COLORS.red}${pkg.undocumented} undocumented${COLORS.reset}`);
38
+ process.stdout.write(` ${COLORS.dim}${pkg.total} declarations:${COLORS.reset} ${parts.join(", ")}
39
+ `);
40
+ } else {
41
+ process.stdout.write(` ${COLORS.dim}no exported declarations${COLORS.reset}
42
+ `);
43
+ }
44
+ }
45
+ const { summary } = result;
46
+ process.stdout.write(`
47
+ ${COLORS.bold}Summary:${COLORS.reset} ${summary.percentage}% coverage (${summary.documented} documented, ${summary.partial} partial, ${summary.undocumented} undocumented of ${summary.total} total)
48
+ `);
49
+ const minCoverage = options?.minCoverage ?? 0;
50
+ if (options?.strict && summary.percentage < minCoverage) {
51
+ process.stderr.write(`
52
+ ${COLORS.red}Coverage ${summary.percentage}% is below minimum threshold of ${minCoverage}%${COLORS.reset}
53
+ `);
54
+ }
55
+ process.stdout.write(`
56
+ `);
57
+ }
58
+
59
+ export { resolveJsonMode, printCheckTsdocHuman };
@@ -0,0 +1,39 @@
1
+ // @bun
2
+ // packages/tooling/src/cli/internal/tsdoc-types.ts
3
+ import { z } from "zod";
4
+ var coverageLevelSchema = z.enum([
5
+ "documented",
6
+ "partial",
7
+ "undocumented"
8
+ ]);
9
+ var declarationCoverageSchema = z.object({
10
+ name: z.string(),
11
+ kind: z.string(),
12
+ level: coverageLevelSchema,
13
+ file: z.string(),
14
+ line: z.number()
15
+ });
16
+ var coverageSummarySchema = z.object({
17
+ documented: z.number(),
18
+ partial: z.number(),
19
+ undocumented: z.number(),
20
+ total: z.number(),
21
+ percentage: z.number()
22
+ });
23
+ var packageCoverageSchema = z.object({
24
+ name: z.string(),
25
+ path: z.string(),
26
+ declarations: z.array(declarationCoverageSchema),
27
+ documented: z.number(),
28
+ partial: z.number(),
29
+ undocumented: z.number(),
30
+ total: z.number(),
31
+ percentage: z.number()
32
+ });
33
+ var tsDocCheckResultSchema = z.object({
34
+ ok: z.boolean(),
35
+ packages: z.array(packageCoverageSchema),
36
+ summary: coverageSummarySchema
37
+ });
38
+
39
+ export { coverageLevelSchema, declarationCoverageSchema, coverageSummarySchema, packageCoverageSchema, tsDocCheckResultSchema };
@@ -0,0 +1,74 @@
1
+ /** Get current git branch name */
2
+ declare function getCurrentBranch(): string;
3
+ declare function runGit(args: readonly string[]): {
4
+ readonly ok: boolean;
5
+ readonly lines: readonly string[];
6
+ };
7
+ /** Check if branch is a TDD RED phase branch */
8
+ declare function isRedPhaseBranch(branch: string): boolean;
9
+ /** Check if branch is a scaffold branch */
10
+ declare function isScaffoldBranch(branch: string): boolean;
11
+ /** Check if branch is a changeset release branch */
12
+ declare function isReleaseBranch(branch: string): boolean;
13
+ /** Determine if a file path is test-related */
14
+ declare function isTestOnlyPath(path: string): boolean;
15
+ /** Check if all paths in the list are test-related */
16
+ declare function areFilesTestOnly(paths: readonly string[]): boolean;
17
+ interface PushChangedFiles {
18
+ readonly files: readonly string[];
19
+ readonly deterministic: boolean;
20
+ readonly source: "upstream" | "baseRef" | "undetermined";
21
+ }
22
+ /** Check if bypass is safe: deterministic range with test-only changes */
23
+ declare function canBypassRedPhaseByChangedFiles(changedFiles: PushChangedFiles): boolean;
24
+ /**
25
+ * Check whether any changed files are package source files.
26
+ *
27
+ * Matches files under "packages/PKGNAME/src/" (any depth).
28
+ */
29
+ declare function hasPackageSourceChanges(changedFiles: PushChangedFiles): boolean;
30
+ /** Determine which files have changed for the current push */
31
+ declare function getChangedFilesForPush(): PushChangedFiles;
32
+ /** Check if any branch in context is a RED phase branch */
33
+ declare function hasRedPhaseBranchInContext(currentBranch: string): boolean;
34
+ type ScriptMap = Readonly<Record<string, string | undefined>>;
35
+ type VerificationPlan = {
36
+ readonly ok: true;
37
+ readonly scripts: readonly string[];
38
+ readonly source: "verify:push" | "verify:ci" | "fallback";
39
+ } | {
40
+ readonly ok: false;
41
+ readonly error: string;
42
+ };
43
+ /**
44
+ * Derive strict pre-push verification from package scripts.
45
+ *
46
+ * Priority:
47
+ * 1) `verify:push`
48
+ * 2) `verify:ci`
49
+ * 3) fallback sequence: `typecheck`, `check|lint`, `build`, `test`
50
+ */
51
+ declare function createVerificationPlan(scripts: ScriptMap): VerificationPlan;
52
+ /** Read and normalize scripts from package.json */
53
+ declare function readPackageScripts(cwd?: string): ScriptMap;
54
+ interface BunVersionCheckResult {
55
+ readonly matches: boolean;
56
+ readonly expected?: string;
57
+ readonly actual?: string;
58
+ }
59
+ /**
60
+ * Check that the local Bun version matches the pinned version in ".bun-version".
61
+ *
62
+ * @param projectRoot - Directory containing ".bun-version" (defaults to cwd)
63
+ * @returns Result indicating whether versions match
64
+ */
65
+ declare function checkBunVersion(projectRoot?: string): BunVersionCheckResult;
66
+ /**
67
+ * Print a one-line TSDoc coverage summary across all workspace packages.
68
+ *
69
+ * Discovers package entry points ("packages/STAR/src/index.ts"), analyzes
70
+ * TSDoc coverage, and outputs a single summary line. This is advisory
71
+ * only -- the result does not affect the exit code.
72
+ */
73
+ declare function printTsdocSummary(log: (msg: string) => void): Promise<void>;
74
+ export { getCurrentBranch, runGit, isRedPhaseBranch, isScaffoldBranch, isReleaseBranch, isTestOnlyPath, areFilesTestOnly, PushChangedFiles, canBypassRedPhaseByChangedFiles, hasPackageSourceChanges, getChangedFilesForPush, hasRedPhaseBranchInContext, VerificationPlan, createVerificationPlan, readPackageScripts, BunVersionCheckResult, checkBunVersion, printTsdocSummary };
@@ -0,0 +1,32 @@
1
+ // @bun
2
+ import {
3
+ printCheckTsdocHuman,
4
+ resolveJsonMode
5
+ } from "./tooling-5ynz680q.js";
6
+ import {
7
+ analyzeCheckTsdoc
8
+ } from "./tooling-5xxctk9b.js";
9
+ // packages/tooling/src/cli/check-tsdoc.ts
10
+ async function runCheckTsdoc(options = {}) {
11
+ const result = analyzeCheckTsdoc(options);
12
+ if (!result) {
13
+ process.stderr.write(`No packages found with src/index.ts entry points.
14
+ ` + `Searched: packages/*/src/index.ts, apps/*/src/index.ts, src/index.ts
15
+ ` + `Use --package <path> to specify a package path explicitly.
16
+ `);
17
+ process.exitCode = 1;
18
+ return;
19
+ }
20
+ if (resolveJsonMode(options)) {
21
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
22
+ `);
23
+ } else {
24
+ printCheckTsdocHuman(result, {
25
+ strict: options.strict,
26
+ minCoverage: options.minCoverage
27
+ });
28
+ }
29
+ process.exitCode = result.ok ? 0 : 1;
30
+ }
31
+
32
+ export { runCheckTsdoc };
@@ -0,0 +1,86 @@
1
+ // @bun
2
+ import {
3
+ entryToSubpath
4
+ } from "./tooling-ayps7c4x.js";
5
+
6
+ // packages/tooling/src/cli/internal/exports-fs.ts
7
+ function matchesExclude(subpath, excludes) {
8
+ return excludes.some((pattern) => new Bun.Glob(pattern).match(subpath));
9
+ }
10
+ var CLI_EXCLUSION_PATTERNS = [
11
+ "**/cli.ts",
12
+ "**/cli/index.ts",
13
+ "**/bin.ts",
14
+ "**/bin/index.ts"
15
+ ];
16
+ function isCliEntrypoint(entry) {
17
+ return CLI_EXCLUSION_PATTERNS.some((pattern) => new Bun.Glob(pattern).match(entry));
18
+ }
19
+ function buildExportValue(entry) {
20
+ const distPath = entry.replace(/^src\//, "").replace(/\.[cm]?[jt]sx?$/, "");
21
+ return {
22
+ import: {
23
+ types: `./dist/${distPath}.d.ts`,
24
+ default: `./dist/${distPath}.js`
25
+ }
26
+ };
27
+ }
28
+ function discoverEntries(packageRoot) {
29
+ const glob = new Bun.Glob("src/**/*.ts");
30
+ const entries = [];
31
+ for (const match of glob.scanSync({ cwd: packageRoot, dot: false })) {
32
+ if (match.includes("__tests__") || match.endsWith(".test.ts")) {
33
+ continue;
34
+ }
35
+ entries.push(match);
36
+ }
37
+ return entries.toSorted();
38
+ }
39
+ function addConfigFileExports(expected, pkg) {
40
+ const CONFIG_RE = /\.(json|jsonc|yml|yaml|toml)$/;
41
+ const configFiles = (pkg.files ?? []).filter((file) => CONFIG_RE.test(file) && file !== "package.json");
42
+ for (const file of configFiles) {
43
+ expected[`./${file}`] = `./${file}`;
44
+ let base = file.replace(CONFIG_RE, "");
45
+ const match = base.match(/^(.+)\.preset(?:\.(.+))?$/);
46
+ if (match?.[1]) {
47
+ base = match[2] ? `${match[1]}-${match[2]}` : match[1];
48
+ }
49
+ if (base !== file) {
50
+ expected[`./${base}`] = `./${file}`;
51
+ }
52
+ }
53
+ }
54
+ function computeExpectedExports(packageRoot, workspace, pkg) {
55
+ const entries = discoverEntries(packageRoot);
56
+ const exportsConfig = typeof workspace.config?.exports === "object" ? workspace.config.exports : undefined;
57
+ const excludes = exportsConfig?.exclude ?? [];
58
+ const customExports = exportsConfig?.customExports ?? {};
59
+ const expected = {};
60
+ const subpathEntries = new Map;
61
+ for (const entry of entries) {
62
+ if (isCliEntrypoint(entry))
63
+ continue;
64
+ const subpath = entryToSubpath(entry);
65
+ if (matchesExclude(subpath, excludes))
66
+ continue;
67
+ const existing = subpathEntries.get(subpath);
68
+ if (existing) {
69
+ if (!existing.endsWith("/index.ts") && entry.endsWith("/index.ts")) {
70
+ continue;
71
+ }
72
+ }
73
+ subpathEntries.set(subpath, entry);
74
+ }
75
+ for (const [subpath, entry] of subpathEntries) {
76
+ expected[subpath] = buildExportValue(entry);
77
+ }
78
+ for (const [key, value] of Object.entries(customExports)) {
79
+ expected[`./${key.replace(/^\.\//, "")}`] = value;
80
+ }
81
+ addConfigFileExports(expected, pkg);
82
+ expected["./package.json"] = "./package.json";
83
+ return expected;
84
+ }
85
+
86
+ export { computeExpectedExports };