vouchington-tooling 0.20.0 → 0.22.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 (257) hide show
  1. package/README.md +73 -10
  2. package/dist/agent-blackboard/snapshot-partition-format.mjs +2 -72
  3. package/dist/agent-blackboard/snapshot-partition-guards.d.mts +3 -0
  4. package/dist/agent-blackboard/snapshot-partition-guards.mjs +9 -0
  5. package/dist/agent-blackboard/snapshot-partition-predicates.d.mts +3 -0
  6. package/dist/agent-blackboard/snapshot-partition-predicates.mjs +65 -0
  7. package/dist/api-contract-discovery/header-contract-parser.d.mts +3 -0
  8. package/dist/api-contract-discovery/header-contract-parser.mjs +93 -0
  9. package/dist/api-contract-discovery/header-contract-registry.d.mts +3 -0
  10. package/dist/api-contract-discovery/header-contract-registry.mjs +38 -0
  11. package/dist/api-contract-discovery/header-contract-types.d.mts +19 -0
  12. package/dist/api-contract-discovery/header-contract-types.mjs +1 -0
  13. package/dist/api-contract-discovery/index.d.mts +31 -0
  14. package/dist/api-contract-discovery/index.mjs +26 -0
  15. package/dist/api-contract-discovery/query-contract-extraction.d.mts +3 -0
  16. package/dist/api-contract-discovery/query-contract-extraction.mjs +65 -0
  17. package/dist/api-contract-discovery/query-contract-literals.d.mts +8 -0
  18. package/dist/api-contract-discovery/query-contract-literals.mjs +54 -0
  19. package/dist/api-contract-discovery/query-contract-registry.d.mts +3 -0
  20. package/dist/api-contract-discovery/query-contract-registry.mjs +68 -0
  21. package/dist/api-contract-discovery/query-contract-types.d.mts +8 -0
  22. package/dist/api-contract-discovery/query-contract-types.mjs +1 -0
  23. package/dist/api-contract-discovery/registered-route-catalog.d.mts +10 -0
  24. package/dist/api-contract-discovery/registered-route-catalog.mjs +81 -0
  25. package/dist/api-contract-discovery/registered-route-handler-analysis.d.mts +2 -0
  26. package/dist/api-contract-discovery/registered-route-handler-analysis.mjs +74 -0
  27. package/dist/api-contract-discovery/request-contract-implicit.d.mts +17 -0
  28. package/dist/api-contract-discovery/request-contract-implicit.mjs +73 -0
  29. package/dist/api-contract-discovery/request-contract-lenient.d.mts +28 -0
  30. package/dist/api-contract-discovery/request-contract-lenient.mjs +59 -0
  31. package/dist/api-contract-discovery/request-contract-registry.d.mts +14 -0
  32. package/dist/api-contract-discovery/request-contract-registry.mjs +61 -0
  33. package/dist/api-contract-discovery/request-contract-route-analysis.d.mts +25 -0
  34. package/dist/api-contract-discovery/request-contract-route-analysis.mjs +68 -0
  35. package/dist/api-contract-discovery/request-contract-types.d.mts +12 -0
  36. package/dist/api-contract-discovery/request-contract-types.mjs +1 -0
  37. package/dist/api-contract-discovery/response-contract-ctx-analysis.d.mts +7 -0
  38. package/dist/api-contract-discovery/response-contract-ctx-analysis.mjs +28 -0
  39. package/dist/api-contract-discovery/response-contract-error-branch.d.mts +15 -0
  40. package/dist/api-contract-discovery/response-contract-error-branch.mjs +47 -0
  41. package/dist/api-contract-discovery/response-contract-implicit-sources.test-helpers.d.mts +145 -0
  42. package/dist/api-contract-discovery/response-contract-implicit-sources.test-helpers.mjs +145 -0
  43. package/dist/api-contract-discovery/response-contract-implicit.d.mts +5 -0
  44. package/dist/api-contract-discovery/response-contract-implicit.mjs +110 -0
  45. package/dist/api-contract-discovery/response-contract-lenient.d.mts +23 -0
  46. package/dist/api-contract-discovery/response-contract-lenient.mjs +52 -0
  47. package/dist/api-contract-discovery/response-contract-media.d.mts +3 -0
  48. package/dist/api-contract-discovery/response-contract-media.mjs +46 -0
  49. package/dist/api-contract-discovery/response-contract-registration.d.mts +11 -0
  50. package/dist/api-contract-discovery/response-contract-registration.mjs +61 -0
  51. package/dist/api-contract-discovery/response-contract-registry.d.mts +7 -0
  52. package/dist/api-contract-discovery/response-contract-registry.mjs +76 -0
  53. package/dist/api-contract-discovery/response-contract-route-analysis.d.mts +13 -0
  54. package/dist/api-contract-discovery/response-contract-route-analysis.mjs +144 -0
  55. package/dist/api-contract-discovery/response-contract-status.d.mts +27 -0
  56. package/dist/api-contract-discovery/response-contract-status.mjs +80 -0
  57. package/dist/api-contract-discovery/response-contract-types.d.mts +16 -0
  58. package/dist/api-contract-discovery/response-contract-types.mjs +1 -0
  59. package/dist/api-contract-discovery/test-setup.test-helpers.d.mts +3 -0
  60. package/dist/api-contract-discovery/test-setup.test-helpers.mjs +2 -0
  61. package/dist/ast-grep-examples/companion-parity-compare.d.mts +3 -0
  62. package/dist/ast-grep-examples/companion-parity-compare.mjs +58 -0
  63. package/dist/ast-grep-examples/companion-parity.d.mts +2 -1
  64. package/dist/ast-grep-examples/companion-parity.mjs +2 -58
  65. package/dist/browser-session-runner/attempt.mjs +29 -48
  66. package/dist/browser-session-runner/termination.d.mts +11 -0
  67. package/dist/browser-session-runner/termination.mjs +34 -0
  68. package/dist/cli/parse-direct-options.d.mts +26 -0
  69. package/dist/cli/parse-direct-options.mjs +72 -0
  70. package/dist/cli/parse-forwarded-commands.d.mts +42 -0
  71. package/dist/cli/parse-forwarded-commands.mjs +29 -0
  72. package/dist/cli/parse-options.d.mts +1 -2
  73. package/dist/cli/parse-options.mjs +2 -72
  74. package/dist/cli/parse.mjs +4 -26
  75. package/dist/compiler-build/freshness.d.mts +27 -0
  76. package/dist/compiler-build/freshness.mjs +116 -0
  77. package/dist/compiler-build/index.d.mts +2 -0
  78. package/dist/compiler-build/index.mjs +2 -0
  79. package/dist/compiler-build/settlement.d.mts +10 -0
  80. package/dist/compiler-build/settlement.mjs +14 -0
  81. package/dist/config-inventory/call-arguments.d.mts +1 -0
  82. package/dist/config-inventory/call-arguments.mjs +30 -0
  83. package/dist/config-inventory/collect.d.mts +3 -0
  84. package/dist/config-inventory/collect.mjs +43 -0
  85. package/dist/config-inventory/dynamic-configs.d.mts +2 -0
  86. package/dist/config-inventory/dynamic-configs.mjs +9 -0
  87. package/dist/config-inventory/env-expression-readers.d.mts +3 -0
  88. package/dist/config-inventory/env-expression-readers.mjs +117 -0
  89. package/dist/config-inventory/env-prefix-readers.d.mts +2 -0
  90. package/dist/config-inventory/env-prefix-readers.mjs +14 -0
  91. package/dist/config-inventory/env-reference-matchers.d.mts +3 -0
  92. package/dist/config-inventory/env-reference-matchers.mjs +9 -0
  93. package/dist/config-inventory/env-var-accumulator.d.mts +6 -0
  94. package/dist/config-inventory/env-var-accumulator.mjs +82 -0
  95. package/dist/config-inventory/env-vars.d.mts +4 -0
  96. package/dist/config-inventory/env-vars.mjs +108 -0
  97. package/dist/config-inventory/format.d.mts +2 -0
  98. package/dist/config-inventory/format.mjs +69 -0
  99. package/dist/config-inventory/index.d.mts +3 -0
  100. package/dist/config-inventory/index.mjs +2 -0
  101. package/dist/config-inventory/package-gates.d.mts +2 -0
  102. package/dist/config-inventory/package-gates.mjs +54 -0
  103. package/dist/config-inventory/shared.d.mts +3 -0
  104. package/dist/config-inventory/shared.mjs +7 -0
  105. package/dist/config-inventory/types.d.mts +96 -0
  106. package/dist/config-inventory/types.mjs +1 -0
  107. package/dist/contract-schema/contract-schema-bounded-array.d.mts +15 -0
  108. package/dist/contract-schema/contract-schema-bounded-array.mjs +32 -0
  109. package/dist/contract-schema/contract-schema-type-extractor.d.mts +2 -8
  110. package/dist/contract-schema/contract-schema-type-extractor.mjs +2 -31
  111. package/dist/coverage-transport/control-v2-parse-object.d.mts +12 -0
  112. package/dist/coverage-transport/control-v2-parse-object.mjs +28 -0
  113. package/dist/coverage-transport/control-v2.mjs +2 -27
  114. package/dist/dependency-license-policy/audit-directory.d.mts +28 -0
  115. package/dist/dependency-license-policy/audit-directory.mjs +115 -0
  116. package/dist/dependency-license-policy/audit-signals.d.mts +12 -0
  117. package/dist/dependency-license-policy/audit-signals.mjs +44 -0
  118. package/dist/dependency-license-policy/audit-store.d.mts +12 -0
  119. package/dist/dependency-license-policy/audit-store.mjs +32 -0
  120. package/dist/dependency-license-policy/collect.d.mts +3 -1
  121. package/dist/dependency-license-policy/collect.mjs +16 -21
  122. package/dist/dependency-license-policy/execute-pnpm.d.mts +10 -0
  123. package/dist/dependency-license-policy/execute-pnpm.mjs +83 -0
  124. package/dist/dependency-license-policy/types.d.mts +11 -9
  125. package/dist/dependency-license-policy/workspace.d.mts +3 -1
  126. package/dist/dependency-license-policy/workspace.mjs +12 -5
  127. package/dist/gh-api-shell-quoting/argument-scan.d.mts +7 -0
  128. package/dist/gh-api-shell-quoting/argument-scan.mjs +35 -0
  129. package/dist/gh-api-shell-quoting/logical-line.d.mts +3 -0
  130. package/dist/gh-api-shell-quoting/logical-line.mjs +90 -0
  131. package/dist/gh-api-shell-quoting/scan.d.mts +2 -4
  132. package/dist/gh-api-shell-quoting/scan.mjs +1 -119
  133. package/dist/gh-cli/diff.d.mts +24 -4
  134. package/dist/gh-cli/diff.mjs +111 -4
  135. package/dist/gh-cli/index.d.mts +2 -1
  136. package/dist/gh-cli/index.mjs +1 -1
  137. package/dist/gha-workspace-policy/docker-options.d.mts +4 -0
  138. package/dist/gha-workspace-policy/docker-options.mjs +93 -0
  139. package/dist/gha-workspace-policy/docker-workspace-user.mjs +1 -168
  140. package/dist/gha-workspace-policy/shell-token-modes.d.mts +10 -0
  141. package/dist/gha-workspace-policy/shell-token-modes.mjs +31 -0
  142. package/dist/gha-workspace-policy/shell-tokens.d.mts +1 -0
  143. package/dist/gha-workspace-policy/shell-tokens.mjs +65 -0
  144. package/dist/index.d.mts +2 -1
  145. package/dist/index.mjs +1 -1
  146. package/dist/openapi-document/contract-schema-types.d.mts +1 -1
  147. package/dist/openapi-document/openapi-types.d.mts +1 -1
  148. package/dist/pnpm-install/native-family.d.mts +3 -0
  149. package/dist/pnpm-install/native-family.mjs +31 -0
  150. package/dist/pnpm-install/native-health.d.mts +1 -4
  151. package/dist/pnpm-install/native-health.mjs +2 -31
  152. package/dist/retrospective-facts/local-merge.d.mts +2 -0
  153. package/dist/retrospective-facts/local-merge.mjs +21 -0
  154. package/dist/retrospective-facts/local.mjs +1 -21
  155. package/dist/retrospective-transcript/claude-blocks.d.mts +3 -0
  156. package/dist/retrospective-transcript/claude-blocks.mjs +38 -0
  157. package/dist/retrospective-transcript/claude.mjs +4 -34
  158. package/dist/retrospective-transcript/codex-payload.d.mts +2 -0
  159. package/dist/retrospective-transcript/codex-payload.mjs +51 -0
  160. package/dist/retrospective-transcript/codex-records.d.mts +2 -0
  161. package/dist/retrospective-transcript/codex-records.mjs +95 -0
  162. package/dist/retrospective-transcript/codex.mjs +2 -144
  163. package/dist/retrospective-transcript/index.d.mts +3 -16
  164. package/dist/retrospective-transcript/index.mjs +4 -48
  165. package/dist/retrospective-transcript/resolve-transcript-file.d.mts +19 -0
  166. package/dist/retrospective-transcript/resolve-transcript-file.mjs +50 -0
  167. package/dist/session-friction/command-segments.d.mts +3 -0
  168. package/dist/session-friction/command-segments.mjs +94 -0
  169. package/dist/session-friction/log-file.d.mts +7 -0
  170. package/dist/session-friction/log-file.mjs +83 -0
  171. package/dist/session-friction/log.mjs +3 -83
  172. package/dist/session-friction/normalize.mjs +1 -94
  173. package/dist/sql-scanner/comments.d.mts +1 -0
  174. package/dist/sql-scanner/comments.mjs +101 -0
  175. package/dist/sql-scanner/index.d.mts +1 -1
  176. package/dist/sql-scanner/index.mjs +1 -100
  177. package/dist/sql-scanner/literals.mjs +1 -35
  178. package/dist/sql-scanner/quote-decode.d.mts +2 -0
  179. package/dist/sql-scanner/quote-decode.mjs +35 -0
  180. package/dist/vitest-diagnostics/index.d.mts +7 -0
  181. package/dist/vitest-diagnostics/index.mjs +4 -0
  182. package/dist/vitest-diagnostics/vitest-fork-exit-error-detail.d.mts +7 -0
  183. package/dist/vitest-diagnostics/vitest-fork-exit-error-detail.mjs +26 -0
  184. package/dist/vitest-diagnostics/vitest-fork-exit-records.d.mts +26 -0
  185. package/dist/vitest-diagnostics/vitest-fork-exit-records.mjs +161 -0
  186. package/dist/vitest-diagnostics/vitest-fork-exit-sentinel.d.mts +14 -0
  187. package/dist/vitest-diagnostics/vitest-fork-exit-sentinel.mjs +131 -0
  188. package/dist/vitest-diagnostics/vitest-fork-leak-detection.d.mts +26 -0
  189. package/dist/vitest-diagnostics/vitest-fork-leak-detection.mjs +96 -0
  190. package/dist/vitest-diagnostics/vitest-fork-leak-diagnostics.d.mts +3 -0
  191. package/dist/vitest-diagnostics/vitest-fork-leak-diagnostics.mjs +14 -0
  192. package/dist/vitest-diagnostics/vitest-fork-leak-episode.d.mts +37 -0
  193. package/dist/vitest-diagnostics/vitest-fork-leak-episode.mjs +118 -0
  194. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-errors.d.mts +2 -0
  195. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-errors.mjs +42 -0
  196. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-reporter.d.mts +12 -0
  197. package/dist/vitest-diagnostics/vitest-worker-exit-diagnostics-reporter.mjs +142 -0
  198. package/dist/workflow-policy/concurrency-scope.d.mts +4 -0
  199. package/dist/workflow-policy/concurrency-scope.mjs +66 -0
  200. package/dist/workflow-policy/concurrency.d.mts +10 -0
  201. package/dist/workflow-policy/concurrency.mjs +77 -0
  202. package/dist/workflow-policy/condition-entailment.d.mts +20 -0
  203. package/dist/workflow-policy/condition-entailment.mjs +132 -0
  204. package/dist/workflow-policy/condition-split.d.mts +15 -0
  205. package/dist/workflow-policy/condition-split.mjs +71 -0
  206. package/dist/workflow-policy/graph.d.mts +2 -0
  207. package/dist/workflow-policy/graph.mjs +138 -0
  208. package/dist/workflow-policy/index.d.mts +7 -0
  209. package/dist/workflow-policy/index.mjs +6 -0
  210. package/dist/workflow-policy/permission-mismatches.d.mts +3 -0
  211. package/dist/workflow-policy/permission-mismatches.mjs +52 -0
  212. package/dist/workflow-policy/permissions.d.mts +16 -0
  213. package/dist/workflow-policy/permissions.mjs +83 -0
  214. package/dist/workflow-policy/secret-readiness.d.mts +18 -0
  215. package/dist/workflow-policy/secret-readiness.mjs +163 -0
  216. package/dist/workflow-policy/shell-comments.d.mts +7 -0
  217. package/dist/workflow-policy/shell-comments.mjs +28 -0
  218. package/dist/workflow-policy/types.d.mts +90 -0
  219. package/dist/workflow-policy/types.mjs +1 -0
  220. package/dist/workspace-gates/manifest-version-parser.mjs +1 -52
  221. package/dist/workspace-gates/manifest-version-skips.d.mts +1 -0
  222. package/dist/workspace-gates/manifest-version-skips.mjs +52 -0
  223. package/docs/workflow-and-compiler.md +74 -0
  224. package/package.json +26 -2
  225. package/skills/agent-workflow/SKILL.md +4 -4
  226. package/skills/agent-workflow/references/review-response.md +47 -44
  227. package/skills/backend-vitest-test-authoring/SKILL.md +9 -6
  228. package/skills/blackboard/SKILL.md +8 -8
  229. package/skills/dependabot/SKILL.md +68 -67
  230. package/skills/dotnet-test-authoring/SKILL.md +11 -12
  231. package/skills/git-commit-checklist/SKILL.md +3 -3
  232. package/skills/github-actions-authoring/SKILL.md +40 -38
  233. package/skills/github-actions-checklist/SKILL.md +93 -85
  234. package/skills/github-issue/SKILL.md +82 -78
  235. package/skills/nextjs-vitest-test-authoring/SKILL.md +8 -6
  236. package/skills/npm-publishing/SKILL.md +42 -38
  237. package/skills/organize-github-issues/SKILL.md +18 -22
  238. package/skills/package-json-checklist/SKILL.md +5 -5
  239. package/skills/planning/SKILL.md +3 -3
  240. package/skills/playwright-authoring/SKILL.md +10 -9
  241. package/skills/postgres-node-performance-tuning/SKILL.md +10 -6
  242. package/skills/postgres-partitioning-uuid-v7/SKILL.md +10 -7
  243. package/skills/pr-description/SKILL.md +4 -3
  244. package/skills/retrospective/SKILL.md +14 -15
  245. package/skills/retrospective-distill/SKILL.md +14 -14
  246. package/skills/review-ci-logs/SKILL.md +18 -19
  247. package/skills/review-github-issue-taxonomy/SKILL.md +21 -18
  248. package/skills/revisit-followups/SKILL.md +3 -3
  249. package/skills/stacked-prs/SKILL.md +30 -61
  250. package/skills/static-analysis-checklist/SKILL.md +6 -4
  251. package/skills/storybook-authoring/SKILL.md +9 -9
  252. package/skills/swift-test-authoring/SKILL.md +10 -9
  253. package/skills/test-authoring/SKILL.md +14 -14
  254. package/skills/test-authoring/references/core-practice.md +12 -11
  255. package/skills/test-authoring/references/dependency-boundaries.md +36 -35
  256. package/skills/test-authoring/references/tautological-tests.md +45 -44
  257. package/skills/vitest-test-authoring/SKILL.md +9 -6
