@workos/oagen 0.4.0 → 0.5.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/README.md CHANGED
@@ -128,7 +128,6 @@ const myEmitter: Emitter = {
128
128
  /* ... */
129
129
  ],
130
130
  generateErrors: () => [],
131
- generateConfig: () => [],
132
131
  generateTests: () => [],
133
132
  fileHeader: () => "// Auto-generated by oagen. Do not edit.",
134
133
  };
@@ -1662,7 +1662,9 @@ function extractParams(params, location, operationContext) {
1662
1662
  description: p.description,
1663
1663
  deprecated: p.deprecated || p.schema?.deprecated || void 0,
1664
1664
  default: p.schema?.default,
1665
- example: p.example ?? p.schema?.example
1665
+ example: p.example ?? p.schema?.example,
1666
+ style: p.style,
1667
+ explode: p.explode
1666
1668
  }));
1667
1669
  }
1668
1670
  function extractRequestBody(body, op, inlineModels) {
@@ -3426,6 +3428,30 @@ function safeParse(parser, source) {
3426
3428
  * Supports any language with a tree-sitter grammar package.
3427
3429
  */
3428
3430
  const parserCache = /* @__PURE__ */ new Map();
3431
+ /**
3432
+ * When replacing an existing docstring with a generated one, carry over any
3433
+ * `@deprecated` lines from the existing doc that aren't present in the
3434
+ * generated doc. This prevents hand-written deprecation notices from being
3435
+ * silently dropped during docstring refresh.
3436
+ */
3437
+ function preserveDeprecatedTags(existingDoc, generatedDoc) {
3438
+ if (!existingDoc.includes("@deprecated")) return generatedDoc;
3439
+ if (generatedDoc.includes("@deprecated")) return generatedDoc;
3440
+ const deprecatedParts = [];
3441
+ for (const line of existingDoc.split("\n")) if (/@deprecated\b/.test(line)) {
3442
+ const match = line.match(/@deprecated\b.*/);
3443
+ if (match) {
3444
+ const cleaned = match[0].replace(/\s*\*\/\s*$/, "");
3445
+ deprecatedParts.push(` * ${cleaned}`);
3446
+ }
3447
+ }
3448
+ if (deprecatedParts.length === 0) return generatedDoc;
3449
+ const closingIdx = generatedDoc.lastIndexOf("*/");
3450
+ if (closingIdx === -1) return generatedDoc;
3451
+ const before = generatedDoc.slice(0, closingIdx);
3452
+ const after = generatedDoc.slice(closingIdx);
3453
+ return before + deprecatedParts.join("\n") + "\n " + after;
3454
+ }
3429
3455
  function findIgnoredRegions(source) {
3430
3456
  const regions = [];
3431
3457
  const startTag = "@oagen-ignore-start";
@@ -3539,10 +3565,21 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
3539
3565
  }
3540
3566
  for (const anchor of existingStatements.importAnchors) lastImportEndIndex = existingContent.slice(0, existingContent.indexOf(anchor)).split("\n").length - 1 + anchor.split("\n").length - 1;
3541
3567
  if (lastImportEndIndex === -1 && existingStatements.importInsertionAnchor) lastImportEndIndex = existingContent.slice(0, existingContent.indexOf(existingStatements.importInsertionAnchor)).split("\n").length - 1 + existingStatements.importInsertionAnchor.split("\n").length - 1;
3568
+ const existingExportedNames = /* @__PURE__ */ new Set();
3542
3569
  for (const stmt of existingStatements.statements) {
3543
- if (stmt.kind === "reexport") existingReexports.add(adapter.normalizeReexport ? adapter.normalizeReexport(stmt.text.trim()) : stmt.text.trim());
3544
- if (stmt.key) existingKeys.add(stmt.key);
3545
- else existingUnnamedTexts.add(stmt.text.trim());
3570
+ if (stmt.kind === "reexport") {
3571
+ existingReexports.add(adapter.normalizeReexport ? adapter.normalizeReexport(stmt.text.trim()) : stmt.text.trim());
3572
+ const braceMatch = stmt.text.match(/\{([^}]+)\}/);
3573
+ if (braceMatch) for (const part of braceMatch[1].split(",")) {
3574
+ const segments = part.trim().split(/\s+as\s+/);
3575
+ const exportedName = (segments[1] ?? segments[0]).trim();
3576
+ if (exportedName) existingExportedNames.add(exportedName);
3577
+ }
3578
+ }
3579
+ if (stmt.key) {
3580
+ existingKeys.add(stmt.key);
3581
+ existingExportedNames.add(stmt.key);
3582
+ } else existingUnnamedTexts.add(stmt.text.trim());
3546
3583
  }
3547
3584
  const headerLine = header.trim();
3548
3585
  const newImports = [];
@@ -3602,6 +3639,17 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
3602
3639
  preserved++;
3603
3640
  continue;
3604
3641
  }
