@workos/oagen 0.9.2 → 0.10.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.
@@ -6668,10 +6668,14 @@ async function compatExtractCommand(opts) {
6668
6668
  console.log(`Extracted ${snapshot.symbols.length} symbols → ${outputPath}`);
6669
6669
  }
6670
6670
  /**
6671
- * Enrich snapshot symbols with operationId and route from the parsed spec.
6671
+ * Enrich snapshot symbols with spec-level identity.
6672
6672
  *
6673
- * Matches callable symbols to spec operations by comparing method names
6674
- * derived from the spec against the symbol's fqName.
6673
+ * - Callable symbols get `operationId` and `route` from spec operations.
6674
+ * - Property/field/constructor/service_accessor symbols get `schemaName`
6675
+ * from spec models, enabling cross-language grouping in reports.
6676
+ *
6677
+ * The `schemaName` uses the spec-level identity (e.g. "GenerateLinkBody.admin_emails")
6678
+ * which is the same regardless of the language's naming conventions.
6675
6679
  */
6676
6680
  function enrichWithSpecContext(snapshot, spec) {
6677
6681
  const opLookup = /* @__PURE__ */ new Map();
@@ -6683,15 +6687,38 @@ function enrichWithSpecContext(snapshot, spec) {
6683
6687
  path: op.path
6684
6688
  });
6685
6689
  }
6690
+ const norm = (s) => s.replace(/_/g, "").toLowerCase();
6691
+ const modelLookup = /* @__PURE__ */ new Map();
6692
+ for (const model of spec.models) {
6693
+ const fieldMap = /* @__PURE__ */ new Map();
6694
+ for (const field of model.fields) fieldMap.set(norm(field.name), field.name);
6695
+ modelLookup.set(norm(model.name), {
6696
+ specName: model.name,
6697
+ fields: fieldMap
6698
+ });
6699
+ }
6686
6700
  for (const sym of snapshot.symbols) {
6687
- if (sym.kind !== "callable") continue;
6688
- const match = opLookup.get(sym.fqName);
6689
- if (match) {
6690
- sym.operationId = match.operationId;
6691
- sym.route = {
6692
- method: match.method,
6693
- path: match.path
6694
- };
6701
+ if (sym.kind === "callable") {
6702
+ const match = opLookup.get(sym.fqName);
6703
+ if (match) {
6704
+ sym.operationId = match.operationId;
6705
+ sym.route = {
6706
+ method: match.method,
6707
+ path: match.path
6708
+ };
6709
+ }
6710
+ }
6711
+ if (sym.ownerFqName) {
6712
+ const modelMatch = modelLookup.get(norm(sym.ownerFqName));
6713
+ if (modelMatch) {
6714
+ const localName = sym.fqName.includes(".") ? sym.fqName.split(".").pop() : sym.fqName;
6715
+ const fieldMatch = modelMatch.fields.get(norm(localName));
6716
+ if (fieldMatch) sym.schemaName = `${modelMatch.specName}.${fieldMatch}`;
6717
+ else sym.schemaName = modelMatch.specName;
6718
+ }
6719
+ } else {
6720
+ const modelMatch = modelLookup.get(norm(sym.fqName));
6721
+ if (modelMatch) sym.schemaName = modelMatch.specName;
6695
6722
  }
6696
6723
  }
6697
6724
  }
