@workos/oagen 0.14.0 → 0.14.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/dist/cli/index.mjs +138 -32
- package/dist/cli/index.mjs.map +1 -1
- package/dist/compat/index.mjs +1 -1
- package/dist/differ-fdF9qogA.d.mts.map +1 -1
- package/dist/{overlay-DGcHh1KN.mjs → overlay-Dlk7bV5R.mjs} +139 -33
- package/dist/overlay-Dlk7bV5R.mjs.map +1 -0
- package/dist/verify/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/overlay-DGcHh1KN.mjs.map +0 -1
package/dist/compat/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as safeParse$1, v as ExtractorError } from "../tree-sitter-DcUvm0KA.mjs";
|
|
2
|
-
import { A as getDefaultPolicy, C as defaultSeverityForCategory, D as isCompatibleSchemaVersion, E as COMPAT_SCHEMA_VERSION, O as validateSnapshot, S as summarizeChanges, T as apiSurfaceToSnapshot, _ as nodeHints, a as registerExtractor, b as classifyAddedSymbol, c as filterSurface, d as specDerivedHttpKeys, f as specDerivedMethodPaths, g as namedTypeWordsOverlap, h as defaultIsNullableOnlyDifference, i as getExtractor, j as mergePolicy, k as ALL_LANGUAGE_IDS, l as specDerivedEnumValues, m as NAMED_TYPE_RE, n as isPatchableChange, o as diffSnapshots, p as specDerivedNames, r as patchOverlay, s as diffSurfaces, t as buildOverlayLookup, u as specDerivedFieldPaths, v as resolveHints, w as severityMeetsThreshold, x as classifySymbolChanges, y as typeExistsInSurface } from "../overlay-
|
|
2
|
+
import { A as getDefaultPolicy, C as defaultSeverityForCategory, D as isCompatibleSchemaVersion, E as COMPAT_SCHEMA_VERSION, O as validateSnapshot, S as summarizeChanges, T as apiSurfaceToSnapshot, _ as nodeHints, a as registerExtractor, b as classifyAddedSymbol, c as filterSurface, d as specDerivedHttpKeys, f as specDerivedMethodPaths, g as namedTypeWordsOverlap, h as defaultIsNullableOnlyDifference, i as getExtractor, j as mergePolicy, k as ALL_LANGUAGE_IDS, l as specDerivedEnumValues, m as NAMED_TYPE_RE, n as isPatchableChange, o as diffSnapshots, p as specDerivedNames, r as patchOverlay, s as diffSurfaces, t as buildOverlayLookup, u as specDerivedFieldPaths, v as resolveHints, w as severityMeetsThreshold, x as classifySymbolChanges, y as typeExistsInSurface } from "../overlay-Dlk7bV5R.mjs";
|
|
3
3
|
import path, { basename, dirname, join, relative, resolve } from "node:path";
|
|
4
4
|
import Parser from "tree-sitter";
|
|
5
5
|
import ts from "typescript";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"differ-fdF9qogA.d.mts","names":[],"sources":["../src/compat/classify.ts","../src/compat/spec-filter.ts","../src/compat/differ.ts"],"mappings":";;;;UAaiB,gBAAA;EAcJ;EAZX,QAAA,EAAU,oBAAA;EAAV;EAEA,QAAA,EAAU,oBAAA;EAAV;EAEA,MAAA;EAAA;EAEA,kBAAA;EAEA;EAAA,UAAA,EAAY,gBAAA;EAEZ;EAAA,GAAA,EAAK,MAAA;EAEL;EAAA,GAAA,EAAK,MAAA;EAEL;EAAA,OAAA;EAOW;;AAIb;;;;EAJE,WAAA;AAAA;;UAIe,oBAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA;IACE,QAAA;IACA,QAAA;IACA,QAAA;EAAA;AAAA;;;;;iBAYY,qBAAA,CACd,QAAA,EAAU,YAAA,EACV,SAAA,EAAW,YAAA,cACX,MAAA,EAAQ,iBAAA,GACP,gBAAA;;;;iBAkUa,mBAAA,CAAoB,MAAA,EAAQ,YAAA,GAAe,gBAAA;;iBAc3C,gBAAA,CAAiB,OAAA,EAAS,gBAAA,KAAqB,oBAAA;;;;AAhY/D;;;;iBCJgB,gBAAA,CAAiB,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,aAAA,GAAgB,GAAA;;;;;iBA0CvD,qBAAA,CAAsB,IAAA,EAAM,OAAA,GAAU,GAAA,SAAY,GAAA;;;;;;iBAalD,qBAAA,CAAsB,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,aAAA,GAAgB,GAAA;;;;;;iBAqB5D,sBAAA,CAAuB,IAAA,EAAM,OAAA,GAAU,GAAA;;;;;iBAcvC,mBAAA,CAAoB,IAAA,EAAM,OAAA,GAAU,GAAA;;;;;;;;;;;;AD1CpD;;;;iBC8FgB,aAAA,CACd,OAAA,EAAS,UAAA,EACT,YAAA,EAAc,GAAA,UACd,IAAA;EACE,UAAA,GAAa,GAAA;EACb,WAAA,GAAc,GAAA;EACd,UAAA,GAAa,GAAA,SAAY,GAAA;AAAA,IAE1B,UAAA;;;;UChIc,gBAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA,EAAS,oBAAA;AAAA;;;;;iBAOK,aAAA,CACd,QAAA,EAAU,cAAA,EACV,SAAA,EAAW,cAAA,EACX,MAAA,GAAS,iBAAA,GACR,gBAAA;;;;;;AFJH;;;
|
|
1
|
+
{"version":3,"file":"differ-fdF9qogA.d.mts","names":[],"sources":["../src/compat/classify.ts","../src/compat/spec-filter.ts","../src/compat/differ.ts"],"mappings":";;;;UAaiB,gBAAA;EAcJ;EAZX,QAAA,EAAU,oBAAA;EAAV;EAEA,QAAA,EAAU,oBAAA;EAAV;EAEA,MAAA;EAAA;EAEA,kBAAA;EAEA;EAAA,UAAA,EAAY,gBAAA;EAEZ;EAAA,GAAA,EAAK,MAAA;EAEL;EAAA,GAAA,EAAK,MAAA;EAEL;EAAA,OAAA;EAOW;;AAIb;;;;EAJE,WAAA;AAAA;;UAIe,oBAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA;IACE,QAAA;IACA,QAAA;IACA,QAAA;EAAA;AAAA;;;;;iBAYY,qBAAA,CACd,QAAA,EAAU,YAAA,EACV,SAAA,EAAW,YAAA,cACX,MAAA,EAAQ,iBAAA,GACP,gBAAA;;;;iBAkUa,mBAAA,CAAoB,MAAA,EAAQ,YAAA,GAAe,gBAAA;;iBAc3C,gBAAA,CAAiB,OAAA,EAAS,gBAAA,KAAqB,oBAAA;;;;AAhY/D;;;;iBCJgB,gBAAA,CAAiB,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,aAAA,GAAgB,GAAA;;;;;iBA0CvD,qBAAA,CAAsB,IAAA,EAAM,OAAA,GAAU,GAAA,SAAY,GAAA;;;;;;iBAalD,qBAAA,CAAsB,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,aAAA,GAAgB,GAAA;;;;;;iBAqB5D,sBAAA,CAAuB,IAAA,EAAM,OAAA,GAAU,GAAA;;;;;iBAcvC,mBAAA,CAAoB,IAAA,EAAM,OAAA,GAAU,GAAA;;;;;;;;;;;;AD1CpD;;;;iBC8FgB,aAAA,CACd,OAAA,EAAS,UAAA,EACT,YAAA,EAAc,GAAA,UACd,IAAA;EACE,UAAA,GAAa,GAAA;EACb,WAAA,GAAc,GAAA;EACd,UAAA,GAAa,GAAA,SAAY,GAAA;AAAA,IAE1B,UAAA;;;;UChIc,gBAAA;EACf,OAAA,EAAS,gBAAA;EACT,OAAA,EAAS,oBAAA;AAAA;;;;;iBAOK,aAAA,CACd,QAAA,EAAU,cAAA,EACV,SAAA,EAAW,cAAA,EACX,MAAA,GAAS,iBAAA,GACR,gBAAA;;;;;;AFJH;;;iBEopBgB,YAAA,CAAa,QAAA,EAAU,UAAA,EAAY,SAAA,EAAW,UAAA,EAAY,KAAA,EAAO,aAAA,GAAgB,UAAA"}
|
|
@@ -913,7 +913,10 @@ function diffSnapshots(baseline, candidate, policy) {
|
|
|
913
913
|
changes.push(classifyAddedSymbol(candSym));
|
|
914
914
|
}
|
|
915
915
|
detectForkedSchemas(changes, baseline, candidate);
|
|
916
|
-
|
|
916
|
+
const typeRenames = detectTypeRenames(changes, baseline, candidate);
|
|
917
|
+
const enumRenames = detectEnumRenames(changes, baseline, candidate);
|
|
918
|
+
inferTransitiveTypeRenames(typeRenames, baseline, candidate, changes);
|
|
919
|
+
cascadeRenameDowngrades(changes, typeRenames, enumRenames, baseline, candidate);
|
|
917
920
|
return {
|
|
918
921
|
changes,
|
|
919
922
|
summary: summarizeChanges(changes)
|
|
@@ -1023,27 +1026,86 @@ function detectTypeRenames(changes, baseline, candidate) {
|
|
|
1023
1026
|
const candidateTypeFields = collectTypeFieldSets(candidate);
|
|
1024
1027
|
const baselineTypeNames = new Set(baselineTypeFields.keys());
|
|
1025
1028
|
const candidateTypesSorted = [...candidateTypeFields.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
if (!removedFields || removedFields.size === 0) continue;
|
|
1029
|
+
const parentRemovalByName = /* @__PURE__ */ new Map();
|
|
1030
|
+
for (const change of changes) if (change.category === "symbol_removed" && change.severity === "breaking") parentRemovalByName.set(change.old.symbol ?? "", change);
|
|
1031
|
+
for (const [baselineTypeName, baselineFields] of baselineTypeFields) {
|
|
1032
|
+
if (baselineFields.size === 0) continue;
|
|
1033
|
+
const sameNameFields = candidateTypeFields.get(baselineTypeName);
|
|
1034
|
+
if (sameNameFields && setEquals(baselineFields, sameNameFields)) continue;
|
|
1033
1035
|
const match = candidateTypesSorted.find(([candName, candFields]) => {
|
|
1034
1036
|
if (baselineTypeNames.has(candName)) return false;
|
|
1035
|
-
for (const f of
|
|
1037
|
+
for (const f of baselineFields) if (!candFields.has(f)) return false;
|
|
1036
1038
|
return true;
|
|
1037
1039
|
});
|
|
1038
1040
|
if (!match) continue;
|
|
1039
1041
|
const [newName] = match;
|
|
1040
|
-
renameMap.set(
|
|
1041
|
-
|
|
1042
|
-
|
|
1042
|
+
renameMap.set(baselineTypeName, newName);
|
|
1043
|
+
const parentRemoval = parentRemovalByName.get(baselineTypeName);
|
|
1044
|
+
if (parentRemoval) {
|
|
1045
|
+
parentRemoval.severity = "soft-risk";
|
|
1046
|
+
parentRemoval.remediation = `Type "${baselineTypeName}" appears to have been renamed to "${newName}" — the new type has every field of the old (a non-strict superset). Field accesses and method calls on values of type "${newName}" continue to work; only explicit "${baselineTypeName}" type annotations need to migrate. Consider emitting a deprecated alias \`type ${baselineTypeName} = ${newName}\` in languages that support it.`;
|
|
1047
|
+
}
|
|
1043
1048
|
}
|
|
1044
1049
|
return renameMap;
|
|
1045
1050
|
}
|
|
1046
1051
|
/**
|
|
1052
|
+
* Transitive rename inference. After `detectTypeRenames` records direct
|
|
1053
|
+
* structural matches (e.g. `ApiKeyWithValue → OrganizationApiKeyWithValue`),
|
|
1054
|
+
* walk the parent's fields and follow the `typeRef.name` of each: if the
|
|
1055
|
+
* baseline parent's field references an alias-type `OldOwner` and the new
|
|
1056
|
+
* parent's same-named field references a different alias `NewOwner`, the
|
|
1057
|
+
* pair is the same type-concept under a renamed name. Record the
|
|
1058
|
+
* secondary rename so the cascade can downgrade `OldOwner`'s
|
|
1059
|
+
* `symbol_removed` and any `field_type_changed` pointing at the pair.
|
|
1060
|
+
*
|
|
1061
|
+
* Why this is necessary: discriminated-union owner types (Go's
|
|
1062
|
+
* `APIKeyWithValueOwner`, PHP/Python's nested constructor params) are
|
|
1063
|
+
* frequently emitted as `kind: 'alias'` symbols with no extractable
|
|
1064
|
+
* field children — so `collectTypeFieldSets` returns empty for them and
|
|
1065
|
+
* the structural matcher can't pair them directly. The transitive
|
|
1066
|
+
* lookup gives us a deterministic, conservative way to propagate the
|
|
1067
|
+
* rename: it only fires when the parent rename has already been
|
|
1068
|
+
* positively identified, and only follows fields that share the same
|
|
1069
|
+
* wire name across baseline and candidate.
|
|
1070
|
+
*/
|
|
1071
|
+
function inferTransitiveTypeRenames(typeRenames, baseline, candidate, changes) {
|
|
1072
|
+
if (typeRenames.size === 0) return;
|
|
1073
|
+
const baselineFieldType = /* @__PURE__ */ new Map();
|
|
1074
|
+
for (const sym of baseline.symbols) {
|
|
1075
|
+
if (sym.kind !== "field" && sym.kind !== "property") continue;
|
|
1076
|
+
if (!sym.ownerFqName || !sym.typeRef) continue;
|
|
1077
|
+
const localName = sym.fqName.includes(".") ? sym.fqName.slice(sym.fqName.lastIndexOf(".") + 1) : sym.fqName;
|
|
1078
|
+
baselineFieldType.set(`${sym.ownerFqName}.${localName.toLowerCase()}`, sym.typeRef.name);
|
|
1079
|
+
}
|
|
1080
|
+
const candidateFieldType = /* @__PURE__ */ new Map();
|
|
1081
|
+
for (const sym of candidate.symbols) {
|
|
1082
|
+
if (sym.kind !== "field" && sym.kind !== "property") continue;
|
|
1083
|
+
if (!sym.ownerFqName || !sym.typeRef) continue;
|
|
1084
|
+
const localName = sym.fqName.includes(".") ? sym.fqName.slice(sym.fqName.lastIndexOf(".") + 1) : sym.fqName;
|
|
1085
|
+
candidateFieldType.set(`${sym.ownerFqName}.${localName.toLowerCase()}`, sym.typeRef.name);
|
|
1086
|
+
}
|
|
1087
|
+
const parentRemovalByName = /* @__PURE__ */ new Map();
|
|
1088
|
+
for (const change of changes) if (change.category === "symbol_removed" && change.severity === "breaking") parentRemovalByName.set(change.old.symbol ?? "", change);
|
|
1089
|
+
const initialEntries = [...typeRenames.entries()];
|
|
1090
|
+
for (const [oldOwner, newOwner] of initialEntries) for (const [baselineKey, baselineFieldTypeRef] of baselineFieldType) {
|
|
1091
|
+
if (!baselineKey.startsWith(`${oldOwner}.`)) continue;
|
|
1092
|
+
const localName = baselineKey.slice(oldOwner.length + 1);
|
|
1093
|
+
const candidateKey = `${newOwner}.${localName}`;
|
|
1094
|
+
const candidateFieldTypeRef = candidateFieldType.get(candidateKey);
|
|
1095
|
+
if (!candidateFieldTypeRef) continue;
|
|
1096
|
+
const oldT = bareTypeName(baselineFieldTypeRef);
|
|
1097
|
+
const newT = bareTypeName(candidateFieldTypeRef);
|
|
1098
|
+
if (!oldT || !newT || oldT === newT) continue;
|
|
1099
|
+
if (typeRenames.has(oldT)) continue;
|
|
1100
|
+
typeRenames.set(oldT, newT);
|
|
1101
|
+
const removal = parentRemovalByName.get(oldT);
|
|
1102
|
+
if (removal) {
|
|
1103
|
+
removal.severity = "soft-risk";
|
|
1104
|
+
removal.remediation = `Type "${oldT}" appears to have been renamed to "${newT}" — inferred transitively because the renamed parent "${oldOwner}" → "${newOwner}" has a "${localName}" field whose type swapped from "${oldT}" to "${newT}". Consider emitting a deprecated alias \`type ${oldT} = ${newT}\` in languages that support it.`;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
/**
|
|
1047
1109
|
* Detect enum canonical-flips: a baseline enum disappears and a candidate
|
|
1048
1110
|
* enum with the **same wire-value set** takes its place.
|
|
1049
1111
|
*
|
|
@@ -1066,24 +1128,21 @@ function detectEnumRenames(changes, baseline, candidate) {
|
|
|
1066
1128
|
const candidateEnumValues = collectEnumValueSets(candidate);
|
|
1067
1129
|
const baselineEnumNames = new Set(baselineEnumValues.keys());
|
|
1068
1130
|
const candidateEnumsSorted = [...candidateEnumValues.entries()].sort((a, b) => a[0].localeCompare(b[0]));
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
const match = candidateEnumsSorted.find(([candName, candValues]) => {
|
|
1077
|
-
if (baselineEnumNames.has(candName)) return false;
|
|
1078
|
-
if (candValues.size !== removedValues.size) return false;
|
|
1079
|
-
for (const v of removedValues) if (!candValues.has(v)) return false;
|
|
1080
|
-
return true;
|
|
1081
|
-
});
|
|
1131
|
+
const parentRemovalByName = /* @__PURE__ */ new Map();
|
|
1132
|
+
for (const change of changes) if (change.category === "symbol_removed" && change.severity === "breaking") parentRemovalByName.set(change.old.symbol ?? "", change);
|
|
1133
|
+
for (const [baselineEnumName, baselineValues] of baselineEnumValues) {
|
|
1134
|
+
if (baselineValues.size === 0) continue;
|
|
1135
|
+
const sameNameValues = candidateEnumValues.get(baselineEnumName);
|
|
1136
|
+
if (sameNameValues && setEquals(baselineValues, sameNameValues)) continue;
|
|
1137
|
+
const match = candidateEnumsSorted.find(([candName, candValues]) => !baselineEnumNames.has(candName) && setEquals(baselineValues, candValues));
|
|
1082
1138
|
if (!match) continue;
|
|
1083
1139
|
const [newName] = match;
|
|
1084
|
-
renameMap.set(
|
|
1085
|
-
|
|
1086
|
-
|
|
1140
|
+
renameMap.set(baselineEnumName, newName);
|
|
1141
|
+
const parentRemoval = parentRemovalByName.get(baselineEnumName);
|
|
1142
|
+
if (parentRemoval) {
|
|
1143
|
+
parentRemoval.severity = "soft-risk";
|
|
1144
|
+
parentRemoval.remediation = `Enum "${baselineEnumName}" appears to have been renamed to "${newName}" — both enums have identical wire values, so on-the-wire serialization is unchanged. This is typically the emitter's dedup canonical-flip after a new same-shape enum joined the spec. Consider emitting a deprecated alias in languages that support it, or pinning the canonical via emitter config.`;
|
|
1145
|
+
}
|
|
1087
1146
|
}
|
|
1088
1147
|
return renameMap;
|
|
1089
1148
|
}
|
|
@@ -1101,8 +1160,40 @@ function detectEnumRenames(changes, baseline, candidate) {
|
|
|
1101
1160
|
* Each cascaded change gets a remediation pointing at the parent rename
|
|
1102
1161
|
* so the reviewer can find the explanation in the report.
|
|
1103
1162
|
*/
|
|
1104
|
-
function cascadeRenameDowngrades(changes, typeRenames, enumRenames) {
|
|
1105
|
-
|
|
1163
|
+
function cascadeRenameDowngrades(changes, typeRenames, enumRenames, baseline, candidate) {
|
|
1164
|
+
let baselineTypeFields;
|
|
1165
|
+
let candidateTypeFields;
|
|
1166
|
+
const getBaselineFields = () => {
|
|
1167
|
+
if (!baselineTypeFields) baselineTypeFields = collectTypeFieldSets(baseline);
|
|
1168
|
+
return baselineTypeFields;
|
|
1169
|
+
};
|
|
1170
|
+
const getCandidateFields = () => {
|
|
1171
|
+
if (!candidateTypeFields) candidateTypeFields = collectTypeFieldSets(candidate);
|
|
1172
|
+
return candidateTypeFields;
|
|
1173
|
+
};
|
|
1174
|
+
/**
|
|
1175
|
+
* Structural equivalence: candidate type's field set is a non-strict
|
|
1176
|
+
* superset of baseline type's field set, AND the candidate type was
|
|
1177
|
+
* newly added in this diff (not a swap to a pre-existing type, which
|
|
1178
|
+
* is a real signature break the consumer chose). When `New ⊇ Old` and
|
|
1179
|
+
* `New` is new, every field the consumer accessed on the old type
|
|
1180
|
+
* still exists on the value they receive — so the swap is non-breaking
|
|
1181
|
+
* at the value level even though the declared type changed. This is
|
|
1182
|
+
* the fork-detector's positive signal, applied as a severity
|
|
1183
|
+
* downgrade in addition to the existing remediation hint.
|
|
1184
|
+
*/
|
|
1185
|
+
const isStructurallyEquivalent = (oldT, newT) => {
|
|
1186
|
+
if (!oldT || !newT || oldT === newT) return false;
|
|
1187
|
+
const baselineFields = getBaselineFields();
|
|
1188
|
+
const candidateFields = getCandidateFields();
|
|
1189
|
+
if (baselineFields.has(newT)) return false;
|
|
1190
|
+
const oldFields = baselineFields.get(oldT);
|
|
1191
|
+
const newFields = candidateFields.get(newT);
|
|
1192
|
+
if (!oldFields || !newFields) return false;
|
|
1193
|
+
if (oldFields.size === 0) return false;
|
|
1194
|
+
for (const f of oldFields) if (!newFields.has(f)) return false;
|
|
1195
|
+
return true;
|
|
1196
|
+
};
|
|
1106
1197
|
for (const change of changes) {
|
|
1107
1198
|
if (change.severity !== "breaking") continue;
|
|
1108
1199
|
if (change.category === "symbol_removed") {
|
|
@@ -1122,6 +1213,9 @@ function cascadeRenameDowngrades(changes, typeRenames, enumRenames) {
|
|
|
1122
1213
|
if (typeRenames.get(oldT) === newT) {
|
|
1123
1214
|
change.severity = "soft-risk";
|
|
1124
1215
|
change.remediation = `Return type swap matches recorded rename "${oldT}" → "${newT}". The underlying field set is preserved (see the rename advisory on "${oldT}").`;
|
|
1216
|
+
} else if (isStructurallyEquivalent(oldT, newT)) {
|
|
1217
|
+
change.severity = "soft-risk";
|
|
1218
|
+
if (!change.remediation) change.remediation = `Return type swap from "${oldT}" to "${newT}" is structurally equivalent — every field on "${oldT}" still exists on "${newT}". Consumer code accessing fields on the returned value continues to work; only explicit "${oldT}" type annotations need to migrate.`;
|
|
1125
1219
|
}
|
|
1126
1220
|
continue;
|
|
1127
1221
|
}
|
|
@@ -1131,6 +1225,9 @@ function cascadeRenameDowngrades(changes, typeRenames, enumRenames) {
|
|
|
1131
1225
|
if ((typeRenames.get(oldT) ?? enumRenames.get(oldT)) === newT) {
|
|
1132
1226
|
change.severity = "soft-risk";
|
|
1133
1227
|
change.remediation = `Field type swap matches recorded rename "${oldT}" → "${newT}". On-the-wire shape is unchanged (see the rename advisory on "${oldT}").`;
|
|
1228
|
+
} else if (isStructurallyEquivalent(oldT, newT)) {
|
|
1229
|
+
change.severity = "soft-risk";
|
|
1230
|
+
if (!change.remediation) change.remediation = `Field type swap from "${oldT}" to "${newT}" is structurally equivalent — every field on "${oldT}" still exists on "${newT}".`;
|
|
1134
1231
|
}
|
|
1135
1232
|
}
|
|
1136
1233
|
}
|
|
@@ -1160,6 +1257,12 @@ function collectEnumValueSets(snapshot) {
|
|
|
1160
1257
|
}
|
|
1161
1258
|
return result;
|
|
1162
1259
|
}
|
|
1260
|
+
/** Set equality for the small string sets used by rename detection. */
|
|
1261
|
+
function setEquals(a, b) {
|
|
1262
|
+
if (a.size !== b.size) return false;
|
|
1263
|
+
for (const v of a) if (!b.has(v)) return false;
|
|
1264
|
+
return true;
|
|
1265
|
+
}
|
|
1163
1266
|
/**
|
|
1164
1267
|
* Strip array/nullable suffixes so we compare bare type names. Languages
|
|
1165
1268
|
* encode these differently (`Foo[]`, `Foo | null`, `Foo?`, `List<Foo>`,
|
|
@@ -1168,10 +1271,13 @@ function collectEnumValueSets(snapshot) {
|
|
|
1168
1271
|
*/
|
|
1169
1272
|
function bareTypeName(t) {
|
|
1170
1273
|
let s = t.trim();
|
|
1274
|
+
s = s.replace(/^\*/, "");
|
|
1171
1275
|
s = s.replace(/\s*\|\s*null$/, "").replace(/\?$/, "");
|
|
1172
1276
|
s = s.replace(/\[\]$/, "");
|
|
1173
|
-
const
|
|
1174
|
-
if (
|
|
1277
|
+
const angleGeneric = s.match(/^[A-Za-z_][A-Za-z0-9_.]*<\s*([A-Za-z_*][A-Za-z0-9_.*]*)\s*>$/);
|
|
1278
|
+
if (angleGeneric) return bareTypeName(angleGeneric[1]);
|
|
1279
|
+
const bracketGeneric = s.match(/^[A-Za-z_][A-Za-z0-9_.]*\[\s*([A-Za-z_*][A-Za-z0-9_.*]*)\s*\]$/);
|
|
1280
|
+
if (bracketGeneric) return bareTypeName(bracketGeneric[1]);
|
|
1175
1281
|
return s;
|
|
1176
1282
|
}
|
|
1177
1283
|
/**
|
|
@@ -1968,4 +2074,4 @@ function patchOverlay(overlay, changes, baseline) {
|
|
|
1968
2074
|
//#endregion
|
|
1969
2075
|
export { getDefaultPolicy as A, defaultSeverityForCategory as C, isCompatibleSchemaVersion as D, COMPAT_SCHEMA_VERSION as E, validateSnapshot as O, summarizeChanges as S, apiSurfaceToSnapshot as T, nodeHints as _, registerExtractor as a, classifyAddedSymbol as b, filterSurface as c, specDerivedHttpKeys as d, specDerivedMethodPaths as f, namedTypeWordsOverlap as g, defaultIsNullableOnlyDifference as h, getExtractor as i, mergePolicy as j, ALL_LANGUAGE_IDS as k, specDerivedEnumValues as l, NAMED_TYPE_RE as m, isPatchableChange as n, diffSnapshots as o, specDerivedNames as p, patchOverlay as r, diffSurfaces as s, buildOverlayLookup as t, specDerivedFieldPaths as u, resolveHints as v, severityMeetsThreshold as w, classifySymbolChanges as x, typeExistsInSurface as y };
|
|
1970
2076
|
|
|
1971
|
-
//# sourceMappingURL=overlay-
|
|
2077
|
+
//# sourceMappingURL=overlay-Dlk7bV5R.mjs.map
|