@openpkg-ts/spec 0.3.1 → 0.4.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/dist/index.d.ts CHANGED
@@ -123,6 +123,21 @@ type OpenPkg = {
123
123
  extensions?: SpecExtension;
124
124
  };
125
125
  declare function dereference(spec: OpenPkg): OpenPkg;
126
+ type BreakingSeverity = "high" | "medium" | "low";
127
+ interface CategorizedBreaking {
128
+ id: string;
129
+ name: string;
130
+ kind: SpecExportKind;
131
+ severity: BreakingSeverity;
132
+ reason: string;
133
+ }
134
+ /** Minimal member change info for categorization (avoids circular dep with SDK) */
135
+ interface MemberChangeInfo {
136
+ className: string;
137
+ memberName: string;
138
+ memberKind: "method" | "property" | "accessor" | "constructor";
139
+ changeType: "added" | "removed" | "signature-changed";
140
+ }
126
141
  type SpecDiff = {
127
142
  breaking: string[];
128
143
  nonBreaking: string[];
@@ -137,6 +152,16 @@ type SpecDiff = {
137
152
  driftResolved: number;
138
153
  };
139
154
  declare function diffSpec(oldSpec: OpenPkg, newSpec: OpenPkg): SpecDiff;
155
+ /**
156
+ * Categorize breaking changes by severity
157
+ *
158
+ * @param breaking - Array of breaking change IDs
159
+ * @param oldSpec - Previous spec version
160
+ * @param newSpec - Current spec version
161
+ * @param memberChanges - Optional member-level changes for classes
162
+ * @returns Categorized breaking changes sorted by severity (high first)
163
+ */
164
+ declare function categorizeBreakingChanges(breaking: string[], oldSpec: OpenPkg, newSpec: OpenPkg, memberChanges?: MemberChangeInfo[]): CategorizedBreaking[];
140
165
  declare function normalize(spec: OpenPkg): OpenPkg;
141
166
  type SpecError = {
142
167
  instancePath: string;
@@ -151,4 +176,4 @@ declare function validateSpec(spec: unknown): {
151
176
  };
152
177
  declare function assertSpec(spec: unknown): asserts spec is OpenPkg;
153
178
  declare function getValidationErrors(spec: unknown): SpecError[];
154
- export { validateSpec, normalize, getValidationErrors, diffSpec, dereference, assertSpec, SpecVisibility, SpecTypeParameter, SpecTypeKind, SpecType, SpecTag, SpecSource, SpecSignatureReturn, SpecSignatureParameter, SpecSignature, SpecSchema, SpecMember, SpecExtension, SpecExportKind, SpecExport, SpecExample, SpecDocsMetadata, SpecDocSignal, SpecDocDrift, SCHEMA_VERSION, SCHEMA_URL, OpenPkgMeta, OpenPkg, JSON_SCHEMA_DRAFT };
179
+ export { validateSpec, normalize, getValidationErrors, diffSpec, dereference, categorizeBreakingChanges, assertSpec, SpecVisibility, SpecTypeParameter, SpecTypeKind, SpecType, SpecTag, SpecSource, SpecSignatureReturn, SpecSignatureParameter, SpecSignature, SpecSchema, SpecMember, SpecExtension, SpecExportKind, SpecExport, SpecExample, SpecDocsMetadata, SpecDocSignal, SpecDocDrift, SpecDiff, SCHEMA_VERSION, SCHEMA_URL, OpenPkgMeta, OpenPkg, MemberChangeInfo, JSON_SCHEMA_DRAFT, CategorizedBreaking, BreakingSeverity };
package/dist/index.js CHANGED
@@ -204,6 +204,70 @@ function sortKeys(value) {
204
204
  }
205
205
  return result;
206
206
  }
207
+ function categorizeBreakingChanges(breaking, oldSpec, newSpec, memberChanges) {
208
+ const oldExportMap = toExportMap(oldSpec.exports);
209
+ const newExportMap = toExportMap(newSpec.exports);
210
+ const categorized = [];
211
+ for (const id of breaking) {
212
+ const oldExport = oldExportMap.get(id);
213
+ const newExport = newExportMap.get(id);
214
+ if (!newExport) {
215
+ const kind = oldExport?.kind ?? "variable";
216
+ categorized.push({
217
+ id,
218
+ name: oldExport?.name ?? id,
219
+ kind,
220
+ severity: kind === "function" || kind === "class" ? "high" : "medium",
221
+ reason: "removed"
222
+ });
223
+ continue;
224
+ }
225
+ if (oldExport?.kind === "class" && memberChanges?.length) {
226
+ const classChanges = memberChanges.filter((mc) => mc.className === id);
227
+ if (classChanges.length > 0) {
228
+ const hasConstructorChange = classChanges.some((mc) => mc.memberKind === "constructor");
229
+ const hasMethodRemoval = classChanges.some((mc) => mc.changeType === "removed" && mc.memberKind === "method");
230
+ categorized.push({
231
+ id,
232
+ name: oldExport.name,
233
+ kind: "class",
234
+ severity: hasConstructorChange || hasMethodRemoval ? "high" : "medium",
235
+ reason: hasConstructorChange ? "constructor changed" : hasMethodRemoval ? "methods removed" : "methods changed"
236
+ });
237
+ continue;
238
+ }
239
+ }
240
+ if (oldExport?.kind === "interface" || oldExport?.kind === "type") {
241
+ categorized.push({
242
+ id,
243
+ name: oldExport.name,
244
+ kind: oldExport.kind,
245
+ severity: "medium",
246
+ reason: "type definition changed"
247
+ });
248
+ continue;
249
+ }
250
+ if (oldExport?.kind === "function") {
251
+ categorized.push({
252
+ id,
253
+ name: oldExport.name,
254
+ kind: "function",
255
+ severity: "high",
256
+ reason: "signature changed"
257
+ });
258
+ continue;
259
+ }
260
+ categorized.push({
261
+ id,
262
+ name: oldExport?.name ?? id,
263
+ kind: oldExport?.kind ?? "variable",
264
+ severity: "low",
265
+ reason: "changed"
266
+ });
267
+ }
268
+ const severityOrder = { high: 0, medium: 1, low: 2 };
269
+ return categorized.sort((a, b) => severityOrder[a.severity] - severityOrder[b.severity]);
270
+ }
207
271
  // src/normalize.ts
208
272
  var DEFAULT_ECOSYSTEM = "js/ts";
209
273
  var arrayFieldsByExport = ["signatures", "members", "examples", "tags"];
@@ -704,6 +768,7 @@ export {
704
768
  getValidationErrors,
705
769
  diffSpec,
706
770
  dereference,
771
+ categorizeBreakingChanges,
707
772
  assertSpec,
708
773
  SCHEMA_VERSION,
709
774
  SCHEMA_URL,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/spec",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Shared schema, validation, and diff utilities for OpenPkg specs",
5
5
  "keywords": [
6
6
  "openpkg",