@@ -6741,6 +6768,7 @@ function severityMeetsThreshold(severity, threshold) {
6741
6768
  */
6742
6769
  function classifySymbolChanges(baseline, candidate, policy) {
6743
6770
  const changes = [];
6771
+ const specRef = baseline.schemaName ?? candidate?.schemaName;
6744
6772
  if (!candidate) {
6745
6773
  changes.push(makeChange({
6746
6774
  category: "symbol_removed",
@@ -6748,7 +6776,8 @@ function classifySymbolChanges(baseline, candidate, policy) {
6748
6776
  old: { symbol: baseline.fqName },
6749
6777
  new: { symbol: "(removed)" },
6750
6778
  message: `Symbol "${baseline.displayName}" was removed`,
6751
- policy
6779
+ policy,
6780
+ specRef
6752
6781
  }));
6753
6782
  return changes;
6754
6783
  }
@@ -6758,16 +6787,18 @@ function classifySymbolChanges(baseline, candidate, policy) {
6758
6787
  old: { name: baseline.fqName },
6759
6788
  new: { name: candidate.fqName },
6760
6789
  message: `Symbol renamed from "${baseline.displayName}" to "${candidate.displayName}"`,
6761
- policy
6790
+ policy,
6791
+ specRef
6762
6792
  }));
6763
- if (baseline.parameters && candidate.parameters) changes.push(...classifyParameterChanges(baseline, candidate, policy));
6793
+ if (baseline.parameters && candidate.parameters) changes.push(...classifyParameterChanges(baseline, candidate, policy, specRef));
6764
6794
  if (baseline.returns && candidate.returns && baseline.returns.name !== candidate.returns.name) changes.push(makeChange({
6765
6795
  category: "return_type_changed",
6766
6796
  symbol: baseline.fqName,
6767
6797
  old: { returnType: baseline.returns.name },
6768
6798
  new: { returnType: candidate.returns.name },
6769
6799
  message: `Return type changed for "${baseline.displayName}" from "${baseline.returns.name}" to "${candidate.returns.name}"`,
6770
- policy
6800
+ policy,
6801
+ specRef
6771
6802
  }));
6772
6803
  if (baseline.typeRef && candidate.typeRef && baseline.typeRef.name !== candidate.typeRef.name) changes.push(makeChange({
6773
6804
  category: "field_type_changed",
@@ -6775,7 +6806,8 @@ function classifySymbolChanges(baseline, candidate, policy) {
6775
6806
  old: { type: baseline.typeRef.name },
6776
6807
  new: { type: candidate.typeRef.name },
6777
6808
  message: `Type changed for "${baseline.displayName}" from "${baseline.typeRef.name}" to "${candidate.typeRef.name}"`,
6778
- policy
6809
+ policy,
6810
+ specRef
6779
6811
  }));
6780
6812
  if (baseline.kind === "enum_member" && candidate.kind === "enum_member" && baseline.value !== void 0 && candidate.value !== void 0 && baseline.value !== candidate.value) changes.push(makeChange({
6781
6813
  category: "enum_member_value_changed",
@@ -6790,7 +6822,7 @@ function classifySymbolChanges(baseline, candidate, policy) {
6790
6822
  /**
6791
6823
  * Classify parameter-level changes between two symbol versions.
6792
6824
  */
6793
- function classifyParameterChanges(baseline, candidate, policy) {
6825
+ function classifyParameterChanges(baseline, candidate, policy, specRef) {
6794
6826
  const changes = [];
6795
6827
  const baseParams = baseline.parameters ?? [];
6796
6828
  const candParams = candidate.parameters ?? [];
@@ -6810,6 +6842,7 @@ function classifyParameterChanges(baseline, candidate, policy) {
6810
6842
  new: { parameter: positionalMatch.publicName },
6811
6843
  message: `Parameter "${baseParam.publicName}" renamed to "${positionalMatch.publicName}" on "${baseline.displayName}"`,
6812
6844
  policy,
6845
+ specRef,
6813
6846
  severityOverride: isBreakingRename ? void 0 : "soft-risk"
6814
6847
  }));
6815
6848
  } else changes.push(makeChange({
@@ -6818,7 +6851,8 @@ function classifyParameterChanges(baseline, candidate, policy) {
6818
6851
  old: { parameter: baseParam.publicName },
6819
6852
  new: { parameter: "(removed)" },
6820
6853
  message: `Parameter "${baseParam.publicName}" removed from "${baseline.displayName}"`,
6821
- policy
6854
+ policy,
6855
+ specRef
6822
6856
  }));
6823
6857
  continue;
6824
6858
  }
@@ -6834,7 +6868,8 @@ function classifyParameterChanges(baseline, candidate, policy) {
6834
6868
  required: "true"
6835
6869
  },
6836
6870
  message: `Parameter "${baseParam.publicName}" became required on "${baseline.displayName}"`,
6837
- policy
6871
+ policy,
6872
+ specRef
6838
6873
  }));
6839
6874
  if (baseParam.type.name !== candParam.type.name) changes.push(makeChange({
6840
6875
  category: "parameter_type_narrowed",
@@ -6848,7 +6883,8 @@ function classifyParameterChanges(baseline, candidate, policy) {
6848
6883
  type: candParam.type.name
6849
6884
  },
6850
6885
  message: `Parameter type changed for "${baseParam.publicName}" on "${baseline.displayName}"`,
6851
- policy
6886
+ policy,
6887
+ specRef
6852
6888
  }));
6853
6889
  if (baseParam.position !== candParam.position) if (isConstructor ? policy.constructorOrderMatters : baseParam.sensitivity.order) {
6854
6890
  const category = isConstructor ? "constructor_position_changed_order_sensitive" : "parameter_position_changed_order_sensitive";
@@ -6864,7 +6900,8 @@ function classifyParameterChanges(baseline, candidate, policy) {
6864
6900
  position: String(candParam.position)
6865
6901
  },
6866
6902
  message: `Parameter "${baseParam.publicName}" moved from position ${baseParam.position} to ${candParam.position} on "${baseline.displayName}"`,
6867
- policy
6903
+ policy,
6904
+ specRef
6868
6905
  }));