@@ -0,0 +1,83 @@
1
+ import { spawn } from 'node:child_process';
2
+ const MAX_BUFFER_BYTES = 1024 * 1024;
3
+ export function signalFromAbort(signal) {
4
+ if (signal.reason === 'SIGINT' || signal.reason === 'SIGTERM' || signal.reason === 'SIGHUP') {
5
+ return signal.reason;
6
+ }
7
+ return 'SIGTERM';
8
+ }
9
+ export function stopChildProcess(child, signal) {
10
+ if (child.pid === undefined) {
11
+ child.kill(signal);
12
+ return;
13
+ }
14
+ try {
15
+ process.kill(-child.pid, signal);
16
+ }
17
+ catch {
18
+ child.kill(signal);
19
+ }
20
+ }
21
+ export function collectChildStream(stream, chunks, onOverflow) {
22
+ if (stream === null)
23
+ throw new Error('license audit child is missing a stdio pipe');
24
+ stream.setEncoding('utf8');
25
+ stream.on('data', (chunk) => {
26
+ chunks.push(chunk);
27
+ if (Buffer.byteLength(chunks.join('')) > MAX_BUFFER_BYTES)
28
+ onOverflow();
29
+ });
30
+ }
31
+ function finishOnce(settle) {
32
+ let settled = false;
33
+ return (result) => {
34
+ if (settled)
35
+ return;
36
+ settled = true;
37
+ settle(result);
38
+ };
39
+ }
40
+ /** Runs pnpm in its own process group so a signal can reach it and its children. */
41
+ export const executePnpm = (command, args, options) => new Promise((resolve) => {
42
+ const child = spawn(command, args, {
43
+ cwd: options.cwd,
44
+ detached: true,
45
+ stdio: ['ignore', 'pipe', 'pipe'],
46
+ });
47
+ const stdout = [];
48
+ const stderr = [];
49
+ let overflow = false;
50
+ const stop = () => {
51
+ stopChildProcess(child, overflow ? 'SIGKILL' : signalFromAbort(options.signal));
52
+ };
53
+ if (options.signal.aborted)
54
+ stop();
55
+ else
56
+ options.signal.addEventListener('abort', stop, { once: true });
57
+ const markOverflow = () => {
58
+ overflow = true;
59
+ stop();
60
+ };
61
+ collectChildStream(child.stdout, stdout, markOverflow);
62
+ collectChildStream(child.stderr, stderr, markOverflow);
63
+ const finish = finishOnce((result) => {
64
+ options.signal.removeEventListener('abort', stop);
65
+ resolve(result);
66
+ });
67
+ child.once('error', (error) => {
68
+ finish({ error, status: null, stderr: stderr.join(''), stdout: stdout.join('') });
69
+ });
70
+ child.once('close', (status) => {
71
+ const result = { status, stderr: stderr.join(''), stdout: stdout.join('') };
72
+ if (!overflow) {
73
+ finish(result);
74
+ return;
75
+ }
76
+ finish({
77
+ ...result,
78
+ error: Object.assign(new Error('child output maxBuffer exceeded'), {
79
+ code: 'ERR_CHILD_PROCESS_STDIO_MAXBUFFER',
80
+ }),
81
+ });
82
+ });
83
+ });
@@ -4,15 +4,17 @@ export interface PnpmLicenseReportEntry {
4
4
  }
