@contractual/differs.json-schema 0.1.0-dev.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 +180 -0
- package/dist/classifiers.d.ts +87 -0
- package/dist/classifiers.d.ts.map +1 -0
- package/dist/classifiers.js +357 -0
- package/dist/classifiers.js.map +1 -0
- package/dist/compare.d.ts +37 -0
- package/dist/compare.d.ts.map +1 -0
- package/dist/compare.js +232 -0
- package/dist/compare.js.map +1 -0
- package/dist/differ.d.ts +57 -0
- package/dist/differ.d.ts.map +1 -0
- package/dist/differ.js +304 -0
- package/dist/differ.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/ref-resolver.d.ts +73 -0
- package/dist/ref-resolver.d.ts.map +1 -0
- package/dist/ref-resolver.js +345 -0
- package/dist/ref-resolver.js.map +1 -0
- package/dist/types.d.ts +276 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +210 -0
- package/dist/types.js.map +1 -0
- package/dist/walker.d.ts +17 -0
- package/dist/walker.d.ts.map +1 -0
- package/dist/walker.js +1195 -0
- package/dist/walker.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,cAAc,EAEnB,KAAK,oBAAoB,EAG1B,MAAM,YAAY,CAAC;AAKpB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC5B,YAAY,EAAE,OAAO,EACrB,YAAY,EAAE,OAAO,EACrB,OAAO,CAAC,EAAE,cAAc,GACvB,aAAa,CAuDf;AA0KD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,YAAY,EAAE,OAAO,EACrB,YAAY,EAAE,OAAO,GACpB,oBAAoB,CAetB"}
|
package/dist/compare.js
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strands-compatible JSON Schema comparison API
|
|
3
|
+
*
|
|
4
|
+
* Provides schema comparison with output format matching the Strands API
|
|
5
|
+
* (https://strands.octue.com/api/compare-schemas)
|
|
6
|
+
*/
|
|
7
|
+
import { resolveRefs } from './ref-resolver.js';
|
|
8
|
+
import { walk } from './walker.js';
|
|
9
|
+
import { classify, classifyPropertyAdded } from './classifiers.js';
|
|
10
|
+
/**
|
|
11
|
+
* Compare two JSON Schema objects and return Strands-compatible result
|
|
12
|
+
*
|
|
13
|
+
* @param sourceSchema - The source (old/baseline) schema object
|
|
14
|
+
* @param targetSchema - The target (new/current) schema object
|
|
15
|
+
* @param options - Optional comparison options
|
|
16
|
+
* @returns CompareResult in Strands API format
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* const source = { type: 'object', properties: { name: { type: 'string' } } };
|
|
21
|
+
* const target = { type: 'object', properties: { name: { type: 'number' } } };
|
|
22
|
+
*
|
|
23
|
+
* const result = compareSchemas(source, target, { currentVersion: '1.0.0' });
|
|
24
|
+
* console.log(result.version); // 'major'
|
|
25
|
+
* console.log(result.newVersion); // { major: 2, minor: 0, patch: 0, version: '2.0.0' }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function compareSchemas(sourceSchema, targetSchema, options) {
|
|
29
|
+
// Resolve $refs in both schemas
|
|
30
|
+
const resolvedSourceResult = resolveRefs(sourceSchema);
|
|
31
|
+
const resolvedTargetResult = resolveRefs(targetSchema);
|
|
32
|
+
const resolvedSource = resolvedSourceResult.schema;
|
|
33
|
+
const resolvedTarget = resolvedTargetResult.schema;
|
|
34
|
+
// Walk both schemas and detect raw changes
|
|
35
|
+
const rawChanges = walk(resolvedSource, resolvedTarget, '');
|
|
36
|
+
// Classify changes and generate traces
|
|
37
|
+
const traces = [];
|
|
38
|
+
let hasBreaking = false;
|
|
39
|
+
let hasNonBreaking = false;
|
|
40
|
+
let hasPatch = false;
|
|
41
|
+
let hasUnknown = false;
|
|
42
|
+
for (const raw of rawChanges) {
|
|
43
|
+
const severity = classifyChange(raw, resolvedTarget);
|
|
44
|
+
// Track highest severity
|
|
45
|
+
switch (severity) {
|
|
46
|
+
case 'breaking':
|
|
47
|
+
hasBreaking = true;
|
|
48
|
+
break;
|
|
49
|
+
case 'non-breaking':
|
|
50
|
+
hasNonBreaking = true;
|
|
51
|
+
break;
|
|
52
|
+
case 'patch':
|
|
53
|
+
hasPatch = true;
|
|
54
|
+
break;
|
|
55
|
+
case 'unknown':
|
|
56
|
+
hasUnknown = true;
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
// Generate trace
|
|
60
|
+
const trace = rawChangeToTrace(raw, severity);
|
|
61
|
+
traces.push(trace);
|
|
62
|
+
}
|
|
63
|
+
// Determine version bump
|
|
64
|
+
const version = determineVersion(hasBreaking, hasNonBreaking, hasPatch, hasUnknown, traces);
|
|
65
|
+
// Compute new version if current version provided
|
|
66
|
+
const newVersion = options?.currentVersion
|
|
67
|
+
? computeNewVersion(options.currentVersion, version)
|
|
68
|
+
: null;
|
|
69
|
+
return {
|
|
70
|
+
version,
|
|
71
|
+
traces,
|
|
72
|
+
newVersion,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Classify a change, with context-aware classification for property-added
|
|
77
|
+
*/
|
|
78
|
+
function classifyChange(change, newSchema) {
|
|
79
|
+
if (change.type === 'property-added') {
|
|
80
|
+
return classifyPropertyAdded(change, newSchema);
|
|
81
|
+
}
|
|
82
|
+
return classify(change);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Convert a RawChange to a Strands trace
|
|
86
|
+
*/
|
|
87
|
+
function rawChangeToTrace(change, severity) {
|
|
88
|
+
const compatibility = severityToCompatibility(severity);
|
|
89
|
+
// Determine left/right paths based on change type
|
|
90
|
+
const isAddition = change.type.includes('added') ||
|
|
91
|
+
change.type === 'type-widened' ||
|
|
92
|
+
change.type === 'constraint-loosened';
|
|
93
|
+
const isRemoval = change.type.includes('removed') ||
|
|
94
|
+
change.type === 'type-narrowed' ||
|
|
95
|
+
change.type === 'constraint-tightened';
|
|
96
|
+
let left = change.path;
|
|
97
|
+
let right = change.path;
|
|
98
|
+
// For additions, left is null (didn't exist in source)
|
|
99
|
+
if (isAddition && change.oldValue === undefined) {
|
|
100
|
+
left = null;
|
|
101
|
+
}
|
|
102
|
+
// For removals, right is null (doesn't exist in target)
|
|
103
|
+
if (isRemoval && change.newValue === undefined) {
|
|
104
|
+
right = null;
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
compatibility,
|
|
108
|
+
left,
|
|
109
|
+
right,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Map ChangeSeverity to Strands compatibility
|
|
114
|
+
*/
|
|
115
|
+
function severityToCompatibility(severity) {
|
|
116
|
+
switch (severity) {
|
|
117
|
+
case 'breaking':
|
|
118
|
+
return 'incompatible';
|
|
119
|
+
case 'non-breaking':
|
|
120
|
+
case 'patch':
|
|
121
|
+
return 'compatible';
|
|
122
|
+
case 'unknown':
|
|
123
|
+
default:
|
|
124
|
+
return 'unknown';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Determine the version bump level based on detected changes
|
|
129
|
+
*/
|
|
130
|
+
function determineVersion(hasBreaking, hasNonBreaking, hasPatch, hasUnknown, traces) {
|
|
131
|
+
// No changes = equal
|
|
132
|
+
if (traces.length === 0) {
|
|
133
|
+
return 'equal';
|
|
134
|
+
}
|
|
135
|
+
// If there are unknown changes, we can't determine version
|
|
136
|
+
if (hasUnknown && !hasBreaking && !hasNonBreaking) {
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
// Priority: breaking > non-breaking > patch
|
|
140
|
+
if (hasBreaking) {
|
|
141
|
+
return 'major';
|
|
142
|
+
}
|
|
143
|
+
if (hasNonBreaking) {
|
|
144
|
+
return 'minor';
|
|
145
|
+
}
|
|
146
|
+
if (hasPatch) {
|
|
147
|
+
return 'patch';
|
|
148
|
+
}
|
|
149
|
+
// Only unknown changes
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Compute the new semantic version based on current version and bump level
|
|
154
|
+
*/
|
|
155
|
+
function computeNewVersion(currentVersion, bumpLevel) {
|
|
156
|
+
if (bumpLevel === null || bumpLevel === 'equal') {
|
|
157
|
+
// Parse current version and return as-is for equal
|
|
158
|
+
const parsed = parseVersion(currentVersion);
|
|
159
|
+
if (!parsed)
|
|
160
|
+
return null;
|
|
161
|
+
return bumpLevel === 'equal' ? parsed : null;
|
|
162
|
+
}
|
|
163
|
+
const parsed = parseVersion(currentVersion);
|
|
164
|
+
if (!parsed) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
let { major, minor, patch } = parsed;
|
|
168
|
+
switch (bumpLevel) {
|
|
169
|
+
case 'major':
|
|
170
|
+
major += 1;
|
|
171
|
+
minor = 0;
|
|
172
|
+
patch = 0;
|
|
173
|
+
break;
|
|
174
|
+
case 'minor':
|
|
175
|
+
minor += 1;
|
|
176
|
+
patch = 0;
|
|
177
|
+
break;
|
|
178
|
+
case 'patch':
|
|
179
|
+
patch += 1;
|
|
180
|
+
break;
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
major,
|
|
184
|
+
minor,
|
|
185
|
+
patch,
|
|
186
|
+
version: `${major}.${minor}.${patch}`,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Parse a semver string into components
|
|
191
|
+
*/
|
|
192
|
+
function parseVersion(version) {
|
|
193
|
+
// Remove 'v' prefix if present
|
|
194
|
+
const cleaned = version.startsWith('v') ? version.slice(1) : version;
|
|
195
|
+
// Match semver pattern (with optional prerelease/build)
|
|
196
|
+
const match = cleaned.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
197
|
+
if (!match) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const major = parseInt(match[1], 10);
|
|
201
|
+
const minor = parseInt(match[2], 10);
|
|
202
|
+
const patch = parseInt(match[3], 10);
|
|
203
|
+
return {
|
|
204
|
+
major,
|
|
205
|
+
minor,
|
|
206
|
+
patch,
|
|
207
|
+
version: `${major}.${minor}.${patch}`,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Compare two JSON Schema objects and return a simple compatibility result
|
|
212
|
+
*
|
|
213
|
+
* Convenience function for quick compatibility checks.
|
|
214
|
+
*
|
|
215
|
+
* @param sourceSchema - The source (old/baseline) schema object
|
|
216
|
+
* @param targetSchema - The target (new/current) schema object
|
|
217
|
+
* @returns 'compatible' | 'incompatible' | 'unknown'
|
|
218
|
+
*/
|
|
219
|
+
export function checkCompatibility(sourceSchema, targetSchema) {
|
|
220
|
+
const result = compareSchemas(sourceSchema, targetSchema);
|
|
221
|
+
// If any trace is incompatible, overall is incompatible
|
|
222
|
+
if (result.traces.some((t) => t.compatibility === 'incompatible')) {
|
|
223
|
+
return 'incompatible';
|
|
224
|
+
}
|
|
225
|
+
// If any trace is unknown, overall is unknown
|
|
226
|
+
if (result.traces.some((t) => t.compatibility === 'unknown')) {
|
|
227
|
+
return 'unknown';
|
|
228
|
+
}
|
|
229
|
+
// All compatible
|
|
230
|
+
return 'compatible';
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"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"}
|
package/dist/differ.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema Structural Differ
|
|
3
|
+
*
|
|
4
|
+
* Compares two JSON Schema files and detects structural changes,
|
|
5
|
+
* classifying them by severity for semver bump decisions.
|
|
6
|
+
*/
|
|
7
|
+
import type { DiffResult, RawChange } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Format a human-readable message for a change
|
|
10
|
+
*
|
|
11
|
+
* @param change - The raw change to format
|
|
12
|
+
* @returns Human-readable message describing the change
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatChangeMessage(change: RawChange): string;
|
|
15
|
+
/**
|
|
16
|
+
* Diff two JSON Schema files and detect structural changes
|
|
17
|
+
*
|
|
18
|
+
* Reads both schema files, resolves $ref references, walks the schemas
|
|
19
|
+
* to detect differences, and classifies each change by severity.
|
|
20
|
+
*
|
|
21
|
+
* @param oldPath - Path to the old/base schema file
|
|
22
|
+
* @param newPath - Path to the new/changed schema file
|
|
23
|
+
* @returns DiffResult with classified changes and suggested bump
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const result = await diffJsonSchema('v1/schema.json', 'v2/schema.json');
|
|
28
|
+
*
|
|
29
|
+
* console.log(`Suggested bump: ${result.suggestedBump}`);
|
|
30
|
+
* console.log(`Breaking changes: ${result.summary.breaking}`);
|
|
31
|
+
*
|
|
32
|
+
* for (const change of result.changes) {
|
|
33
|
+
* console.log(`[${change.severity}] ${change.message}`);
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function diffJsonSchema(oldPath: string, newPath: string): Promise<DiffResult>;
|
|
38
|
+
/**
|
|
39
|
+
* Diff two JSON Schema objects and detect structural changes
|
|
40
|
+
*
|
|
41
|
+
* Like diffJsonSchema but accepts schema objects directly instead of file paths.
|
|
42
|
+
*
|
|
43
|
+
* @param oldSchema - The old/base schema object
|
|
44
|
+
* @param newSchema - The new/changed schema object
|
|
45
|
+
* @returns DiffResult with classified changes and suggested bump
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const oldSchema = { type: 'object', properties: { name: { type: 'string' } } };
|
|
50
|
+
* const newSchema = { type: 'object', properties: { name: { type: 'number' } } };
|
|
51
|
+
*
|
|
52
|
+
* const result = diffJsonSchemaObjects(oldSchema, newSchema);
|
|
53
|
+
* console.log(`Suggested bump: ${result.suggestedBump}`);
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function diffJsonSchemaObjects(oldSchema: unknown, newSchema: unknown): DiffResult;
|
|
57
|
+
//# sourceMappingURL=differ.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.d.ts","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAA0B,SAAS,EAAE,MAAM,YAAY,CAAC;AAKhF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CA4F7D;AA+ED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAgG1F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,UAAU,CA6CxF"}
|
package/dist/differ.js
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Schema Structural Differ
|
|
3
|
+
*
|
|
4
|
+
* Compares two JSON Schema files and detects structural changes,
|
|
5
|
+
* classifying them by severity for semver bump decisions.
|
|
6
|
+
*/
|
|
7
|
+
import { readFile } from 'node:fs/promises';
|
|
8
|
+
import { resolveRefs } from './ref-resolver.js';
|
|
9
|
+
import { walk } from './walker.js';
|
|
10
|
+
import { classify, classifyPropertyAdded } from './classifiers.js';
|
|
11
|
+
/**
|
|
12
|
+
* Format a human-readable message for a change
|
|
13
|
+
*
|
|
14
|
+
* @param change - The raw change to format
|
|
15
|
+
* @returns Human-readable message describing the change
|
|
16
|
+
*/
|
|
17
|
+
export function formatChangeMessage(change) {
|
|
18
|
+
const pathDisplay = change.path || '/';
|
|
19
|
+
switch (change.type) {
|
|
20
|
+
case 'property-added':
|
|
21
|
+
return `Property added at ${pathDisplay}`;
|
|
22
|
+
case 'property-removed':
|
|
23
|
+
return `Property removed at ${pathDisplay}`;
|
|
24
|
+
case 'required-added':
|
|
25
|
+
return `Field "${formatRequiredFieldName(change.newValue)}" made required at ${pathDisplay}`;
|
|
26
|
+
case 'required-removed':
|
|
27
|
+
return `Field "${formatRequiredFieldName(change.oldValue)}" made optional at ${pathDisplay}`;
|
|
28
|
+
case 'type-changed':
|
|
29
|
+
return `Type changed from ${formatValue(change.oldValue)} to ${formatValue(change.newValue)} at ${pathDisplay}`;
|
|
30
|
+
case 'type-narrowed':
|
|
31
|
+
return `Type narrowed from ${formatValue(change.oldValue)} to ${formatValue(change.newValue)} at ${pathDisplay}`;
|
|
32
|
+
case 'type-widened':
|
|
33
|
+
return `Type widened from ${formatValue(change.oldValue)} to ${formatValue(change.newValue)} at ${pathDisplay}`;
|
|
34
|
+
case 'enum-value-added':
|
|
35
|
+
return `Enum value ${formatValue(change.newValue)} added at ${pathDisplay}`;
|
|
36
|
+
case 'enum-value-removed':
|
|
37
|
+
return `Enum value ${formatValue(change.oldValue)} removed at ${pathDisplay}`;
|
|
38
|
+
case 'enum-added':
|
|
39
|
+
return `Enum constraint added at ${pathDisplay}`;
|
|
40
|
+
case 'enum-removed':
|
|
41
|
+
return `Enum constraint removed at ${pathDisplay}`;
|
|
42
|
+
case 'constraint-tightened':
|
|
43
|
+
return formatConstraintMessage(change, 'tightened', pathDisplay);
|
|
44
|
+
case 'constraint-loosened':
|
|
45
|
+
return formatConstraintMessage(change, 'loosened', pathDisplay);
|
|
46
|
+
case 'format-added':
|
|
47
|
+
return `Format "${change.newValue}" added at ${pathDisplay}`;
|
|
48
|
+
case 'format-removed':
|
|
49
|
+
return `Format "${change.oldValue}" removed at ${pathDisplay}`;
|
|
50
|
+
case 'format-changed':
|
|
51
|
+
return `Format changed from "${change.oldValue}" to "${change.newValue}" at ${pathDisplay}`;
|
|
52
|
+
case 'additional-properties-denied':
|
|
53
|
+
return `Additional properties denied at ${pathDisplay}`;
|
|
54
|
+
case 'additional-properties-allowed':
|
|
55
|
+
return `Additional properties allowed at ${pathDisplay}`;
|
|
56
|
+
case 'additional-properties-changed':
|
|
57
|
+
return `Additional properties schema changed at ${pathDisplay}`;
|
|
58
|
+
case 'items-changed':
|
|
59
|
+
return `Array items schema changed at ${pathDisplay}`;
|
|
60
|
+
case 'min-items-increased':
|
|
61
|
+
return `Minimum items increased from ${formatValue(change.oldValue)} to ${formatValue(change.newValue)} at ${pathDisplay}`;
|
|
62
|
+
case 'max-items-decreased':
|
|
63
|
+
return `Maximum items decreased from ${formatValue(change.oldValue)} to ${formatValue(change.newValue)} at ${pathDisplay}`;
|
|
64
|
+
case 'composition-changed':
|
|
65
|
+
return `Composition (allOf/anyOf/oneOf) changed at ${pathDisplay}`;
|
|
66
|
+
case 'ref-target-changed':
|
|
67
|
+
return `Reference target changed at ${pathDisplay}`;
|
|
68
|
+
case 'description-changed':
|
|
69
|
+
return `Description changed at ${pathDisplay}`;
|
|
70
|
+
case 'title-changed':
|
|
71
|
+
return `Title changed at ${pathDisplay}`;
|
|
72
|
+
case 'default-changed':
|
|
73
|
+
return `Default value changed at ${pathDisplay}`;
|
|
74
|
+
case 'examples-changed':
|
|
75
|
+
return `Examples changed at ${pathDisplay}`;
|
|
76
|
+
case 'unknown-change':
|
|
77
|
+
default:
|
|
78
|
+
return `Unknown change at ${pathDisplay}`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Format a value for display in messages
|
|
83
|
+
*/
|
|
84
|
+
function formatValue(value) {
|
|
85
|
+
if (value === undefined) {
|
|
86
|
+
return 'undefined';
|
|
87
|
+
}
|
|
88
|
+
if (value === null) {
|
|
89
|
+
return 'null';
|
|
90
|
+
}
|
|
91
|
+
if (typeof value === 'string') {
|
|
92
|
+
return `"${value}"`;
|
|
93
|
+
}
|
|
94
|
+
if (Array.isArray(value)) {
|
|
95
|
+
return JSON.stringify(value);
|
|
96
|
+
}
|
|
97
|
+
if (typeof value === 'object') {
|
|
98
|
+
return JSON.stringify(value);
|
|
99
|
+
}
|
|
100
|
+
return String(value);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Format required field name from change value
|
|
104
|
+
*/
|
|
105
|
+
function formatRequiredFieldName(value) {
|
|
106
|
+
if (typeof value === 'string') {
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
return String(value);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Format constraint change message
|
|
113
|
+
*/
|
|
114
|
+
function formatConstraintMessage(change, direction, pathDisplay) {
|
|
115
|
+
const constraintName = extractConstraintName(change.path);
|
|
116
|
+
const oldVal = change.oldValue !== undefined ? formatValue(change.oldValue) : 'none';
|
|
117
|
+
const newVal = change.newValue !== undefined ? formatValue(change.newValue) : 'none';
|
|
118
|
+
if (constraintName) {
|
|
119
|
+
return `Constraint "${constraintName}" ${direction} from ${oldVal} to ${newVal} at ${pathDisplay}`;
|
|
120
|
+
}
|
|
121
|
+
return `Constraint ${direction} from ${oldVal} to ${newVal} at ${pathDisplay}`;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Extract constraint name from path
|
|
125
|
+
*/
|
|
126
|
+
function extractConstraintName(path) {
|
|
127
|
+
const segments = path.split('/');
|
|
128
|
+
const lastSegment = segments[segments.length - 1];
|
|
129
|
+
if (lastSegment && lastSegment !== '') {
|
|
130
|
+
return lastSegment;
|
|
131
|
+
}
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Classify a change, with context-aware classification for property-added
|
|
136
|
+
*
|
|
137
|
+
* @param change - The raw change to classify
|
|
138
|
+
* @param newSchema - The new schema for context (used for property-added)
|
|
139
|
+
* @returns The severity classification
|
|
140
|
+
*/
|
|
141
|
+
function classifyChange(change, newSchema) {
|
|
142
|
+
if (change.type === 'property-added') {
|
|
143
|
+
return classifyPropertyAdded(change, newSchema);
|
|
144
|
+
}
|
|
145
|
+
return classify(change);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Diff two JSON Schema files and detect structural changes
|
|
149
|
+
*
|
|
150
|
+
* Reads both schema files, resolves $ref references, walks the schemas
|
|
151
|
+
* to detect differences, and classifies each change by severity.
|
|
152
|
+
*
|
|
153
|
+
* @param oldPath - Path to the old/base schema file
|
|
154
|
+
* @param newPath - Path to the new/changed schema file
|
|
155
|
+
* @returns DiffResult with classified changes and suggested bump
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* const result = await diffJsonSchema('v1/schema.json', 'v2/schema.json');
|
|
160
|
+
*
|
|
161
|
+
* console.log(`Suggested bump: ${result.suggestedBump}`);
|
|
162
|
+
* console.log(`Breaking changes: ${result.summary.breaking}`);
|
|
163
|
+
*
|
|
164
|
+
* for (const change of result.changes) {
|
|
165
|
+
* console.log(`[${change.severity}] ${change.message}`);
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
export async function diffJsonSchema(oldPath, newPath) {
|
|
170
|
+
// Read both schema files
|
|
171
|
+
let oldContent;
|
|
172
|
+
let newContent;
|
|
173
|
+
try {
|
|
174
|
+
oldContent = await readFile(oldPath, 'utf-8');
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
throw new Error(`Failed to read old schema file "${oldPath}": ${error instanceof Error ? error.message : String(error)}`);
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
newContent = await readFile(newPath, 'utf-8');
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw new Error(`Failed to read new schema file "${newPath}": ${error instanceof Error ? error.message : String(error)}`);
|
|
184
|
+
}
|
|
185
|
+
// Parse JSON
|
|
186
|
+
let oldSchema;
|
|
187
|
+
let newSchema;
|
|
188
|
+
try {
|
|
189
|
+
oldSchema = JSON.parse(oldContent);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
throw new Error(`Failed to parse old schema as JSON "${oldPath}": ${error instanceof Error ? error.message : String(error)}`);
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
newSchema = JSON.parse(newContent);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
throw new Error(`Failed to parse new schema as JSON "${newPath}": ${error instanceof Error ? error.message : String(error)}`);
|
|
199
|
+
}
|
|
200
|
+
// Resolve $refs in both schemas
|
|
201
|
+
const resolvedOldResult = resolveRefs(oldSchema);
|
|
202
|
+
const resolvedNewResult = resolveRefs(newSchema);
|
|
203
|
+
const resolvedOld = resolvedOldResult.schema;
|
|
204
|
+
const resolvedNew = resolvedNewResult.schema;
|
|
205
|
+
// Log warnings if any $refs couldn't be resolved
|
|
206
|
+
const allWarnings = [
|
|
207
|
+
...resolvedOldResult.warnings.map((w) => `[old] ${w}`),
|
|
208
|
+
...resolvedNewResult.warnings.map((w) => `[new] ${w}`),
|
|
209
|
+
];
|
|
210
|
+
if (allWarnings.length > 0) {
|
|
211
|
+
// Warnings are informational; diff continues with best effort
|
|
212
|
+
// In production, you might want to expose these in the result
|
|
213
|
+
}
|
|
214
|
+
// Walk both schemas and detect raw changes
|
|
215
|
+
const rawChanges = walk(resolvedOld, resolvedNew, '');
|
|
216
|
+
// Classify changes and build final Change objects
|
|
217
|
+
const changes = rawChanges.map((raw) => ({
|
|
218
|
+
path: raw.path,
|
|
219
|
+
severity: classifyChange(raw, resolvedNew),
|
|
220
|
+
category: raw.type,
|
|
221
|
+
message: formatChangeMessage(raw),
|
|
222
|
+
oldValue: raw.oldValue,
|
|
223
|
+
newValue: raw.newValue,
|
|
224
|
+
}));
|
|
225
|
+
// Calculate summary counts
|
|
226
|
+
const summary = {
|
|
227
|
+
breaking: changes.filter((c) => c.severity === 'breaking').length,
|
|
228
|
+
nonBreaking: changes.filter((c) => c.severity === 'non-breaking').length,
|
|
229
|
+
patch: changes.filter((c) => c.severity === 'patch').length,
|
|
230
|
+
unknown: changes.filter((c) => c.severity === 'unknown').length,
|
|
231
|
+
};
|
|
232
|
+
// Determine suggested semver bump based on highest severity
|
|
233
|
+
const suggestedBump = summary.breaking > 0
|
|
234
|
+
? 'major'
|
|
235
|
+
: summary.nonBreaking > 0
|
|
236
|
+
? 'minor'
|
|
237
|
+
: summary.patch > 0
|
|
238
|
+
? 'patch'
|
|
239
|
+
: 'none';
|
|
240
|
+
return {
|
|
241
|
+
contract: '',
|
|
242
|
+
changes,
|
|
243
|
+
summary,
|
|
244
|
+
suggestedBump,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Diff two JSON Schema objects and detect structural changes
|
|
249
|
+
*
|
|
250
|
+
* Like diffJsonSchema but accepts schema objects directly instead of file paths.
|
|
251
|
+
*
|
|
252
|
+
* @param oldSchema - The old/base schema object
|
|
253
|
+
* @param newSchema - The new/changed schema object
|
|
254
|
+
* @returns DiffResult with classified changes and suggested bump
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* ```typescript
|
|
258
|
+
* const oldSchema = { type: 'object', properties: { name: { type: 'string' } } };
|
|
259
|
+
* const newSchema = { type: 'object', properties: { name: { type: 'number' } } };
|
|
260
|
+
*
|
|
261
|
+
* const result = diffJsonSchemaObjects(oldSchema, newSchema);
|
|
262
|
+
* console.log(`Suggested bump: ${result.suggestedBump}`);
|
|
263
|
+
* ```
|
|
264
|
+
*/
|
|
265
|
+
export function diffJsonSchemaObjects(oldSchema, newSchema) {
|
|
266
|
+
// Resolve $refs in both schemas
|
|
267
|
+
const resolvedOldResult = resolveRefs(oldSchema);
|
|
268
|
+
const resolvedNewResult = resolveRefs(newSchema);
|
|
269
|
+
const resolvedOld = resolvedOldResult.schema;
|
|
270
|
+
const resolvedNew = resolvedNewResult.schema;
|
|
271
|
+
// Walk both schemas and detect raw changes
|
|
272
|
+
const rawChanges = walk(resolvedOld, resolvedNew, '');
|
|
273
|
+
// Classify changes and build final Change objects
|
|
274
|
+
const changes = rawChanges.map((raw) => ({
|
|
275
|
+
path: raw.path,
|
|
276
|
+
severity: classifyChange(raw, resolvedNew),
|
|
277
|
+
category: raw.type,
|
|
278
|
+
message: formatChangeMessage(raw),
|
|
279
|
+
oldValue: raw.oldValue,
|
|
280
|
+
newValue: raw.newValue,
|
|
281
|
+
}));
|
|
282
|
+
// Calculate summary counts
|
|
283
|
+
const summary = {
|
|
284
|
+
breaking: changes.filter((c) => c.severity === 'breaking').length,
|
|
285
|
+
nonBreaking: changes.filter((c) => c.severity === 'non-breaking').length,
|
|
286
|
+
patch: changes.filter((c) => c.severity === 'patch').length,
|
|
287
|
+
unknown: changes.filter((c) => c.severity === 'unknown').length,
|
|
288
|
+
};
|
|
289
|
+
// Determine suggested semver bump based on highest severity
|
|
290
|
+
const suggestedBump = summary.breaking > 0
|
|
291
|
+
? 'major'
|
|
292
|
+
: summary.nonBreaking > 0
|
|
293
|
+
? 'minor'
|
|
294
|
+
: summary.patch > 0
|
|
295
|
+
? 'patch'
|
|
296
|
+
: 'none';
|
|
297
|
+
return {
|
|
298
|
+
contract: '',
|
|
299
|
+
changes,
|
|
300
|
+
summary,
|
|
301
|
+
suggestedBump,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
//# sourceMappingURL=differ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"differ.js","sourceRoot":"","sources":["../src/differ.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAEnE;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAiB;IACnD,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,IAAI,GAAG,CAAC;IAEvC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,gBAAgB;YACnB,OAAO,qBAAqB,WAAW,EAAE,CAAC;QAE5C,KAAK,kBAAkB;YACrB,OAAO,uBAAuB,WAAW,EAAE,CAAC;QAE9C,KAAK,gBAAgB;YACnB,OAAO,UAAU,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,WAAW,EAAE,CAAC;QAE/F,KAAK,kBAAkB;YACrB,OAAO,UAAU,uBAAuB,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,WAAW,EAAE,CAAC;QAE/F,KAAK,cAAc;YACjB,OAAO,qBAAqB,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,EAAE,CAAC;QAElH,KAAK,eAAe;YAClB,OAAO,sBAAsB,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,EAAE,CAAC;QAEnH,KAAK,cAAc;YACjB,OAAO,qBAAqB,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,EAAE,CAAC;QAElH,KAAK,kBAAkB;YACrB,OAAO,cAAc,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,WAAW,EAAE,CAAC;QAE9E,KAAK,oBAAoB;YACvB,OAAO,cAAc,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,WAAW,EAAE,CAAC;QAEhF,KAAK,YAAY;YACf,OAAO,4BAA4B,WAAW,EAAE,CAAC;QAEnD,KAAK,cAAc;YACjB,OAAO,8BAA8B,WAAW,EAAE,CAAC;QAErD,KAAK,sBAAsB;YACzB,OAAO,uBAAuB,CAAC,MAAM,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QAEnE,KAAK,qBAAqB;YACxB,OAAO,uBAAuB,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;QAElE,KAAK,cAAc;YACjB,OAAO,WAAW,MAAM,CAAC,QAAQ,cAAc,WAAW,EAAE,CAAC;QAE/D,KAAK,gBAAgB;YACnB,OAAO,WAAW,MAAM,CAAC,QAAQ,gBAAgB,WAAW,EAAE,CAAC;QAEjE,KAAK,gBAAgB;YACnB,OAAO,wBAAwB,MAAM,CAAC,QAAQ,SAAS,MAAM,CAAC,QAAQ,QAAQ,WAAW,EAAE,CAAC;QAE9F,KAAK,8BAA8B;YACjC,OAAO,mCAAmC,WAAW,EAAE,CAAC;QAE1D,KAAK,+BAA+B;YAClC,OAAO,oCAAoC,WAAW,EAAE,CAAC;QAE3D,KAAK,+BAA+B;YAClC,OAAO,2CAA2C,WAAW,EAAE,CAAC;QAElE,KAAK,eAAe;YAClB,OAAO,iCAAiC,WAAW,EAAE,CAAC;QAExD,KAAK,qBAAqB;YACxB,OAAO,gCAAgC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,EAAE,CAAC;QAE7H,KAAK,qBAAqB;YACxB,OAAO,gCAAgC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,WAAW,EAAE,CAAC;QAE7H,KAAK,qBAAqB;YACxB,OAAO,8CAA8C,WAAW,EAAE,CAAC;QAErE,KAAK,oBAAoB;YACvB,OAAO,+BAA+B,WAAW,EAAE,CAAC;QAEtD,KAAK,qBAAqB;YACxB,OAAO,0BAA0B,WAAW,EAAE,CAAC;QAEjD,KAAK,eAAe;YAClB,OAAO,oBAAoB,WAAW,EAAE,CAAC;QAE3C,KAAK,iBAAiB;YACpB,OAAO,4BAA4B,WAAW,EAAE,CAAC;QAEnD,KAAK,kBAAkB;YACrB,OAAO,uBAAuB,WAAW,EAAE,CAAC;QAE9C,KAAK,gBAAgB,CAAC;QACtB;YACE,OAAO,qBAAqB,WAAW,EAAE,CAAC;IAC9C,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,WAAW,CAAC,KAAc;IACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,KAAK,GAAG,CAAC;IACtB,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAAC,KAAc;IAC7C,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,SAAS,uBAAuB,CAC9B,MAAiB,EACjB,SAAmC,EACnC,WAAmB;IAEnB,MAAM,cAAc,GAAG,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACrF,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAErF,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,eAAe,cAAc,KAAK,SAAS,SAAS,MAAM,OAAO,MAAM,OAAO,WAAW,EAAE,CAAC;IACrG,CAAC;IAED,OAAO,cAAc,SAAS,SAAS,MAAM,OAAO,MAAM,OAAO,WAAW,EAAE,CAAC;AACjF,CAAC;AAED;;GAEG;AACH,SAAS,qBAAqB,CAAC,IAAY;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACjC,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClD,IAAI,WAAW,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;QACtC,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,cAAc,CAAC,MAAiB,EAAE,SAAkB;IAC3D,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QACrC,OAAO,qBAAqB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAe,EAAE,OAAe;IACnE,yBAAyB;IACzB,IAAI,UAAkB,CAAC;IACvB,IAAI,UAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,mCAAmC,OAAO,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACzG,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,mCAAmC,OAAO,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CACzG,CAAC;IACJ,CAAC;IAED,aAAa;IACb,IAAI,SAAkB,CAAC;IACvB,IAAI,SAAkB,CAAC;IAEvB,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,uCAAuC,OAAO,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7G,CAAC;IACJ,CAAC;IAED,IAAI,CAAC;QACH,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,uCAAuC,OAAO,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7G,CAAC;IACJ,CAAC;IAED,gCAAgC;IAChC,MAAM,iBAAiB,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,iBAAiB,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAEjD,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAC7C,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAE7C,iDAAiD;IACjD,MAAM,WAAW,GAAG;QAClB,GAAG,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;QACtD,GAAG,iBAAiB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;KACvD,CAAC;IAEF,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,8DAA8D;QAC9D,8DAA8D;IAChE,CAAC;IAED,2CAA2C;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAEtD,kDAAkD;IAClD,MAAM,OAAO,GAAa,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACjD,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,QAAQ,EAAE,cAAc,CAAC,GAAG,EAAE,WAAW,CAAC;QAC1C,QAAQ,EAAE,GAAG,CAAC,IAAI;QAClB,OAAO,EAAE,mBAAmB,CAAC,GAAG,CAAC;QACjC,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAC,CAAC,CAAC;IAEJ,2BAA2B;IAC3B,MAAM,OAAO,GAAG;QACd,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;QACjE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,CAAC,MAAM;QACxE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM;QAC3D,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM;KAChE,CAAC;IAEF,4DAA4D;IAC5D,MAAM,aAAa,GACjB,OAAO,CAAC,QAAQ,GAAG,CAAC;QAClB,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,OAAO,CAAC,WAAW,GAAG,CAAC;YACvB,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC;gBACjB,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,MAAM,CAAC;IAEjB,OAAO;QACL,QAAQ,EAAE,EAAE;QACZ,OAAO;QACP,OAAO;QACP,aAAa;KACd,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAkB,EAAE,SAAkB;IAC1E,gCAAgC;IAChC,MAAM,iBAAiB,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IACjD,MAAM,iBAAiB,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;IAEjD,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAC7C,MAAM,WAAW,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAE7C,2CAA2C;IAC3C,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,EAAE,CAAC,CAAC;IAEtD,kDAAkD;IAClD,MAAM,OAAO,GAAa,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACjD,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,QAAQ,EAAE,cAAc,CAAC,GAAG,EAAE,WAAW,CAAC;QAC1C,QAAQ,EAAE,GAAG,CAAC,IAAI;QAClB,OAAO,EAAE,mBAAmB,CAAC,GAAG,CAAC;QACjC,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAC,CAAC,CAAC;IAEJ,2BAA2B;IAC3B,MAAM,OAAO,GAAG;QACd,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC,CAAC,MAAM;QACjE,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,cAAc,CAAC,CAAC,MAAM;QACxE,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM;QAC3D,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,MAAM;KAChE,CAAC;IAEF,4DAA4D;IAC5D,MAAM,aAAa,GACjB,OAAO,CAAC,QAAQ,GAAG,CAAC;QAClB,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,OAAO,CAAC,WAAW,GAAG,CAAC;YACvB,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,OAAO,CAAC,KAAK,GAAG,CAAC;gBACjB,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,MAAM,CAAC;IAEjB,OAAO;QACL,QAAQ,EAAE,EAAE;QACZ,OAAO;QACP,OAAO;QACP,aAAa;KACd,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @contractual/differs.json-schema
|
|
3
|
+
*
|
|
4
|
+
* Detect and classify breaking changes between JSON Schema versions.
|
|
5
|
+
*
|
|
6
|
+
* This package provides tools to compare JSON Schema documents and determine
|
|
7
|
+
* the semantic versioning impact of changes. It identifies breaking changes
|
|
8
|
+
* (major), non-breaking additions (minor), and documentation changes (patch).
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { compareSchemas } from '@contractual/differs.json-schema';
|
|
13
|
+
*
|
|
14
|
+
* const result = compareSchemas(oldSchema, newSchema, { currentVersion: '1.0.0' });
|
|
15
|
+
* console.log(result.version); // 'major' | 'minor' | 'patch' | 'equal' | null
|
|
16
|
+
* console.log(result.newVersion); // { major: 2, minor: 0, patch: 0, version: '2.0.0' }
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { diffJsonSchema, diffJsonSchemaObjects } from '@contractual/differs.json-schema';
|
|
22
|
+
*
|
|
23
|
+
* // Compare files
|
|
24
|
+
* const result = await diffJsonSchema('v1/schema.json', 'v2/schema.json');
|
|
25
|
+
* console.log(result.suggestedBump); // 'major' | 'minor' | 'patch' | 'none'
|
|
26
|
+
*
|
|
27
|
+
* // Compare schema objects directly
|
|
28
|
+
* const result2 = diffJsonSchemaObjects(oldSchema, newSchema);
|
|
29
|
+
* for (const change of result2.changes) {
|
|
30
|
+
* console.log(`[${change.severity}] ${change.message}`);
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @packageDocumentation
|
|
35
|
+
*/
|
|
36
|
+
export { compareSchemas, checkCompatibility } from './compare.js';
|
|
37
|
+
export { diffJsonSchema, diffJsonSchemaObjects, formatChangeMessage } from './differ.js';
|
|
38
|
+
export { classify, classifyPropertyAdded, classifyAll, CLASSIFICATION_SETS, } from './classifiers.js';
|
|
39
|
+
export { resolveRefs, hasUnresolvedRefs, extractRefs, validateRefs, type ResolveResult, } from './ref-resolver.js';
|
|
40
|
+
export { walk } from './walker.js';
|
|
41
|
+
export type { CompareResult, CompareOptions, StrandsTrace, StrandsCompatibility, StrandsVersion, SemanticVersion, JsonSchemaDraft, } from './types.js';
|
|
42
|
+
export type { ResolvedSchema, JSONSchemaType, NormalizedType, ConstraintKey, ConstraintDirection, CompositionKeyword, MetadataKey, AnnotationKey, ContentKey, WalkerContext, } from './types.js';
|
|
43
|
+
export { isSchemaObject, isSchemaArray, normalizeType, arraysEqual, deepEqual, escapeJsonPointer, joinPath, CONSTRAINT_KEYS, CONSTRAINT_DIRECTION, COMPOSITION_KEYWORDS, METADATA_KEYS, ANNOTATION_KEYS, CONTENT_KEYS, DEFAULT_MAX_DEPTH, } from './types.js';
|
|
44
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAMH,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAMlE,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAMzF,OAAO,EACL,QAAQ,EACR,qBAAqB,EACrB,WAAW,EACX,mBAAmB,GACpB,MAAM,kBAAkB,CAAC;AAM1B,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,KAAK,aAAa,GACnB,MAAM,mBAAmB,CAAC;AAM3B,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAMnC,YAAY,EACV,aAAa,EACb,cAAc,EACd,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAMpB,YAAY,EACV,cAAc,EACd,cAAc,EACd,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,UAAU,EACV,aAAa,GACd,MAAM,YAAY,CAAC;AAMpB,OAAO,EACL,cAAc,EACd,aAAa,EACb,aAAa,EACb,WAAW,EACX,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,eAAe,EACf,oBAAoB,EACpB,oBAAoB,EACpB,aAAa,EACb,eAAe,EACf,YAAY,EACZ,iBAAiB,GAClB,MAAM,YAAY,CAAC"}
|