uicontract 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/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/bin/uic.d.ts +9 -0
- package/dist/bin/uic.d.ts.map +1 -0
- package/dist/bin/uic.js +87 -0
- package/dist/bin/uic.js.map +1 -0
- package/dist/commands/annotate.d.ts +21 -0
- package/dist/commands/annotate.d.ts.map +1 -0
- package/dist/commands/annotate.js +236 -0
- package/dist/commands/annotate.js.map +1 -0
- package/dist/commands/describe.d.ts +19 -0
- package/dist/commands/describe.d.ts.map +1 -0
- package/dist/commands/describe.js +146 -0
- package/dist/commands/describe.js.map +1 -0
- package/dist/commands/diff.d.ts +57 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +404 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/find.d.ts +34 -0
- package/dist/commands/find.d.ts.map +1 -0
- package/dist/commands/find.js +229 -0
- package/dist/commands/find.js.map +1 -0
- package/dist/commands/index.d.ts +18 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +11 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/list.d.ts +23 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +238 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/name.d.ts +21 -0
- package/dist/commands/name.d.ts.map +1 -0
- package/dist/commands/name.js +154 -0
- package/dist/commands/name.js.map +1 -0
- package/dist/commands/scan.d.ts +32 -0
- package/dist/commands/scan.d.ts.map +1 -0
- package/dist/commands/scan.js +294 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/fuzzy-match.d.ts +45 -0
- package/dist/fuzzy-match.d.ts.map +1 -0
- package/dist/fuzzy-match.js +134 -0
- package/dist/fuzzy-match.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `uic diff` command — compare two manifests and detect breaking changes.
|
|
3
|
+
*
|
|
4
|
+
* stdout: diff report (human-readable or JSON)
|
|
5
|
+
* stderr: logs, errors, help text
|
|
6
|
+
*/
|
|
7
|
+
import type { Manifest, ManifestElement } from '@uicontract/core';
|
|
8
|
+
export declare const DIFF_HELP = "uic diff <old-manifest> <new-manifest> [options]\n\nCompare two manifests and report changes. Exits non-zero if breaking changes found.\n\nARGUMENTS\n <old-manifest> Path to the baseline manifest\n <new-manifest> Path to the new/current manifest\n\nOPTIONS\n --allow-breaking <reason> Override exit code to 0 even with breaking changes\n --config <path> Path to .uicrc.json config file (auto-detected if omitted)\n --json Output diff result as JSON\n --help, -h Show this help message\n\nCHANGE CATEGORIES\n BREAKING: REMOVED, RENAMED, TYPE_CHANGED, ROUTE_CHANGED\n Informational: ADDED, LABEL_CHANGED, HANDLER_CHANGED, MOVED\n\nEXIT CODES\n 0 No breaking changes (or --allow-breaking used with no protected scope violations)\n 1 Breaking changes found, error, or protected scope violation\n\nEXAMPLES\n uic diff baseline.json current.json\n uic diff old.json new.json --json\n uic diff old.json new.json --allow-breaking \"Intentional redesign of nav\"\n uic diff old.json new.json --config ./my-config.json\n\nRun \"uicontract --help\" for the full list of commands.\n";
|
|
9
|
+
export type DiffCategory = 'ADDED' | 'REMOVED' | 'RENAMED' | 'TYPE_CHANGED' | 'ROUTE_CHANGED' | 'LABEL_CHANGED' | 'HANDLER_CHANGED' | 'MOVED';
|
|
10
|
+
export interface DiffChange {
|
|
11
|
+
category: DiffCategory;
|
|
12
|
+
breaking: boolean;
|
|
13
|
+
agentId: string;
|
|
14
|
+
details: string;
|
|
15
|
+
oldElement?: ManifestElement;
|
|
16
|
+
newElement?: ManifestElement;
|
|
17
|
+
}
|
|
18
|
+
export interface DiffSummary {
|
|
19
|
+
added: number;
|
|
20
|
+
removed: number;
|
|
21
|
+
renamed: number;
|
|
22
|
+
typeChanged: number;
|
|
23
|
+
routeChanged: number;
|
|
24
|
+
labelChanged: number;
|
|
25
|
+
handlerChanged: number;
|
|
26
|
+
moved: number;
|
|
27
|
+
total: number;
|
|
28
|
+
breakingCount: number;
|
|
29
|
+
}
|
|
30
|
+
export interface DiffResult {
|
|
31
|
+
changes: DiffChange[];
|
|
32
|
+
breaking: boolean;
|
|
33
|
+
summary: DiffSummary;
|
|
34
|
+
}
|
|
35
|
+
export interface DiffArgs {
|
|
36
|
+
oldManifest: string | undefined;
|
|
37
|
+
newManifest: string | undefined;
|
|
38
|
+
allowBreaking: string | undefined;
|
|
39
|
+
configPath: string | undefined;
|
|
40
|
+
json: boolean;
|
|
41
|
+
help: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface DiffArgsError {
|
|
44
|
+
error: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function parseDiffArgs(args: string[]): DiffArgs | DiffArgsError;
|
|
47
|
+
/** Compare two manifests and return categorized changes. */
|
|
48
|
+
export declare function diffManifests(oldManifest: Manifest, newManifest: Manifest): DiffResult;
|
|
49
|
+
/**
|
|
50
|
+
* Check which breaking changes touch a protected scope.
|
|
51
|
+
* Returns the subset of breaking changes whose agentId starts with
|
|
52
|
+
* any prefix in `protectedScopes`.
|
|
53
|
+
*/
|
|
54
|
+
export declare function findProtectedScopeViolations(changes: DiffChange[], protectedScopes: string[]): DiffChange[];
|
|
55
|
+
export declare function formatDiffReport(result: DiffResult, allowBreaking: string | undefined, protectedViolations?: DiffChange[]): string;
|
|
56
|
+
export declare function diffCommand(args: string[]): Promise<number>;
|
|
57
|
+
//# sourceMappingURL=diff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAa,MAAM,kBAAkB,CAAC;AAM7E,eAAO,MAAM,SAAS,2oCA8BrB,CAAC;AAMF,MAAM,MAAM,YAAY,GACpB,OAAO,GACP,SAAS,GACT,SAAS,GACT,cAAc,GACd,eAAe,GACf,eAAe,GACf,iBAAiB,GACjB,OAAO,CAAC;AAEZ,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B,UAAU,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;CACtB;AAMD,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,GAAG,aAAa,CA4DtE;AAMD,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,GAAG,UAAU,CAyJtF;AAMD;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,UAAU,EAAE,EACrB,eAAe,EAAE,MAAM,EAAE,GACxB,UAAU,EAAE,CAOd;AAMD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,UAAU,EAClB,aAAa,EAAE,MAAM,GAAG,SAAS,EACjC,mBAAmB,GAAE,UAAU,EAAO,GACrC,MAAM,CAwDR;AAMD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA0GjE"}
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `uic diff` command — compare two manifests and detect breaking changes.
|
|
3
|
+
*
|
|
4
|
+
* stdout: diff report (human-readable or JSON)
|
|
5
|
+
* stderr: logs, errors, help text
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs/promises';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { deserializeManifest, loadConfig } from '@uicontract/core';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Help text
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
export const DIFF_HELP = `\
|
|
14
|
+
uic diff <old-manifest> <new-manifest> [options]
|
|
15
|
+
|
|
16
|
+
Compare two manifests and report changes. Exits non-zero if breaking changes found.
|
|
17
|
+
|
|
18
|
+
ARGUMENTS
|
|
19
|
+
<old-manifest> Path to the baseline manifest
|
|
20
|
+
<new-manifest> Path to the new/current manifest
|
|
21
|
+
|
|
22
|
+
OPTIONS
|
|
23
|
+
--allow-breaking <reason> Override exit code to 0 even with breaking changes
|
|
24
|
+
--config <path> Path to .uicrc.json config file (auto-detected if omitted)
|
|
25
|
+
--json Output diff result as JSON
|
|
26
|
+
--help, -h Show this help message
|
|
27
|
+
|
|
28
|
+
CHANGE CATEGORIES
|
|
29
|
+
BREAKING: REMOVED, RENAMED, TYPE_CHANGED, ROUTE_CHANGED
|
|
30
|
+
Informational: ADDED, LABEL_CHANGED, HANDLER_CHANGED, MOVED
|
|
31
|
+
|
|
32
|
+
EXIT CODES
|
|
33
|
+
0 No breaking changes (or --allow-breaking used with no protected scope violations)
|
|
34
|
+
1 Breaking changes found, error, or protected scope violation
|
|
35
|
+
|
|
36
|
+
EXAMPLES
|
|
37
|
+
uic diff baseline.json current.json
|
|
38
|
+
uic diff old.json new.json --json
|
|
39
|
+
uic diff old.json new.json --allow-breaking "Intentional redesign of nav"
|
|
40
|
+
uic diff old.json new.json --config ./my-config.json
|
|
41
|
+
|
|
42
|
+
Run "uicontract --help" for the full list of commands.
|
|
43
|
+
`;
|
|
44
|
+
export function parseDiffArgs(args) {
|
|
45
|
+
let allowBreaking;
|
|
46
|
+
let configPath;
|
|
47
|
+
let json = false;
|
|
48
|
+
let help = false;
|
|
49
|
+
const positionals = [];
|
|
50
|
+
for (let i = 0; i < args.length; i++) {
|
|
51
|
+
const arg = args[i];
|
|
52
|
+
if (arg === '--help' || arg === '-h') {
|
|
53
|
+
help = true;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (arg === '--json') {
|
|
57
|
+
json = true;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (arg === '--allow-breaking') {
|
|
61
|
+
const next = args[i + 1];
|
|
62
|
+
if (next === undefined || next.startsWith('-')) {
|
|
63
|
+
return { error: 'Missing value for --allow-breaking. Provide a reason. Example: --allow-breaking "Intentional redesign"' };
|
|
64
|
+
}
|
|
65
|
+
allowBreaking = next;
|
|
66
|
+
i++;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (arg === '--config') {
|
|
70
|
+
const next = args[i + 1];
|
|
71
|
+
if (next === undefined || next.startsWith('-')) {
|
|
72
|
+
return { error: 'Missing value for --config. Provide a path. Example: --config .uicrc.json' };
|
|
73
|
+
}
|
|
74
|
+
configPath = next;
|
|
75
|
+
i++;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
if (arg !== undefined && arg.startsWith('-')) {
|
|
79
|
+
return { error: `Unknown option: ${arg}. Run "uicontract diff --help" for usage.` };
|
|
80
|
+
}
|
|
81
|
+
if (arg !== undefined) {
|
|
82
|
+
positionals.push(arg);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!help && positionals.length < 2) {
|
|
86
|
+
return { error: 'Missing required arguments. Usage: uic diff <old-manifest> <new-manifest>' };
|
|
87
|
+
}
|
|
88
|
+
if (positionals.length > 2) {
|
|
89
|
+
return { error: `Unexpected argument: "${positionals[2] ?? ''}". Run "uicontract diff --help" for usage.` };
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
oldManifest: positionals[0],
|
|
93
|
+
newManifest: positionals[1],
|
|
94
|
+
allowBreaking,
|
|
95
|
+
configPath,
|
|
96
|
+
json,
|
|
97
|
+
help,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Diff logic (pure function — no I/O)
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/** Compare two manifests and return categorized changes. */
|
|
104
|
+
export function diffManifests(oldManifest, newManifest) {
|
|
105
|
+
const oldById = new Map();
|
|
106
|
+
for (const el of oldManifest.elements) {
|
|
107
|
+
oldById.set(el.agentId, el);
|
|
108
|
+
}
|
|
109
|
+
const newById = new Map();
|
|
110
|
+
for (const el of newManifest.elements) {
|
|
111
|
+
newById.set(el.agentId, el);
|
|
112
|
+
}
|
|
113
|
+
const changes = [];
|
|
114
|
+
// Build location maps for rename detection
|
|
115
|
+
// key: "filePath:line" -> element
|
|
116
|
+
const oldByLocation = new Map();
|
|
117
|
+
for (const el of oldManifest.elements) {
|
|
118
|
+
oldByLocation.set(`${el.filePath}:${String(el.line)}`, el);
|
|
119
|
+
}
|
|
120
|
+
const newByLocation = new Map();
|
|
121
|
+
for (const el of newManifest.elements) {
|
|
122
|
+
newByLocation.set(`${el.filePath}:${String(el.line)}`, el);
|
|
123
|
+
}
|
|
124
|
+
// Track IDs involved in renames to avoid double-counting
|
|
125
|
+
const renamedOldIds = new Set();
|
|
126
|
+
const renamedNewIds = new Set();
|
|
127
|
+
// Detect renames: IDs that were removed where a new ID appeared at the same location
|
|
128
|
+
for (const [oldId, oldEl] of oldById) {
|
|
129
|
+
if (newById.has(oldId))
|
|
130
|
+
continue; // not removed
|
|
131
|
+
const location = `${oldEl.filePath}:${String(oldEl.line)}`;
|
|
132
|
+
const newAtSameLocation = newByLocation.get(location);
|
|
133
|
+
if (newAtSameLocation !== undefined && !oldById.has(newAtSameLocation.agentId)) {
|
|
134
|
+
// New element at same location with a different ID → rename
|
|
135
|
+
renamedOldIds.add(oldId);
|
|
136
|
+
renamedNewIds.add(newAtSameLocation.agentId);
|
|
137
|
+
changes.push({
|
|
138
|
+
category: 'RENAMED',
|
|
139
|
+
breaking: true,
|
|
140
|
+
agentId: oldId,
|
|
141
|
+
details: `Renamed: "${oldId}" → "${newAtSameLocation.agentId}" at ${oldEl.filePath}:${String(oldEl.line)}`,
|
|
142
|
+
oldElement: oldEl,
|
|
143
|
+
newElement: newAtSameLocation,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Removed elements (in old, not in new, not renamed)
|
|
148
|
+
for (const [oldId, oldEl] of oldById) {
|
|
149
|
+
if (newById.has(oldId))
|
|
150
|
+
continue;
|
|
151
|
+
if (renamedOldIds.has(oldId))
|
|
152
|
+
continue;
|
|
153
|
+
changes.push({
|
|
154
|
+
category: 'REMOVED',
|
|
155
|
+
breaking: true,
|
|
156
|
+
agentId: oldId,
|
|
157
|
+
details: `Removed: "${oldId}" was in ${oldEl.filePath}:${String(oldEl.line)}`,
|
|
158
|
+
oldElement: oldEl,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
// Added elements (in new, not in old, not from rename)
|
|
162
|
+
for (const [newId, newEl] of newById) {
|
|
163
|
+
if (oldById.has(newId))
|
|
164
|
+
continue;
|
|
165
|
+
if (renamedNewIds.has(newId))
|
|
166
|
+
continue;
|
|
167
|
+
changes.push({
|
|
168
|
+
category: 'ADDED',
|
|
169
|
+
breaking: false,
|
|
170
|
+
agentId: newId,
|
|
171
|
+
details: `Added: "${newId}" in ${newEl.filePath}:${String(newEl.line)}`,
|
|
172
|
+
newElement: newEl,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Changed elements (same agentId in both)
|
|
176
|
+
for (const [id, oldEl] of oldById) {
|
|
177
|
+
const newEl = newById.get(id);
|
|
178
|
+
if (newEl === undefined)
|
|
179
|
+
continue;
|
|
180
|
+
if (oldEl.type !== newEl.type) {
|
|
181
|
+
changes.push({
|
|
182
|
+
category: 'TYPE_CHANGED',
|
|
183
|
+
breaking: true,
|
|
184
|
+
agentId: id,
|
|
185
|
+
details: `Type changed: "${oldEl.type}" → "${newEl.type}"`,
|
|
186
|
+
oldElement: oldEl,
|
|
187
|
+
newElement: newEl,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (oldEl.route !== newEl.route) {
|
|
191
|
+
changes.push({
|
|
192
|
+
category: 'ROUTE_CHANGED',
|
|
193
|
+
breaking: true,
|
|
194
|
+
agentId: id,
|
|
195
|
+
details: `Route changed: "${oldEl.route ?? '(none)'}" → "${newEl.route ?? '(none)'}"`,
|
|
196
|
+
oldElement: oldEl,
|
|
197
|
+
newElement: newEl,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (oldEl.label !== newEl.label) {
|
|
201
|
+
changes.push({
|
|
202
|
+
category: 'LABEL_CHANGED',
|
|
203
|
+
breaking: false,
|
|
204
|
+
agentId: id,
|
|
205
|
+
details: `Label changed: "${oldEl.label ?? '(none)'}" → "${newEl.label ?? '(none)'}"`,
|
|
206
|
+
oldElement: oldEl,
|
|
207
|
+
newElement: newEl,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (oldEl.handler !== newEl.handler) {
|
|
211
|
+
changes.push({
|
|
212
|
+
category: 'HANDLER_CHANGED',
|
|
213
|
+
breaking: false,
|
|
214
|
+
agentId: id,
|
|
215
|
+
details: `Handler changed: "${oldEl.handler ?? '(none)'}" → "${newEl.handler ?? '(none)'}"`,
|
|
216
|
+
oldElement: oldEl,
|
|
217
|
+
newElement: newEl,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (oldEl.filePath !== newEl.filePath || oldEl.line !== newEl.line) {
|
|
221
|
+
changes.push({
|
|
222
|
+
category: 'MOVED',
|
|
223
|
+
breaking: false,
|
|
224
|
+
agentId: id,
|
|
225
|
+
details: `Moved: ${oldEl.filePath}:${String(oldEl.line)} → ${newEl.filePath}:${String(newEl.line)}`,
|
|
226
|
+
oldElement: oldEl,
|
|
227
|
+
newElement: newEl,
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const summary = {
|
|
232
|
+
added: changes.filter((c) => c.category === 'ADDED').length,
|
|
233
|
+
removed: changes.filter((c) => c.category === 'REMOVED').length,
|
|
234
|
+
renamed: changes.filter((c) => c.category === 'RENAMED').length,
|
|
235
|
+
typeChanged: changes.filter((c) => c.category === 'TYPE_CHANGED').length,
|
|
236
|
+
routeChanged: changes.filter((c) => c.category === 'ROUTE_CHANGED').length,
|
|
237
|
+
labelChanged: changes.filter((c) => c.category === 'LABEL_CHANGED').length,
|
|
238
|
+
handlerChanged: changes.filter((c) => c.category === 'HANDLER_CHANGED').length,
|
|
239
|
+
moved: changes.filter((c) => c.category === 'MOVED').length,
|
|
240
|
+
total: changes.length,
|
|
241
|
+
breakingCount: changes.filter((c) => c.breaking).length,
|
|
242
|
+
};
|
|
243
|
+
return {
|
|
244
|
+
changes,
|
|
245
|
+
breaking: summary.breakingCount > 0,
|
|
246
|
+
summary,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
// ---------------------------------------------------------------------------
|
|
250
|
+
// Protected scope checking (pure function)
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
/**
|
|
253
|
+
* Check which breaking changes touch a protected scope.
|
|
254
|
+
* Returns the subset of breaking changes whose agentId starts with
|
|
255
|
+
* any prefix in `protectedScopes`.
|
|
256
|
+
*/
|
|
257
|
+
export function findProtectedScopeViolations(changes, protectedScopes) {
|
|
258
|
+
if (protectedScopes.length === 0)
|
|
259
|
+
return [];
|
|
260
|
+
return changes.filter((c) => c.breaking &&
|
|
261
|
+
protectedScopes.some((scope) => c.agentId.startsWith(scope)));
|
|
262
|
+
}
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
// Formatting
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
export function formatDiffReport(result, allowBreaking, protectedViolations = []) {
|
|
267
|
+
if (result.changes.length === 0) {
|
|
268
|
+
return 'UIC Manifest Diff\n=================\n\nNo changes detected.\n';
|
|
269
|
+
}
|
|
270
|
+
const lines = ['UIC Manifest Diff', '=================', ''];
|
|
271
|
+
// Protected scope violations section (always shown first if any)
|
|
272
|
+
if (protectedViolations.length > 0) {
|
|
273
|
+
lines.push(`PROTECTED SCOPE VIOLATIONS (${String(protectedViolations.length)}):`);
|
|
274
|
+
lines.push(' These changes affect protected scopes and cannot be overridden with --allow-breaking.');
|
|
275
|
+
for (const change of protectedViolations) {
|
|
276
|
+
lines.push(` !! ${change.category} ${change.agentId}`);
|
|
277
|
+
lines.push(` ${change.details}`);
|
|
278
|
+
}
|
|
279
|
+
lines.push('');
|
|
280
|
+
}
|
|
281
|
+
const protectedIds = new Set(protectedViolations.map((c) => `${c.category}:${c.agentId}`));
|
|
282
|
+
const breakingNonProtected = result.changes.filter((c) => c.breaking && !protectedIds.has(`${c.category}:${c.agentId}`));
|
|
283
|
+
const nonBreaking = result.changes.filter((c) => !c.breaking);
|
|
284
|
+
if (breakingNonProtected.length > 0) {
|
|
285
|
+
lines.push(`BREAKING CHANGES (${String(breakingNonProtected.length)}):`);
|
|
286
|
+
for (const change of breakingNonProtected) {
|
|
287
|
+
lines.push(` \u2717 ${change.category} ${change.agentId}`);
|
|
288
|
+
lines.push(` ${change.details}`);
|
|
289
|
+
}
|
|
290
|
+
lines.push('');
|
|
291
|
+
}
|
|
292
|
+
if (nonBreaking.length > 0) {
|
|
293
|
+
lines.push(`NON-BREAKING CHANGES (${String(nonBreaking.length)}):`);
|
|
294
|
+
for (const change of nonBreaking) {
|
|
295
|
+
const prefix = change.category === 'ADDED' ? '+' : '~';
|
|
296
|
+
lines.push(` ${prefix} ${change.category} ${change.agentId}`);
|
|
297
|
+
lines.push(` ${change.details}`);
|
|
298
|
+
}
|
|
299
|
+
lines.push('');
|
|
300
|
+
}
|
|
301
|
+
lines.push(`Summary: ${String(result.summary.total)} change(s) (${String(result.summary.breakingCount)} breaking, ${String(result.summary.total - result.summary.breakingCount)} non-breaking)`);
|
|
302
|
+
if (allowBreaking !== undefined && result.breaking) {
|
|
303
|
+
lines.push(`\nBreaking changes allowed: "${allowBreaking}"`);
|
|
304
|
+
}
|
|
305
|
+
return lines.join('\n') + '\n';
|
|
306
|
+
}
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// Main command
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
export async function diffCommand(args) {
|
|
311
|
+
const parsed = parseDiffArgs(args);
|
|
312
|
+
if ('error' in parsed) {
|
|
313
|
+
process.stderr.write(`Error: ${parsed.error}\n`);
|
|
314
|
+
return 1;
|
|
315
|
+
}
|
|
316
|
+
if (parsed.help) {
|
|
317
|
+
process.stderr.write(DIFF_HELP);
|
|
318
|
+
return 0;
|
|
319
|
+
}
|
|
320
|
+
const oldPath = path.resolve(parsed.oldManifest);
|
|
321
|
+
const newPath = path.resolve(parsed.newManifest);
|
|
322
|
+
// Load config
|
|
323
|
+
let config;
|
|
324
|
+
if (parsed.configPath !== undefined) {
|
|
325
|
+
// Explicit config path: read and validate directly
|
|
326
|
+
const configFilePath = path.resolve(parsed.configPath);
|
|
327
|
+
try {
|
|
328
|
+
const raw = await fs.readFile(configFilePath, 'utf-8');
|
|
329
|
+
const { validateConfig } = await import('@uicontract/core');
|
|
330
|
+
const jsonParsed = JSON.parse(raw);
|
|
331
|
+
config = validateConfig(jsonParsed);
|
|
332
|
+
}
|
|
333
|
+
catch (err) {
|
|
334
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
335
|
+
process.stderr.write(`Error: Failed to load config "${configFilePath}": ${message}\n`);
|
|
336
|
+
return 1;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
try {
|
|
341
|
+
config = await loadConfig(process.cwd());
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
345
|
+
process.stderr.write(`Error: Failed to load config: ${message}\n`);
|
|
346
|
+
return 1;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// Load old manifest
|
|
350
|
+
let oldManifest;
|
|
351
|
+
try {
|
|
352
|
+
const raw = await fs.readFile(oldPath, 'utf-8');
|
|
353
|
+
oldManifest = deserializeManifest(raw);
|
|
354
|
+
}
|
|
355
|
+
catch (err) {
|
|
356
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
357
|
+
process.stderr.write(`Error: Failed to load old manifest "${oldPath}": ${message}\n`);
|
|
358
|
+
return 1;
|
|
359
|
+
}
|
|
360
|
+
// Load new manifest
|
|
361
|
+
let newManifest;
|
|
362
|
+
try {
|
|
363
|
+
const raw = await fs.readFile(newPath, 'utf-8');
|
|
364
|
+
newManifest = deserializeManifest(raw);
|
|
365
|
+
}
|
|
366
|
+
catch (err) {
|
|
367
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
368
|
+
process.stderr.write(`Error: Failed to load new manifest "${newPath}": ${message}\n`);
|
|
369
|
+
return 1;
|
|
370
|
+
}
|
|
371
|
+
const result = diffManifests(oldManifest, newManifest);
|
|
372
|
+
// Check protected scope violations
|
|
373
|
+
const protectedViolations = findProtectedScopeViolations(result.changes, config.protectedScopes);
|
|
374
|
+
if (parsed.json) {
|
|
375
|
+
const jsonOutput = {
|
|
376
|
+
...result,
|
|
377
|
+
protectedScopeViolations: protectedViolations.map((c) => c.agentId),
|
|
378
|
+
};
|
|
379
|
+
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
process.stdout.write(formatDiffReport(result, parsed.allowBreaking, protectedViolations));
|
|
383
|
+
}
|
|
384
|
+
// Exit code logic:
|
|
385
|
+
// 1. Protected scope violations ALWAYS exit 1 (--allow-breaking does NOT override)
|
|
386
|
+
if (protectedViolations.length > 0) {
|
|
387
|
+
return 1;
|
|
388
|
+
}
|
|
389
|
+
// 2. Breaking changes follow policy
|
|
390
|
+
if (result.breaking) {
|
|
391
|
+
if (parsed.allowBreaking !== undefined) {
|
|
392
|
+
// --allow-breaking overrides the policy
|
|
393
|
+
return 0;
|
|
394
|
+
}
|
|
395
|
+
if (config.breakingChangePolicy === 'warn') {
|
|
396
|
+
// "warn" policy: print (already done above) but exit 0
|
|
397
|
+
return 0;
|
|
398
|
+
}
|
|
399
|
+
// "block" policy (default): exit 1
|
|
400
|
+
return 1;
|
|
401
|
+
}
|
|
402
|
+
return 0;
|
|
403
|
+
}
|
|
404
|
+
//# sourceMappingURL=diff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff.js","sourceRoot":"","sources":["../../src/commands/diff.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `uic find` command -- fuzzy search for elements in a manifest.
|
|
3
|
+
*
|
|
4
|
+
* stdout: matching elements (human-readable or JSON)
|
|
5
|
+
* stderr: logs, errors, help text
|
|
6
|
+
*/
|
|
7
|
+
import type { ManifestElement } from '@uicontract/core';
|
|
8
|
+
export declare const FIND_HELP = "uic find <query> [options]\n\nSearch for interactive UI elements in a manifest by name, label, route, or handler.\n\nARGUMENTS\n <query> Text to search for (case-insensitive)\n\nOPTIONS\n --manifest <path> Path to manifest file (default: manifest.json)\n --type <type> Filter results to a specific element type\n --exact Use exact substring matching (no fuzzy matching)\n --fuzzy Use fuzzy matching (default)\n --json Output matching elements as JSON array\n --help, -h Show this help message\n\nEXAMPLES\n uic find \"login\"\n uic find \"pase subscribtion\" # fuzzy match finds \"pause-subscription\"\n uic find \"button\" --type button\n uic find \"settings\" --exact # strict substring match only\n uic find \"settings\" --manifest dist/manifest.json --json\n\nRun \"uicontract --help\" for the full list of commands.\n";
|
|
9
|
+
export interface FindArgs {
|
|
10
|
+
query: string | undefined;
|
|
11
|
+
manifest: string;
|
|
12
|
+
type: string | undefined;
|
|
13
|
+
json: boolean;
|
|
14
|
+
help: boolean;
|
|
15
|
+
exact: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface FindArgsError {
|
|
18
|
+
error: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function parseFindArgs(args: string[]): FindArgs | FindArgsError;
|
|
21
|
+
/** Check if an element matches the query (case-insensitive substring). */
|
|
22
|
+
export declare function elementMatchesQuery(el: ManifestElement, query: string): boolean;
|
|
23
|
+
/** Scored search result for fuzzy mode. */
|
|
24
|
+
export interface ScoredElement {
|
|
25
|
+
element: ManifestElement;
|
|
26
|
+
score: number;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Score an element against a query using fuzzy matching.
|
|
30
|
+
* Returns null if the element does not meet the threshold.
|
|
31
|
+
*/
|
|
32
|
+
export declare function scoreElement(el: ManifestElement, query: string): ScoredElement | null;
|
|
33
|
+
export declare function findCommand(args: string[]): Promise<number>;
|
|
34
|
+
//# sourceMappingURL=find.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find.d.ts","sourceRoot":"","sources":["../../src/commands/find.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAOxD,eAAO,MAAM,SAAS,i7BAwBrB,CAAC;AAMF,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,QAAQ,GAAG,aAAa,CA8DtE;AAmBD,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAS/E;AAED,2CAA2C;AAC3C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,eAAe,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAKrF;AAaD,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA4FjE"}
|