@mortice/mapwright 0.8.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/CHANGELOG.md +161 -0
- package/LICENSE +21 -0
- package/README.md +262 -0
- package/dist/catalogue/canonicalisation.d.ts +36 -0
- package/dist/catalogue/canonicalisation.js +262 -0
- package/dist/catalogue/config.d.ts +13 -0
- package/dist/catalogue/config.js +77 -0
- package/dist/catalogue/deterministic.d.ts +3 -0
- package/dist/catalogue/deterministic.js +25 -0
- package/dist/catalogue/diagnostics.d.ts +2 -0
- package/dist/catalogue/diagnostics.js +34 -0
- package/dist/catalogue/evidence.d.ts +1 -0
- package/dist/catalogue/evidence.js +10 -0
- package/dist/catalogue/examples.d.ts +3 -0
- package/dist/catalogue/examples.js +20 -0
- package/dist/catalogue/identity.d.ts +61 -0
- package/dist/catalogue/identity.js +114 -0
- package/dist/catalogue/ignore.d.ts +4 -0
- package/dist/catalogue/ignore.js +29 -0
- package/dist/catalogue/indexer.d.ts +20 -0
- package/dist/catalogue/indexer.js +1393 -0
- package/dist/catalogue/linkage.d.ts +16 -0
- package/dist/catalogue/linkage.js +419 -0
- package/dist/catalogue/moduleResolution.d.ts +4 -0
- package/dist/catalogue/moduleResolution.js +67 -0
- package/dist/catalogue/ordering.d.ts +13 -0
- package/dist/catalogue/ordering.js +86 -0
- package/dist/catalogue/packages.d.ts +12 -0
- package/dist/catalogue/packages.js +475 -0
- package/dist/catalogue/pythonReExports.d.ts +7 -0
- package/dist/catalogue/pythonReExports.js +113 -0
- package/dist/catalogue/resolveRefs.d.ts +40 -0
- package/dist/catalogue/resolveRefs.js +284 -0
- package/dist/catalogue/scanner.d.ts +8 -0
- package/dist/catalogue/scanner.js +99 -0
- package/dist/catalogue/schema.d.ts +289 -0
- package/dist/catalogue/schema.js +1 -0
- package/dist/catalogue/store.d.ts +41 -0
- package/dist/catalogue/store.js +209 -0
- package/dist/catalogue/templates.d.ts +18 -0
- package/dist/catalogue/templates.js +158 -0
- package/dist/catalogue/version.d.ts +12 -0
- package/dist/catalogue/version.js +30 -0
- package/dist/cli/commands/adoptionPlan.d.ts +8 -0
- package/dist/cli/commands/adoptionPlan.js +24 -0
- package/dist/cli/commands/auditDocs.d.ts +8 -0
- package/dist/cli/commands/auditDocs.js +24 -0
- package/dist/cli/commands/changeImpact.d.ts +10 -0
- package/dist/cli/commands/changeImpact.js +36 -0
- package/dist/cli/commands/changes.d.ts +12 -0
- package/dist/cli/commands/changes.js +31 -0
- package/dist/cli/commands/check.d.ts +9 -0
- package/dist/cli/commands/check.js +4 -0
- package/dist/cli/commands/compatibility.d.ts +10 -0
- package/dist/cli/commands/compatibility.js +43 -0
- package/dist/cli/commands/diff.d.ts +31 -0
- package/dist/cli/commands/diff.js +118 -0
- package/dist/cli/commands/dogfood.d.ts +8 -0
- package/dist/cli/commands/dogfood.js +24 -0
- package/dist/cli/commands/drift.d.ts +8 -0
- package/dist/cli/commands/drift.js +13 -0
- package/dist/cli/commands/index.d.ts +9 -0
- package/dist/cli/commands/index.js +9 -0
- package/dist/cli/commands/indexWorkspace.d.ts +8 -0
- package/dist/cli/commands/indexWorkspace.js +23 -0
- package/dist/cli/commands/recommendConfig.d.ts +7 -0
- package/dist/cli/commands/recommendConfig.js +20 -0
- package/dist/cli/commands/schema.d.ts +1 -0
- package/dist/cli/commands/schema.js +37 -0
- package/dist/cli/commands/template.d.ts +5 -0
- package/dist/cli/commands/template.js +44 -0
- package/dist/cli/commands/validate.d.ts +9 -0
- package/dist/cli/commands/validate.js +85 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +211 -0
- package/dist/comparison/artefactValidate.d.ts +19 -0
- package/dist/comparison/artefactValidate.js +334 -0
- package/dist/comparison/candidates.d.ts +12 -0
- package/dist/comparison/candidates.js +391 -0
- package/dist/comparison/compare.d.ts +15 -0
- package/dist/comparison/compare.js +240 -0
- package/dist/comparison/compatibility.d.ts +26 -0
- package/dist/comparison/compatibility.js +316 -0
- package/dist/comparison/drift.d.ts +34 -0
- package/dist/comparison/drift.js +71 -0
- package/dist/comparison/identity.d.ts +30 -0
- package/dist/comparison/identity.js +73 -0
- package/dist/comparison/impact.d.ts +14 -0
- package/dist/comparison/impact.js +291 -0
- package/dist/comparison/index.d.ts +6 -0
- package/dist/comparison/index.js +6 -0
- package/dist/comparison/integrity.d.ts +12 -0
- package/dist/comparison/integrity.js +199 -0
- package/dist/comparison/ordering.d.ts +4 -0
- package/dist/comparison/ordering.js +69 -0
- package/dist/comparison/projections.d.ts +19 -0
- package/dist/comparison/projections.js +185 -0
- package/dist/comparison/relationshipPolicy.d.ts +19 -0
- package/dist/comparison/relationshipPolicy.js +98 -0
- package/dist/comparison/schema.d.ts +178 -0
- package/dist/comparison/schema.js +16 -0
- package/dist/comparison/store.d.ts +58 -0
- package/dist/comparison/store.js +462 -0
- package/dist/comparison/validate.d.ts +23 -0
- package/dist/comparison/validate.js +137 -0
- package/dist/comparison/version.d.ts +9 -0
- package/dist/comparison/version.js +11 -0
- package/dist/extractors/docTags.d.ts +9 -0
- package/dist/extractors/docTags.js +106 -0
- package/dist/extractors/generic.d.ts +5 -0
- package/dist/extractors/generic.js +3 -0
- package/dist/extractors/javascript.d.ts +3 -0
- package/dist/extractors/javascript.js +4 -0
- package/dist/extractors/markdown.d.ts +7 -0
- package/dist/extractors/markdown.js +269 -0
- package/dist/extractors/python.d.ts +7 -0
- package/dist/extractors/python.js +314 -0
- package/dist/extractors/typescript.d.ts +7 -0
- package/dist/extractors/typescript.js +387 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mcp/resources.d.ts +18 -0
- package/dist/mcp/resources.js +57 -0
- package/dist/mcp/server.d.ts +2 -0
- package/dist/mcp/server.js +95 -0
- package/dist/mcp/tools.d.ts +29 -0
- package/dist/mcp/tools.js +902 -0
- package/dist/queries/adoption.d.ts +62 -0
- package/dist/queries/adoption.js +366 -0
- package/dist/queries/capabilities.d.ts +45 -0
- package/dist/queries/capabilities.js +124 -0
- package/dist/queries/changes.d.ts +33 -0
- package/dist/queries/changes.js +53 -0
- package/dist/queries/dependencies.d.ts +17 -0
- package/dist/queries/dependencies.js +94 -0
- package/dist/queries/dogfood.d.ts +50 -0
- package/dist/queries/dogfood.js +116 -0
- package/dist/queries/editingContext.d.ts +84 -0
- package/dist/queries/editingContext.js +240 -0
- package/dist/queries/examples.d.ts +8 -0
- package/dist/queries/examples.js +16 -0
- package/dist/queries/files.d.ts +8 -0
- package/dist/queries/files.js +14 -0
- package/dist/queries/lists.d.ts +94 -0
- package/dist/queries/lists.js +184 -0
- package/dist/queries/overview.d.ts +2 -0
- package/dist/queries/overview.js +8 -0
- package/dist/queries/publicApis.d.ts +60 -0
- package/dist/queries/publicApis.js +120 -0
- package/dist/queries/ranking.d.ts +67 -0
- package/dist/queries/ranking.js +387 -0
- package/dist/queries/recommendConfig.d.ts +47 -0
- package/dist/queries/recommendConfig.js +123 -0
- package/dist/queries/relationships.d.ts +5 -0
- package/dist/queries/relationships.js +15 -0
- package/dist/queries/reuseCandidates.d.ts +34 -0
- package/dist/queries/reuseCandidates.js +181 -0
- package/dist/queries/symbols.d.ts +40 -0
- package/dist/queries/symbols.js +41 -0
- package/dist/security/pathPolicy.d.ts +12 -0
- package/dist/security/pathPolicy.js +46 -0
- package/dist/security/secretPatterns.d.ts +1 -0
- package/dist/security/secretPatterns.js +4 -0
- package/dist/security/sourceAccess.d.ts +12 -0
- package/dist/security/sourceAccess.js +35 -0
- package/package.json +60 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
import { runIndexCommand } from "./commands/indexWorkspace.js";
|
|
4
|
+
import { runValidateCommand } from "./commands/validate.js";
|
|
5
|
+
import { runCheckCommand } from "./commands/check.js";
|
|
6
|
+
import { runSchemaCommand } from "./commands/schema.js";
|
|
7
|
+
import { runAuditDocsCommand } from "./commands/auditDocs.js";
|
|
8
|
+
import { runAdoptionPlanCommand } from "./commands/adoptionPlan.js";
|
|
9
|
+
import { runTemplateCommand } from "./commands/template.js";
|
|
10
|
+
import { runDogfoodCommand } from "./commands/dogfood.js";
|
|
11
|
+
import { runRecommendConfigCommand } from "./commands/recommendConfig.js";
|
|
12
|
+
import { runDiffCommand } from "./commands/diff.js";
|
|
13
|
+
import { runDriftCommand } from "./commands/drift.js";
|
|
14
|
+
import { runChangesCommand } from "./commands/changes.js";
|
|
15
|
+
import { runCompatibilityCommand } from "./commands/compatibility.js";
|
|
16
|
+
import { runChangeImpactCommand } from "./commands/changeImpact.js";
|
|
17
|
+
function main(argv) {
|
|
18
|
+
const [command, ...rest] = argv;
|
|
19
|
+
const args = parseArgs(rest);
|
|
20
|
+
if (command === "index") {
|
|
21
|
+
const output = runIndexCommand({
|
|
22
|
+
workspace: getString(args, "workspace", "."),
|
|
23
|
+
config: getOptionalString(args, "config"),
|
|
24
|
+
mode: getOptionalString(args, "mode"),
|
|
25
|
+
write: args.has("write") ? true : args.has("no-write") ? false : undefined,
|
|
26
|
+
pretty: args.has("pretty")
|
|
27
|
+
});
|
|
28
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (command === "validate") {
|
|
32
|
+
const { output, exitCode } = runValidateCommand({
|
|
33
|
+
workspace: getString(args, "workspace", "."),
|
|
34
|
+
config: getOptionalString(args, "config"),
|
|
35
|
+
failOn: getOptionalString(args, "fail-on")
|
|
36
|
+
});
|
|
37
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
38
|
+
process.exitCode = exitCode;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (command === "schema") {
|
|
42
|
+
process.stdout.write(`${JSON.stringify(runSchemaCommand(), null, 2)}\n`);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (command === "check") {
|
|
46
|
+
const { output, exitCode } = runCheckCommand({
|
|
47
|
+
workspace: getString(args, "workspace", "."),
|
|
48
|
+
config: getOptionalString(args, "config"),
|
|
49
|
+
failOn: getOptionalString(args, "fail-on")
|
|
50
|
+
});
|
|
51
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
52
|
+
process.exitCode = exitCode;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (command === "audit-docs") {
|
|
56
|
+
const output = runAuditDocsCommand({
|
|
57
|
+
workspace: getString(args, "workspace", "."),
|
|
58
|
+
config: getOptionalString(args, "config"),
|
|
59
|
+
includeInfo: args.has("include-info") ? true : undefined,
|
|
60
|
+
includeGenerated: args.has("include-generated") ? true : undefined,
|
|
61
|
+
maxGaps: getOptionalNumber(args, "max-gaps")
|
|
62
|
+
});
|
|
63
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
64
|
+
process.exitCode = output.ok ? 0 : 1;
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (command === "adoption-plan") {
|
|
68
|
+
const output = runAdoptionPlanCommand({
|
|
69
|
+
workspace: getString(args, "workspace", "."),
|
|
70
|
+
config: getOptionalString(args, "config"),
|
|
71
|
+
includeInfo: args.has("include-info") ? true : undefined,
|
|
72
|
+
includeGenerated: args.has("include-generated") ? true : undefined,
|
|
73
|
+
maxGaps: getOptionalNumber(args, "max-gaps")
|
|
74
|
+
});
|
|
75
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
76
|
+
process.exitCode = output.ok ? 0 : 1;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (command === "template") {
|
|
80
|
+
const firstPositional = rest.find((arg) => !arg.startsWith("--"));
|
|
81
|
+
const templateId = firstPositional ?? getOptionalString(args, "template-id");
|
|
82
|
+
const output = runTemplateCommand({
|
|
83
|
+
templateId,
|
|
84
|
+
list: args.has("list") || templateId === "list"
|
|
85
|
+
});
|
|
86
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
87
|
+
process.exitCode = output.ok ? 0 : 1;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (command === "dogfood") {
|
|
91
|
+
const output = runDogfoodCommand({
|
|
92
|
+
workspace: getString(args, "workspace", "."),
|
|
93
|
+
config: getOptionalString(args, "config"),
|
|
94
|
+
mode: getOptionalString(args, "mode"),
|
|
95
|
+
maxDiagnostics: getOptionalNumber(args, "max-diagnostics"),
|
|
96
|
+
maxGaps: getOptionalNumber(args, "max-gaps")
|
|
97
|
+
});
|
|
98
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
99
|
+
process.exitCode = output.ok ? 0 : 1;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (command === "recommend-config") {
|
|
103
|
+
const output = runRecommendConfigCommand({
|
|
104
|
+
workspace: getString(args, "workspace", "."),
|
|
105
|
+
config: getOptionalString(args, "config"),
|
|
106
|
+
mode: getOptionalString(args, "mode"),
|
|
107
|
+
limit: getOptionalNumber(args, "limit")
|
|
108
|
+
});
|
|
109
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
110
|
+
process.exitCode = output.ok ? 0 : 1;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (command === "diff") {
|
|
114
|
+
const { output, exitCode } = runDiffCommand({
|
|
115
|
+
baseWorkspace: getString(args, "base-workspace", getString(args, "workspace", ".")),
|
|
116
|
+
baseCatalogueDir: getString(args, "base", ".mapwright"),
|
|
117
|
+
headWorkspace: getString(args, "head-workspace", getString(args, "workspace", ".")),
|
|
118
|
+
headCatalogueDir: getString(args, "head", ".mapwright"),
|
|
119
|
+
writeDir: getOptionalString(args, "write"),
|
|
120
|
+
failOn: getOptionalString(args, "fail-on")
|
|
121
|
+
});
|
|
122
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
123
|
+
process.exitCode = exitCode;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (command === "changes") {
|
|
127
|
+
const { output, exitCode } = runChangesCommand({
|
|
128
|
+
comparisonDir: getString(args, "comparison-dir", ""),
|
|
129
|
+
domain: getOptionalString(args, "domain"),
|
|
130
|
+
status: getOptionalString(args, "status"),
|
|
131
|
+
offset: getOptionalNumber(args, "offset"),
|
|
132
|
+
limit: getOptionalNumber(args, "limit"),
|
|
133
|
+
failOn: getOptionalString(args, "fail-on")
|
|
134
|
+
});
|
|
135
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
136
|
+
process.exitCode = exitCode;
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (command === "compatibility") {
|
|
140
|
+
const { output, exitCode } = runCompatibilityCommand({
|
|
141
|
+
comparisonDir: getString(args, "comparison-dir", ""),
|
|
142
|
+
offset: getOptionalNumber(args, "offset"),
|
|
143
|
+
limit: getOptionalNumber(args, "limit"),
|
|
144
|
+
failOn: getOptionalString(args, "fail-on")
|
|
145
|
+
});
|
|
146
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
147
|
+
process.exitCode = exitCode;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (command === "change-impact") {
|
|
151
|
+
const { output, exitCode } = runChangeImpactCommand({
|
|
152
|
+
comparisonDir: getString(args, "comparison-dir", ""),
|
|
153
|
+
changeId: getOptionalString(args, "change-id"),
|
|
154
|
+
maxDepth: getOptionalNumber(args, "max-depth"),
|
|
155
|
+
offset: getOptionalNumber(args, "offset"),
|
|
156
|
+
limit: getOptionalNumber(args, "limit")
|
|
157
|
+
});
|
|
158
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
159
|
+
process.exitCode = exitCode;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (command === "drift") {
|
|
163
|
+
const { output, exitCode } = runDriftCommand({
|
|
164
|
+
workspace: getString(args, "workspace", "."),
|
|
165
|
+
catalogueDir: getOptionalString(args, "catalogue-dir"),
|
|
166
|
+
config: getOptionalString(args, "config")
|
|
167
|
+
});
|
|
168
|
+
process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
169
|
+
process.exitCode = exitCode;
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
process.stderr.write("Usage: mapwright <index|validate|check|schema|audit-docs|adoption-plan|template|dogfood|recommend-config|diff|changes|compatibility|change-impact|drift> [options]\n");
|
|
173
|
+
process.exitCode = 2;
|
|
174
|
+
}
|
|
175
|
+
function parseArgs(args) {
|
|
176
|
+
const out = new Map();
|
|
177
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
178
|
+
const arg = args[i];
|
|
179
|
+
if (!arg.startsWith("--")) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const key = arg.replace(/^--/, "");
|
|
183
|
+
const next = args[i + 1];
|
|
184
|
+
if (next && !next.startsWith("--")) {
|
|
185
|
+
out.set(key, next);
|
|
186
|
+
i += 1;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
out.set(key, "true");
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return out;
|
|
193
|
+
}
|
|
194
|
+
function getString(args, key, fallback) {
|
|
195
|
+
return args.get(key) ?? fallback;
|
|
196
|
+
}
|
|
197
|
+
function getOptionalString(args, key) {
|
|
198
|
+
return args.get(key);
|
|
199
|
+
}
|
|
200
|
+
function getOptionalNumber(args, key) {
|
|
201
|
+
const value = args.get(key);
|
|
202
|
+
if (!value) {
|
|
203
|
+
return undefined;
|
|
204
|
+
}
|
|
205
|
+
const parsed = Number(value);
|
|
206
|
+
if (!Number.isFinite(parsed)) {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
return parsed;
|
|
210
|
+
}
|
|
211
|
+
main(process.argv.slice(2));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ChangeRecord, ComparisonError, ComparisonSnapshot, CompatibilityFinding, RecordDomain, TransitionCandidate } from "./schema.js";
|
|
2
|
+
export declare const REGISTERED_DOMAINS: Set<RecordDomain>;
|
|
3
|
+
export declare const REGISTERED_STATUSES: Set<string>;
|
|
4
|
+
export declare const REGISTERED_FACETS: Set<string>;
|
|
5
|
+
export declare const REGISTERED_CANDIDATE_KINDS: Set<string>;
|
|
6
|
+
export declare const REGISTERED_CONFIDENCE: Set<string>;
|
|
7
|
+
export declare const REGISTERED_CLASSIFICATIONS: Set<string>;
|
|
8
|
+
export declare const REGISTERED_RULE_IDS: Set<string>;
|
|
9
|
+
export declare const REGISTERED_EVIDENCE_REASONS: Set<string>;
|
|
10
|
+
export declare function validateArtefactSemantics(comparison: ComparisonSnapshot, lockMeta?: {
|
|
11
|
+
comparisonSchemaVersion?: number;
|
|
12
|
+
comparisonId?: string;
|
|
13
|
+
baseCatalogueId?: string;
|
|
14
|
+
headCatalogueId?: string;
|
|
15
|
+
}): ComparisonError | null;
|
|
16
|
+
export declare function candidateIdentityDiscriminator(candidate: Pick<TransitionCandidate, "ambiguity" | "candidateKind" | "headRecordId" | "identityDiscriminator">): string;
|
|
17
|
+
export declare function attachChangeIdentity(change: ChangeRecord): ChangeRecord;
|
|
18
|
+
export declare function attachCandidateIdentity(candidate: TransitionCandidate): TransitionCandidate;
|
|
19
|
+
export declare function attachFindingIdentity(finding: CompatibilityFinding, identityBaseRecordId: string | null, identityHeadRecordId: string | null): CompatibilityFinding;
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vocabularies and full semantic validation for loaded comparison artefacts.
|
|
3
|
+
*/
|
|
4
|
+
import { comparisonIdentityId, changeIdentityId } from "./identity.js";
|
|
5
|
+
import { orderChangeRecords, orderCompatibilityFindings } from "./ordering.js";
|
|
6
|
+
import { COMPATIBILITY_RULE_VERSION } from "./version.js";
|
|
7
|
+
export const REGISTERED_DOMAINS = new Set([
|
|
8
|
+
"packages",
|
|
9
|
+
"files",
|
|
10
|
+
"symbols",
|
|
11
|
+
"publicApis",
|
|
12
|
+
"tests",
|
|
13
|
+
"examples",
|
|
14
|
+
"capabilities",
|
|
15
|
+
"relationships",
|
|
16
|
+
"diagnostics"
|
|
17
|
+
]);
|
|
18
|
+
export const REGISTERED_STATUSES = new Set(["added", "removed", "modified", "unchanged"]);
|
|
19
|
+
export const REGISTERED_FACETS = new Set([
|
|
20
|
+
"signature_changed",
|
|
21
|
+
"implementation_changed",
|
|
22
|
+
"documentation_changed",
|
|
23
|
+
"module_path_changed",
|
|
24
|
+
"file_path_changed",
|
|
25
|
+
"visibility_changed",
|
|
26
|
+
"export_status_changed",
|
|
27
|
+
"canonical_target_changed",
|
|
28
|
+
"capability_links_changed",
|
|
29
|
+
"implementation_target_changed",
|
|
30
|
+
"stability_changed",
|
|
31
|
+
"contracts_changed",
|
|
32
|
+
"errors_changed",
|
|
33
|
+
"example_links_changed",
|
|
34
|
+
"test_links_changed",
|
|
35
|
+
"public_apis_changed",
|
|
36
|
+
"tests_changed",
|
|
37
|
+
"capabilities_changed",
|
|
38
|
+
"entrypoints_changed",
|
|
39
|
+
"console_scripts_changed",
|
|
40
|
+
"import_packages_changed",
|
|
41
|
+
"content_fingerprint_changed",
|
|
42
|
+
"public_surface_changed",
|
|
43
|
+
"evidence_changed",
|
|
44
|
+
"confidence_changed",
|
|
45
|
+
"local_discriminant_changed",
|
|
46
|
+
"severity_changed",
|
|
47
|
+
"import_path_changed",
|
|
48
|
+
"exported_name_changed",
|
|
49
|
+
"declaration_name_changed"
|
|
50
|
+
]);
|
|
51
|
+
export const REGISTERED_CANDIDATE_KINDS = new Set([
|
|
52
|
+
"probable_move",
|
|
53
|
+
"probable_rename",
|
|
54
|
+
"probable_move_and_rename",
|
|
55
|
+
"possible_replacement",
|
|
56
|
+
"ambiguous_candidate",
|
|
57
|
+
"public_surface_transition"
|
|
58
|
+
]);
|
|
59
|
+
export const REGISTERED_CONFIDENCE = new Set(["high", "medium", "low", "ambiguous"]);
|
|
60
|
+
export const REGISTERED_CLASSIFICATIONS = new Set([
|
|
61
|
+
"breaking",
|
|
62
|
+
"potentially_breaking",
|
|
63
|
+
"non_breaking",
|
|
64
|
+
"documentation_only",
|
|
65
|
+
"unknown",
|
|
66
|
+
"not_applicable"
|
|
67
|
+
]);
|
|
68
|
+
export const REGISTERED_RULE_IDS = new Set([
|
|
69
|
+
"public-export-removed",
|
|
70
|
+
"public-export-added",
|
|
71
|
+
"documentation-only",
|
|
72
|
+
"implementation-target-stable-export",
|
|
73
|
+
"signature-fingerprint-or-display-changed",
|
|
74
|
+
"public-api-modified-unknown",
|
|
75
|
+
"public-surface-transition",
|
|
76
|
+
"python-console-script-removed",
|
|
77
|
+
"python-console-script-target-changed",
|
|
78
|
+
"python-console-script-renamed",
|
|
79
|
+
"python-console-script-added",
|
|
80
|
+
"python-console-script-metadata",
|
|
81
|
+
"stability-stable-to-deprecated",
|
|
82
|
+
"stability-stable-to-internal",
|
|
83
|
+
"stability-stable-to-experimental",
|
|
84
|
+
"stability-experimental-to-stable",
|
|
85
|
+
"stability-deprecated-to-stable",
|
|
86
|
+
"stability-deprecated-to-internal",
|
|
87
|
+
"stability-experimental-to-deprecated",
|
|
88
|
+
"stability-experimental-to-internal",
|
|
89
|
+
"stability-internal-to-stable",
|
|
90
|
+
"stability-internal-to-experimental",
|
|
91
|
+
"stability-transition-unknown-policy"
|
|
92
|
+
]);
|
|
93
|
+
export const REGISTERED_EVIDENCE_REASONS = new Set([
|
|
94
|
+
"package_continuity",
|
|
95
|
+
"exported_name_match",
|
|
96
|
+
"import_path_match",
|
|
97
|
+
"language_kind_match",
|
|
98
|
+
"declaration_name_match",
|
|
99
|
+
"signature_fingerprint_match",
|
|
100
|
+
"implementation_fingerprint_match",
|
|
101
|
+
"qualified_container_match",
|
|
102
|
+
"module_topology_match",
|
|
103
|
+
"documentation_fingerprint_match",
|
|
104
|
+
"capability_linkage_match",
|
|
105
|
+
"content_hash_match",
|
|
106
|
+
"basename_match"
|
|
107
|
+
]);
|
|
108
|
+
function edgeSortKey(edge) {
|
|
109
|
+
return `${edge.type}|${edge.fromKind}:${edge.fromId}|${edge.toKind}:${edge.toId}|${edge.provenance ?? ""}`;
|
|
110
|
+
}
|
|
111
|
+
function sameIdOrder(actual, expected) {
|
|
112
|
+
if (actual.length !== expected.length)
|
|
113
|
+
return false;
|
|
114
|
+
for (let i = 0; i < actual.length; i += 1) {
|
|
115
|
+
if (actual[i] !== expected[i])
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
function invalid(message, details) {
|
|
121
|
+
return { code: "comparison_artifact_invalid", message, details };
|
|
122
|
+
}
|
|
123
|
+
export function validateArtefactSemantics(comparison, lockMeta) {
|
|
124
|
+
const m = comparison.manifest;
|
|
125
|
+
if (lockMeta) {
|
|
126
|
+
if (lockMeta.comparisonSchemaVersion !== m.comparisonSchemaVersion) {
|
|
127
|
+
return invalid("Lock comparisonSchemaVersion does not match manifest.");
|
|
128
|
+
}
|
|
129
|
+
if (lockMeta.comparisonId !== m.comparisonId) {
|
|
130
|
+
return invalid("Lock comparisonId does not match manifest.");
|
|
131
|
+
}
|
|
132
|
+
if (lockMeta.baseCatalogueId !== m.baseCatalogueId) {
|
|
133
|
+
return invalid("Lock baseCatalogueId does not match manifest.");
|
|
134
|
+
}
|
|
135
|
+
if (lockMeta.headCatalogueId !== m.headCatalogueId) {
|
|
136
|
+
return invalid("Lock headCatalogueId does not match manifest.");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!m.options || !Array.isArray(m.options.enabledDomains)) {
|
|
140
|
+
return invalid("Manifest options.enabledDomains missing.");
|
|
141
|
+
}
|
|
142
|
+
const expectedComparisonId = comparisonIdentityId({
|
|
143
|
+
baseCatalogueId: m.baseCatalogueId,
|
|
144
|
+
headCatalogueId: m.headCatalogueId,
|
|
145
|
+
options: m.options
|
|
146
|
+
});
|
|
147
|
+
if (expectedComparisonId !== m.comparisonId) {
|
|
148
|
+
return {
|
|
149
|
+
code: "comparison_identity_mismatch",
|
|
150
|
+
message: "Recomputed comparison ID does not match manifest.comparisonId.",
|
|
151
|
+
details: { expected: expectedComparisonId, actual: m.comparisonId }
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const changeIds = new Set();
|
|
155
|
+
const factualKeys = new Set();
|
|
156
|
+
for (const change of comparison.changes) {
|
|
157
|
+
if (!REGISTERED_DOMAINS.has(change.domain)) {
|
|
158
|
+
return invalid(`Unsupported change domain ${change.domain}.`);
|
|
159
|
+
}
|
|
160
|
+
if (!REGISTERED_STATUSES.has(change.status)) {
|
|
161
|
+
return invalid(`Unsupported change status ${change.status}.`);
|
|
162
|
+
}
|
|
163
|
+
if (change.status === "added" && (change.baseRecordId != null || !change.headRecordId)) {
|
|
164
|
+
return invalid(`Invalid base/head combination for added change ${change.id}.`);
|
|
165
|
+
}
|
|
166
|
+
if (change.status === "removed" && (change.headRecordId != null || !change.baseRecordId)) {
|
|
167
|
+
return invalid(`Invalid base/head combination for removed change ${change.id}.`);
|
|
168
|
+
}
|
|
169
|
+
if ((change.status === "modified" || change.status === "unchanged") &&
|
|
170
|
+
(!change.baseRecordId || !change.headRecordId)) {
|
|
171
|
+
return invalid(`Invalid base/head combination for ${change.status} change ${change.id}.`);
|
|
172
|
+
}
|
|
173
|
+
for (const facet of change.facets ?? []) {
|
|
174
|
+
if (!REGISTERED_FACETS.has(facet.facet)) {
|
|
175
|
+
return invalid(`Unknown facet ${facet.facet} on change ${change.id}.`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
const expectedId = changeIdentityId({
|
|
179
|
+
comparisonId: m.comparisonId,
|
|
180
|
+
domain: change.domain,
|
|
181
|
+
baseRecordId: change.baseRecordId,
|
|
182
|
+
headRecordId: change.headRecordId,
|
|
183
|
+
factualChangeKind: change.status,
|
|
184
|
+
discriminator: change.identityDiscriminator ?? null
|
|
185
|
+
});
|
|
186
|
+
if (expectedId !== change.id) {
|
|
187
|
+
return invalid(`Incorrect change ID for ${change.id}.`, { expected: expectedId });
|
|
188
|
+
}
|
|
189
|
+
if (changeIds.has(change.id)) {
|
|
190
|
+
return invalid(`Duplicate change ID ${change.id}.`);
|
|
191
|
+
}
|
|
192
|
+
changeIds.add(change.id);
|
|
193
|
+
const factualKey = `${change.domain}|${change.baseRecordId}|${change.headRecordId}|${change.status}|${change.identityDiscriminator ?? ""}`;
|
|
194
|
+
if (factualKeys.has(factualKey)) {
|
|
195
|
+
return invalid(`Duplicate factual change tuple for ${factualKey}.`);
|
|
196
|
+
}
|
|
197
|
+
factualKeys.add(factualKey);
|
|
198
|
+
}
|
|
199
|
+
const orderedChanges = orderChangeRecords(comparison.changes);
|
|
200
|
+
if (!sameIdOrder(comparison.changes.map((c) => c.id), orderedChanges.map((c) => c.id))) {
|
|
201
|
+
return invalid("Changes are not in declared deterministic order.");
|
|
202
|
+
}
|
|
203
|
+
const candidateIds = new Set();
|
|
204
|
+
for (const candidate of comparison.candidates) {
|
|
205
|
+
if (!REGISTERED_DOMAINS.has(candidate.domain)) {
|
|
206
|
+
return invalid(`Unsupported candidate domain ${candidate.domain}.`);
|
|
207
|
+
}
|
|
208
|
+
if (!candidate.baseRecordId || !candidate.headRecordId) {
|
|
209
|
+
return invalid(`Candidate ${candidate.id} missing base/head IDs.`);
|
|
210
|
+
}
|
|
211
|
+
if (!REGISTERED_CANDIDATE_KINDS.has(candidate.candidateKind)) {
|
|
212
|
+
return invalid(`Unregistered candidate kind ${candidate.candidateKind}.`);
|
|
213
|
+
}
|
|
214
|
+
if (!REGISTERED_CONFIDENCE.has(candidate.confidence)) {
|
|
215
|
+
return invalid(`Unregistered candidate confidence ${candidate.confidence}.`);
|
|
216
|
+
}
|
|
217
|
+
if (candidate.ambiguity && candidate.candidateKind !== "ambiguous_candidate" && candidate.confidence !== "ambiguous") {
|
|
218
|
+
// ambiguity true should mark confidence ambiguous or kind ambiguous_candidate
|
|
219
|
+
if (!candidate.ambiguity) {
|
|
220
|
+
/* unreachable */
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (candidate.ambiguity && candidate.confidence !== "ambiguous") {
|
|
224
|
+
return invalid(`Ambiguous candidate ${candidate.id} must have confidence ambiguous.`);
|
|
225
|
+
}
|
|
226
|
+
for (const reason of candidate.evidenceReasons ?? []) {
|
|
227
|
+
if (!REGISTERED_EVIDENCE_REASONS.has(reason)) {
|
|
228
|
+
return invalid(`Unregistered candidate evidence reason ${reason}.`);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
for (const alt of candidate.alternativeCandidateIds ?? []) {
|
|
232
|
+
if (typeof alt !== "string" || !alt) {
|
|
233
|
+
return invalid(`Invalid alternativeCandidateIds on ${candidate.id}.`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
const expectedDiscriminator = candidate.identityDiscriminator ??
|
|
237
|
+
(candidate.ambiguity
|
|
238
|
+
? `ambiguous_${candidate.candidateKind}:${candidate.headRecordId}`
|
|
239
|
+
: candidate.candidateKind);
|
|
240
|
+
const expectedId = changeIdentityId({
|
|
241
|
+
comparisonId: m.comparisonId,
|
|
242
|
+
domain: candidate.domain,
|
|
243
|
+
baseRecordId: candidate.baseRecordId,
|
|
244
|
+
headRecordId: candidate.headRecordId,
|
|
245
|
+
factualChangeKind: "modified",
|
|
246
|
+
discriminator: expectedDiscriminator
|
|
247
|
+
});
|
|
248
|
+
if (expectedId !== candidate.id) {
|
|
249
|
+
return invalid(`Incorrect candidate ID for ${candidate.id}.`, { expected: expectedId });
|
|
250
|
+
}
|
|
251
|
+
if (candidateIds.has(candidate.id)) {
|
|
252
|
+
return invalid(`Duplicate candidate ID ${candidate.id}.`);
|
|
253
|
+
}
|
|
254
|
+
candidateIds.add(candidate.id);
|
|
255
|
+
}
|
|
256
|
+
const sortedCandidateIds = [...comparison.candidates.map((c) => c.id)].sort((a, b) => a.localeCompare(b));
|
|
257
|
+
if (!sameIdOrder(comparison.candidates.map((c) => c.id), sortedCandidateIds)) {
|
|
258
|
+
return invalid("Candidates are not in declared deterministic order.");
|
|
259
|
+
}
|
|
260
|
+
const findingIds = new Set();
|
|
261
|
+
for (const finding of comparison.compatibilityFindings) {
|
|
262
|
+
if (!REGISTERED_RULE_IDS.has(finding.ruleId)) {
|
|
263
|
+
return invalid(`Unknown compatibility rule ${finding.ruleId}.`);
|
|
264
|
+
}
|
|
265
|
+
if (finding.ruleVersion !== COMPATIBILITY_RULE_VERSION) {
|
|
266
|
+
return invalid(`Unsupported rule version ${finding.ruleVersion}.`);
|
|
267
|
+
}
|
|
268
|
+
if (!REGISTERED_CLASSIFICATIONS.has(finding.classification)) {
|
|
269
|
+
return invalid(`Unregistered classification ${finding.classification}.`);
|
|
270
|
+
}
|
|
271
|
+
if (!REGISTERED_CONFIDENCE.has(finding.confidence)) {
|
|
272
|
+
return invalid(`Unregistered finding confidence ${finding.confidence}.`);
|
|
273
|
+
}
|
|
274
|
+
if (finding.changeId != null && finding.changeId !== "" && !changeIds.has(finding.changeId)) {
|
|
275
|
+
return invalid(`Dangling finding changeId ${finding.changeId}.`);
|
|
276
|
+
}
|
|
277
|
+
const identityBase = finding.identityBaseRecordId ?? finding.publicApiId ?? null;
|
|
278
|
+
const identityHead = finding.identityHeadRecordId ?? finding.changeId ?? null;
|
|
279
|
+
const identityDiscriminator = finding.identityDiscriminator ?? `compat:${finding.ruleId}`;
|
|
280
|
+
const expectedId = changeIdentityId({
|
|
281
|
+
comparisonId: m.comparisonId,
|
|
282
|
+
domain: "publicApis",
|
|
283
|
+
baseRecordId: identityBase,
|
|
284
|
+
headRecordId: identityHead,
|
|
285
|
+
factualChangeKind: "modified",
|
|
286
|
+
discriminator: identityDiscriminator
|
|
287
|
+
});
|
|
288
|
+
if (expectedId !== finding.id) {
|
|
289
|
+
return invalid(`Incorrect finding ID for ${finding.id}.`, { expected: expectedId });
|
|
290
|
+
}
|
|
291
|
+
if (findingIds.has(finding.id)) {
|
|
292
|
+
return invalid(`Duplicate finding ID ${finding.id}.`);
|
|
293
|
+
}
|
|
294
|
+
findingIds.add(finding.id);
|
|
295
|
+
}
|
|
296
|
+
const orderedFindings = orderCompatibilityFindings(comparison.compatibilityFindings);
|
|
297
|
+
if (!sameIdOrder(comparison.compatibilityFindings.map((f) => f.id), orderedFindings.map((f) => f.id))) {
|
|
298
|
+
return invalid("Compatibility findings are not in declared deterministic order.");
|
|
299
|
+
}
|
|
300
|
+
for (const side of ["base", "head"]) {
|
|
301
|
+
const edges = comparison.graphIndex[side];
|
|
302
|
+
const sorted = [...edges].sort((a, b) => edgeSortKey(a).localeCompare(edgeSortKey(b)));
|
|
303
|
+
for (let i = 0; i < edges.length; i += 1) {
|
|
304
|
+
if (edgeSortKey(edges[i]) !== edgeSortKey(sorted[i])) {
|
|
305
|
+
return invalid(`Graph index ${side} edges are not in declared deterministic order.`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
export function candidateIdentityDiscriminator(candidate) {
|
|
312
|
+
if (candidate.identityDiscriminator)
|
|
313
|
+
return candidate.identityDiscriminator;
|
|
314
|
+
return candidate.ambiguity
|
|
315
|
+
? `ambiguous_${candidate.candidateKind}:${candidate.headRecordId}`
|
|
316
|
+
: candidate.candidateKind;
|
|
317
|
+
}
|
|
318
|
+
export function attachChangeIdentity(change) {
|
|
319
|
+
return { ...change, identityDiscriminator: change.identityDiscriminator ?? null };
|
|
320
|
+
}
|
|
321
|
+
export function attachCandidateIdentity(candidate) {
|
|
322
|
+
return {
|
|
323
|
+
...candidate,
|
|
324
|
+
identityDiscriminator: candidateIdentityDiscriminator(candidate)
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
export function attachFindingIdentity(finding, identityBaseRecordId, identityHeadRecordId) {
|
|
328
|
+
return {
|
|
329
|
+
...finding,
|
|
330
|
+
identityBaseRecordId,
|
|
331
|
+
identityHeadRecordId,
|
|
332
|
+
identityDiscriminator: `compat:${finding.ruleId}`
|
|
333
|
+
};
|
|
334
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CatalogueSnapshot } from "../catalogue/schema.js";
|
|
2
|
+
import { type ComparisonCollisionTracker } from "./identity.js";
|
|
3
|
+
import type { ChangeRecord, TransitionCandidate } from "./schema.js";
|
|
4
|
+
export declare function matchAllTransitionCandidates(comparisonId: string, changes: ChangeRecord[], collisions: ComparisonCollisionTracker, catalogues?: {
|
|
5
|
+
base: CatalogueSnapshot;
|
|
6
|
+
head: CatalogueSnapshot;
|
|
7
|
+
}): {
|
|
8
|
+
candidates: TransitionCandidate[];
|
|
9
|
+
warnings: string[];
|
|
10
|
+
};
|
|
11
|
+
/** @deprecated Use matchAllTransitionCandidates */
|
|
12
|
+
export declare function matchPublicSurfaceTransitions(comparisonId: string, changes: ChangeRecord[], collisions: ComparisonCollisionTracker): TransitionCandidate[];
|