@contractual/differs.core 0.1.0-dev.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (C) 2025 Omer Morad
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Result assembly — classify, format, summarize, and compute suggestedBump
3
+ */
4
+ import type { RawChange, ChangeSeverity, DiffResult } from '@contractual/types';
5
+ export interface AssembleOptions {
6
+ contract?: string;
7
+ classifyFn?: (change: RawChange, newSchema: unknown) => ChangeSeverity;
8
+ }
9
+ export declare function assembleResult(rawChanges: RawChange[], resolvedNewSchema: unknown, options?: AssembleOptions): DiffResult;
10
+ //# sourceMappingURL=assemble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.d.ts","sourceRoot":"","sources":["../src/assemble.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAU,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAIxF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,KAAK,cAAc,CAAC;CACxE;AAED,wBAAgB,cAAc,CAC5B,UAAU,EAAE,SAAS,EAAE,EACvB,iBAAiB,EAAE,OAAO,EAC1B,OAAO,CAAC,EAAE,eAAe,GACxB,UAAU,CAkCZ"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Result assembly — classify, format, summarize, and compute suggestedBump
3
+ */
4
+ import { classify, classifyPropertyAdded } from './classifiers.js';
5
+ import { formatChangeMessage } from './format.js';
6
+ export function assembleResult(rawChanges, resolvedNewSchema, options) {
7
+ const classifyChange = options?.classifyFn ?? defaultClassify;
8
+ const changes = rawChanges.map((raw) => ({
9
+ path: raw.path,
10
+ severity: classifyChange(raw, resolvedNewSchema),
11
+ category: raw.type,
12
+ message: formatChangeMessage(raw),
13
+ oldValue: raw.oldValue,
14
+ newValue: raw.newValue,
15
+ }));
16
+ const summary = {
17
+ breaking: changes.filter((c) => c.severity === 'breaking').length,
18
+ nonBreaking: changes.filter((c) => c.severity === 'non-breaking').length,
19
+ patch: changes.filter((c) => c.severity === 'patch').length,
20
+ unknown: changes.filter((c) => c.severity === 'unknown').length,
21
+ };
22
+ const suggestedBump = summary.breaking > 0
23
+ ? 'major'
24
+ : summary.nonBreaking > 0
25
+ ? 'minor'
26
+ : summary.patch > 0
27
+ ? 'patch'
28
+ : 'none';
29
+ return {
30
+ contract: options?.contract ?? '',
31
+ changes,
32
+ summary,
33
+ suggestedBump,
34
+ };
35
+ }
36
+ function defaultClassify(change, newSchema) {
37
+ if (change.type === 'property-added') {
38
+ return classifyPropertyAdded(change, newSchema);
39
+ }
40
+ return classify(change);
41
+ }
42
+ //# sourceMappingURL=assemble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assemble.js","sourceRoot":"","sources":["../src/assemble.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAOlD,MAAM,UAAU,cAAc,CAC5B,UAAuB,EACvB,iBAA0B,EAC1B,OAAyB;IAEzB,MAAM,cAAc,GAAG,OAAO,EAAE,UAAU,IAAI,eAAe,CAAC;IAE9D,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,iBAAiB,CAAC;QAChD,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,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,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,OAAO,EAAE,QAAQ,IAAI,EAAE;QACjC,OAAO;QACP,OAAO;QACP,aAAa;KACd,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,MAAiB,EAAE,SAAkB;IAC5D,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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * JSON Schema Change Classifiers
3
+ *
4
+ * Classifies raw structural changes into severity levels for semantic versioning.
5
+ * Follows API compatibility principles where breaking changes require major bumps,
6
+ * additive changes require minor bumps, and metadata changes are patches.
7
+ */
8
+ import type { RawChange, ChangeType, ChangeSeverity } from '@contractual/types';
9
+ /**
10
+ * Export classification sets for external analysis
11
+ */
12
+ export declare const CLASSIFICATION_SETS: {
13
+ readonly breaking: ReadonlySet<ChangeType>;
14
+ readonly nonBreaking: ReadonlySet<ChangeType>;
15
+ readonly patch: ReadonlySet<ChangeType>;
16
+ readonly unknown: ReadonlySet<ChangeType>;
17
+ };
18
+ /**
19
+ * Classify a raw change into a severity level
20
+ *
21
+ * Uses the Strands API classification rules where:
22
+ * - Breaking changes require major version bump
23
+ * - Non-breaking changes require minor version bump
24
+ * - Patch changes are metadata/annotation only
25
+ * - Unknown changes require manual review
26
+ *
27
+ * @param change - The raw change to classify
28
+ * @returns The severity classification
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * const change: RawChange = {
33
+ * path: '/properties/name',
34
+ * type: 'property-removed',
35
+ * oldValue: { type: 'string' },
36
+ * };
37
+ * const severity = classify(change);
38
+ * // severity === 'breaking'
39
+ * ```
40
+ */
41
+ export declare function classify(change: RawChange): ChangeSeverity;
42
+ /**
43
+ * Classify a property-added change with schema context
44
+ *
45
+ * Property additions are breaking if the property is required,
46
+ * otherwise they are non-breaking (additive).
47
+ *
48
+ * @param change - The property-added change
49
+ * @param newSchema - The new schema for context (to check required array)
50
+ * @returns The severity classification
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const change: RawChange = {
55
+ * path: '/properties/email',
56
+ * type: 'property-added',
57
+ * newValue: { type: 'string', format: 'email' },
58
+ * };
59
+ *
60
+ * const schema = {
61
+ * type: 'object',
62
+ * properties: { email: { type: 'string', format: 'email' } },
63
+ * required: ['email'], // email is required!
64
+ * };
65
+ *
66
+ * const severity = classifyPropertyAdded(change, schema);
67
+ * // severity === 'breaking' (because email is in required[])
68
+ * ```
69
+ */
70
+ export declare function classifyPropertyAdded(change: RawChange, newSchema: unknown): ChangeSeverity;
71
+ /**
72
+ * Batch classify multiple changes
73
+ *
74
+ * @param changes - Array of raw changes
75
+ * @param newSchema - Optional schema for context-aware classification
76
+ * @returns Map of change to severity
77
+ */
78
+ export declare function classifyAll(changes: readonly RawChange[], newSchema?: unknown): Map<RawChange, ChangeSeverity>;
79
+ /**
80
+ * Get a human-readable message for a classified change
81
+ *
82
+ * @param change - The raw change
83
+ * @param severity - The classified severity
84
+ * @returns Human-readable description
85
+ */
86
+ export declare function getChangeMessage(change: RawChange, severity: ChangeSeverity): string;
87
+ //# sourceMappingURL=classifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifiers.d.ts","sourceRoot":"","sources":["../src/classifiers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AA+IhF;;GAEG;AACH,eAAO,MAAM,mBAAmB;;;;;CAKtB,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,cAAc,CAsB1D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,GAAG,cAAc,CA6B3F;AAgGD;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,SAAS,SAAS,EAAE,EAC7B,SAAS,CAAC,EAAE,OAAO,GAClB,GAAG,CAAC,SAAS,EAAE,cAAc,CAAC,CAYhC;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,GAAG,MAAM,CAapF"}
@@ -0,0 +1,375 @@
1
+ /**
2
+ * JSON Schema Change Classifiers
3
+ *
4
+ * Classifies raw structural changes into severity levels for semantic versioning.
5
+ * Follows API compatibility principles where breaking changes require major bumps,
6
+ * additive changes require minor bumps, and metadata changes are patches.
7
+ */
8
+ /**
9
+ * Change types that are always breaking (require major version bump)
10
+ *
11
+ * These changes can break existing consumers:
12
+ * - Removing properties removes data they may depend on
13
+ * - Adding required fields forces consumers to provide new data
14
+ * - Type changes can break parsing/validation
15
+ * - Enum removals can invalidate existing data
16
+ * - Tightened constraints can reject previously valid data
17
+ * - Composition option additions can change validation semantics
18
+ *
19
+ * Aligned with Strands API classification rules.
20
+ */
21
+ const BREAKING_CHANGES = new Set([
22
+ 'property-removed',
23
+ 'required-added',
24
+ 'type-changed',
25
+ 'type-narrowed',
26
+ 'enum-value-removed',
27
+ 'enum-added',
28
+ 'constraint-tightened',
29
+ 'additional-properties-denied',
30
+ 'items-changed',
31
+ 'min-items-increased',
32
+ 'max-items-decreased',
33
+ 'ref-target-changed',
34
+ 'dependent-required-added',
35
+ // Composition breaking changes (per Strands API)
36
+ 'anyof-option-added',
37
+ 'oneof-option-added',
38
+ 'allof-member-added',
39
+ 'not-schema-changed',
40
+ // OpenAPI structural breaking changes
41
+ 'path-removed',
42
+ 'operation-removed',
43
+ 'parameter-added',
44
+ 'parameter-removed',
45
+ 'parameter-required-changed',
46
+ 'request-body-added',
47
+ 'response-removed',
48
+ 'security-changed',
49
+ ]);
50
+ /**
51
+ * Change types that are non-breaking (require minor version bump)
52
+ *
53
+ * These changes are backward compatible additions/relaxations:
54
+ * - Adding optional properties extends the schema without breaking
55
+ * - Removing required constraints makes the schema more permissive
56
+ * - Type widening accepts more values
57
+ * - Loosened constraints accept more values
58
+ * - Composition option removals make schema less restrictive
59
+ *
60
+ * Aligned with Strands API classification rules.
61
+ */
62
+ const NON_BREAKING_CHANGES = new Set([
63
+ 'property-added',
64
+ 'required-removed',
65
+ 'type-widened',
66
+ 'enum-value-added',
67
+ 'enum-removed',
68
+ 'constraint-loosened',
69
+ 'additional-properties-allowed',
70
+ 'additional-properties-changed',
71
+ 'dependent-required-removed',
72
+ // Composition non-breaking changes (per Strands API)
73
+ 'anyof-option-removed',
74
+ 'oneof-option-removed',
75
+ 'allof-member-removed',
76
+ // OpenAPI structural non-breaking changes
77
+ 'path-added',
78
+ 'operation-added',
79
+ 'request-body-removed',
80
+ 'response-added',
81
+ 'server-changed',
82
+ ]);
83
+ /**
84
+ * Change types that are patches (documentation/metadata only)
85
+ *
86
+ * These changes don't affect validation behavior:
87
+ * - Description changes are documentation only
88
+ * - Title changes are display metadata
89
+ * - Default/example changes don't affect validation
90
+ * - Format is an annotation (per Strands API) - doesn't affect validation
91
+ * - Annotation keywords (deprecated, readOnly, writeOnly)
92
+ * - Content keywords (contentEncoding, contentMediaType, contentSchema)
93
+ *
94
+ * Aligned with Strands API classification rules.
95
+ */
96
+ const PATCH_CHANGES = new Set([
97
+ // Metadata changes
98
+ 'description-changed',
99
+ 'title-changed',
100
+ 'default-changed',
101
+ 'examples-changed',
102
+ // Format is annotation (patch per Strands API)
103
+ 'format-added',
104
+ 'format-removed',
105
+ 'format-changed',
106
+ // Annotation keywords (Draft 2019-09+)
107
+ 'deprecated-changed',
108
+ 'read-only-changed',
109
+ 'write-only-changed',
110
+ // Content keywords
111
+ 'content-encoding-changed',
112
+ 'content-media-type-changed',
113
+ 'content-schema-changed',
114
+ ]);
115
+ /**
116
+ * Change types that require manual review
117
+ *
118
+ * These changes are too complex to classify automatically:
119
+ * - Generic composition changes require semantic analysis
120
+ * - Complex keywords (propertyNames, dependentSchemas, unevaluated*)
121
+ * - Conditional schema changes (if/then/else)
122
+ * - Unknown changes need human evaluation
123
+ *
124
+ * Aligned with Strands API classification rules.
125
+ */
126
+ const UNKNOWN_CHANGES = new Set([
127
+ // Complex object keywords
128
+ 'property-names-changed',
129
+ 'dependent-schemas-changed',
130
+ 'unevaluated-properties-changed',
131
+ // Complex array keywords
132
+ 'unevaluated-items-changed',
133
+ 'min-contains-changed',
134
+ 'max-contains-changed',
135
+ // Conditional schema
136
+ 'if-then-else-changed',
137
+ // Legacy/generic composition
138
+ 'composition-changed',
139
+ // OpenAPI context-dependent (schema-level changes handled by walker)
140
+ 'parameter-schema-changed',
141
+ 'response-schema-changed',
142
+ // Catch-all
143
+ 'unknown-change',
144
+ ]);
145
+ /**
146
+ * Export classification sets for external analysis
147
+ */
148
+ export const CLASSIFICATION_SETS = {
149
+ breaking: BREAKING_CHANGES,
150
+ nonBreaking: NON_BREAKING_CHANGES,
151
+ patch: PATCH_CHANGES,
152
+ unknown: UNKNOWN_CHANGES,
153
+ };
154
+ /**
155
+ * Classify a raw change into a severity level
156
+ *
157
+ * Uses the Strands API classification rules where:
158
+ * - Breaking changes require major version bump
159
+ * - Non-breaking changes require minor version bump
160
+ * - Patch changes are metadata/annotation only
161
+ * - Unknown changes require manual review
162
+ *
163
+ * @param change - The raw change to classify
164
+ * @returns The severity classification
165
+ *
166
+ * @example
167
+ * ```typescript
168
+ * const change: RawChange = {
169
+ * path: '/properties/name',
170
+ * type: 'property-removed',
171
+ * oldValue: { type: 'string' },
172
+ * };
173
+ * const severity = classify(change);
174
+ * // severity === 'breaking'
175
+ * ```
176
+ */
177
+ export function classify(change) {
178
+ const { type } = change;
179
+ // Check each category in order of specificity
180
+ if (BREAKING_CHANGES.has(type)) {
181
+ return 'breaking';
182
+ }
183
+ if (NON_BREAKING_CHANGES.has(type)) {
184
+ return 'non-breaking';
185
+ }
186
+ if (PATCH_CHANGES.has(type)) {
187
+ return 'patch';
188
+ }
189
+ if (UNKNOWN_CHANGES.has(type)) {
190
+ return 'unknown';
191
+ }
192
+ // Defensive: any unhandled type is unknown
193
+ return 'unknown';
194
+ }
195
+ /**
196
+ * Classify a property-added change with schema context
197
+ *
198
+ * Property additions are breaking if the property is required,
199
+ * otherwise they are non-breaking (additive).
200
+ *
201
+ * @param change - The property-added change
202
+ * @param newSchema - The new schema for context (to check required array)
203
+ * @returns The severity classification
204
+ *
205
+ * @example
206
+ * ```typescript
207
+ * const change: RawChange = {
208
+ * path: '/properties/email',
209
+ * type: 'property-added',
210
+ * newValue: { type: 'string', format: 'email' },
211
+ * };
212
+ *
213
+ * const schema = {
214
+ * type: 'object',
215
+ * properties: { email: { type: 'string', format: 'email' } },
216
+ * required: ['email'], // email is required!
217
+ * };
218
+ *
219
+ * const severity = classifyPropertyAdded(change, schema);
220
+ * // severity === 'breaking' (because email is in required[])
221
+ * ```
222
+ */
223
+ export function classifyPropertyAdded(change, newSchema) {
224
+ // Validate change type
225
+ if (change.type !== 'property-added') {
226
+ return classify(change);
227
+ }
228
+ // Extract property name from path
229
+ const propertyName = extractPropertyName(change.path);
230
+ if (!propertyName) {
231
+ // Cannot determine property name, fall back to non-breaking
232
+ return 'non-breaking';
233
+ }
234
+ // Find the parent schema containing this property
235
+ const parentSchema = findParentSchema(change.path, newSchema);
236
+ if (!parentSchema) {
237
+ // Cannot find parent schema, fall back to non-breaking
238
+ return 'non-breaking';
239
+ }
240
+ // Check if property is in the required array
241
+ const required = getRequiredArray(parentSchema);
242
+ if (required.includes(propertyName)) {
243
+ // Adding a required property is breaking
244
+ return 'breaking';
245
+ }
246
+ // Adding an optional property is non-breaking
247
+ return 'non-breaking';
248
+ }
249
+ /**
250
+ * Extract the property name from a JSON Pointer path
251
+ *
252
+ * @param path - JSON Pointer path (e.g., '/properties/name' or '/properties/user/properties/email')
253
+ * @returns The property name or null if not found
254
+ */
255
+ function extractPropertyName(path) {
256
+ // Match the last /properties/NAME segment
257
+ const match = path.match(/\/properties\/([^/]+)$/);
258
+ if (match?.[1] !== undefined) {
259
+ return decodeJsonPointerSegment(match[1]);
260
+ }
261
+ return null;
262
+ }
263
+ /**
264
+ * Decode a JSON Pointer segment (handles ~0 and ~1 escapes)
265
+ *
266
+ * @param segment - The encoded segment
267
+ * @returns The decoded segment
268
+ */
269
+ function decodeJsonPointerSegment(segment) {
270
+ return segment.replace(/~1/g, '/').replace(/~0/g, '~');
271
+ }
272
+ /**
273
+ * Find the parent schema containing a property
274
+ *
275
+ * @param path - JSON Pointer path to the property
276
+ * @param schema - The root schema
277
+ * @returns The parent schema or null if not found
278
+ */
279
+ function findParentSchema(path, schema) {
280
+ if (!isObject(schema)) {
281
+ return null;
282
+ }
283
+ // Remove the last segment to get parent path
284
+ // e.g., '/properties/name' -> '' (root)
285
+ // e.g., '/properties/user/properties/email' -> '/properties/user'
286
+ const segments = path.split('/').filter(Boolean);
287
+ // We need to navigate to the schema containing /properties/NAME
288
+ // So we remove 'properties' and 'NAME' from the end
289
+ if (segments.length < 2) {
290
+ // Path is too short, parent is root
291
+ return schema;
292
+ }
293
+ // Remove 'NAME' and 'properties' from the end
294
+ const parentSegments = segments.slice(0, -2);
295
+ // Navigate to parent
296
+ let current = schema;
297
+ for (const segment of parentSegments) {
298
+ if (!isObject(current)) {
299
+ return null;
300
+ }
301
+ const decoded = decodeJsonPointerSegment(segment);
302
+ current = current[decoded];
303
+ }
304
+ return current;
305
+ }
306
+ /**
307
+ * Get the required array from a schema object
308
+ *
309
+ * @param schema - The schema object
310
+ * @returns Array of required property names
311
+ */
312
+ function getRequiredArray(schema) {
313
+ if (!isObject(schema)) {
314
+ return [];
315
+ }
316
+ const obj = schema;
317
+ const required = obj['required'];
318
+ if (!Array.isArray(required)) {
319
+ return [];
320
+ }
321
+ return required.filter((item) => typeof item === 'string');
322
+ }
323
+ /**
324
+ * Type guard for objects
325
+ */
326
+ function isObject(value) {
327
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
328
+ }
329
+ /**
330
+ * Batch classify multiple changes
331
+ *
332
+ * @param changes - Array of raw changes
333
+ * @param newSchema - Optional schema for context-aware classification
334
+ * @returns Map of change to severity
335
+ */
336
+ export function classifyAll(changes, newSchema) {
337
+ const results = new Map();
338
+ for (const change of changes) {
339
+ if (change.type === 'property-added' && newSchema !== undefined) {
340
+ results.set(change, classifyPropertyAdded(change, newSchema));
341
+ }
342
+ else {
343
+ results.set(change, classify(change));
344
+ }
345
+ }
346
+ return results;
347
+ }
348
+ /**
349
+ * Get a human-readable message for a classified change
350
+ *
351
+ * @param change - The raw change
352
+ * @param severity - The classified severity
353
+ * @returns Human-readable description
354
+ */
355
+ export function getChangeMessage(change, severity) {
356
+ const severityLabel = severity === 'breaking'
357
+ ? 'BREAKING'
358
+ : severity === 'non-breaking'
359
+ ? 'Non-breaking'
360
+ : severity === 'patch'
361
+ ? 'Patch'
362
+ : 'Unknown';
363
+ const typeLabel = formatChangeType(change.type);
364
+ return `[${severityLabel}] ${typeLabel} at ${change.path}`;
365
+ }
366
+ /**
367
+ * Format a change type into a human-readable label
368
+ */
369
+ function formatChangeType(type) {
370
+ return type
371
+ .split('-')
372
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
373
+ .join(' ');
374
+ }
375
+ //# sourceMappingURL=classifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifiers.js","sourceRoot":"","sources":["../src/classifiers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Human-readable message formatting for raw changes
3
+ */
4
+ import type { RawChange } from '@contractual/types';
5
+ /**
6
+ * Format a human-readable message for a change
7
+ *
8
+ * @param change - The raw change to format
9
+ * @returns Human-readable message describing the change
10
+ */
11
+ export declare function formatChangeMessage(change: RawChange): string;
12
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CA4F7D"}