@trailmark/core 0.1.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/baseline.d.ts +21 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +187 -0
- package/dist/baseline.js.map +1 -0
- package/dist/diff.d.ts +3 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +120 -0
- package/dist/diff.js.map +1 -0
- package/dist/fingerprint.d.ts +8 -0
- package/dist/fingerprint.d.ts.map +1 -0
- package/dist/fingerprint.js +85 -0
- package/dist/fingerprint.js.map +1 -0
- package/dist/hash.d.ts +4 -0
- package/dist/hash.d.ts.map +1 -0
- package/dist/hash.js +29 -0
- package/dist/hash.js.map +1 -0
- package/dist/impact.d.ts +5 -0
- package/dist/impact.d.ts.map +1 -0
- package/dist/impact.js +27 -0
- package/dist/impact.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/issue.d.ts +10 -0
- package/dist/issue.d.ts.map +1 -0
- package/dist/issue.js +40 -0
- package/dist/issue.js.map +1 -0
- package/dist/normalize.d.ts +3 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/normalize.js +59 -0
- package/dist/normalize.js.map +1 -0
- package/dist/report/buildReport.d.ts +3 -0
- package/dist/report/buildReport.d.ts.map +1 -0
- package/dist/report/buildReport.js +223 -0
- package/dist/report/buildReport.js.map +1 -0
- package/dist/report/formatConsole.d.ts +3 -0
- package/dist/report/formatConsole.d.ts.map +1 -0
- package/dist/report/formatConsole.js +112 -0
- package/dist/report/formatConsole.js.map +1 -0
- package/dist/report/index.d.ts +4 -0
- package/dist/report/index.d.ts.map +1 -0
- package/dist/report/index.js +20 -0
- package/dist/report/index.js.map +1 -0
- package/dist/report/sort.d.ts +9 -0
- package/dist/report/sort.d.ts.map +1 -0
- package/dist/report/sort.js +20 -0
- package/dist/report/sort.js.map +1 -0
- package/dist/report/types.d.ts +85 -0
- package/dist/report/types.d.ts.map +1 -0
- package/dist/report/types.js +3 -0
- package/dist/report/types.js.map +1 -0
- package/dist/run.d.ts +8 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +20 -0
- package/dist/run.js.map +1 -0
- package/dist/scope.d.ts +5 -0
- package/dist/scope.d.ts.map +1 -0
- package/dist/scope.js +23 -0
- package/dist/scope.js.map +1 -0
- package/dist/types.d.ts +150 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/url.d.ts +3 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +82 -0
- package/dist/url.js.map +1 -0
- package/package.json +23 -0
- package/src/baseline.ts +217 -0
- package/src/diff.ts +150 -0
- package/src/fingerprint.ts +107 -0
- package/src/hash.ts +29 -0
- package/src/impact.ts +27 -0
- package/src/index.ts +11 -0
- package/src/issue.ts +49 -0
- package/src/normalize.ts +66 -0
- package/src/report/buildReport.ts +276 -0
- package/src/report/formatConsole.ts +137 -0
- package/src/report/index.ts +3 -0
- package/src/report/sort.ts +28 -0
- package/src/report/types.ts +101 -0
- package/src/run.ts +22 -0
- package/src/scope.ts +25 -0
- package/src/types.ts +167 -0
- package/src/url.ts +102 -0
- package/test/baseline.test.ts +151 -0
- package/test/diff.test.ts +113 -0
- package/test/fingerprint.test.ts +41 -0
- package/test/issue.test.ts +79 -0
- package/test/report.build.test.ts +191 -0
- package/test/report.console.test.ts +102 -0
- package/test/url.test.ts +25 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/report/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/report/types.ts"],"names":[],"mappings":""}
|
package/dist/run.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { TrailmarkRun, TrailmarkToolInfo } from "./types";
|
|
2
|
+
export declare const DEFAULT_TOOL_INFO: TrailmarkToolInfo;
|
|
3
|
+
export declare function createEmptyRun(options?: {
|
|
4
|
+
metadata?: Record<string, unknown>;
|
|
5
|
+
tool?: TrailmarkToolInfo;
|
|
6
|
+
startedAt?: string;
|
|
7
|
+
}): TrailmarkRun;
|
|
8
|
+
//# sourceMappingURL=run.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAEzD,eAAO,MAAM,iBAAiB,EAAE,iBAI/B,CAAA;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,IAAI,CAAC,EAAE,iBAAiB,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,YAAY,CASf"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_TOOL_INFO = void 0;
|
|
4
|
+
exports.createEmptyRun = createEmptyRun;
|
|
5
|
+
exports.DEFAULT_TOOL_INFO = {
|
|
6
|
+
name: "trailmark",
|
|
7
|
+
version: "0.1.0",
|
|
8
|
+
scanner: "@axe-core/playwright",
|
|
9
|
+
};
|
|
10
|
+
function createEmptyRun(options) {
|
|
11
|
+
return {
|
|
12
|
+
version: 1,
|
|
13
|
+
startedAt: options?.startedAt ?? new Date().toISOString(),
|
|
14
|
+
tool: options?.tool ?? exports.DEFAULT_TOOL_INFO,
|
|
15
|
+
findings: [],
|
|
16
|
+
scanErrors: [],
|
|
17
|
+
metadata: options?.metadata,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":";;;AAQA,wCAaC;AAnBY,QAAA,iBAAiB,GAAsB;IAClD,IAAI,EAAE,WAAW;IACjB,OAAO,EAAE,OAAO;IAChB,OAAO,EAAE,sBAAsB;CAChC,CAAA;AAED,SAAgB,cAAc,CAAC,OAI9B;IACC,OAAO;QACL,OAAO,EAAE,CAAC;QACV,SAAS,EAAE,OAAO,EAAE,SAAS,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACzD,IAAI,EAAE,OAAO,EAAE,IAAI,IAAI,yBAAiB;QACxC,QAAQ,EAAE,EAAE;QACZ,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,OAAO,EAAE,QAAQ;KAC5B,CAAA;AACH,CAAC"}
|
package/dist/scope.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { BaselineEntry, TrailmarkFindingMeta } from "./types";
|
|
2
|
+
export declare function scopeKeyFromMeta(meta: TrailmarkFindingMeta | undefined): string;
|
|
3
|
+
export declare function scopeKeyFromBaselineEntry(entry: Pick<BaselineEntry, "scopeKey" | "projectName">): string;
|
|
4
|
+
export declare function scopedIssueKey(issueId: string, scopeKey: string): string;
|
|
5
|
+
//# sourceMappingURL=scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAA;AAY7D,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,oBAAoB,GAAG,SAAS,GAAG,MAAM,CAE/E;AAED,wBAAgB,yBAAyB,CACvC,KAAK,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,GAAG,aAAa,CAAC,GACrD,MAAM,CAER;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAExE"}
|
package/dist/scope.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scopeKeyFromMeta = scopeKeyFromMeta;
|
|
4
|
+
exports.scopeKeyFromBaselineEntry = scopeKeyFromBaselineEntry;
|
|
5
|
+
exports.scopedIssueKey = scopedIssueKey;
|
|
6
|
+
const DEFAULT_SCOPE_KEY = "default";
|
|
7
|
+
function normalizeScopeValue(value) {
|
|
8
|
+
const normalized = value?.trim().toLowerCase();
|
|
9
|
+
if (!normalized) {
|
|
10
|
+
return DEFAULT_SCOPE_KEY;
|
|
11
|
+
}
|
|
12
|
+
return normalized;
|
|
13
|
+
}
|
|
14
|
+
function scopeKeyFromMeta(meta) {
|
|
15
|
+
return normalizeScopeValue(meta?.projectName);
|
|
16
|
+
}
|
|
17
|
+
function scopeKeyFromBaselineEntry(entry) {
|
|
18
|
+
return normalizeScopeValue(entry.scopeKey ?? entry.projectName);
|
|
19
|
+
}
|
|
20
|
+
function scopedIssueKey(issueId, scopeKey) {
|
|
21
|
+
return `${scopeKey}::${issueId}`;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=scope.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":";;AAYA,4CAEC;AAED,8DAIC;AAED,wCAEC;AAtBD,MAAM,iBAAiB,GAAG,SAAS,CAAA;AAEnC,SAAS,mBAAmB,CAAC,KAAyB;IACpD,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IAC9C,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO,iBAAiB,CAAA;IAC1B,CAAC;IACD,OAAO,UAAU,CAAA;AACnB,CAAC;AAED,SAAgB,gBAAgB,CAAC,IAAsC;IACrE,OAAO,mBAAmB,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;AAC/C,CAAC;AAED,SAAgB,yBAAyB,CACvC,KAAsD;IAEtD,OAAO,mBAAmB,CAAC,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,WAAW,CAAC,CAAA;AACjE,CAAC;AAED,SAAgB,cAAc,CAAC,OAAe,EAAE,QAAgB;IAC9D,OAAO,GAAG,QAAQ,KAAK,OAAO,EAAE,CAAA;AAClC,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
export type TrailmarkImpactKey = "minor" | "moderate" | "serious" | "critical" | "unknown";
|
|
2
|
+
export type TrailmarkImpact = "minor" | "moderate" | "serious" | "critical" | null | undefined;
|
|
3
|
+
export interface UrlRoutePattern {
|
|
4
|
+
match: string;
|
|
5
|
+
replace: string;
|
|
6
|
+
}
|
|
7
|
+
export interface UrlNormalizationOptions {
|
|
8
|
+
stripHash?: boolean;
|
|
9
|
+
stripQuery?: boolean | {
|
|
10
|
+
allow: string[];
|
|
11
|
+
};
|
|
12
|
+
routePatterns?: UrlRoutePattern[];
|
|
13
|
+
}
|
|
14
|
+
export interface TrailmarkFindingMeta {
|
|
15
|
+
testId?: string;
|
|
16
|
+
testTitle?: string;
|
|
17
|
+
testFile?: string;
|
|
18
|
+
projectName?: string;
|
|
19
|
+
scanLabel?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface TrailmarkScanError {
|
|
22
|
+
pageUrl?: string;
|
|
23
|
+
pageKey?: string;
|
|
24
|
+
message: string;
|
|
25
|
+
stack?: string;
|
|
26
|
+
occurredAt: string;
|
|
27
|
+
meta?: TrailmarkFindingMeta;
|
|
28
|
+
}
|
|
29
|
+
export interface TrailmarkAttachment {
|
|
30
|
+
kind: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
error?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface TrailmarkFinding {
|
|
35
|
+
issueId: string;
|
|
36
|
+
relaxedIssueId: string;
|
|
37
|
+
ruleId: string;
|
|
38
|
+
impact?: TrailmarkImpact;
|
|
39
|
+
tags: string[];
|
|
40
|
+
help?: string;
|
|
41
|
+
helpUrl?: string;
|
|
42
|
+
pageUrl: string;
|
|
43
|
+
pageKey: string;
|
|
44
|
+
target: string[];
|
|
45
|
+
selectorHint?: string;
|
|
46
|
+
targetHash: string;
|
|
47
|
+
nodeHtml?: string;
|
|
48
|
+
failureSummary?: string;
|
|
49
|
+
failureHint?: string;
|
|
50
|
+
occurredAt?: string;
|
|
51
|
+
occurrenceId?: string;
|
|
52
|
+
attachments?: TrailmarkAttachment[];
|
|
53
|
+
meta?: TrailmarkFindingMeta;
|
|
54
|
+
}
|
|
55
|
+
export interface TrailmarkToolInfo {
|
|
56
|
+
name: string;
|
|
57
|
+
version: string;
|
|
58
|
+
scanner: string;
|
|
59
|
+
}
|
|
60
|
+
export interface TrailmarkRun {
|
|
61
|
+
version: 1;
|
|
62
|
+
startedAt: string;
|
|
63
|
+
finishedAt?: string;
|
|
64
|
+
tool: TrailmarkToolInfo;
|
|
65
|
+
findings: TrailmarkFinding[];
|
|
66
|
+
scanErrors: TrailmarkScanError[];
|
|
67
|
+
metadata?: Record<string, unknown>;
|
|
68
|
+
}
|
|
69
|
+
export interface BaselineEntry {
|
|
70
|
+
issueId: string;
|
|
71
|
+
scopeKey?: string;
|
|
72
|
+
projectName?: string;
|
|
73
|
+
relaxedIssueId: string;
|
|
74
|
+
ruleId: string;
|
|
75
|
+
impact?: TrailmarkImpact;
|
|
76
|
+
pageKey: string;
|
|
77
|
+
pageUrl: string;
|
|
78
|
+
targetHash: string;
|
|
79
|
+
selectorHint?: string;
|
|
80
|
+
sampleTarget?: string;
|
|
81
|
+
help?: string;
|
|
82
|
+
helpUrl?: string;
|
|
83
|
+
tags: string[];
|
|
84
|
+
failureHint?: string;
|
|
85
|
+
occurrences: number;
|
|
86
|
+
firstSeenAt: string;
|
|
87
|
+
lastSeenAt: string;
|
|
88
|
+
}
|
|
89
|
+
export interface BaselineFile {
|
|
90
|
+
version: 1;
|
|
91
|
+
createdAt: string;
|
|
92
|
+
tool: TrailmarkToolInfo;
|
|
93
|
+
configHash: string;
|
|
94
|
+
findings: Record<string, BaselineEntry>;
|
|
95
|
+
}
|
|
96
|
+
export interface DiffMatch {
|
|
97
|
+
finding: TrailmarkFinding;
|
|
98
|
+
matchedBy: "exact" | "relaxed";
|
|
99
|
+
baselineIssueId: string;
|
|
100
|
+
baselineScopeKey?: string;
|
|
101
|
+
}
|
|
102
|
+
export interface DiffStats {
|
|
103
|
+
totalCurrent: number;
|
|
104
|
+
totalBaseline: number;
|
|
105
|
+
newCount: number;
|
|
106
|
+
existingCount: number;
|
|
107
|
+
resolvedCount: number;
|
|
108
|
+
newByImpact: Record<string, number>;
|
|
109
|
+
}
|
|
110
|
+
export interface DiffResult {
|
|
111
|
+
newFindings: TrailmarkFinding[];
|
|
112
|
+
existing: DiffMatch[];
|
|
113
|
+
resolved: BaselineEntry[];
|
|
114
|
+
blockingNew: TrailmarkFinding[];
|
|
115
|
+
stats: DiffStats;
|
|
116
|
+
}
|
|
117
|
+
export interface DiffOptions {
|
|
118
|
+
failOnImpact?: string[];
|
|
119
|
+
onDebug?: (message: string, context?: Record<string, unknown>) => void;
|
|
120
|
+
}
|
|
121
|
+
export interface AxeNodeResult {
|
|
122
|
+
target: string[];
|
|
123
|
+
html?: string;
|
|
124
|
+
failureSummary?: string;
|
|
125
|
+
}
|
|
126
|
+
export interface AxeViolation {
|
|
127
|
+
id: string;
|
|
128
|
+
impact?: TrailmarkImpact;
|
|
129
|
+
tags?: string[];
|
|
130
|
+
help?: string;
|
|
131
|
+
helpUrl?: string;
|
|
132
|
+
nodes: AxeNodeResult[];
|
|
133
|
+
}
|
|
134
|
+
export interface AxeLikeResults {
|
|
135
|
+
violations: AxeViolation[];
|
|
136
|
+
}
|
|
137
|
+
export interface NormalizeInput {
|
|
138
|
+
results: AxeLikeResults;
|
|
139
|
+
pageUrl: string;
|
|
140
|
+
urlOptions?: UrlNormalizationOptions;
|
|
141
|
+
meta?: TrailmarkFindingMeta;
|
|
142
|
+
htmlMaxLength?: number;
|
|
143
|
+
failureSummaryMaxLength?: number;
|
|
144
|
+
}
|
|
145
|
+
export interface TargetFingerprintCanonical {
|
|
146
|
+
tag: string | null;
|
|
147
|
+
stableAttrs: Record<string, string>;
|
|
148
|
+
structuralHint: string | null;
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/dist/url.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAqEjD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,uBAA4B,GACpC,MAAM,CA6BR"}
|
package/dist/url.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeUrlToPageKey = normalizeUrlToPageKey;
|
|
4
|
+
const DEFAULT_OPTIONS = {
|
|
5
|
+
stripHash: true,
|
|
6
|
+
stripQuery: true,
|
|
7
|
+
};
|
|
8
|
+
function normalizeQuery(params, allow) {
|
|
9
|
+
const keys = Array.from(new Set(params.keys()))
|
|
10
|
+
.filter((key) => !allow || allow.has(key))
|
|
11
|
+
.sort((a, b) => a.localeCompare(b));
|
|
12
|
+
const pairs = [];
|
|
13
|
+
for (const key of keys) {
|
|
14
|
+
const values = params.getAll(key).sort((a, b) => a.localeCompare(b));
|
|
15
|
+
for (const value of values) {
|
|
16
|
+
pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return pairs.join("&");
|
|
20
|
+
}
|
|
21
|
+
function stripQueryFromFallback(raw, stripQuery) {
|
|
22
|
+
if (stripQuery === true) {
|
|
23
|
+
return raw.replace(/\?.*$/, "");
|
|
24
|
+
}
|
|
25
|
+
if (stripQuery && typeof stripQuery === "object") {
|
|
26
|
+
const [base, rawQuery = ""] = raw.split("?");
|
|
27
|
+
if (!rawQuery) {
|
|
28
|
+
return base;
|
|
29
|
+
}
|
|
30
|
+
const allow = new Set(stripQuery.allow);
|
|
31
|
+
const params = new URLSearchParams(rawQuery);
|
|
32
|
+
const query = normalizeQuery(params, allow);
|
|
33
|
+
return query ? `${base}?${query}` : base;
|
|
34
|
+
}
|
|
35
|
+
return raw;
|
|
36
|
+
}
|
|
37
|
+
function applyRoutePatterns(pageKey, routePatterns) {
|
|
38
|
+
if (!routePatterns || routePatterns.length === 0) {
|
|
39
|
+
return pageKey;
|
|
40
|
+
}
|
|
41
|
+
let result = pageKey;
|
|
42
|
+
for (const pattern of routePatterns) {
|
|
43
|
+
try {
|
|
44
|
+
const matcher = new RegExp(pattern.match);
|
|
45
|
+
if (matcher.test(result)) {
|
|
46
|
+
result = result.replace(matcher, pattern.replace);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
function normalizeUrlToPageKey(rawUrl, options = {}) {
|
|
56
|
+
const merged = {
|
|
57
|
+
...DEFAULT_OPTIONS,
|
|
58
|
+
...options,
|
|
59
|
+
};
|
|
60
|
+
let pageKey;
|
|
61
|
+
try {
|
|
62
|
+
const parsed = new URL(rawUrl);
|
|
63
|
+
const host = parsed.host.toLowerCase();
|
|
64
|
+
const pathname = parsed.pathname || "/";
|
|
65
|
+
let query = "";
|
|
66
|
+
if (merged.stripQuery === false) {
|
|
67
|
+
query = normalizeQuery(parsed.searchParams);
|
|
68
|
+
}
|
|
69
|
+
else if (merged.stripQuery && typeof merged.stripQuery === "object") {
|
|
70
|
+
query = normalizeQuery(parsed.searchParams, new Set(merged.stripQuery.allow));
|
|
71
|
+
}
|
|
72
|
+
const hash = !merged.stripHash ? parsed.hash : "";
|
|
73
|
+
pageKey = `${host}${pathname}${query ? `?${query}` : ""}${hash}`;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
const lowered = rawUrl.trim().toLowerCase();
|
|
77
|
+
const withoutHash = merged.stripHash ? lowered.replace(/#.*$/, "") : lowered;
|
|
78
|
+
pageKey = stripQueryFromFallback(withoutHash, merged.stripQuery);
|
|
79
|
+
}
|
|
80
|
+
return applyRoutePatterns(pageKey, merged.routePatterns);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=url.js.map
|
package/dist/url.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":";;AAqEA,sDAgCC;AAnGD,MAAM,eAAe,GAAwE;IAC3F,SAAS,EAAE,IAAI;IACf,UAAU,EAAE,IAAI;CACjB,CAAA;AAED,SAAS,cAAc,CAAC,MAAuB,EAAE,KAAmB;IAClE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;SAC5C,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;SACzC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;IAErC,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QACpE,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;QACvE,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;AACxB,CAAC;AAED,SAAS,sBAAsB,CAC7B,GAAW,EACX,UAAiD;IAEjD,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;QACxB,OAAO,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;IACjC,CAAC;IAED,IAAI,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QACjD,MAAM,CAAC,IAAI,EAAE,QAAQ,GAAG,EAAE,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,IAAI,CAAA;QACb,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;QACvC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAA;QAC5C,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAC3C,OAAO,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAA;IAC1C,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,kBAAkB,CACzB,OAAe,EACf,aAAuD;IAEvD,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjD,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,IAAI,MAAM,GAAG,OAAO,CAAA;IACpB,KAAK,MAAM,OAAO,IAAI,aAAa,EAAE,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YACzC,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,CAAA;YACnD,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAA;AACf,CAAC;AAED,SAAgB,qBAAqB,CACnC,MAAc,EACd,UAAmC,EAAE;IAErC,MAAM,MAAM,GAA4B;QACtC,GAAG,eAAe;QAClB,GAAG,OAAO;KACX,CAAA;IAED,IAAI,OAAe,CAAA;IAEnB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAA;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,EAAE,CAAA;QACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,IAAI,GAAG,CAAA;QAEvC,IAAI,KAAK,GAAG,EAAE,CAAA;QACd,IAAI,MAAM,CAAC,UAAU,KAAK,KAAK,EAAE,CAAC;YAChC,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,YAAY,CAAC,CAAA;QAC7C,CAAC;aAAM,IAAI,MAAM,CAAC,UAAU,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YACtE,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,YAAY,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAA;QAC/E,CAAC;QAED,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;QACjD,OAAO,GAAG,GAAG,IAAI,GAAG,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,CAAA;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;QAC3C,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;QAC5E,OAAO,GAAG,sBAAsB,CAAC,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;IAClE,CAAC;IAED,OAAO,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,aAAa,CAAC,CAAA;AAC1D,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@trailmark/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "src/index.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./src/index.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"zod": "^3.24.1"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^22.10.5"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"test": "vitest run",
|
|
21
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/baseline.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { hashObject } from "./hash"
|
|
2
|
+
import { IMPACT_ORDER } from "./impact"
|
|
3
|
+
import { DEFAULT_TOOL_INFO } from "./run"
|
|
4
|
+
import { scopedIssueKey, scopeKeyFromMeta } from "./scope"
|
|
5
|
+
import { BaselineEntry, BaselineFile, TrailmarkImpact, TrailmarkRun } from "./types"
|
|
6
|
+
import { z } from "zod"
|
|
7
|
+
|
|
8
|
+
export const BASELINE_SCHEMA_VERSION = 1
|
|
9
|
+
|
|
10
|
+
export class BaselineFormatError extends Error {
|
|
11
|
+
constructor(message: string) {
|
|
12
|
+
super(message)
|
|
13
|
+
this.name = "BaselineFormatError"
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function computeConfigHash(config: unknown): string {
|
|
18
|
+
return hashObject(config)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
22
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeTags(tags: string[]): string[] {
|
|
26
|
+
return Array.from(new Set(tags)).sort((a, b) => a.localeCompare(b))
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const baselineEntrySchema: z.ZodType<BaselineEntry> = z.object({
|
|
30
|
+
issueId: z.string(),
|
|
31
|
+
scopeKey: z.string().optional(),
|
|
32
|
+
projectName: z.string().optional(),
|
|
33
|
+
relaxedIssueId: z.string(),
|
|
34
|
+
ruleId: z.string(),
|
|
35
|
+
impact: z
|
|
36
|
+
.enum(
|
|
37
|
+
IMPACT_ORDER.filter((value) => value !== "unknown") as [
|
|
38
|
+
NonNullable<TrailmarkImpact>,
|
|
39
|
+
...NonNullable<TrailmarkImpact>[],
|
|
40
|
+
],
|
|
41
|
+
)
|
|
42
|
+
.nullable()
|
|
43
|
+
.optional(),
|
|
44
|
+
pageKey: z.string(),
|
|
45
|
+
pageUrl: z.string(),
|
|
46
|
+
targetHash: z.string(),
|
|
47
|
+
selectorHint: z.string().optional(),
|
|
48
|
+
sampleTarget: z.string().optional(),
|
|
49
|
+
help: z.string().optional(),
|
|
50
|
+
helpUrl: z.string().optional(),
|
|
51
|
+
tags: z.array(z.string()).transform((tags) => normalizeTags(tags)),
|
|
52
|
+
failureHint: z.string().optional(),
|
|
53
|
+
occurrences: z.number().finite().min(0),
|
|
54
|
+
firstSeenAt: z.string(),
|
|
55
|
+
lastSeenAt: z.string(),
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const baselineFileSchema: z.ZodType<BaselineFile> = z.object({
|
|
59
|
+
version: z.literal(BASELINE_SCHEMA_VERSION),
|
|
60
|
+
createdAt: z.string(),
|
|
61
|
+
tool: z.object({
|
|
62
|
+
name: z.string(),
|
|
63
|
+
version: z.string(),
|
|
64
|
+
scanner: z.string(),
|
|
65
|
+
}),
|
|
66
|
+
configHash: z.string(),
|
|
67
|
+
findings: z.record(z.string(), baselineEntrySchema),
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
function formatPath(path: (string | number)[]): string {
|
|
71
|
+
if (path.length === 0) {
|
|
72
|
+
return "baseline"
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return path
|
|
76
|
+
.map((segment) => (typeof segment === "number" ? `[${String(segment)}]` : segment))
|
|
77
|
+
.join(".")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function comparableEntry(entry: BaselineEntry): Record<string, unknown> {
|
|
81
|
+
return {
|
|
82
|
+
issueId: entry.issueId,
|
|
83
|
+
scopeKey: entry.scopeKey,
|
|
84
|
+
projectName: entry.projectName,
|
|
85
|
+
relaxedIssueId: entry.relaxedIssueId,
|
|
86
|
+
ruleId: entry.ruleId,
|
|
87
|
+
impact: entry.impact,
|
|
88
|
+
pageKey: entry.pageKey,
|
|
89
|
+
pageUrl: entry.pageUrl,
|
|
90
|
+
targetHash: entry.targetHash,
|
|
91
|
+
selectorHint: entry.selectorHint,
|
|
92
|
+
sampleTarget: entry.sampleTarget,
|
|
93
|
+
help: entry.help,
|
|
94
|
+
helpUrl: entry.helpUrl,
|
|
95
|
+
tags: entry.tags,
|
|
96
|
+
failureHint: entry.failureHint,
|
|
97
|
+
occurrences: entry.occurrences,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function isUnchangedEntry(previous: BaselineEntry, next: BaselineEntry): boolean {
|
|
102
|
+
return hashObject(comparableEntry(previous)) === hashObject(comparableEntry(next))
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function parseBaselineFile(raw: string) {
|
|
106
|
+
let parsed: unknown
|
|
107
|
+
try {
|
|
108
|
+
parsed = JSON.parse(raw)
|
|
109
|
+
} catch {
|
|
110
|
+
return {
|
|
111
|
+
data: null,
|
|
112
|
+
error: true as const,
|
|
113
|
+
message: "trailmark: baseline file is not a valid JSON.",
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
isRecord(parsed) &&
|
|
119
|
+
typeof parsed.version === "number" &&
|
|
120
|
+
parsed.version !== BASELINE_SCHEMA_VERSION
|
|
121
|
+
) {
|
|
122
|
+
return {
|
|
123
|
+
data: null,
|
|
124
|
+
error: true as const,
|
|
125
|
+
message: `trailmark: unsupported baseline version ${parsed.version}. Expected ${BASELINE_SCHEMA_VERSION}.`,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const result = baselineFileSchema.safeParse(parsed)
|
|
130
|
+
if (!result.success) {
|
|
131
|
+
const issue = result.error.issues[0]
|
|
132
|
+
return {
|
|
133
|
+
data: null,
|
|
134
|
+
error: true as const,
|
|
135
|
+
message: `trailmark: invalid baseline format at "${formatPath(issue.path)}": ${issue.message}`,
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
error: false as const,
|
|
141
|
+
data: result.data,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function buildBaselineFromRun(
|
|
146
|
+
run: TrailmarkRun,
|
|
147
|
+
options: {
|
|
148
|
+
createdAt?: string
|
|
149
|
+
configHash: string
|
|
150
|
+
previousBaseline?: BaselineFile
|
|
151
|
+
},
|
|
152
|
+
): BaselineFile {
|
|
153
|
+
const createdAt =
|
|
154
|
+
options.createdAt ?? options.previousBaseline?.createdAt ?? new Date().toISOString()
|
|
155
|
+
const previousFindings = options.previousBaseline?.findings ?? {}
|
|
156
|
+
const findings: Record<string, BaselineEntry> = {}
|
|
157
|
+
|
|
158
|
+
const sortedFindings = [...run.findings].sort((a, b) => {
|
|
159
|
+
const aKey = scopedIssueKey(a.issueId, scopeKeyFromMeta(a.meta))
|
|
160
|
+
const bKey = scopedIssueKey(b.issueId, scopeKeyFromMeta(b.meta))
|
|
161
|
+
return aKey.localeCompare(bKey)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
for (const finding of sortedFindings) {
|
|
165
|
+
const scopeKey = scopeKeyFromMeta(finding.meta)
|
|
166
|
+
const entryKey = scopedIssueKey(finding.issueId, scopeKey)
|
|
167
|
+
const existing = findings[entryKey]
|
|
168
|
+
if (existing) {
|
|
169
|
+
existing.occurrences += 1
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const candidate: BaselineEntry = {
|
|
174
|
+
issueId: finding.issueId,
|
|
175
|
+
scopeKey,
|
|
176
|
+
projectName: finding.meta?.projectName,
|
|
177
|
+
relaxedIssueId: finding.relaxedIssueId,
|
|
178
|
+
ruleId: finding.ruleId,
|
|
179
|
+
impact: finding.impact,
|
|
180
|
+
pageKey: finding.pageKey,
|
|
181
|
+
pageUrl: finding.pageUrl,
|
|
182
|
+
targetHash: finding.targetHash,
|
|
183
|
+
selectorHint: finding.selectorHint,
|
|
184
|
+
sampleTarget: finding.target[0],
|
|
185
|
+
help: finding.help,
|
|
186
|
+
helpUrl: finding.helpUrl,
|
|
187
|
+
tags: normalizeTags([...finding.tags]),
|
|
188
|
+
failureHint: finding.failureHint,
|
|
189
|
+
occurrences: 1,
|
|
190
|
+
firstSeenAt: createdAt,
|
|
191
|
+
lastSeenAt: createdAt,
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const previous = previousFindings[entryKey]
|
|
195
|
+
if (previous) {
|
|
196
|
+
candidate.firstSeenAt = previous.firstSeenAt
|
|
197
|
+
if (isUnchangedEntry(previous, candidate)) {
|
|
198
|
+
candidate.lastSeenAt = previous.lastSeenAt
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
findings[entryKey] = candidate
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const orderedFindings: Record<string, BaselineEntry> = {}
|
|
206
|
+
for (const key of Object.keys(findings).sort((a, b) => a.localeCompare(b))) {
|
|
207
|
+
orderedFindings[key] = findings[key]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
version: BASELINE_SCHEMA_VERSION,
|
|
212
|
+
createdAt,
|
|
213
|
+
tool: run.tool ?? DEFAULT_TOOL_INFO,
|
|
214
|
+
configHash: options.configHash,
|
|
215
|
+
findings: orderedFindings,
|
|
216
|
+
}
|
|
217
|
+
}
|