6869
6906
  } else changes.push(makeChange({
6870
6907
  category: "constructor_reordered_named_friendly",
@@ -6878,7 +6915,8 @@ function classifyParameterChanges(baseline, candidate, policy) {
6878
6915
  position: String(candParam.position)
6879
6916
  },
6880
6917
  message: `Parameter "${baseParam.publicName}" reordered on "${baseline.displayName}" (named-friendly language)`,
6881
- policy
6918
+ policy,
6919
+ specRef
6882
6920
  }));
6883
6921
  }
6884
6922
  for (const candParam of candParams) if (!baseByName.has(candParam.publicName)) {
@@ -6894,7 +6932,8 @@ function classifyParameterChanges(baseline, candidate, policy) {
6894
6932
  required: "true"
6895
6933
  },
6896
6934
  message: `Required parameter "${candParam.publicName}" added to "${baseline.displayName}"`,
6897
- policy
6935
+ policy,
6936
+ specRef
6898
6937
  }));
6899
6938
  else changes.push(makeChange({
6900
6939
  category,
@@ -6902,7 +6941,8 @@ function classifyParameterChanges(baseline, candidate, policy) {
6902
6941
  old: { parameter: "(absent)" },
6903
6942
  new: { parameter: candParam.publicName },
6904
6943
  message: `Optional parameter "${candParam.publicName}" added to "${baseline.displayName}"`,
6905
- policy
6944
+ policy,
6945
+ specRef
6906
6946
  }));
6907
6947
  }
6908
6948
  return changes;
@@ -6912,12 +6952,19 @@ function parameterNameIsPublicApi(param, policy, isConstructor) {
6912
6952
  if (isConstructor) return policy.constructorParameterNamesArePublicApi;
6913
6953
  return policy.methodParameterNamesArePublicApi;
6914
6954
  }
6915
- /** Build a deterministic conceptual change ID. */
6916
- function buildConceptualChangeId(category, symbol, match) {
6955
+ /**
6956
+ * Build a deterministic conceptual change ID.
6957
+ *
6958
+ * When `specRef` is provided (e.g. "GenerateLinkBody.admin_emails"), it is
6959
+ * used instead of the language-specific symbol name. This ensures the same
6960
+ * spec entity produces the same ID across all languages, enabling cross-
6961
+ * language rollup in reports.
6962
+ */
6963
+ function buildConceptualChangeId(category, symbol, match, specRef) {
6917
6964
  const parts = [
6918
6965
  "chg",
6919
6966
  category,
6920
- symbol.replace(/[^a-zA-Z0-9_.]/g, "_")
6967
+ (specRef ?? symbol).replace(/[^a-zA-Z0-9_.]/g, "_")
6921
6968
  ];
6922
6969
  if (match.parameter) parts.push(match.parameter);
6923
6970
  if (match.member) parts.push(match.member);
@@ -6928,7 +6975,7 @@ function makeChange(opts) {
6928
6975
  category: opts.category,
6929
6976
  severity: opts.severityOverride ?? defaultSeverityForCategory(opts.category),
6930
6977
  symbol: opts.symbol,
6931
- conceptualChangeId: buildConceptualChangeId(opts.category, opts.symbol, opts.old),
6978
+ conceptualChangeId: buildConceptualChangeId(opts.category, opts.symbol, opts.old, opts.specRef),
6932
6979
  provenance: opts.provenance ?? "unknown",
6933
6980
  old: opts.old,
6934
6981
  new: opts.new,
@@ -6943,7 +6990,7 @@ function classifyAddedSymbol(symbol) {
6943
6990
  category: "symbol_added",
6944
6991
  severity: "additive",
6945
6992
  symbol: symbol.fqName,
6946
- conceptualChangeId: buildConceptualChangeId("symbol_added", symbol.fqName, {}),
6993
+ conceptualChangeId: buildConceptualChangeId("symbol_added", symbol.fqName, {}, symbol.schemaName),
6947
6994
  provenance: "unknown",
6948
6995
  old: { symbol: "(absent)" },
6949
6996
  new: { symbol: symbol.fqName },