@workos/oagen 0.19.5 → 0.20.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.
@@ -3148,11 +3148,21 @@ function extractKotlinClassMembers(classBody, source) {
3148
3148
  }
3149
3149
  members.push({
3150
3150
  key: memberName,
3151
- text: source.slice(startIdx, endIdx)
3151
+ text: source.slice(startIdx, endIdx),
3152
+ startIndex: startIdx,
3153
+ endIndex: endIdx
3152
3154
  });
3153
3155
  }
3154
3156
  return members;
3155
3157
  }
3158
+ /**
3159
+ * KDoc signature that identifies a generator-emitted service accessor inside
3160
+ * the WorkOS class. The Kotlin client emitter writes every accessor with this
3161
+ * exact doc preamble — when the existing file carries a member with this doc
3162
+ * but the regenerated content omits it, the member is stale (its mount target
3163
+ * was remapped or removed) and gets pruned.
3164
+ */
3165
+ const KOTLIN_MANAGED_ACCESSOR_DOC = /\/\*\*[\s\S]*?Lazily-constructed \[[^\]]+\] accessor for this \[WorkOS\] client\.[\s\S]*?\*\//;
3156
3166
  const kotlinMergeAdapter = {
3157
3167
  language: "kotlin",
3158
3168
  grammarModule: "tree-sitter-kotlin",
@@ -3193,6 +3203,9 @@ const kotlinMergeAdapter = {
3193
3203
  renderImports(imports) {
3194
3204
  return imports.map((entry) => entry.text);
3195
3205
  },
3206
+ isManagedMember(member) {
3207
+ return KOTLIN_MANAGED_ACCESSOR_DOC.test(member.text);
3208
+ },
3196
3209
  shouldSkipDeepMerge(_symbolName, existingMemberKeys, newMembers) {
3197
3210
  const newText = newMembers.map((m) => m.text).join("\n");
3198
3211
  for (const match of newText.matchAll(/this\.(\w+)/g)) {
@@ -4288,6 +4301,7 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
4288
4301
  let result = existingContent;
4289
4302
  if (toAppend.length > 0) result = result.trimEnd() + "\n\n" + toAppend.join("\n\n") + "\n";
4290
4303
  let deepAdded = 0;
4304
+ let deepPruned = 0;
4291
4305
  const insertions = [];
4292
4306
  if (adapter.extractMembers) {
4293
4307
  const parser = await getParser(language);
@@ -4297,6 +4311,37 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
4297
4311
  const generatedSymbols = adapter.extractMembers(generatedTree, generatedContent);
4298
4312
  const existingTree = safeParse(parser, existingContent);
4299
4313
  const deepIgnoredSymbols = buildIgnoredSymbolNames(adapter.extractDocstrings(existingTree, existingContent), findIgnoredRegions(existingContent));
4314
+ if (adapter.isManagedMember) {
4315
+ const pruneEdits = [];
4316
+ for (const [symbolName, genSymbol] of generatedSymbols) {
4317
+ if (deepIgnoredSymbols.has(symbolName)) continue;
4318
+ const existSymbol = resultSymbols.get(symbolName);
4319
+ if (!existSymbol) continue;
4320
+ const genMemberKeys = new Set(genSymbol.members.map((m) => m.key));
4321
+ for (const existMember of existSymbol.members) {
4322
+ if (genMemberKeys.has(existMember.key)) continue;
4323
+ if (existMember.startIndex === void 0 || existMember.endIndex === void 0) continue;
4324
+ if (!adapter.isManagedMember(existMember)) continue;
4325
+ pruneEdits.push({
4326
+ start: existMember.startIndex,
4327
+ end: existMember.endIndex
4328
+ });
4329
+ }
4330
+ }
4331
+ if (pruneEdits.length > 0) {
4332
+ pruneEdits.sort((a, b) => b.start - a.start);
4333
+ for (const edit of pruneEdits) {
4334
+ let end = edit.end;
4335
+ while (end < result.length && (result[end] === " " || result[end] === " ")) end++;
4336
+ if (result[end] === "\n") end++;
4337
+ result = result.slice(0, edit.start) + result.slice(end);
4338
+ }
4339
+ deepPruned = pruneEdits.length;
4340
+ const reTree = safeParse(parser, result);
4341
+ resultSymbols.clear();
4342
+ for (const [k, v] of adapter.extractMembers(reTree, result)) resultSymbols.set(k, v);
4343
+ }
4344
+ }
4300
4345
  for (const [symbolName, genSymbol] of generatedSymbols) {
4301
4346
  if (deepIgnoredSymbols.has(symbolName)) continue;
4302
4347
  const existSymbol = resultSymbols.get(symbolName);
@@ -4479,7 +4524,7 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
4479
4524
  }
4480
4525
  }
4481
4526
  const totalAdded = (toAppend.length > 0 || deepAdded > 0 ? adapter.renderImports ? adapter.renderImports(newImports).length : newImports.length : 0) + toAppend.length + deepAdded;
4482
- if (totalAdded === 0 && docstringUpdates === 0) return {
4527
+ if (totalAdded === 0 && docstringUpdates === 0 && deepPruned === 0) return {
4483
4528
  content: existingContent,
4484
4529
  added: 0,
4485
4530
  preserved,
@@ -7822,9 +7867,11 @@ function collectTypeFieldSets(snapshot) {
7822
7867
  * (e.g. `ApiKey` → `OrganizationApiKey` + `UserApiKey`). The wire shape
7823
7868
  * returned by individual endpoints is unchanged — `OrganizationApiKey`
7824
7869
  * has the same fields `ApiKey` had — so consumer code accessing those
7825
- * fields keeps working. The compat report flags `ApiKey` as removed
7826
- * because its symbol is gone; this pass downgrades the removal to
7827
- * soft-risk and records the rename so its child fields/methods cascade.
7870
+ * fields *through a return value* keeps working. The parent symbol
7871
+ * itself, however, is gone: callers who referenced it by name (Sorbet
7872
+ * annotations, `is_a?` checks, pattern matches, deserialization targets)
7873
+ * will fail at runtime. The parent removal therefore stays `breaking`;
7874
+ * we only attach a remediation hint pointing at the new name.
7828
7875
  *
7829
7876
  * Identity criteria (must all hold):
7830
7877
  * 1. The removed symbol owns ≥ 1 field/property in the baseline (i.e.
@@ -7839,10 +7886,11 @@ function collectTypeFieldSets(snapshot) {
7839
7886
  *
7840
7887
  * Returns a `removedName -> newName` map so a downstream cascade pass
7841
7888
  * can downgrade owned-field removals and `*_type_changed` pointing at
7842
- * the same pair.
7889
+ * the same pair — those *are* downgraded to `soft-risk`, since values
7890
+ * flowing through the new type continue to expose the same fields.
7843
7891
  *
7844
- * Mutates matching `symbol_removed` entries in `changes`: severity →
7845
- * `soft-risk`, attaches a `remediation` describing the rename.
7892
+ * Mutates matching `symbol_removed` entries in `changes`: attaches a
7893
+ * `remediation` describing the rename. Severity is left at `breaking`.
7846
7894
  */
7847
7895
  function detectTypeRenames(changes, baseline, candidate) {
7848
7896
  const renameMap = /* @__PURE__ */ new Map();
@@ -7865,10 +7913,7 @@ function detectTypeRenames(changes, baseline, candidate) {
7865
7913
  const [newName] = match;
7866
7914
  renameMap.set(baselineTypeName, newName);
7867
7915
  const parentRemoval = parentRemovalByName.get(baselineTypeName);
7868
- if (parentRemoval) {
7869
- parentRemoval.severity = "soft-risk";
7870
- 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.`;
7871
- }
7916
+ if (parentRemoval) 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.`;
7872
7917
  }
7873
7918
  return renameMap;
7874
7919
  }
@@ -7879,8 +7924,9 @@ function detectTypeRenames(changes, baseline, candidate) {
7879
7924
  * baseline parent's field references an alias-type `OldOwner` and the new
7880
7925
  * parent's same-named field references a different alias `NewOwner`, the
7881
7926
  * pair is the same type-concept under a renamed name. Record the
7882
- * secondary rename so the cascade can downgrade `OldOwner`'s
7883
- * `symbol_removed` and any `field_type_changed` pointing at the pair.
7927
+ * secondary rename so the cascade can downgrade any `field_type_changed`
7928
+ * pointing at the pair, and attach a remediation hint to `OldOwner`'s
7929
+ * `symbol_removed` (which stays `breaking` — see `detectTypeRenames`).
7884
7930
  *
7885
7931
  * Why this is necessary: discriminated-union owner types (Go's
7886
7932
  * `APIKeyWithValueOwner`, PHP/Python's nested constructor params) are
@@ -7923,10 +7969,7 @@ function inferTransitiveTypeRenames(typeRenames, baseline, candidate, changes) {
7923
7969
  if (typeRenames.has(oldT)) continue;
7924
7970
  typeRenames.set(oldT, newT);
7925
7971
  const removal = parentRemovalByName.get(oldT);
7926
- if (removal) {
7927
- removal.severity = "soft-risk";
7928
- 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.`;
7929
- }
7972
+ if (removal) 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.`;
7930
7973
  }
7931
7974
  }
7932
7975
  /**
@@ -7963,10 +8006,7 @@ function detectEnumRenames(changes, baseline, candidate) {
7963
8006
  const [newName] = match;
7964
8007
  renameMap.set(baselineEnumName, newName);
7965
8008
  const parentRemoval = parentRemovalByName.get(baselineEnumName);
7966
- if (parentRemoval) {
7967
- parentRemoval.severity = "soft-risk";
7968
- 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.`;
7969
- }
8009
+ if (parentRemoval) 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.`;
7970
8010
  }
7971
8011
  return renameMap;
7972
8012
  }