5
5
  /** Shape emitted by `pnpm licenses list --json`. */
6
6
  export type PnpmLicenseReport = Record<string, PnpmLicenseReportEntry[]>;
7
- export type PnpmExecutor = (command: string, args: string[], options: {
8
- cwd: string;
9
- encoding: 'utf8';
10
- }) => {
11
- error?: Error;
12
- status: number | null;
13
- stderr: string;
14
- stdout: string;
15
- };
7
+ export interface PnpmCommandResult {
8
+ readonly error?: Error;
9
+ readonly status: number | null;
10
+ readonly stderr: string;
11
+ readonly stdout: string;
12
+ }
13
+ export type PnpmExecutor = (command: string, args: readonly string[], options: {
14
+ readonly cwd: string;
15
+ readonly encoding: 'utf8';
16
+ readonly signal: AbortSignal;
17
+ }) => PnpmCommandResult | Promise<PnpmCommandResult>;
16
18
  export interface DependencyLicenseAllowlistEntry {
17
19
  /** Exact SPDX atom allowed by this entry. */
18
20
  readonly licenseId: string;
@@ -1,3 +1,4 @@
1
+ import { type LicenseAuditWorkspaceOptions } from './audit-directory.mts';
1
2
  export interface PnpmLicenseAuditWorkspace {
2
3
  readonly cleanup: () => void;
3
4
  readonly cwd: string;
@@ -19,4 +20,5 @@ export declare function renderPnpmLicenseAuditFiles(lockfileSource: string, work
19
20
  lockfile: string;
20
21
  workspace: string;
21
22
  }): PnpmLicenseAuditFiles;
22
- export declare function preparePnpmLicenseAuditWorkspace(repoRoot: string, lockfileSource: string, workspaceSource: string): PnpmLicenseAuditWorkspace;
23
+ export declare function licenseAuditParentDirectory(directory?: string): string;
24
+ export declare function preparePnpmLicenseAuditWorkspace(repoRoot: string, lockfileSource: string, workspaceSource: string, options?: LicenseAuditWorkspaceOptions): PnpmLicenseAuditWorkspace;
@@ -1,7 +1,8 @@
1
- import { copyFileSync, existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
1
+ import { copyFileSync, existsSync, mkdtempSync, writeFileSync } from 'node:fs';
2
2
  import { tmpdir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  import { isMap, parseDocument, stringify as stringifyYaml } from 'yaml';
5
+ import { LICENSE_AUDIT_DIRECTORY_PREFIX, reclaimStaleLicenseAuditDirectories, removeAuditDirectory, writeAuditPid, } from './audit-directory.mjs';
5
6
  const PLATFORM_KEYS = ['os', 'cpu', 'libc'];
6
7
  function parseYamlDocument(source, path) {
7
8
  const document = parseDocument(source);
@@ -71,9 +72,15 @@ export function renderPnpmLicenseAuditFiles(lockfileSource, workspaceSource, pat
71
72
  workspace: stringifyYaml({ ...workspace, packages: [], supportedArchitectures }),
72
73
  };
73
74
  }
74
- export function preparePnpmLicenseAuditWorkspace(repoRoot, lockfileSource, workspaceSource) {
75
- const auditRoot = mkdtempSync(join(tmpdir(), 'dependency-license-audit-'));
75
+ export function licenseAuditParentDirectory(directory) {
76
+ return directory ?? tmpdir();
77
+ }
78
+ export function preparePnpmLicenseAuditWorkspace(repoRoot, lockfileSource, workspaceSource, options = {}) {
79
+ const directory = licenseAuditParentDirectory(options.directory);
80
+ reclaimStaleLicenseAuditDirectories(directory, options);
81
+ const auditRoot = mkdtempSync(join(directory, LICENSE_AUDIT_DIRECTORY_PREFIX));
76
82
  try {
83
+ writeAuditPid(auditRoot, options.pid);
77
84
  copyFileSync(join(repoRoot, 'package.json'), join(auditRoot, 'package.json'));
78
85
  const npmrc = join(repoRoot, '.npmrc');
79
86
  if (existsSync(npmrc))
@@ -86,11 +93,11 @@ export function preparePnpmLicenseAuditWorkspace(repoRoot, lockfileSource, works
86
93
  writeFileSync(join(auditRoot, 'pnpm-workspace.yaml'), files.workspace, 'utf8');
87
94
  }
88
95
  catch (error) {
89
- rmSync(auditRoot, { force: true, recursive: true });
96
+ removeAuditDirectory(auditRoot);
90
97
  throw error;
91
98
  }
92
99
  return {
93
100
  cwd: auditRoot,
94
- cleanup: () => rmSync(auditRoot, { force: true, recursive: true }),
101
+ cleanup: () => removeAuditDirectory(auditRoot),
95
102
  };
96
103
  }
@@ -0,0 +1,7 @@
1
+ export type ShellQuotingHit = {
2
+ offset: number;
3
+ excerpt: string;
4
+ };
5
+ export declare function consumeArgumentCharacter(text: string, index: number, frame: {
6
+ scanningArgs: boolean;
7
+ }, hits: ShellQuotingHit[], offsets: number[]): number | undefined;
@@ -0,0 +1,35 @@
1
+ function excerptAround(text, index) {
2
+ return text.slice(Math.max(0, index - 24), Math.min(text.length, index + 12)).trim();
3
+ }
4
+ // While `frame.scanningArgs` is set, `;`, `|`, and a real `&` end the argument list. `&&` is the
5
+ // Bash and-operator, `2>&1`/`>&2`/`<&3` are fd-duplication redirects, and a whitespace-preceded
6
+ // `&` backgrounds the command. Only an `&` or `?` embedded in an unquoted argument is unsafe.
7
+ // Returns the next scan index when this character is consumed, otherwise undefined so the caller
8
+ // can keep matching a `gh api` head.
9
+ export function consumeArgumentCharacter(text, index, frame, hits, offsets) {
10
+ if (!frame.scanningArgs)
11
+ return undefined;
12
+ const char = text[index];
13
+ if (char === ';' || char === '|') {
14
+ frame.scanningArgs = false;
15
+ return index + 1;
16
+ }
17
+ if (char === '&') {
18
+ if (text[index + 1] === '&') {
19
+ frame.scanningArgs = false;
20
+ return index + 2;
21
+ }
22
+ if (index > 0 && /[><]/.test(text[index - 1]))
23
+ return index + 1;
24
+ if (!(index > 0 && /[ \t]/.test(text[index - 1])))
25
+ hits.push({ offset: offsets[index], excerpt: excerptAround(text, index) });
26
+ frame.scanningArgs = false;
27
+ return index + 1;
28
+ }
29
+ if (char === '?') {
30
+ hits.push({ offset: offsets[index], excerpt: excerptAround(text, index) });
31
+ frame.scanningArgs = false;
32
+ return index + 1;
33
+ }
34
+ return undefined;
35
+ }
@@ -0,0 +1,3 @@
1
+ import { type ShellQuotingHit } from './argument-scan.mts';
2
+ export type { ShellQuotingHit } from './argument-scan.mts';
3
+ export declare function scanLogicalLine(text: string, offsets: number[]): ShellQuotingHit[];
@@ -0,0 +1,90 @@
1
+ import { consumeArgumentCharacter } from './argument-scan.mjs';
2
+ const GH_API_BOUNDARY = /[\s;|&(){}]/;
3
+ const GH_API_HEAD = /^gh[ \t]+api\b/;
4
+ function matchGhApiHead(text, index) {
5
+ if (index > 0 && !GH_API_BOUNDARY.test(text[index - 1]))
6
+ return 0;
7
+ const match = GH_API_HEAD.exec(text.slice(index));
8
+ return match ? match[0].length : 0;
9
+ }
10
+ function newFrame() {
11
+ return { inSingleQuote: false, inDoubleQuote: false, scanningArgs: false, parenDepth: 0 };
12
+ }
13
+ function pushSubstitutionFrame(stack) {
14
+ const frame = newFrame();
15
+ frame.parenDepth = 1;
16
+ stack.push(frame);
17
+ }
18
+ // Scans one already-joined logical line for `gh api` calls and returns at most one hit per call —
19
+ // the first unquoted `?` or `&`, whichever comes first. `offsets[k]` maps character k of `text`
20
+ // back to its absolute offset in the original source, so callers can report real line numbers.
21
+ export function scanLogicalLine(text, offsets) {
22
+ const hits = [];
23
+ const stack = [newFrame()];
24
+ let i = 0;
25
+ while (i < text.length) {
26
+ const frame = stack[stack.length - 1];
27
+ const char = text[i];
28
+ if (frame.inSingleQuote) {
29
+ if (char === "'")
30
+ frame.inSingleQuote = false;
31
+ i += 1;
32
+ continue;
33
+ }
34
+ if (char === '\\') {
35
+ i += 2;
36
+ continue;
37
+ }
38
+ if (frame.inDoubleQuote) {
39
+ if (char === '"') {
40
+ frame.inDoubleQuote = false;
41
+ i += 1;
42
+ continue;
43
+ }
44
+ if (char === '$' && text[i + 1] === '(') {
45
+ pushSubstitutionFrame(stack);
46
+ i += 2;
47
+ continue;
48
+ }
49
+ i += 1;
50
+ continue;
51
+ }
52
+ if (char === '#' && (i === 0 || /\s/.test(text[i - 1])))
53
+ break;
54
+ if (char === "'") {
55
+ frame.inSingleQuote = true;
56
+ i += 1;
57
+ continue;
58
+ }
59
+ if (char === '"') {
60
+ frame.inDoubleQuote = true;
61
+ i += 1;
62
+ continue;
63
+ }
64
+ if (char === '$' && text[i + 1] === '(') {
65
+ pushSubstitutionFrame(stack);
66
+ i += 2;
67
+ continue;
68
+ }
69
+ if (stack.length > 1 && (char === '(' || char === ')')) {
70
+ frame.parenDepth += char === '(' ? 1 : -1;
71
+ i += 1;
72
+ if (frame.parenDepth === 0)
73
+ stack.pop();
74
+ continue;
75
+ }
76
+ const argumentIndex = consumeArgumentCharacter(text, i, frame, hits, offsets);
77
+ if (argumentIndex !== undefined) {
78
+ i = argumentIndex;
79
+ continue;
80
+ }
81
+ const headLength = frame.scanningArgs ? 0 : matchGhApiHead(text, i);
82
+ if (headLength > 0) {
83
+ frame.scanningArgs = true;
84
+ i += headLength;
85
+ continue;
86
+ }
87
+ i += 1;
88
+ }
89
+ return hits;
90
+ }
@@ -1,7 +1,5 @@
1
- export type ShellQuotingHit = {
2
- offset: number;
3
- excerpt: string;
4
- };
1
+ import { type ShellQuotingHit } from './logical-line.mts';
2
+ export type { ShellQuotingHit } from './logical-line.mts';
5
3
  export declare function lineNumberAt(source: string, offset: number): number;
6
4
  /** Hits in `text` with offsets relative to `text` itself, not any enclosing document. */
7
5
  export declare function ghApiShellQuotingHits(text: string): ShellQuotingHit[];
@@ -10,125 +10,7 @@
10
10
  // call's argument list. A general-purpose shell tokenizer whose "expandable" notion conflates
11
11
  // glob metacharacters with `$`/backtick expansion would false-positive on a quoted
12
12
  // `"…${VAR}…?per_page=1"`, so this scanner tracks its own narrower quote-state machine instead.
13
- const GH_API_BOUNDARY = /[\s;|&(){}]/;
14
- const GH_API_HEAD = /^gh[ \t]+api\b/;
15
- function matchGhApiHead(text, index) {
16
- if (index > 0 && !GH_API_BOUNDARY.test(text[index - 1]))
17
- return 0;
18
- const match = GH_API_HEAD.exec(text.slice(index));
19
- return match ? match[0].length : 0;
20
- }
21
- function excerptAround(text, index) {
22
- return text.slice(Math.max(0, index - 24), Math.min(text.length, index + 12)).trim();
23
- }
24
- function newFrame() {
25
- return { inSingleQuote: false, inDoubleQuote: false, scanningArgs: false, parenDepth: 0 };
26
- }
27
- function pushSubstitutionFrame(stack) {
28
- const frame = newFrame();
29
- frame.parenDepth = 1;
30
- stack.push(frame);
31
- }
32
- // Scans one already-joined logical line for `gh api` calls and returns at most one hit per call —
33
- // the first unquoted `?` or `&`, whichever comes first. `offsets[k]` maps character k of `text`
34
- // back to its absolute offset in the original source, so callers can report real line numbers.
35
- function scanLogicalLine(text, offsets) {
36
- const hits = [];
37
- const stack = [newFrame()];
38
- let i = 0;
39
- while (i < text.length) {
40
- const frame = stack[stack.length - 1];
41
- const char = text[i];
42
- if (frame.inSingleQuote) {
43
- if (char === "'")
44
- frame.inSingleQuote = false;
45
- i += 1;
46
- continue;
47
- }
48
- if (char === '\\') {
49
- i += 2;
50
- continue;
51
- }
52
- if (frame.inDoubleQuote) {
53
- if (char === '"') {
54
- frame.inDoubleQuote = false;
55
- i += 1;
56
- continue;
57
- }
58
- if (char === '$' && text[i + 1] === '(') {
59
- pushSubstitutionFrame(stack);
60
- i += 2;
61
- continue;
62
- }
63
- i += 1;
64
- continue;
65
- }
66
- if (char === '#' && (i === 0 || /\s/.test(text[i - 1])))
67
- break;
68
- if (char === "'") {
69
- frame.inSingleQuote = true;
70
- i += 1;
71
- continue;
72
- }
73
- if (char === '"') {
74
- frame.inDoubleQuote = true;
75
- i += 1;
76
- continue;
77
- }
78
- if (char === '$' && text[i + 1] === '(') {
79
- pushSubstitutionFrame(stack);
80
- i += 2;
81
- continue;
82
- }
83
- if (stack.length > 1 && (char === '(' || char === ')')) {
84
- frame.parenDepth += char === '(' ? 1 : -1;
85
- i += 1;
86
- if (frame.parenDepth === 0)
87
- stack.pop();
88
- continue;
89
- }
90
- if (frame.scanningArgs) {
91
- if (char === ';' || char === '|') {
92
- frame.scanningArgs = false;
93
- i += 1;
94
- continue;
95
- }
96
- if (char === '&') {
97
- if (text[i + 1] === '&') {
98
- frame.scanningArgs = false;
99
- i += 2;
100
- continue;
101
- }
102
- // `2>&1`/`>&2`/`<&3` fd-duplication redirects don't end the argument list; whitespace-
103
- // preceded `&` is Bash's background operator and does. Only an `&` embedded in an
104
- // unquoted argument is unsafe.
105
- if (i > 0 && /[><]/.test(text[i - 1])) {
106
- i += 1;
107
- continue;
108
- }
109
- if (!(i > 0 && /[ \t]/.test(text[i - 1])))
110
- hits.push({ offset: offsets[i], excerpt: excerptAround(text, i) });
111
- frame.scanningArgs = false;
112
- i += 1;
113
- continue;
114
- }
115
- if (char === '?') {
116
- hits.push({ offset: offsets[i], excerpt: excerptAround(text, i) });
117
- frame.scanningArgs = false;
118
- i += 1;
119
- continue;
120
- }
121
- }
122
- const headLength = frame.scanningArgs ? 0 : matchGhApiHead(text, i);
123
- if (headLength > 0) {
124
- frame.scanningArgs = true;
125
- i += headLength;
126
- continue;
127
- }
128
- i += 1;
129
- }
130
- return hits;
131
- }
13
+ import { scanLogicalLine } from './logical-line.mjs';
132
14
  // A logical line joins physical lines across a trailing, unescaped `\` — bash line continuation —
133
15
  // so a `gh api \` / URL-on-next-line split (common in multi-line `run:` steps) is scanned as one
134
16
  // command instead of two truncated fragments.
@@ -1,6 +1,26 @@
1
- import type { RunTextCommand } from './exec.mts';
1
+ /** A command that emits a unified diff on stdout, such as `git diff` or `gh pr diff`. */
2
+ export interface DiffCommand {
3
+ readonly executable: string;
4
+ readonly args: readonly string[];
5
+ readonly cwd?: string;
6
+ }
7
+ export type DiffBlockCallback = (block: string) => void | Promise<void>;
8
+ export declare class DiffCommandError extends Error {
9
+ readonly command: DiffCommand;
10
+ readonly details: {
11
+ code?: number | null;
12
+ signal?: NodeJS.Signals | null;
13
+ stderr: string;
14
+ };
15
+ constructor(command: DiffCommand, details: {
16
+ code?: number | null;
17
+ signal?: NodeJS.Signals | null;
18
+ stderr: string;
19
+ }, options?: ErrorOptions);
20
+ }
2
21
  /**
3
- * Runs `git diff <base>...HEAD` and returns the raw diff text. The base is parameterized rather
4
- * than hardcoded so callers can diff against any ref (`origin/main`, a release branch, etc.).
22
+ * Spawns a command without a shell and emits its unified-diff output one file patch at a time.
23
+ * A preamble is retained with the first patch (or emitted alone); callbacks are provisional until
24
+ * this promise resolves, because a later command failure rejects after prior patches were emitted.
5
25
  */
6
- export declare function getDiffAgainstBase(runGit: RunTextCommand, base: string): Promise<string>;
26
+ export declare function processDiffCommand(command: DiffCommand, onBlock: DiffBlockCallback): Promise<void>;
@@ -1,7 +1,114 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { StringDecoder } from 'node:string_decoder';
3
+ const STDERR_TAIL_BYTES = 16 * 1024;
4
+ export class DiffCommandError extends Error {
5
+ command;
6
+ details;
7
+ constructor(command, details, options) {
8
+ const status = details.code === null ? details.signal : details.code;
9
+ super(`${command.executable} ${status === undefined ? 'failed to start' : `exited with ${status}`}${details.stderr ? `: ${details.stderr}` : ''}`, options);
10
+ this.command = command;
11
+ this.details = details;
12
+ this.name = 'DiffCommandError';
13
+ }
14
+ }
15
+ function appendTail(tail, chunk) {
16
+ const combined = Buffer.concat([tail, chunk]);
17
+ return combined.length <= STDERR_TAIL_BYTES ? combined : combined.subarray(-STDERR_TAIL_BYTES);
18
+ }
19
+ function diagnostic(tail) {
20
+ return tail.toString('utf8').trim();
21
+ }
1
22
  /**
2
- * Runs `git diff <base>...HEAD` and returns the raw diff text. The base is parameterized rather
3
- * than hardcoded so callers can diff against any ref (`origin/main`, a release branch, etc.).
23
+ * Spawns a command without a shell and emits its unified-diff output one file patch at a time.
24
+ * A preamble is retained with the first patch (or emitted alone); callbacks are provisional until
25
+ * this promise resolves, because a later command failure rejects after prior patches were emitted.
4
26
  */
5
- export async function getDiffAgainstBase(runGit, base) {
6
- return runGit(['diff', `${base}...HEAD`]);
27
+ export async function processDiffCommand(command, onBlock) {
28
+ const child = spawn(command.executable, [...command.args], {
29
+ cwd: command.cwd,
30
+ shell: false,
31
+ stdio: ['ignore', 'pipe', 'pipe'],
32
+ });
33
+ let stderr = Buffer.alloc(0);
34
+ let streamError;
35
+ const terminate = () => {
36
+ if (child.exitCode === null && !child.killed)
37
+ child.kill('SIGKILL');
38
+ };
39
+ child.stderr.on('data', (chunk) => {
40
+ stderr = appendTail(stderr, chunk);
41
+ });
42
+ child.stderr.on('error', (error) => {
43
+ streamError ??= error;
44
+ terminate();
45
+ });
46
+ const completion = new Promise((resolve) => {
47
+ child.once('error', (error) => resolve({ code: null, error, signal: null }));
48
+ child.once('close', (code, signal) => resolve({ code, signal }));
49
+ });
50
+ const decoder = new StringDecoder('utf8');
51
+ let block = '';
52
+ let pending = '';
53
+ let sawFileHeader = false;
54
+ let callbackError;
55
+ const acceptLine = async (line) => {
56
+ if (line.startsWith('diff --git ') && sawFileHeader) {
57
+ try {
58
+ await onBlock(block);
59
+ }
60
+ catch (error) {
61
+ callbackError = error;
62
+ throw error;
63
+ }
64
+ block = '';
65
+ }
66
+ if (line.startsWith('diff --git '))
67
+ sawFileHeader = true;
68
+ block += line;
69
+ };
70
+ const acceptText = async (text) => {
71
+ pending += text;
72
+ for (let newline = pending.indexOf('\n'); newline >= 0; newline = pending.indexOf('\n')) {
73
+ const line = pending.slice(0, newline + 1);
74
+ pending = pending.slice(newline + 1);
75
+ await acceptLine(line);
76
+ }
77
+ };
78
+ try {
79
+ for await (const chunk of child.stdout)
80
+ await acceptText(decoder.write(chunk));
81
+ await acceptText(decoder.end());
82
+ if (pending)
83
+ await acceptLine(pending);
84
+ if (streamError)
85
+ throw streamError;
86
+ if (block) {
87
+ try {
88
+ await onBlock(block);
89
+ }
90
+ catch (error) {
91
+ callbackError = error;
92
+ throw error;
93
+ }
94
+ }
95
+ const result = await completion;
96
+ if (streamError)
97
+ throw streamError;
98
+ if (result.error)
99
+ throw new DiffCommandError(command, { stderr: diagnostic(stderr) }, { cause: result.error });
100
+ if (result.code !== 0 || result.signal !== null)
101
+ throw new DiffCommandError(command, {
102
+ code: result.code,
103
+ signal: result.signal,
104
+ stderr: diagnostic(stderr),
105
+ });
106
+ }
107
+ catch (error) {
108
+ terminate();
109
+ await completion;
110
+ if (error === callbackError || error instanceof DiffCommandError)
111
+ throw error;
112
+ throw new DiffCommandError(command, { stderr: diagnostic(stderr) }, { cause: streamError ?? error });
113
+ }
7
114
  }
@@ -1,6 +1,7 @@
1
1
  export { createCommandRunner, runGh, runGit } from './exec.mts';
2
2
  export type { ExecFileText, RunTextCommand } from './exec.mts';
3
- export { getDiffAgainstBase } from './diff.mts';
3
+ export { DiffCommandError, processDiffCommand } from './diff.mts';
4
+ export type { DiffBlockCallback, DiffCommand } from './diff.mts';
4
5
  export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from './body-length.mts';
5
6
  export type { GitHubBodyLengthValidation } from './body-length.mts';
6
7
  export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './pr-create.mts';
@@ -1,4 +1,4 @@
1
1
  export { createCommandRunner, runGh, runGit } from './exec.mjs';
2
- export { getDiffAgainstBase } from './diff.mjs';
2
+ export { DiffCommandError, processDiffCommand } from './diff.mjs';
3
3
  export { GITHUB_BODY_MAX_CHARACTERS, validateGitHubBodyLength } from './body-length.mjs';
4
4
  export { assertHeadPushed, buildGhPrCreateArgs, createPullRequest, DetachedHeadError, HeadNotPushedError, HeadOutOfDateError, resolveHeadBranch, } from './pr-create.mjs';
@@ -0,0 +1,4 @@
1
+ export declare function dockerOptions(block: string): {
2
+ user: string | undefined;
3
+ volumes: string[];
4
+ } | undefined;