vdelta 0.2.2 → 0.3.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/adapter.d.ts +155 -0
- package/dist/adapter.js +30 -0
- package/dist/adapter.js.map +1 -0
- package/dist/adapters/registry.d.ts +72 -0
- package/dist/adapters/registry.js +78 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/vitest/adapter.d.ts +19 -0
- package/dist/adapters/vitest/adapter.js +192 -0
- package/dist/adapters/vitest/adapter.js.map +1 -0
- package/dist/adapters/vitest/recorder.d.ts +10 -19
- package/dist/adapters/vitest/recorder.js +8 -3
- package/dist/adapters/vitest/recorder.js.map +1 -1
- package/dist/cli.js +25 -5
- package/dist/cli.js.map +1 -1
- package/dist/compare.js +45 -11
- package/dist/compare.js.map +1 -1
- package/dist/gate.js +15 -3
- package/dist/gate.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/run.d.ts +22 -14
- package/dist/run.js +118 -123
- package/dist/run.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The adapter seam. The core (`run` / `compare` / `gate`) never reaches into a
|
|
3
|
+
* concrete runner: it resolves an {@link Adapter} descriptor from the registry
|
|
4
|
+
* (`src/adapters/registry.ts`) and talks to it through this interface only.
|
|
5
|
+
*
|
|
6
|
+
* Every type here is a promotion of a shape that already existed on the vitest
|
|
7
|
+
* side, so the seam is a rearrangement of what already ran rather than a new
|
|
8
|
+
* mechanism: {@link RecordContext} is the recorder's context object verbatim,
|
|
9
|
+
* {@link DetectResult} is the runner-token scan's return value,
|
|
10
|
+
* {@link InstrumentedChild} is the argv/env injection `vdelta run` already
|
|
11
|
+
* performed, and {@link CommandSelector} is the §6.4 inclusion-intent split.
|
|
12
|
+
*
|
|
13
|
+
* Dependency direction inside an adapter is one-way: `detect` / `instrument` /
|
|
14
|
+
* `splitCommandSelector` know only the runner's *argv surface*, `record` knows
|
|
15
|
+
* only its *structured channel*.
|
|
16
|
+
*/
|
|
17
|
+
import type { RunRecord } from './schema.js';
|
|
18
|
+
/**
|
|
19
|
+
* The three-valued capability convention (§3.4). The value set is closed
|
|
20
|
+
* (§14); capability *names* stay open so an adapter may declare its own.
|
|
21
|
+
*/
|
|
22
|
+
export declare const CAPABILITY_VALUES: readonly ["pass", "fail", "unsupported"];
|
|
23
|
+
export type CapabilityValue = (typeof CAPABILITY_VALUES)[number];
|
|
24
|
+
export type CapabilityDeclaration = Readonly<Record<string, CapabilityValue>>;
|
|
25
|
+
/**
|
|
26
|
+
* Where the adapter's structured channel lands. A single capture file is the
|
|
27
|
+
* only kind today; per-process channels are follow-up F-2, at which point the
|
|
28
|
+
* channel's creation and teardown move from the core into the adapter.
|
|
29
|
+
*/
|
|
30
|
+
export interface CaptureChannel {
|
|
31
|
+
readonly kind: 'single-file';
|
|
32
|
+
readonly path: string;
|
|
33
|
+
}
|
|
34
|
+
/** What {@link Adapter.detect} returns; `null` means only "not mine". */
|
|
35
|
+
export interface DetectResult {
|
|
36
|
+
/** argv index of the runner binary token. */
|
|
37
|
+
readonly tokenIndex: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The instrumented child invocation. `argv` fully replaces the caller's argv;
|
|
41
|
+
* `env` carries only the additions to layer onto the inherited environment.
|
|
42
|
+
*/
|
|
43
|
+
export interface InstrumentedChild {
|
|
44
|
+
readonly argv: readonly string[];
|
|
45
|
+
readonly env: Readonly<Record<string, string>>;
|
|
46
|
+
}
|
|
47
|
+
/** The §6.4 inclusion-intent split of a child argv (§5.1 canonical command). */
|
|
48
|
+
export interface CommandSelector {
|
|
49
|
+
command: string[];
|
|
50
|
+
selector: string[];
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Everything the recorder needs beyond the runner's own structured channel:
|
|
54
|
+
* repo identity, provenance, the canonicalized invocation and the child's raw
|
|
55
|
+
* streams. Runner-neutral by construction — no field carries runner vocabulary.
|
|
56
|
+
*/
|
|
57
|
+
export interface RecordContext {
|
|
58
|
+
worktree: string;
|
|
59
|
+
repoIdentity: string;
|
|
60
|
+
branch: string;
|
|
61
|
+
cwdRel: string;
|
|
62
|
+
command: string[];
|
|
63
|
+
selector: string[];
|
|
64
|
+
head: string | null;
|
|
65
|
+
treeDigest: string;
|
|
66
|
+
dirtyDiffDigest: string;
|
|
67
|
+
childExitCode: number;
|
|
68
|
+
rawStdout: string;
|
|
69
|
+
rawStderr: string;
|
|
70
|
+
adapterVersion: string;
|
|
71
|
+
recordedAtMs: number;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The capability names a declaration says the composition cannot meet, sorted
|
|
75
|
+
* (§4.2 disclosure rule). `unsupported` is the only degraded value: `fail`
|
|
76
|
+
* means the capability is met and the observation is red, not that evidence is
|
|
77
|
+
* missing. This is what a report's `failure_evidence.degraded_capabilities`
|
|
78
|
+
* and every per-test claim that carries it are derived from.
|
|
79
|
+
*/
|
|
80
|
+
export declare function degradedCapabilities(caps: CapabilityDeclaration): string[];
|
|
81
|
+
/** §6.4 selector containment, as declared by an adapter that can decide it. */
|
|
82
|
+
export type SelectorRelation = 'equal' | 'subset' | 'superset' | 'disjoint' | 'unknown';
|
|
83
|
+
export type SelectorMatch = 'yes' | 'no' | 'unknown';
|
|
84
|
+
/**
|
|
85
|
+
* The capture channel is unreadable, of an unsupported version, or malformed.
|
|
86
|
+
* The core maps this onto degraded raw passthrough (INV-5): veridelta is never
|
|
87
|
+
* worse than its absence.
|
|
88
|
+
*/
|
|
89
|
+
export declare class AdapterCaptureError extends Error {
|
|
90
|
+
constructor(message: string);
|
|
91
|
+
}
|
|
92
|
+
export interface Adapter {
|
|
93
|
+
readonly name: string;
|
|
94
|
+
readonly compositionId: string;
|
|
95
|
+
readonly declaredCapabilities: CapabilityDeclaration;
|
|
96
|
+
readonly declaredEnvVars: readonly string[];
|
|
97
|
+
/**
|
|
98
|
+
* Whether this argv is this adapter's invocation. `null` means "not mine"
|
|
99
|
+
* and nothing else — an adapter alone can never conclude "this is a wrapper
|
|
100
|
+
* command"; that is a registry-level conclusion.
|
|
101
|
+
*/
|
|
102
|
+
detect(argv: readonly string[]): DetectResult | null;
|
|
103
|
+
/**
|
|
104
|
+
* The env that points this adapter's reporter at the channel.
|
|
105
|
+
*
|
|
106
|
+
* Exported to the child *whether or not* the adapter claimed the argv. A
|
|
107
|
+
* reporter registered in the project's own configuration (spec §4.2 ambient
|
|
108
|
+
* recording — the RECOMMENDED deployment) has no other way to find the
|
|
109
|
+
* channel, and it is deliberately inert without it. Gating this on argv
|
|
110
|
+
* detection would silently stop recording every wrapper invocation
|
|
111
|
+
* (`vdelta run -- npm test`), which is exactly the stream-severing §4.2
|
|
112
|
+
* warns about.
|
|
113
|
+
*
|
|
114
|
+
* The channel is one file today, so two adapters pointing their reporters at
|
|
115
|
+
* it would race (last writer wins). That is the pre-existing single-channel
|
|
116
|
+
* hazard follow-up F-2 exists for, not something detection was protecting
|
|
117
|
+
* against — pre-seam this env was exported unconditionally.
|
|
118
|
+
*/
|
|
119
|
+
channelEnv(channel: CaptureChannel): Readonly<Record<string, string>>;
|
|
120
|
+
/**
|
|
121
|
+
* The reporter-injected argv plus the env additions for the child. Only
|
|
122
|
+
* applied when this adapter recognizes the argv: injecting runner flags into
|
|
123
|
+
* a command that is not that runner kills it outright (INV-5 — veridelta is
|
|
124
|
+
* never worse than its absence), and a wrapper cannot forward them anyway
|
|
125
|
+
* (§4.3-7). {@link channelEnv} is what covers the uninstrumented case.
|
|
126
|
+
*/
|
|
127
|
+
instrument(argv: readonly string[], channel: CaptureChannel): InstrumentedChild;
|
|
128
|
+
/**
|
|
129
|
+
* Whether the capture now sitting in the channel is this adapter's, decided
|
|
130
|
+
* on the channel's own payload rather than on argv.
|
|
131
|
+
*
|
|
132
|
+
* Consulted only when no adapter claimed the argv: the ambient case, where
|
|
133
|
+
* the reporter came from the project's configuration instead of from
|
|
134
|
+
* {@link instrument}, so the argv carries no evidence of which runner ran.
|
|
135
|
+
* MUST NOT throw — an absent, empty, foreign or malformed channel is simply
|
|
136
|
+
* not a claim. A claim asserts authorship only; a payload this adapter owns
|
|
137
|
+
* but cannot use still fails in {@link record}, with its own diagnostic.
|
|
138
|
+
*/
|
|
139
|
+
claimsCapture(channel: CaptureChannel): boolean;
|
|
140
|
+
/** §6.4 inclusion intent, decided purely on the runner's own CLI surface. */
|
|
141
|
+
splitCommandSelector(argv: readonly string[]): CommandSelector;
|
|
142
|
+
/**
|
|
143
|
+
* Read the capture channel, validate it, and build the Run record. Parsing
|
|
144
|
+
* and version checking belong to the *adapter* — the core knows nothing
|
|
145
|
+
* about the channel's payload. Failures throw {@link AdapterCaptureError},
|
|
146
|
+
* which the core maps onto degraded passthrough.
|
|
147
|
+
*/
|
|
148
|
+
record(channel: CaptureChannel, ctx: RecordContext): RunRecord;
|
|
149
|
+
/**
|
|
150
|
+
* §6.4, optional. Undeclared means every relation is `unknown`, which is
|
|
151
|
+
* exactly the comparator's pre-seam behavior (`selector-relation-unknown`).
|
|
152
|
+
*/
|
|
153
|
+
selectorRelation?(a: readonly string[], b: readonly string[]): SelectorRelation;
|
|
154
|
+
selectorMatches?(selector: readonly string[], testId: string): SelectorMatch;
|
|
155
|
+
}
|
package/dist/adapter.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The three-valued capability convention (§3.4). The value set is closed
|
|
3
|
+
* (§14); capability *names* stay open so an adapter may declare its own.
|
|
4
|
+
*/
|
|
5
|
+
export const CAPABILITY_VALUES = ['pass', 'fail', 'unsupported'];
|
|
6
|
+
/**
|
|
7
|
+
* The capability names a declaration says the composition cannot meet, sorted
|
|
8
|
+
* (§4.2 disclosure rule). `unsupported` is the only degraded value: `fail`
|
|
9
|
+
* means the capability is met and the observation is red, not that evidence is
|
|
10
|
+
* missing. This is what a report's `failure_evidence.degraded_capabilities`
|
|
11
|
+
* and every per-test claim that carries it are derived from.
|
|
12
|
+
*/
|
|
13
|
+
export function degradedCapabilities(caps) {
|
|
14
|
+
return Object.entries(caps)
|
|
15
|
+
.filter(([, value]) => value === 'unsupported')
|
|
16
|
+
.map(([name]) => name)
|
|
17
|
+
.sort();
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The capture channel is unreadable, of an unsupported version, or malformed.
|
|
21
|
+
* The core maps this onto degraded raw passthrough (INV-5): veridelta is never
|
|
22
|
+
* worse than its absence.
|
|
23
|
+
*/
|
|
24
|
+
export class AdapterCaptureError extends Error {
|
|
25
|
+
constructor(message) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'AdapterCaptureError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAkBA;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,aAAa,CAAU,CAAA;AAyDzE;;;;;;GAMG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAA2B;IAC9D,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;SACxB,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,KAAK,aAAa,CAAC;SAC9C,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC;SACrB,IAAI,EAAE,CAAA;AACX,CAAC;AAWD;;;;GAIG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAA;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The adapter registry: the one place the core is allowed to know which
|
|
3
|
+
* concrete adapters exist. A static array in a fixed order — no registration
|
|
4
|
+
* API until adapters become external plugins.
|
|
5
|
+
*
|
|
6
|
+
* Detection is deliberately *total*: every adapter's `detect` is evaluated and
|
|
7
|
+
* the outcome distinguishes zero, one, and several matches, because only the
|
|
8
|
+
* registry can tell "no adapter recognizes this argv" (a wrapper command such
|
|
9
|
+
* as `pnpm test`) from "this adapter is not the one". Zero and ambiguous both
|
|
10
|
+
* stay recordable-but-degraded at the call site, never a hard failure (INV-5).
|
|
11
|
+
*/
|
|
12
|
+
import type { Adapter, CaptureChannel, DetectResult } from '../adapter.js';
|
|
13
|
+
/** Every known adapter, in a deterministic order. */
|
|
14
|
+
export declare const ADAPTERS: readonly Adapter[];
|
|
15
|
+
/** Known adapter names, in registry order — for diagnostics and `--adapter`. */
|
|
16
|
+
export declare function adapterNames(adapters?: readonly Adapter[]): readonly string[];
|
|
17
|
+
/**
|
|
18
|
+
* Look an adapter up by name without failing. Callers that must stay
|
|
19
|
+
* fail-closed rather than abort — the comparator and the gate, which resolve
|
|
20
|
+
* `record.instrument.adapter` from records they did not write — use this.
|
|
21
|
+
*/
|
|
22
|
+
export declare function findAdapter(name: string, adapters?: readonly Adapter[]): Adapter | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve an explicitly requested adapter name. An unknown name is user input
|
|
25
|
+
* error, not a degradation path: silently passing through would turn a typo
|
|
26
|
+
* into a silent loss of recording, which is the opposite of INV-5's intent.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveAdapter(name: string, adapters?: readonly Adapter[]): Adapter;
|
|
29
|
+
/**
|
|
30
|
+
* The env additions every registered adapter needs to find the channel, in
|
|
31
|
+
* registry order. Exported to the child unconditionally, detection or no
|
|
32
|
+
* detection: a reporter configured in the project itself (spec §4.2 ambient
|
|
33
|
+
* recording) is inert without it, and pre-seam `vdelta run` set it on every
|
|
34
|
+
* child for exactly that reason.
|
|
35
|
+
*
|
|
36
|
+
* Later adapters win on a colliding name. With one single-file channel that
|
|
37
|
+
* collision is the F-2 hazard, not a decision this function can make.
|
|
38
|
+
*/
|
|
39
|
+
export declare function ambientChannelEnv(channel: CaptureChannel, adapters?: readonly Adapter[]): Record<string, string>;
|
|
40
|
+
/**
|
|
41
|
+
* Which adapter owns the capture that landed in the channel, or `null` when
|
|
42
|
+
* none claims it. The ambient counterpart of {@link detectAdapter}: the argv
|
|
43
|
+
* said nothing, so the payload decides.
|
|
44
|
+
*
|
|
45
|
+
* Registry order breaks a tie. Two adapters claiming one capture would mean
|
|
46
|
+
* two reporters wrote to the same single-file channel, which is the F-2
|
|
47
|
+
* last-writer-wins hazard rather than an ambiguity the user could resolve —
|
|
48
|
+
* there is no argv to name a winner with, so degrading here would discard a
|
|
49
|
+
* capture that is genuinely present.
|
|
50
|
+
*/
|
|
51
|
+
export declare function claimCapture(channel: CaptureChannel, adapters?: readonly Adapter[]): Adapter | null;
|
|
52
|
+
/**
|
|
53
|
+
* Outcome of evaluating every adapter's `detect` against one child argv.
|
|
54
|
+
* `none` and `ambiguous` are both "the registry declines to choose": the
|
|
55
|
+
* caller degrades to raw passthrough and says why.
|
|
56
|
+
*/
|
|
57
|
+
export type AdapterDetection = {
|
|
58
|
+
readonly kind: 'none';
|
|
59
|
+
} | {
|
|
60
|
+
readonly kind: 'unique';
|
|
61
|
+
readonly adapter: Adapter;
|
|
62
|
+
readonly detected: DetectResult;
|
|
63
|
+
} | {
|
|
64
|
+
readonly kind: 'ambiguous';
|
|
65
|
+
readonly candidates: readonly Adapter[];
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Evaluate *all* adapters against the argv. Never short-circuits on the first
|
|
69
|
+
* match: two adapters claiming the same argv is a distinguishable outcome that
|
|
70
|
+
* must reach the user as a candidate list, not be hidden by registry order.
|
|
71
|
+
*/
|
|
72
|
+
export declare function detectAdapter(argv: readonly string[], adapters?: readonly Adapter[]): AdapterDetection;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { vitestAdapter } from './vitest/adapter.js';
|
|
2
|
+
/** Every known adapter, in a deterministic order. */
|
|
3
|
+
export const ADAPTERS = [vitestAdapter];
|
|
4
|
+
/** Known adapter names, in registry order — for diagnostics and `--adapter`. */
|
|
5
|
+
export function adapterNames(adapters = ADAPTERS) {
|
|
6
|
+
return adapters.map((a) => a.name);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Look an adapter up by name without failing. Callers that must stay
|
|
10
|
+
* fail-closed rather than abort — the comparator and the gate, which resolve
|
|
11
|
+
* `record.instrument.adapter` from records they did not write — use this.
|
|
12
|
+
*/
|
|
13
|
+
export function findAdapter(name, adapters = ADAPTERS) {
|
|
14
|
+
return adapters.find((a) => a.name === name);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve an explicitly requested adapter name. An unknown name is user input
|
|
18
|
+
* error, not a degradation path: silently passing through would turn a typo
|
|
19
|
+
* into a silent loss of recording, which is the opposite of INV-5's intent.
|
|
20
|
+
*/
|
|
21
|
+
export function resolveAdapter(name, adapters = ADAPTERS) {
|
|
22
|
+
const adapter = findAdapter(name, adapters);
|
|
23
|
+
if (adapter === undefined) {
|
|
24
|
+
throw new Error(`unknown adapter '${name}' — known adapters: ${adapterNames(adapters).join(', ')}`);
|
|
25
|
+
}
|
|
26
|
+
return adapter;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The env additions every registered adapter needs to find the channel, in
|
|
30
|
+
* registry order. Exported to the child unconditionally, detection or no
|
|
31
|
+
* detection: a reporter configured in the project itself (spec §4.2 ambient
|
|
32
|
+
* recording) is inert without it, and pre-seam `vdelta run` set it on every
|
|
33
|
+
* child for exactly that reason.
|
|
34
|
+
*
|
|
35
|
+
* Later adapters win on a colliding name. With one single-file channel that
|
|
36
|
+
* collision is the F-2 hazard, not a decision this function can make.
|
|
37
|
+
*/
|
|
38
|
+
export function ambientChannelEnv(channel, adapters = ADAPTERS) {
|
|
39
|
+
const env = {};
|
|
40
|
+
for (const adapter of adapters) {
|
|
41
|
+
Object.assign(env, adapter.channelEnv(channel));
|
|
42
|
+
}
|
|
43
|
+
return env;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Which adapter owns the capture that landed in the channel, or `null` when
|
|
47
|
+
* none claims it. The ambient counterpart of {@link detectAdapter}: the argv
|
|
48
|
+
* said nothing, so the payload decides.
|
|
49
|
+
*
|
|
50
|
+
* Registry order breaks a tie. Two adapters claiming one capture would mean
|
|
51
|
+
* two reporters wrote to the same single-file channel, which is the F-2
|
|
52
|
+
* last-writer-wins hazard rather than an ambiguity the user could resolve —
|
|
53
|
+
* there is no argv to name a winner with, so degrading here would discard a
|
|
54
|
+
* capture that is genuinely present.
|
|
55
|
+
*/
|
|
56
|
+
export function claimCapture(channel, adapters = ADAPTERS) {
|
|
57
|
+
return adapters.find((a) => a.claimsCapture(channel)) ?? null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Evaluate *all* adapters against the argv. Never short-circuits on the first
|
|
61
|
+
* match: two adapters claiming the same argv is a distinguishable outcome that
|
|
62
|
+
* must reach the user as a candidate list, not be hidden by registry order.
|
|
63
|
+
*/
|
|
64
|
+
export function detectAdapter(argv, adapters = ADAPTERS) {
|
|
65
|
+
const matches = [];
|
|
66
|
+
for (const adapter of adapters) {
|
|
67
|
+
const detected = adapter.detect(argv);
|
|
68
|
+
if (detected !== null)
|
|
69
|
+
matches.push({ adapter, detected });
|
|
70
|
+
}
|
|
71
|
+
if (matches.length === 0)
|
|
72
|
+
return { kind: 'none' };
|
|
73
|
+
if (matches.length > 1)
|
|
74
|
+
return { kind: 'ambiguous', candidates: matches.map((m) => m.adapter) };
|
|
75
|
+
const only = matches[0];
|
|
76
|
+
return { kind: 'unique', adapter: only.adapter, detected: only.detected };
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/adapters/registry.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEnD,qDAAqD;AACrD,MAAM,CAAC,MAAM,QAAQ,GAAuB,CAAC,aAAa,CAAC,CAAA;AAE3D,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAC1B,WAA+B,QAAQ;IAEvC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;AACpC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,WAA+B,QAAQ;IAEvC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,IAAY,EACZ,WAA+B,QAAQ;IAEvC,MAAM,OAAO,GAAG,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAC3C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,oBAAoB,IAAI,uBAAuB,YAAY,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACnF,CAAA;IACH,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAC/B,OAAuB,EACvB,WAA+B,QAAQ;IAEvC,MAAM,GAAG,GAA2B,EAAE,CAAA;IACtC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;IACjD,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAuB,EACvB,WAA+B,QAAQ;IAEvC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAA;AAC/D,CAAC;AAgBD;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAuB,EACvB,WAA+B,QAAQ;IAEvC,MAAM,OAAO,GAAmD,EAAE,CAAA;IAClE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QACrC,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAA;IAC5D,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAA;IACjD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QACpB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAA;IACzE,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAE,CAAA;IACxB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAA;AAC3E,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type Adapter, type CapabilityDeclaration, type CommandSelector } from '../../adapter.js';
|
|
2
|
+
/**
|
|
3
|
+
* The invocation's selector is its inclusion intent (§6.4): the vitest CLI
|
|
4
|
+
* positional filters. The canonical command excludes them (§5.1).
|
|
5
|
+
*
|
|
6
|
+
* `--flag value` pairs for known value-taking flags (see
|
|
7
|
+
* {@link VITEST_VALUE_FLAGS}) are folded into a single `--flag=value`
|
|
8
|
+
* canonical token so that this form and the pre-joined `--flag=value` form
|
|
9
|
+
* produce byte-identical `command` arrays (and thus the same stream key).
|
|
10
|
+
*/
|
|
11
|
+
export declare function splitCommandSelector(cmd: readonly string[]): CommandSelector;
|
|
12
|
+
/**
|
|
13
|
+
* Initial capability declaration for `vitest-native/1` (§3.4). Reproduces the
|
|
14
|
+
* composition's documented standing byte for byte: only `source-region-text`
|
|
15
|
+
* is degraded (CE-1 — vitest's structured channel carries no failing-source
|
|
16
|
+
* region text), everything else this composition claims is met.
|
|
17
|
+
*/
|
|
18
|
+
export declare const VITEST_CAPABILITIES: CapabilityDeclaration;
|
|
19
|
+
export declare const vitestAdapter: Adapter;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vitest adapter, descriptor side: the runner-facing half of the seam
|
|
3
|
+
* (`src/adapter.ts`). Everything vitest-specific about *invoking* a run lives
|
|
4
|
+
* here — locating the vitest binary in the child argv, injecting the capture
|
|
5
|
+
* reporter, and splitting inclusion intent out of the command (§6.4) using
|
|
6
|
+
* vitest's own CLI surface. The recorder half (capture → RunRecord) stays in
|
|
7
|
+
* `./recorder.ts`; this module only owns reading and parsing the channel.
|
|
8
|
+
*/
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
import { dirname, join } from 'node:path';
|
|
11
|
+
import { fileURLToPath } from 'node:url';
|
|
12
|
+
import { AdapterCaptureError, } from '../../adapter.js';
|
|
13
|
+
import { ADAPTER_NAME, buildRunRecord, COMPOSITION_ID, DECLARED_ENV_VARS, } from './recorder.js';
|
|
14
|
+
/**
|
|
15
|
+
* Absolute path of the in-process vitest reporter that writes the capture.
|
|
16
|
+
* Resolved relative to this module so it points at the built sibling
|
|
17
|
+
* (`dist/adapters/vitest/reporter.js`) rather than at a source path.
|
|
18
|
+
*/
|
|
19
|
+
function reporterModulePath() {
|
|
20
|
+
return join(dirname(fileURLToPath(import.meta.url)), 'reporter.js');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The one env var the capture reporter reads (`reporter.ts:61`). Held here
|
|
24
|
+
* rather than inline so `channelEnv` and the reporter's contract stay a single
|
|
25
|
+
* fact; the reporter is inert without it, by design, so that it can sit
|
|
26
|
+
* permanently in a project's vitest config (spec §4.2 ambient recording).
|
|
27
|
+
*/
|
|
28
|
+
const CAPTURE_FILE_ENV = 'VDELTA_CAPTURE_FILE';
|
|
29
|
+
function channelEnv(channel) {
|
|
30
|
+
return { [CAPTURE_FILE_ENV]: channel.path };
|
|
31
|
+
}
|
|
32
|
+
/** Parse the channel, or `undefined` when there is nothing readable in it. */
|
|
33
|
+
function readCapture(channel) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(readFileSync(channel.path, 'utf8'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Locate the vitest invocation inside the child argv; null when absent. */
|
|
42
|
+
function findVitestToken(cmd) {
|
|
43
|
+
for (let i = 0; i < cmd.length; i++) {
|
|
44
|
+
const token = cmd[i];
|
|
45
|
+
if (/(^|[\\/])vitest(\.mjs|\.js)?$/.test(token) || token === 'vitest')
|
|
46
|
+
return i;
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* vitest 4.x CLI flags that always take their value as a *separate* argv
|
|
52
|
+
* token (`--flag value`), never combined into the flag token itself. Used
|
|
53
|
+
* by {@link splitCommandSelector} to recognize `--flag value` pairs and fold
|
|
54
|
+
* them into a single `--flag=value` canonical token so that space-separated
|
|
55
|
+
* and `=`-joined invocations normalize to the same command array (and
|
|
56
|
+
* therefore the same stream key — see `streamKey` in src/compare.ts).
|
|
57
|
+
*
|
|
58
|
+
* Deliberately excludes flags whose value is *optional*
|
|
59
|
+
* (`--changed`, `--silent`, `--coverage`, `--browser`, `--inspect`, etc.):
|
|
60
|
+
* for those, the token following the flag cannot be distinguished from a
|
|
61
|
+
* positional selector without vitest's own arg-parsing rules, so folding
|
|
62
|
+
* them here would risk silently swallowing a selector token. Flags outside
|
|
63
|
+
* this list keep the historical (pre-fix) behavior: a space-separated value
|
|
64
|
+
* is treated as a selector token, which may cause selector-based stream
|
|
65
|
+
* splitting and an abstain (`comparability: 'none'`) rather than a
|
|
66
|
+
* false-positive comparison.
|
|
67
|
+
*
|
|
68
|
+
* Maintenance: this list targets vitest 4.x. Revisit when bumping the
|
|
69
|
+
* vitest minor/major version (see issue #15 Open Question — no automated
|
|
70
|
+
* mechanism keeps this in sync with vitest's own CLI surface).
|
|
71
|
+
*/
|
|
72
|
+
const VITEST_VALUE_FLAGS = new Set([
|
|
73
|
+
'--project',
|
|
74
|
+
'--config',
|
|
75
|
+
'-c',
|
|
76
|
+
'--root',
|
|
77
|
+
'-r',
|
|
78
|
+
'--dir',
|
|
79
|
+
'--reporter',
|
|
80
|
+
'--outputFile',
|
|
81
|
+
'--pool',
|
|
82
|
+
'--maxWorkers',
|
|
83
|
+
'--minWorkers',
|
|
84
|
+
'--environment',
|
|
85
|
+
'--testNamePattern',
|
|
86
|
+
'-t',
|
|
87
|
+
'--testTimeout',
|
|
88
|
+
'--hookTimeout',
|
|
89
|
+
'--teardownTimeout',
|
|
90
|
+
'--retry',
|
|
91
|
+
'--bail',
|
|
92
|
+
'--maxConcurrency',
|
|
93
|
+
'--shard',
|
|
94
|
+
'--exclude',
|
|
95
|
+
'--mode',
|
|
96
|
+
'--workspace',
|
|
97
|
+
]);
|
|
98
|
+
/**
|
|
99
|
+
* The invocation's selector is its inclusion intent (§6.4): the vitest CLI
|
|
100
|
+
* positional filters. The canonical command excludes them (§5.1).
|
|
101
|
+
*
|
|
102
|
+
* `--flag value` pairs for known value-taking flags (see
|
|
103
|
+
* {@link VITEST_VALUE_FLAGS}) are folded into a single `--flag=value`
|
|
104
|
+
* canonical token so that this form and the pre-joined `--flag=value` form
|
|
105
|
+
* produce byte-identical `command` arrays (and thus the same stream key).
|
|
106
|
+
*/
|
|
107
|
+
export function splitCommandSelector(cmd) {
|
|
108
|
+
const idx = findVitestToken(cmd);
|
|
109
|
+
if (idx === null)
|
|
110
|
+
return { command: [...cmd], selector: [] };
|
|
111
|
+
const command = cmd.slice(0, idx + 1);
|
|
112
|
+
const selector = [];
|
|
113
|
+
for (let i = idx + 1; i < cmd.length; i++) {
|
|
114
|
+
const token = cmd[i];
|
|
115
|
+
if (token === 'run' && i === idx + 1) {
|
|
116
|
+
command.push(token);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (!token.startsWith('-')) {
|
|
120
|
+
selector.push(token);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (token.includes('=')) {
|
|
124
|
+
command.push(token);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const next = cmd[i + 1];
|
|
128
|
+
if (VITEST_VALUE_FLAGS.has(token) &&
|
|
129
|
+
next !== undefined &&
|
|
130
|
+
!next.startsWith('-')) {
|
|
131
|
+
command.push(`${token}=${next}`);
|
|
132
|
+
i++;
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
command.push(token);
|
|
136
|
+
}
|
|
137
|
+
return { command, selector };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Initial capability declaration for `vitest-native/1` (§3.4). Reproduces the
|
|
141
|
+
* composition's documented standing byte for byte: only `source-region-text`
|
|
142
|
+
* is degraded (CE-1 — vitest's structured channel carries no failing-source
|
|
143
|
+
* region text), everything else this composition claims is met.
|
|
144
|
+
*/
|
|
145
|
+
export const VITEST_CAPABILITIES = {
|
|
146
|
+
verdicts: 'pass',
|
|
147
|
+
'source-location': 'pass',
|
|
148
|
+
suppression: 'pass',
|
|
149
|
+
inventory: 'pass',
|
|
150
|
+
'failure-evidence': 'pass',
|
|
151
|
+
'source-region-text': 'unsupported',
|
|
152
|
+
};
|
|
153
|
+
export const vitestAdapter = {
|
|
154
|
+
name: ADAPTER_NAME,
|
|
155
|
+
compositionId: COMPOSITION_ID,
|
|
156
|
+
declaredCapabilities: VITEST_CAPABILITIES,
|
|
157
|
+
declaredEnvVars: DECLARED_ENV_VARS,
|
|
158
|
+
detect(argv) {
|
|
159
|
+
const i = findVitestToken(argv);
|
|
160
|
+
return i === null ? null : { tokenIndex: i };
|
|
161
|
+
},
|
|
162
|
+
channelEnv,
|
|
163
|
+
instrument(argv, channel) {
|
|
164
|
+
return {
|
|
165
|
+
argv: [
|
|
166
|
+
...argv,
|
|
167
|
+
'--reporter=default',
|
|
168
|
+
`--reporter=${reporterModulePath()}`,
|
|
169
|
+
'--includeTaskLocation',
|
|
170
|
+
],
|
|
171
|
+
env: channelEnv(channel),
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
splitCommandSelector,
|
|
175
|
+
claimsCapture(channel) {
|
|
176
|
+
// Authorship only, and from the payload's own self-identification
|
|
177
|
+
// (`capture.ts:34`, a literal `'vitest'`). Deliberately not a version or
|
|
178
|
+
// shape check: a capture this adapter wrote but cannot read must reach
|
|
179
|
+
// `record` so the run degrades with *that* diagnostic ("unsupported
|
|
180
|
+
// capture version N") instead of the generic "is the child a vitest
|
|
181
|
+
// invocation?", which is what the pre-seam code path said.
|
|
182
|
+
return readCapture(channel)?.runner === 'vitest';
|
|
183
|
+
},
|
|
184
|
+
record(channel, ctx) {
|
|
185
|
+
const capture = readCapture(channel);
|
|
186
|
+
if (capture === undefined) {
|
|
187
|
+
throw new AdapterCaptureError('no capture from the vitest reporter — is the child a vitest invocation?');
|
|
188
|
+
}
|
|
189
|
+
return buildRunRecord(capture, ctx);
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
//# sourceMappingURL=adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/adapters/vitest/adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,30 +1,21 @@
|
|
|
1
|
+
import { AdapterCaptureError, type RecordContext } from '../../adapter.js';
|
|
1
2
|
import { type RunRecord } from '../../schema.js';
|
|
2
3
|
import type { Capture, CapturedTest } from './capture.js';
|
|
3
4
|
export declare const ADAPTER_NAME = "vitest";
|
|
4
5
|
export declare const COMPOSITION_ID = "vitest-native/1";
|
|
5
|
-
/** CE-1: vitest's structured channel has no failing-source-region text (expC Q1). */
|
|
6
|
-
export declare const DEGRADED_CAPABILITIES: string[];
|
|
7
6
|
/** Env vars whose values (fingerprinted, never stored) are comparison-relevant. */
|
|
8
7
|
export declare const DECLARED_ENV_VARS: readonly ["CI", "NODE_ENV", "TZ", "LANG"];
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* A capture the recorder cannot turn into a record: unsupported capture
|
|
10
|
+
* version, or an ambiguity it refuses to guess through (§12 fail-closed).
|
|
11
|
+
* A subtype of `AdapterCaptureError` so the core degrades to raw passthrough
|
|
12
|
+
* (INV-5) without knowing which adapter raised it.
|
|
13
|
+
*/
|
|
14
|
+
export declare class RecorderError extends AdapterCaptureError {
|
|
10
15
|
constructor(message: string);
|
|
11
16
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
repoIdentity: string;
|
|
15
|
-
branch: string;
|
|
16
|
-
cwdRel: string;
|
|
17
|
-
command: string[];
|
|
18
|
-
selector: string[];
|
|
19
|
-
head: string | null;
|
|
20
|
-
treeDigest: string;
|
|
21
|
-
dirtyDiffDigest: string;
|
|
22
|
-
childExitCode: number;
|
|
23
|
-
rawStdout: string;
|
|
24
|
-
rawStderr: string;
|
|
25
|
-
adapterVersion: string;
|
|
26
|
-
recordedAtMs: number;
|
|
27
|
-
}
|
|
17
|
+
/** Re-exported from the seam: the context shape is runner-neutral (§4.1). */
|
|
18
|
+
export type { RecordContext };
|
|
28
19
|
export declare function buildRunRecord(capture: Capture, ctx: RecordContext): RunRecord;
|
|
29
20
|
/** The effective evidence-affecting configuration (§3.1, contract §5.4). */
|
|
30
21
|
export declare function instrumentConfigDigest(capture: Capture): string;
|
|
@@ -11,16 +11,21 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { readFileSync, realpathSync } from 'node:fs';
|
|
13
13
|
import { isAbsolute, join, relative } from 'node:path';
|
|
14
|
+
import { AdapterCaptureError } from '../../adapter.js';
|
|
14
15
|
import { canonicalDigest } from '../../digest.js';
|
|
15
16
|
import { redactText, redactValue } from '../../redact.js';
|
|
16
17
|
import { SCHEMA_VERSION, } from '../../schema.js';
|
|
17
18
|
export const ADAPTER_NAME = 'vitest';
|
|
18
19
|
export const COMPOSITION_ID = 'vitest-native/1';
|
|
19
|
-
/** CE-1: vitest's structured channel has no failing-source-region text (expC Q1). */
|
|
20
|
-
export const DEGRADED_CAPABILITIES = ['source-region-text'];
|
|
21
20
|
/** Env vars whose values (fingerprinted, never stored) are comparison-relevant. */
|
|
22
21
|
export const DECLARED_ENV_VARS = ['CI', 'NODE_ENV', 'TZ', 'LANG'];
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* A capture the recorder cannot turn into a record: unsupported capture
|
|
24
|
+
* version, or an ambiguity it refuses to guess through (§12 fail-closed).
|
|
25
|
+
* A subtype of `AdapterCaptureError` so the core degrades to raw passthrough
|
|
26
|
+
* (INV-5) without knowing which adapter raised it.
|
|
27
|
+
*/
|
|
28
|
+
export class RecorderError extends AdapterCaptureError {
|
|
24
29
|
constructor(message) {
|
|
25
30
|
super(message);
|
|
26
31
|
this.name = 'RecorderError';
|