@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.
@@ -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"}
@@ -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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"}
@@ -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":"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"}
@@ -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"}