@stacknext/regression 0.0.0-stage → 0.6.1
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/LICENSE +21 -0
- package/README.md +17 -2
- package/dist/capture.d.ts +47 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +207 -0
- package/dist/capture.js.map +1 -0
- package/dist/compare.d.ts +11 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +583 -0
- package/dist/compare.js.map +1 -0
- package/dist/constants.d.ts +33 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +56 -0
- package/dist/constants.js.map +1 -0
- package/dist/environment.d.ts +23 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +86 -0
- package/dist/environment.js.map +1 -0
- package/dist/identity.d.ts +35 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +121 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/schema.d.ts +1300 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +328 -0
- package/dist/schema.js.map +1 -0
- package/dist/stats.d.ts +11 -0
- package/dist/stats.d.ts.map +1 -0
- package/dist/stats.js +101 -0
- package/dist/stats.js.map +1 -0
- package/dist/storage.d.ts +63 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +305 -0
- package/dist/storage.js.map +1 -0
- package/package.json +50 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 StackNext contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @stacknext/regression
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Versioned capture-run, baseline and comparison schemas plus deterministic
|
|
4
|
+
comparison logic for [StackNext](https://github.com/marrrkkk/stacknext).
|
|
5
|
+
|
|
6
|
+
Playwright-linked samples are compared using medians and explicit thresholds.
|
|
7
|
+
Statuses are `improved`, `regressed`, `unchanged` or `inconclusive`; comparisons
|
|
8
|
+
are not statistical tests and do not prove causality. Records are redacted
|
|
9
|
+
local JSON files under `.stacknext`.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install --save-dev @stacknext/cli @stacknext/next @stacknext/playwright
|
|
13
|
+
npx stacknext capture --name baseline -- npx playwright test --workers=1
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
This package is ESM-only and requires Node.js 20.9 or newer.
|
|
17
|
+
|
|
18
|
+
License: MIT.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type DiagnosticFinding, type TraceDetail } from '@stacknext/core';
|
|
2
|
+
import type { CapturedFinding, CapturedSample, CapturedTrace, SampleSubmission, ScenarioIdentity } from './schema.js';
|
|
3
|
+
export interface CorrelationVerification {
|
|
4
|
+
verified: boolean;
|
|
5
|
+
reason?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Proves that a trace belongs to the injected Playwright request. Matching the
|
|
9
|
+
* random trace id alone is not enough: a server span must name the injected
|
|
10
|
+
* span id as its parent.
|
|
11
|
+
*/
|
|
12
|
+
export declare function verifyTraceCorrelation(detail: TraceDetail, traceId: string, injectedSpanId: string): CorrelationVerification;
|
|
13
|
+
/** Converts one correlated collector trace into bounded, id-free scenario evidence. */
|
|
14
|
+
export declare function capturedTraceFromDetail(detail: TraceDetail, injectedSpanId: string): CapturedTrace;
|
|
15
|
+
export declare function capturedFinding(finding: DiagnosticFinding): CapturedFinding;
|
|
16
|
+
export declare function dedupeCapturedFindings(findings: CapturedFinding[]): CapturedFinding[];
|
|
17
|
+
export declare function capturedFindings(findings: DiagnosticFinding[]): CapturedFinding[];
|
|
18
|
+
export interface SampleEvidenceInput {
|
|
19
|
+
submission: SampleSubmission;
|
|
20
|
+
lookupTrace: (traceId: string) => TraceDetail | null;
|
|
21
|
+
findingsForTrace: (traceId: string) => DiagnosticFinding[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Builds the stored sample from collector state. A sample is verified only when
|
|
25
|
+
* every submitted trace exists and proves the injected parent relationship.
|
|
26
|
+
* Missing telemetry is recorded as incomplete instead of being ignored.
|
|
27
|
+
*/
|
|
28
|
+
export declare function capturedSampleFromSubmission(input: SampleEvidenceInput): CapturedSample;
|
|
29
|
+
/** Adds a sample once and returns the merged scenario evidence. */
|
|
30
|
+
export declare function mergeScenarioSample(input: {
|
|
31
|
+
identity: ScenarioIdentity;
|
|
32
|
+
samples: CapturedSample[];
|
|
33
|
+
previousFindings?: CapturedFinding[];
|
|
34
|
+
notes?: string[];
|
|
35
|
+
limitations?: string[];
|
|
36
|
+
}): {
|
|
37
|
+
identity: ScenarioIdentity;
|
|
38
|
+
samples: CapturedSample[];
|
|
39
|
+
findings: CapturedFinding[];
|
|
40
|
+
notes: string[];
|
|
41
|
+
limitations: string[];
|
|
42
|
+
};
|
|
43
|
+
export declare function createSampleId(): string;
|
|
44
|
+
export declare function createRunId(): string;
|
|
45
|
+
export declare function createBaselineId(): string;
|
|
46
|
+
export declare function createComparisonId(): string;
|
|
47
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"AACA,OAAO,EAIL,KAAK,iBAAiB,EAEtB,KAAK,WAAW,EACjB,MAAM,iBAAiB,CAAA;AAMxB,OAAO,KAAK,EAEV,eAAe,EAEf,cAAc,EAEd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,aAAa,CAAA;AAGpB,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,GACrB,uBAAuB,CAezB;AA4CD,uFAAuF;AACvF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,WAAW,EACnB,cAAc,EAAE,MAAM,GACrB,aAAa,CAiDf;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,iBAAiB,GAAG,eAAe,CAQ3E;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,EAAE,GAC1B,eAAe,EAAE,CAMnB;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,eAAe,EAAE,CAEjF;AAED,MAAM,WAAW,mBAAmB;IAClC,UAAU,EAAE,gBAAgB,CAAA;IAC5B,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,WAAW,GAAG,IAAI,CAAA;IACpD,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,iBAAiB,EAAE,CAAA;CAC3D;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,mBAAmB,GACzB,cAAc,CAwDhB;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAA;IACpC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CACvB,GAAG;IACF,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB,CAmBA;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,WAAW,IAAI,MAAM,CAEpC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
|
package/dist/capture.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { fingerprint, redactText, stripUrlQueryInText, } from '@stacknext/core';
|
|
3
|
+
import { diagnosticErrorFingerprint, sqlFingerprint, sqlPreview, } from '@stacknext/diagnostics';
|
|
4
|
+
import { spanIdentity, traceIdentity } from './identity.js';
|
|
5
|
+
/**
|
|
6
|
+
* Proves that a trace belongs to the injected Playwright request. Matching the
|
|
7
|
+
* random trace id alone is not enough: a server span must name the injected
|
|
8
|
+
* span id as its parent.
|
|
9
|
+
*/
|
|
10
|
+
export function verifyTraceCorrelation(detail, traceId, injectedSpanId) {
|
|
11
|
+
if (detail.traceId.toLowerCase() !== traceId.toLowerCase()) {
|
|
12
|
+
return { verified: false, reason: 'trace-id-mismatch' };
|
|
13
|
+
}
|
|
14
|
+
if (detail.spans.length === 0) {
|
|
15
|
+
return { verified: false, reason: 'trace-has-no-spans' };
|
|
16
|
+
}
|
|
17
|
+
const parent = injectedSpanId.toLowerCase();
|
|
18
|
+
const linked = detail.spans.some((span) => span.parentSpanId?.toLowerCase() === parent);
|
|
19
|
+
if (!linked) {
|
|
20
|
+
return { verified: false, reason: 'injected-parent-not-found' };
|
|
21
|
+
}
|
|
22
|
+
return { verified: true };
|
|
23
|
+
}
|
|
24
|
+
function shortText(value, maxLength) {
|
|
25
|
+
return stripUrlQueryInText(redactText(value, maxLength));
|
|
26
|
+
}
|
|
27
|
+
function capturedSpan(span) {
|
|
28
|
+
const identity = spanIdentity(span);
|
|
29
|
+
return {
|
|
30
|
+
key: identity.key,
|
|
31
|
+
name: identity.name,
|
|
32
|
+
kind: span.kind,
|
|
33
|
+
category: identity.category,
|
|
34
|
+
durationMs: Math.round(span.durationMs * 1_000) / 1_000,
|
|
35
|
+
status: span.status,
|
|
36
|
+
...(span.nextSpanType ? { nextSpanType: shortText(span.nextSpanType, 200) } : {}),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function capturedQuery(query) {
|
|
40
|
+
const shape = sqlPreview(query.sql);
|
|
41
|
+
return {
|
|
42
|
+
key: sqlFingerprint(query.sql),
|
|
43
|
+
shape,
|
|
44
|
+
durationMs: query.durationMs,
|
|
45
|
+
source: query.source,
|
|
46
|
+
success: query.success,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function capturedError(error) {
|
|
50
|
+
const route = error.route ?? error.requestPath ?? null;
|
|
51
|
+
return {
|
|
52
|
+
key: diagnosticErrorFingerprint(error),
|
|
53
|
+
name: shortText(error.name, 200),
|
|
54
|
+
message: shortText(error.message, 600),
|
|
55
|
+
route: route ? shortText(route, 500) : null,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function compareStrings(left, right) {
|
|
59
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
60
|
+
}
|
|
61
|
+
/** Converts one correlated collector trace into bounded, id-free scenario evidence. */
|
|
62
|
+
export function capturedTraceFromDetail(detail, injectedSpanId) {
|
|
63
|
+
const identity = traceIdentity(detail);
|
|
64
|
+
const root = detail.spans.find((span) => !span.parentSpanId) ??
|
|
65
|
+
[...detail.spans].sort((a, b) => a.startTime - b.startTime)[0];
|
|
66
|
+
const spans = detail.spans
|
|
67
|
+
.map(capturedSpan)
|
|
68
|
+
.sort((a, b) => compareStrings(a.key, b.key) ||
|
|
69
|
+
b.durationMs - a.durationMs ||
|
|
70
|
+
compareStrings(a.status, b.status))
|
|
71
|
+
.slice(0, 500);
|
|
72
|
+
const queries = detail.databaseQueries
|
|
73
|
+
.map(capturedQuery)
|
|
74
|
+
.sort((a, b) => compareStrings(a.key, b.key) ||
|
|
75
|
+
(a.durationMs ?? -1) - (b.durationMs ?? -1) ||
|
|
76
|
+
Number(a.success) - Number(b.success))
|
|
77
|
+
.slice(0, 200);
|
|
78
|
+
const errors = detail.errors
|
|
79
|
+
.map(capturedError)
|
|
80
|
+
.sort((a, b) => compareStrings(a.key, b.key) ||
|
|
81
|
+
compareStrings(a.name, b.name) ||
|
|
82
|
+
compareStrings(a.message, b.message))
|
|
83
|
+
.slice(0, 100);
|
|
84
|
+
return {
|
|
85
|
+
traceId: detail.traceId.toLowerCase(),
|
|
86
|
+
injectedSpanId: injectedSpanId.toLowerCase(),
|
|
87
|
+
key: identity.key,
|
|
88
|
+
method: identity.method,
|
|
89
|
+
route: identity.route,
|
|
90
|
+
rootName: identity.rootName,
|
|
91
|
+
statusCode: detail.request?.statusCode ?? root?.statusCode ?? null,
|
|
92
|
+
durationMs: Math.round(detail.durationMs * 1_000) / 1_000,
|
|
93
|
+
spanCount: detail.spans.length,
|
|
94
|
+
errorCount: detail.errors.length,
|
|
95
|
+
databaseQueryCount: detail.databaseQueries.length,
|
|
96
|
+
fetchCount: spans.filter((span) => span.category === 'http').length,
|
|
97
|
+
spans,
|
|
98
|
+
queries,
|
|
99
|
+
errors,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function capturedFinding(finding) {
|
|
103
|
+
return {
|
|
104
|
+
key: fingerprint([finding.ruleId, finding.fingerprint]),
|
|
105
|
+
ruleId: finding.ruleId,
|
|
106
|
+
category: finding.category,
|
|
107
|
+
severity: finding.severity,
|
|
108
|
+
status: finding.status,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function dedupeCapturedFindings(findings) {
|
|
112
|
+
const unique = new Map();
|
|
113
|
+
for (const finding of findings) {
|
|
114
|
+
if (!unique.has(finding.key))
|
|
115
|
+
unique.set(finding.key, finding);
|
|
116
|
+
}
|
|
117
|
+
return [...unique.values()].sort((a, b) => compareStrings(a.key, b.key));
|
|
118
|
+
}
|
|
119
|
+
export function capturedFindings(findings) {
|
|
120
|
+
return dedupeCapturedFindings(findings.map(capturedFinding));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Builds the stored sample from collector state. A sample is verified only when
|
|
124
|
+
* every submitted trace exists and proves the injected parent relationship.
|
|
125
|
+
* Missing telemetry is recorded as incomplete instead of being ignored.
|
|
126
|
+
*/
|
|
127
|
+
export function capturedSampleFromSubmission(input) {
|
|
128
|
+
const reasons = new Set();
|
|
129
|
+
const traces = [];
|
|
130
|
+
const findings = [];
|
|
131
|
+
const seen = new Set();
|
|
132
|
+
let references = 0;
|
|
133
|
+
for (const reference of input.submission.traces) {
|
|
134
|
+
const key = `${reference.traceId.toLowerCase()}:${reference.injectedSpanId.toLowerCase()}`;
|
|
135
|
+
if (seen.has(key))
|
|
136
|
+
continue;
|
|
137
|
+
seen.add(key);
|
|
138
|
+
references += 1;
|
|
139
|
+
const detail = input.lookupTrace(reference.traceId);
|
|
140
|
+
if (!detail) {
|
|
141
|
+
reasons.add('trace-not-found');
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const verification = verifyTraceCorrelation(detail, reference.traceId, reference.injectedSpanId);
|
|
145
|
+
if (!verification.verified) {
|
|
146
|
+
reasons.add(verification.reason ?? 'correlation-unverified');
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
traces.push(capturedTraceFromDetail(detail, reference.injectedSpanId));
|
|
150
|
+
findings.push(...input.findingsForTrace(reference.traceId).map(capturedFinding));
|
|
151
|
+
}
|
|
152
|
+
traces.sort((a, b) => compareStrings(a.key, b.key) ||
|
|
153
|
+
compareStrings(a.rootName, b.rootName) ||
|
|
154
|
+
compareStrings(a.traceId, b.traceId));
|
|
155
|
+
if (references === 0)
|
|
156
|
+
reasons.add('no-request-trace-recorded');
|
|
157
|
+
const verified = references > 0 && traces.length === references;
|
|
158
|
+
if (!verified && references > 0 && traces.length === 0) {
|
|
159
|
+
reasons.add('no-verified-traces');
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
id: input.submission.id,
|
|
163
|
+
status: input.submission.status,
|
|
164
|
+
expectedStatus: input.submission.expectedStatus,
|
|
165
|
+
retry: input.submission.retry,
|
|
166
|
+
repeatEachIndex: input.submission.repeatEachIndex,
|
|
167
|
+
durationMs: Math.round(input.submission.durationMs * 1_000) / 1_000,
|
|
168
|
+
capturedAt: input.submission.capturedAt,
|
|
169
|
+
correlation: {
|
|
170
|
+
status: verified ? 'verified' : 'incomplete',
|
|
171
|
+
reasons: [...reasons].sort().slice(0, 20),
|
|
172
|
+
traces: traces.slice(0, 20),
|
|
173
|
+
},
|
|
174
|
+
findings: dedupeCapturedFindings(findings),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Adds a sample once and returns the merged scenario evidence. */
|
|
178
|
+
export function mergeScenarioSample(input) {
|
|
179
|
+
const samples = [...input.samples].sort((a, b) => a.capturedAt - b.capturedAt ||
|
|
180
|
+
a.retry - b.retry ||
|
|
181
|
+
a.repeatEachIndex - b.repeatEachIndex ||
|
|
182
|
+
compareStrings(a.id, b.id));
|
|
183
|
+
const findings = dedupeCapturedFindings([
|
|
184
|
+
...(input.previousFindings ?? []),
|
|
185
|
+
...samples.flatMap((sample) => sample.findings),
|
|
186
|
+
]);
|
|
187
|
+
return {
|
|
188
|
+
identity: input.identity,
|
|
189
|
+
samples: samples.slice(0, 100),
|
|
190
|
+
findings,
|
|
191
|
+
notes: [...new Set(input.notes ?? [])].slice(0, 30),
|
|
192
|
+
limitations: [...new Set(input.limitations ?? [])].slice(0, 30),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
export function createSampleId() {
|
|
196
|
+
return `smp-${randomBytes(8).toString('hex')}`;
|
|
197
|
+
}
|
|
198
|
+
export function createRunId() {
|
|
199
|
+
return `run-${randomBytes(8).toString('hex')}`;
|
|
200
|
+
}
|
|
201
|
+
export function createBaselineId() {
|
|
202
|
+
return `base-${randomBytes(8).toString('hex')}`;
|
|
203
|
+
}
|
|
204
|
+
export function createComparisonId() {
|
|
205
|
+
return `cmp-${randomBytes(8).toString('hex')}`;
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Baseline, Comparison, ComparisonOptions, EnvironmentDifference, TestRun } from './schema.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compares a saved baseline with a candidate run. Ids and timestamps are never
|
|
4
|
+
* used to match scenarios or traces; matching uses the stored stable keys.
|
|
5
|
+
*/
|
|
6
|
+
export declare function compareBaseline(baseline: Baseline, candidate: TestRun, options?: Partial<ComparisonOptions>, meta?: {
|
|
7
|
+
id?: string;
|
|
8
|
+
now?: number;
|
|
9
|
+
}): Comparison;
|
|
10
|
+
export declare function environmentDifferencesOf(comparison: Comparison): EnvironmentDifference[];
|
|
11
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,QAAQ,EAKR,UAAU,EACV,iBAAiB,EAEjB,qBAAqB,EAGrB,OAAO,EACR,MAAM,aAAa,CAAA;AAonBpB;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,OAAO,EAClB,OAAO,GAAE,OAAO,CAAC,iBAAiB,CAAM,EACxC,IAAI,GAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GACvC,UAAU,CAwGZ;AAED,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,UAAU,GACrB,qBAAqB,EAAE,CAEzB"}
|