3642
+ const braceMatch = stmt.text.match(/\{([^}]+)\}/);
3643
+ if (braceMatch) {
3644
+ const exportedNames = braceMatch[1].split(",").map((n) => {
3645
+ const parts = n.trim().split(/\s+as\s+/);
3646
+ return (parts[1] ?? parts[0]).trim();
3647
+ }).filter(Boolean);
3648
+ if (exportedNames.length > 0 && exportedNames.every((n) => existingExportedNames.has(n))) {
3649
+ preserved++;
3650
+ continue;
3651
+ }
3652
+ }
3605
3653
  }
3606
3654
  if (stmt.key && (existingKeys.has(stmt.key) || existingImportedNames.has(stmt.key))) {
3607
3655
  preserved++;
@@ -3703,10 +3751,11 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
3703
3751
  const genDoc = genInfo.docstring && genInfo.docstring.text.trim() !== headerLine ? genInfo.docstring : null;
3704
3752
  if (genDoc) if (existInfo.docstring) {
3705
3753
  if (!existInfo.docstring.text.includes("@oagen-ignore") && existInfo.docstring.text !== genDoc.text) {
3754
+ const newText = preserveDeprecatedTags(existInfo.docstring.text, genDoc.text);
3706
3755
  edits.push({
3707
3756
  start: existInfo.docstring.startIndex,
3708
3757
  end: existInfo.docstring.endIndex,
3709
- newText: genDoc.text
3758
+ newText
3710
3759
  });
3711
3760
  docstringUpdates++;
3712
3761
  }
@@ -3723,14 +3772,16 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
3723
3772
  const matchedExistMembers = /* @__PURE__ */ new Set();
3724
3773
  for (const [memberName, genMember] of genInfo.members) {
3725
3774
  const existMember = existInfo.members.get(memberName);
3726
- if (!existMember || !genMember.docstring) continue;
3775
+ if (!existMember) continue;
3727
3776
  matchedExistMembers.add(memberName);
3777
+ if (!genMember.docstring) continue;
3728
3778
  if (existMember.docstring) {
3729
3779
  if (!existMember.docstring.text.includes("@oagen-ignore") && existMember.docstring.text !== genMember.docstring.text) {
3780
+ const newText = preserveDeprecatedTags(existMember.docstring.text, genMember.docstring.text);
3730
3781
  edits.push({
3731
3782
  start: existMember.docstring.startIndex,
3732
3783
  end: existMember.docstring.endIndex,
3733
- newText: genMember.docstring.text
3784
+ newText
3734
3785
  });
3735
3786
  docstringUpdates++;
3736
3787
  }
@@ -3745,34 +3796,59 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
3745
3796
  docstringUpdates++;
3746
3797
  }
3747
3798
  }
3748
- for (const [_genName, genMember] of genInfo.members) {
3799
+ const unmatchedGenByFp = /* @__PURE__ */ new Map();
3800
+ for (const [genName, genMember] of genInfo.members) {
3801
+ if (matchedExistMembers.has(genName)) continue;
3749
3802
  if (!genMember.docstring || !genMember.urlFingerprint) continue;
3750
- for (const [existName, existMember] of existInfo.members) {
3751
- if (matchedExistMembers.has(existName)) continue;
3752
- if (!existMember.urlFingerprint || existMember.urlFingerprint !== genMember.urlFingerprint) continue;
3753
- if (existMember.docstring) {
3754
- if (existMember.docstring.text.includes("@oagen-ignore")) continue;
3755
- if (existMember.docstring.text !== genMember.docstring.text) {
3756
- edits.push({
3757
- start: existMember.docstring.startIndex,
3758
- end: existMember.docstring.endIndex,
3759
- newText: genMember.docstring.text
3760
- });
3761
- docstringUpdates++;
3762
- }
3763
- } else {
3764
- const lineStart = existMember.declStartIndex - existMember.declColumn;
3765
- const indent = " ".repeat(existMember.declColumn);
3803
+ if (existInfo.members.has(genName)) continue;
3804
+ const bucket = unmatchedGenByFp.get(genMember.urlFingerprint) ?? [];
3805
+ bucket.push({
3806
+ name: genName,
3807
+ member: genMember
3808
+ });
3809
+ unmatchedGenByFp.set(genMember.urlFingerprint, bucket);
3810
+ }
3811
+ const unmatchedExistByFp = /* @__PURE__ */ new Map();
3812
+ for (const [existName, existMember] of existInfo.members) {
3813
+ if (matchedExistMembers.has(existName)) continue;
3814
+ if (!existMember.urlFingerprint) continue;
3815
+ const bucket = unmatchedExistByFp.get(existMember.urlFingerprint) ?? [];
3816
+ bucket.push({
3817
+ name: existName,
3818
+ member: existMember
3819
+ });
3820
+ unmatchedExistByFp.set(existMember.urlFingerprint, bucket);
3821
+ }
3822
+ for (const [fp, genBucket] of unmatchedGenByFp) {
3823
+ if (genBucket.length !== 1) continue;
3824
+ const existBucket = unmatchedExistByFp.get(fp);
3825
+ if (!existBucket || existBucket.length !== 1) continue;
3826
+ const genMember = genBucket[0].member;
3827
+ const existEntry = existBucket[0];
3828
+ const existMember = existEntry.member;
3829
+ if (!genMember.docstring) continue;
3830
+ if (existMember.docstring) {
3831
+ if (existMember.docstring.text.includes("@oagen-ignore")) continue;
3832
+ if (existMember.docstring.text !== genMember.docstring.text) {
3833
+ const newText = preserveDeprecatedTags(existMember.docstring.text, genMember.docstring.text);
3766
3834
  edits.push({
3767
- start: lineStart,
3768
- end: lineStart,
3769
- newText: indent + genMember.docstring.text + "\n"
3835
+ start: existMember.docstring.startIndex,
3836
+ end: existMember.docstring.endIndex,
3837
+ newText
3770
3838
  });
3771
3839
  docstringUpdates++;
3772
3840
  }
3773
- matchedExistMembers.add(existName);
3774
- break;
3841
+ } else {
3842
+ const lineStart = existMember.declStartIndex - existMember.declColumn;
3843
+ const indent = " ".repeat(existMember.declColumn);
3844
+ edits.push({
3845
+ start: lineStart,
3846
+ end: lineStart,
3847
+ newText: indent + genMember.docstring.text + "\n"
3848
+ });
3849
+ docstringUpdates++;
3775
3850
  }
3851
+ matchedExistMembers.add(existEntry.name);
3776
3852
  }
3777
3853
  }
3778
3854
  if (edits.length > 0) {
@@ -4059,12 +4135,16 @@ function spliceExtraImports(existingLines, resultLines) {
4059
4135
  const extraSimple = [...existingSimple].filter((l) => !generatedSimple.has(l));
4060
4136
  const existingFrom = collectFromImports(existingLines);
4061
4137
  const generatedFrom = collectFromImports(resultLines);
4138
+ const allGeneratedIds = /* @__PURE__ */ new Set();
4139
+ for (const ids of generatedFrom.values()) for (const id of ids) allGeneratedIds.add(id);
4062
4140
  const extraFromLines = [];
4063
4141
  for (const [mod, existIds] of existingFrom) {
4064
4142
  const genIds = generatedFrom.get(mod);
4065
- if (!genIds) extraFromLines.push(`from ${mod} import ${[...existIds].join(", ")}`);
4066
- else {
4067
- const extra = [...existIds].filter((id) => !genIds.has(id));
4143
+ if (!genIds) {
4144
+ const extra = [...existIds].filter((id) => !allGeneratedIds.has(id));
4145
+ if (extra.length > 0) extraFromLines.push(`from ${mod} import ${extra.join(", ")}`);
4146
+ } else {
4147
+ const extra = [...existIds].filter((id) => !genIds.has(id) && !allGeneratedIds.has(id));
4068
4148
  if (extra.length > 0) extraFromLines.push(`from ${mod} import ${extra.join(", ")}`);
4069
4149
  }
4070
4150
  }
@@ -4205,7 +4285,9 @@ function resolveOperations(spec, hints, mountRules) {
4205
4285
  service,
4206
4286
  methodName,
4207
4287
  mountOn,
4208
- wrappers
4288
+ wrappers,
4289
+ defaults: hint?.defaults ?? {},
4290
+ inferFromClient: hint?.inferFromClient ?? []
4209
4291
  });
4210
4292
  }
4211
4293
  return resolved;
@@ -4297,7 +4379,6 @@ function generateAllFiles(spec, emitter, ctx) {
4297
4379
  ...emitter.generateResources(spec.services, ctx),
4298
4380
  ...emitter.generateClient(spec, ctx),
4299
4381
  ...emitter.generateErrors(ctx),
4300
- ...emitter.generateConfig(ctx),
4301
4382
  ...emitter.generateTypeSignatures?.(reachableSpec, ctx) ?? [],
4302
4383
  ...emitter.generateTests(reachableSpec, ctx),
4303
4384
  ...emitter.generateManifest?.(spec, ctx) ?? []
@@ -6755,10 +6836,6 @@ export const ${`${toCamelCase(lang)}Emitter`}: Emitter = {
6755
6836
  return [];
6756
6837
  },
6757
6838
 
6758
- generateConfig(_ctx: EmitterContext): GeneratedFile[] {
6759
- return [];
6760
- },
6761
-
6762
6839
  generateTypeSignatures(_spec: ApiSpec, _ctx: EmitterContext): GeneratedFile[] {
6763
6840
  return [];
6764
6841
  },