@cat-factory/kernel 0.179.0 → 0.181.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.
- package/dist/domain/errors.d.ts +47 -1
- package/dist/domain/errors.d.ts.map +1 -1
- package/dist/domain/errors.js +55 -0
- package/dist/domain/errors.js.map +1 -1
- package/dist/domain/validation-detection.d.ts +101 -0
- package/dist/domain/validation-detection.d.ts.map +1 -0
- package/dist/domain/validation-detection.js +166 -0
- package/dist/domain/validation-detection.js.map +1 -0
- package/dist/domain/validation-detectors.d.ts +40 -0
- package/dist/domain/validation-detectors.d.ts.map +1 -0
- package/dist/domain/validation-detectors.js +441 -0
- package/dist/domain/validation-detectors.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/ports/index.d.ts +1 -0
- package/dist/ports/index.d.ts.map +1 -1
- package/dist/ports/index.js +1 -0
- package/dist/ports/index.js.map +1 -1
- package/dist/ports/repositories.d.ts +42 -1
- package/dist/ports/repositories.d.ts.map +1 -1
- package/dist/ports/repositories.js +21 -1
- package/dist/ports/repositories.js.map +1 -1
- package/package.json +2 -2
package/dist/domain/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type DomainErrorCode = 'not_found' | 'validation' | 'conflict' | 'credential_required' | 'forbidden';
|
|
1
|
+
export type DomainErrorCode = 'not_found' | 'validation' | 'conflict' | 'credential_required' | 'forbidden' | 'unavailable' | 'unauthorized' | 'rate_limited';
|
|
2
2
|
export declare class DomainError extends Error {
|
|
3
3
|
readonly code: DomainErrorCode;
|
|
4
4
|
/**
|
|
@@ -80,6 +80,52 @@ export declare class CredentialRequiredError extends DomainError {
|
|
|
80
80
|
export declare class ForbiddenError extends DomainError {
|
|
81
81
|
constructor(message?: string, details?: Record<string, unknown>);
|
|
82
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* A capability this deployment has not wired is required for the action (→ 503 Service
|
|
85
|
+
* Unavailable). BY FAR the most common hand-rolled envelope before it had a class — an
|
|
86
|
+
* opt-in integration (task sources, the skills library, artifact storage, an OAuth
|
|
87
|
+
* provider) that a facade only builds when configured, so its controller answers 503
|
|
88
|
+
* rather than 404.
|
|
89
|
+
*
|
|
90
|
+
* `reason` matters more here than anywhere else: "not configured" is the one error class
|
|
91
|
+
* whose remedy is always a specific setup screen, and a client cannot route to it by
|
|
92
|
+
* string-matching prose. Carry the machine-readable code and let the SPA translate.
|
|
93
|
+
*/
|
|
94
|
+
export declare class UnavailableError extends DomainError {
|
|
95
|
+
constructor(message: string,
|
|
96
|
+
/** Machine-readable code for the client (under `details.reason`), e.g. which integration. */
|
|
97
|
+
reason?: string,
|
|
98
|
+
/** Extra machine-readable context merged alongside `reason`. */
|
|
99
|
+
details?: Record<string, unknown>);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The caller is NOT authenticated (or its credential/signature did not verify) → 401.
|
|
103
|
+
* Distinct from {@link ForbiddenError}, which is an authenticated caller lacking a
|
|
104
|
+
* capability: 401 says "sign in", 403 says "you can't do this".
|
|
105
|
+
*
|
|
106
|
+
* Webhook receivers use this for a failed HMAC check. Keep those messages coarse — a
|
|
107
|
+
* signature verifier that distinguishes "no header" from "bad digest" is an oracle — and
|
|
108
|
+
* put the operator-facing distinction in a log line, not the envelope.
|
|
109
|
+
*/
|
|
110
|
+
export declare class UnauthorizedError extends DomainError {
|
|
111
|
+
constructor(message?: string,
|
|
112
|
+
/** Machine-readable code for the client (under `details.reason`). */
|
|
113
|
+
reason?: string, details?: Record<string, unknown>);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The caller (or the deployment, against an upstream) exceeded a rate limit → 429.
|
|
117
|
+
*
|
|
118
|
+
* `retryAfterSeconds` rides `details` rather than a header because the envelope is what
|
|
119
|
+
* survives every hop this platform has — a queue consumer and the durable driver read the
|
|
120
|
+
* same `details` an HTTP client does, and neither sees response headers.
|
|
121
|
+
*/
|
|
122
|
+
export declare class RateLimitedError extends DomainError {
|
|
123
|
+
constructor(message: string,
|
|
124
|
+
/** Machine-readable code for the client (under `details.reason`). */
|
|
125
|
+
reason?: string,
|
|
126
|
+
/** How long to wait before retrying, when the limiter told us. */
|
|
127
|
+
retryAfterSeconds?: number);
|
|
128
|
+
}
|
|
83
129
|
/**
|
|
84
130
|
* INTERNAL control-flow signal (NOT a wire/HTTP error): a durable-driver write lost an
|
|
85
131
|
* optimistic-concurrency race — the run row was advanced by a concurrent writer (a human
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/domain/errors.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,YAAY,GACZ,UAAU,GACV,qBAAqB,GACrB,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/domain/errors.ts"],"names":[],"mappings":"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"}
|
package/dist/domain/errors.js
CHANGED
|
@@ -66,6 +66,61 @@ export class ForbiddenError extends DomainError {
|
|
|
66
66
|
super('forbidden', message, details);
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* A capability this deployment has not wired is required for the action (→ 503 Service
|
|
71
|
+
* Unavailable). BY FAR the most common hand-rolled envelope before it had a class — an
|
|
72
|
+
* opt-in integration (task sources, the skills library, artifact storage, an OAuth
|
|
73
|
+
* provider) that a facade only builds when configured, so its controller answers 503
|
|
74
|
+
* rather than 404.
|
|
75
|
+
*
|
|
76
|
+
* `reason` matters more here than anywhere else: "not configured" is the one error class
|
|
77
|
+
* whose remedy is always a specific setup screen, and a client cannot route to it by
|
|
78
|
+
* string-matching prose. Carry the machine-readable code and let the SPA translate.
|
|
79
|
+
*/
|
|
80
|
+
export class UnavailableError extends DomainError {
|
|
81
|
+
constructor(message,
|
|
82
|
+
/** Machine-readable code for the client (under `details.reason`), e.g. which integration. */
|
|
83
|
+
reason,
|
|
84
|
+
/** Extra machine-readable context merged alongside `reason`. */
|
|
85
|
+
details) {
|
|
86
|
+
super('unavailable', message, reason ? { reason, ...details } : details);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The caller is NOT authenticated (or its credential/signature did not verify) → 401.
|
|
91
|
+
* Distinct from {@link ForbiddenError}, which is an authenticated caller lacking a
|
|
92
|
+
* capability: 401 says "sign in", 403 says "you can't do this".
|
|
93
|
+
*
|
|
94
|
+
* Webhook receivers use this for a failed HMAC check. Keep those messages coarse — a
|
|
95
|
+
* signature verifier that distinguishes "no header" from "bad digest" is an oracle — and
|
|
96
|
+
* put the operator-facing distinction in a log line, not the envelope.
|
|
97
|
+
*/
|
|
98
|
+
export class UnauthorizedError extends DomainError {
|
|
99
|
+
constructor(message = 'Unauthorized',
|
|
100
|
+
/** Machine-readable code for the client (under `details.reason`). */
|
|
101
|
+
reason, details) {
|
|
102
|
+
super('unauthorized', message, reason ? { reason, ...details } : details);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The caller (or the deployment, against an upstream) exceeded a rate limit → 429.
|
|
107
|
+
*
|
|
108
|
+
* `retryAfterSeconds` rides `details` rather than a header because the envelope is what
|
|
109
|
+
* survives every hop this platform has — a queue consumer and the durable driver read the
|
|
110
|
+
* same `details` an HTTP client does, and neither sees response headers.
|
|
111
|
+
*/
|
|
112
|
+
export class RateLimitedError extends DomainError {
|
|
113
|
+
constructor(message,
|
|
114
|
+
/** Machine-readable code for the client (under `details.reason`). */
|
|
115
|
+
reason,
|
|
116
|
+
/** How long to wait before retrying, when the limiter told us. */
|
|
117
|
+
retryAfterSeconds) {
|
|
118
|
+
super('rate_limited', message, {
|
|
119
|
+
...(reason ? { reason } : {}),
|
|
120
|
+
...(retryAfterSeconds === undefined ? {} : { retryAfterSeconds }),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
69
124
|
/**
|
|
70
125
|
* INTERNAL control-flow signal (NOT a wire/HTTP error): a durable-driver write lost an
|
|
71
126
|
* optimistic-concurrency race — the run row was advanced by a concurrent writer (a human
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/domain/errors.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,iFAAiF;AACjF,gFAAgF;
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/domain/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ValidationEcosystem } from '@cat-factory/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* What a suggested check is FOR. Used only to order the suggestions (an install must
|
|
4
|
+
* precede the commands that need it; a fast lint should fail before a slow build) — it is
|
|
5
|
+
* not persisted, and the wire shape carries `{ label, command }` like any other check.
|
|
6
|
+
*/
|
|
7
|
+
export type ValidationCheckRole = 'install' | 'format' | 'lint' | 'typecheck' | 'test' | 'build';
|
|
8
|
+
/** One root-directory entry, as the `RepoFiles` listing reports it. */
|
|
9
|
+
export interface RepoRootEntry {
|
|
10
|
+
name: string;
|
|
11
|
+
/** `file` | `dir` | `symlink` | `submodule` — the provider's own vocabulary. */
|
|
12
|
+
type: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Everything the detector is allowed to see: the repo root's entries, plus the decoded
|
|
16
|
+
* content of the manifests named in {@link VALIDATION_DETECTION_CONTENT_FILES} that were
|
|
17
|
+
* actually present. A manifest the reader could not fetch is simply absent from `files`,
|
|
18
|
+
* which degrades that ecosystem to presence-only rules rather than failing detection.
|
|
19
|
+
*/
|
|
20
|
+
export interface RepoSurface {
|
|
21
|
+
entries: RepoRootEntry[];
|
|
22
|
+
files: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
/** One suggested check plus the role that orders it. */
|
|
25
|
+
export interface DetectedCheck {
|
|
26
|
+
label: string;
|
|
27
|
+
command: string;
|
|
28
|
+
role: ValidationCheckRole;
|
|
29
|
+
}
|
|
30
|
+
/** What one ecosystem detector produced. `null` ⇒ the ecosystem is absent or yielded nothing. */
|
|
31
|
+
export interface EcosystemDetection {
|
|
32
|
+
ecosystem: ValidationEcosystem;
|
|
33
|
+
checks: DetectedCheck[];
|
|
34
|
+
}
|
|
35
|
+
/** The detection result, as the endpoint returns it (minus the transport `status`). */
|
|
36
|
+
export interface ValidationDetectionResult {
|
|
37
|
+
/** Every ecosystem that contributed at least one check, in canonical order. */
|
|
38
|
+
ecosystems: ValidationEcosystem[];
|
|
39
|
+
checks: {
|
|
40
|
+
label: string;
|
|
41
|
+
command: string;
|
|
42
|
+
}[];
|
|
43
|
+
/** Whether {@link VALIDATION_MAX_CHECKS} dropped suggestions the detectors produced. */
|
|
44
|
+
truncated: boolean;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* A read-only view over a {@link RepoSurface} with the lookups every detector needs. Names
|
|
48
|
+
* are matched case-INSENSITIVELY because the conventions genuinely vary (`Makefile` vs
|
|
49
|
+
* `makefile`, `justfile` vs `Justfile`) and a case-sensitive miss reads to an operator as
|
|
50
|
+
* "the detector is broken", not as "your file is spelled differently".
|
|
51
|
+
*/
|
|
52
|
+
export declare class RepoView {
|
|
53
|
+
private readonly filesByLowerName;
|
|
54
|
+
private readonly dirNames;
|
|
55
|
+
private readonly fileNames;
|
|
56
|
+
constructor(surface: RepoSurface);
|
|
57
|
+
/** Whether a FILE with this name sits at the repo root. */
|
|
58
|
+
has(name: string): boolean;
|
|
59
|
+
/** Whether any of these files sits at the repo root. */
|
|
60
|
+
hasAny(...names: string[]): boolean;
|
|
61
|
+
/** Whether a DIRECTORY with this name sits at the repo root. */
|
|
62
|
+
hasDir(name: string): boolean;
|
|
63
|
+
/** Whether any root FILE ends with this (lowercased) suffix — e.g. `.sln`, `.csproj`. */
|
|
64
|
+
hasFileWithSuffix(suffix: string): boolean;
|
|
65
|
+
/** The decoded content of a root manifest the reader fetched, or undefined. */
|
|
66
|
+
read(name: string): string | undefined;
|
|
67
|
+
/** The first of these root manifests whose content was fetched. */
|
|
68
|
+
readAny(...names: string[]): string | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* A root manifest parsed as JSON, or undefined when it is absent or malformed. A repo
|
|
71
|
+
* with an unparseable `package.json` is a real thing (a merge conflict, a template
|
|
72
|
+
* placeholder); it degrades that ecosystem to its presence-only rules rather than
|
|
73
|
+
* failing the whole detection.
|
|
74
|
+
*/
|
|
75
|
+
json(name: string): Record<string, unknown> | undefined;
|
|
76
|
+
}
|
|
77
|
+
/** A detector: recognise an ecosystem on the surface and propose its checks. */
|
|
78
|
+
export type EcosystemDetector = (view: RepoView) => EcosystemDetection | null;
|
|
79
|
+
/**
|
|
80
|
+
* Compose one ecosystem's result, dropping an ecosystem that produced ONLY setup commands.
|
|
81
|
+
* An `install` on its own verifies nothing — it would open a PR on a checkout no command
|
|
82
|
+
* ever inspected, while costing every run the install — so the whole ecosystem is dropped
|
|
83
|
+
* instead, which is also what lets the task-runner fallback below take over.
|
|
84
|
+
*/
|
|
85
|
+
export declare function ecosystem(id: ValidationEcosystem, checks: (DetectedCheck | null)[]): EcosystemDetection | null;
|
|
86
|
+
/** Build a check, or nothing when its precondition didn't hold (keeps detectors declarative). */
|
|
87
|
+
export declare function check(role: ValidationCheckRole, label: string, command: string | null | undefined): DetectedCheck | null;
|
|
88
|
+
/**
|
|
89
|
+
* Run the detectors over a repo surface and return the suggested checks.
|
|
90
|
+
*
|
|
91
|
+
* LANGUAGE ecosystems are detected first and independently — a polyglot repo (a Go service
|
|
92
|
+
* with a Node SPA beside it) gets both, because either half can break the PR. TASK RUNNERS
|
|
93
|
+
* (`make`, `just`, `task`) are a FALLBACK, used only when no language ecosystem produced
|
|
94
|
+
* anything: in a repo that has both, the language commands are the specific ones, and
|
|
95
|
+
* suggesting `make test` beside `go test ./...` would run the same suite twice on every run.
|
|
96
|
+
*/
|
|
97
|
+
export declare function detectValidationChecks(surface: RepoSurface, detectors: {
|
|
98
|
+
language: readonly EcosystemDetector[];
|
|
99
|
+
taskRunner: readonly EcosystemDetector[];
|
|
100
|
+
}): ValidationDetectionResult;
|
|
101
|
+
//# sourceMappingURL=validation-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-detection.d.ts","sourceRoot":"","sources":["../../src/domain/validation-detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { VALIDATION_MAX_CHECKS } from '@cat-factory/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Suggestion ordering within one ecosystem. Setup first, then the static checks cheapest
|
|
4
|
+
* first (a formatter diff should fail before a type check), then `build` BEFORE `test` —
|
|
5
|
+
* a compile error is the faster, clearer failure, and a suite whose fixtures come from
|
|
6
|
+
* build output would otherwise run against a tree that was never built.
|
|
7
|
+
*/
|
|
8
|
+
const ROLE_ORDER = [
|
|
9
|
+
'install',
|
|
10
|
+
'format',
|
|
11
|
+
'lint',
|
|
12
|
+
'typecheck',
|
|
13
|
+
'build',
|
|
14
|
+
'test',
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* A read-only view over a {@link RepoSurface} with the lookups every detector needs. Names
|
|
18
|
+
* are matched case-INSENSITIVELY because the conventions genuinely vary (`Makefile` vs
|
|
19
|
+
* `makefile`, `justfile` vs `Justfile`) and a case-sensitive miss reads to an operator as
|
|
20
|
+
* "the detector is broken", not as "your file is spelled differently".
|
|
21
|
+
*/
|
|
22
|
+
export class RepoView {
|
|
23
|
+
filesByLowerName = new Map();
|
|
24
|
+
dirNames = new Set();
|
|
25
|
+
fileNames = new Set();
|
|
26
|
+
constructor(surface) {
|
|
27
|
+
for (const entry of surface.entries) {
|
|
28
|
+
const name = entry.name.toLowerCase();
|
|
29
|
+
if (entry.type === 'dir')
|
|
30
|
+
this.dirNames.add(name);
|
|
31
|
+
else
|
|
32
|
+
this.fileNames.add(name);
|
|
33
|
+
}
|
|
34
|
+
for (const [name, content] of Object.entries(surface.files)) {
|
|
35
|
+
this.filesByLowerName.set(name.toLowerCase(), content);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** Whether a FILE with this name sits at the repo root. */
|
|
39
|
+
has(name) {
|
|
40
|
+
return this.fileNames.has(name.toLowerCase());
|
|
41
|
+
}
|
|
42
|
+
/** Whether any of these files sits at the repo root. */
|
|
43
|
+
hasAny(...names) {
|
|
44
|
+
return names.some((n) => this.has(n));
|
|
45
|
+
}
|
|
46
|
+
/** Whether a DIRECTORY with this name sits at the repo root. */
|
|
47
|
+
hasDir(name) {
|
|
48
|
+
return this.dirNames.has(name.toLowerCase());
|
|
49
|
+
}
|
|
50
|
+
/** Whether any root FILE ends with this (lowercased) suffix — e.g. `.sln`, `.csproj`. */
|
|
51
|
+
hasFileWithSuffix(suffix) {
|
|
52
|
+
const lower = suffix.toLowerCase();
|
|
53
|
+
for (const name of this.fileNames)
|
|
54
|
+
if (name.endsWith(lower))
|
|
55
|
+
return true;
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/** The decoded content of a root manifest the reader fetched, or undefined. */
|
|
59
|
+
read(name) {
|
|
60
|
+
return this.filesByLowerName.get(name.toLowerCase());
|
|
61
|
+
}
|
|
62
|
+
/** The first of these root manifests whose content was fetched. */
|
|
63
|
+
readAny(...names) {
|
|
64
|
+
for (const name of names) {
|
|
65
|
+
const content = this.read(name);
|
|
66
|
+
if (content !== undefined)
|
|
67
|
+
return content;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A root manifest parsed as JSON, or undefined when it is absent or malformed. A repo
|
|
73
|
+
* with an unparseable `package.json` is a real thing (a merge conflict, a template
|
|
74
|
+
* placeholder); it degrades that ecosystem to its presence-only rules rather than
|
|
75
|
+
* failing the whole detection.
|
|
76
|
+
*/
|
|
77
|
+
json(name) {
|
|
78
|
+
const raw = this.read(name);
|
|
79
|
+
if (raw === undefined)
|
|
80
|
+
return undefined;
|
|
81
|
+
try {
|
|
82
|
+
const parsed = JSON.parse(raw);
|
|
83
|
+
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)
|
|
84
|
+
? parsed
|
|
85
|
+
: undefined;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// silent-catch-ok: a malformed manifest is a detection MISS, not an error to report —
|
|
89
|
+
// the ecosystem falls back to its presence-only rules and the operator still gets the
|
|
90
|
+
// rest of the suggestions.
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Compose one ecosystem's result, dropping an ecosystem that produced ONLY setup commands.
|
|
97
|
+
* An `install` on its own verifies nothing — it would open a PR on a checkout no command
|
|
98
|
+
* ever inspected, while costing every run the install — so the whole ecosystem is dropped
|
|
99
|
+
* instead, which is also what lets the task-runner fallback below take over.
|
|
100
|
+
*/
|
|
101
|
+
export function ecosystem(id, checks) {
|
|
102
|
+
const kept = checks.filter((c) => c !== null);
|
|
103
|
+
if (!kept.some((c) => c.role !== 'install'))
|
|
104
|
+
return null;
|
|
105
|
+
return { ecosystem: id, checks: kept };
|
|
106
|
+
}
|
|
107
|
+
/** Build a check, or nothing when its precondition didn't hold (keeps detectors declarative). */
|
|
108
|
+
export function check(role, label, command) {
|
|
109
|
+
return command ? { role, label, command } : null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Run the detectors over a repo surface and return the suggested checks.
|
|
113
|
+
*
|
|
114
|
+
* LANGUAGE ecosystems are detected first and independently — a polyglot repo (a Go service
|
|
115
|
+
* with a Node SPA beside it) gets both, because either half can break the PR. TASK RUNNERS
|
|
116
|
+
* (`make`, `just`, `task`) are a FALLBACK, used only when no language ecosystem produced
|
|
117
|
+
* anything: in a repo that has both, the language commands are the specific ones, and
|
|
118
|
+
* suggesting `make test` beside `go test ./...` would run the same suite twice on every run.
|
|
119
|
+
*/
|
|
120
|
+
export function detectValidationChecks(surface, detectors) {
|
|
121
|
+
const view = new RepoView(surface);
|
|
122
|
+
const run = (list) => list.map((d) => d(view)).filter((d) => d !== null);
|
|
123
|
+
const language = run(detectors.language);
|
|
124
|
+
const hits = language.length > 0 ? language : run(detectors.taskRunner);
|
|
125
|
+
// Grouped by ecosystem (so an install stays with the commands that need it), role-ordered
|
|
126
|
+
// within each group, and in the detectors' own canonical order across groups.
|
|
127
|
+
const ordered = hits.flatMap((hit) => [...hit.checks]
|
|
128
|
+
.sort((a, b) => ROLE_ORDER.indexOf(a.role) - ROLE_ORDER.indexOf(b.role))
|
|
129
|
+
.map((c) => ({ ...c, ecosystem: hit.ecosystem })));
|
|
130
|
+
// The cap can slice an ecosystem mid-group and leave only its `install` behind — a command
|
|
131
|
+
// that verifies nothing, costs every run its install time, and reads as "this ecosystem is
|
|
132
|
+
// covered". Drop any group the cut reduced to setup, the same rule `ecosystem()` applies
|
|
133
|
+
// before truncation.
|
|
134
|
+
const capped = ordered.slice(0, VALIDATION_MAX_CHECKS);
|
|
135
|
+
const verified = new Set(capped.filter((c) => c.role !== 'install').map((c) => c.ecosystem));
|
|
136
|
+
const kept = capped.filter((c) => verified.has(c.ecosystem));
|
|
137
|
+
return {
|
|
138
|
+
ecosystems: hits.map((h) => h.ecosystem).filter((id) => verified.has(id)),
|
|
139
|
+
checks: uniquifyLabels(kept),
|
|
140
|
+
truncated: ordered.length > kept.length,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Make the labels unique, which the write contract REQUIRES — two ecosystems both
|
|
145
|
+
* proposing `test` would otherwise produce a suggestion the operator cannot save, with a
|
|
146
|
+
* validation error that names neither offender. Disambiguates with the ecosystem first
|
|
147
|
+
* (`test` / `test (go)`), which is the information a reader actually wants, and falls back
|
|
148
|
+
* to an ordinal only if that still collides.
|
|
149
|
+
*/
|
|
150
|
+
function uniquifyLabels(checks) {
|
|
151
|
+
const taken = new Set();
|
|
152
|
+
return checks.map((c) => {
|
|
153
|
+
const candidates = [c.label, `${c.label} (${c.ecosystem})`];
|
|
154
|
+
let label = candidates.find((l) => !taken.has(l));
|
|
155
|
+
for (let n = 2; label === undefined; n += 1) {
|
|
156
|
+
const numbered = `${c.label} (${c.ecosystem} ${n})`;
|
|
157
|
+
if (!taken.has(numbered))
|
|
158
|
+
label = numbered;
|
|
159
|
+
}
|
|
160
|
+
taken.add(label);
|
|
161
|
+
// The write contract caps a label at 80 chars; every label built here is far shorter,
|
|
162
|
+
// but clamp anyway so a future long label degrades instead of failing the save.
|
|
163
|
+
return { label: label.slice(0, 80), command: c.command };
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=validation-detection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-detection.js","sourceRoot":"","sources":["../../src/domain/validation-detection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { EcosystemDetection, EcosystemDetector, RepoView } from './validation-detection.js';
|
|
2
|
+
/**
|
|
3
|
+
* The root manifests whose CONTENT the reader fetches (everything else is presence-only).
|
|
4
|
+
* Bounded on purpose: the reader turns this into one file read per entry that actually
|
|
5
|
+
* exists, so a new entry here is a new round trip on every detection.
|
|
6
|
+
*/
|
|
7
|
+
export declare const VALIDATION_DETECTION_CONTENT_FILES: readonly string[];
|
|
8
|
+
/**
|
|
9
|
+
* Node: the scripts the repo declares ARE the evidence. Nothing is invented — a repo with
|
|
10
|
+
* no `lint` script gets no lint check, because guessing `npx eslint .` on a project that
|
|
11
|
+
* never configured eslint produces a check that fails for reasons no agent can fix.
|
|
12
|
+
*/
|
|
13
|
+
export declare function detectNode(view: RepoView): EcosystemDetection | null;
|
|
14
|
+
export declare function detectPython(view: RepoView): EcosystemDetection | null;
|
|
15
|
+
export declare function detectGo(view: RepoView): EcosystemDetection | null;
|
|
16
|
+
export declare function detectRust(view: RepoView): EcosystemDetection | null;
|
|
17
|
+
export declare function detectMaven(view: RepoView): EcosystemDetection | null;
|
|
18
|
+
export declare function detectGradle(view: RepoView): EcosystemDetection | null;
|
|
19
|
+
export declare function detectDotnet(view: RepoView): EcosystemDetection | null;
|
|
20
|
+
export declare function detectRuby(view: RepoView): EcosystemDetection | null;
|
|
21
|
+
export declare function detectPhp(view: RepoView): EcosystemDetection | null;
|
|
22
|
+
export declare function detectElixir(view: RepoView): EcosystemDetection | null;
|
|
23
|
+
export declare function detectMake(view: RepoView): EcosystemDetection | null;
|
|
24
|
+
export declare function detectJust(view: RepoView): EcosystemDetection | null;
|
|
25
|
+
export declare function detectTask(view: RepoView): EcosystemDetection | null;
|
|
26
|
+
/** Language ecosystems, in the order their suggestions are presented. */
|
|
27
|
+
export declare const LANGUAGE_DETECTORS: readonly EcosystemDetector[];
|
|
28
|
+
/** Generic task runners — consulted only when no language ecosystem matched. */
|
|
29
|
+
export declare const TASK_RUNNER_DETECTORS: readonly EcosystemDetector[];
|
|
30
|
+
/**
|
|
31
|
+
* The registries as `detectValidationChecks` takes them. Passed explicitly rather than
|
|
32
|
+
* defaulted inside the composer, which would make `validation-detection.ts` import this
|
|
33
|
+
* module and this module import it back — and which would also hide the seam a deployment
|
|
34
|
+
* would extend to teach the detector its own house convention.
|
|
35
|
+
*/
|
|
36
|
+
export declare const DEFAULT_VALIDATION_DETECTORS: {
|
|
37
|
+
readonly language: readonly EcosystemDetector[];
|
|
38
|
+
readonly taskRunner: readonly EcosystemDetector[];
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=validation-detectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation-detectors.d.ts","sourceRoot":"","sources":["../../src/domain/validation-detectors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AAkBhG;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,EAAE,SAAS,MAAM,EAY/D,CAAA;AAwHD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAkBpE;AAuCD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAoCtE;AAID,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAgBlE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAWpE;AAID,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAOrE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAQtE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAWtE;AAID,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAUpE;AASD,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAgBnE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAQtE;AA0FD,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAIpE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAIpE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAIpE;AAID,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,EAAE,SAAS,iBAAiB,EAW1D,CAAA;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,EAAE,SAAS,iBAAiB,EAI7D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B;aACvC,QAAQ;aACR,UAAU;CACF,CAAA"}
|