@workos/oagen 0.22.3 → 0.22.5

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
@@ -1,6 +1,6 @@
1
1
  # oagen
2
2
 
3
- oagen ([**oʊweɪdd͡ʒɛn**](https://ipa-reader.com/?text=o%CA%8Awe%C9%AAdd%CD%A1%CA%92%C9%9Bn)) is a framework for building custom SDK generators from OpenAPI 3.x specifications.
3
+ oagen ([oʊweɪdd͡ʒɛn](https://ipa-reader.com/?text=o%CA%8Awe%C9%AAdd%CD%A1%CA%92%C9%9Bn)) is a framework for building custom SDK generators from OpenAPI 3.x specifications.
4
4
 
5
5
  Its core job is narrow:
6
6
 
@@ -4917,8 +4917,23 @@ async function overwriteWithPreservedRegions(existingContent, generatedContent,
4917
4917
  }
4918
4918
  replacements.sort((a, b) => b.startLine - a.startLine);
4919
4919
  for (const rep of replacements) result.splice(rep.startLine, rep.endLine - rep.startLine, ...rep.content);
4920
+ let regionDeclarationEdits = 0;
4921
+ {
4922
+ const topLevelEntries = blocksByClass.get("");
4923
+ if (topLevelEntries) {
4924
+ const alreadyPlaced = handledBlockKeys.has("");
4925
+ const remaining = [];
4926
+ for (const entry of topLevelEntries) {
4927
+ const outcome = await applyRegionDeclarationOverride(result, entry.lines, language, !alreadyPlaced);
4928
+ if (outcome.edited) regionDeclarationEdits++;
4929
+ if (!alreadyPlaced && !outcome.placed) remaining.push(entry);
4930
+ }
4931
+ if (!alreadyPlaced) if (remaining.length === 0) blocksByClass.delete("");
4932
+ else blocksByClass.set("", remaining);
4933
+ }
4934
+ }
4920
4935
  let effectiveClassEndLines = classEndLines;
4921
- if (replacedClasses.size > 0) effectiveClassEndLines = await extractClassEndLines(result.join("\n"), language);
4936
+ if (replacedClasses.size > 0 || regionDeclarationEdits > 0) effectiveClassEndLines = await extractClassEndLines(result.join("\n"), language);
4922
4937
  const insertions = [];
4923
4938
  let insertionOrder = 0;
4924
4939
  for (const [className, classBlocks] of blocksByClass) {
@@ -4949,7 +4964,7 @@ async function overwriteWithPreservedRegions(existingContent, generatedContent,
4949
4964
  }
4950
4965
  if (entry.lines.some((l) => /^\s*import\s/.test(l))) while (insertLine >= 2 && result[insertLine - 1]?.trim() === "" && /^\s*(import|using|from)\s/.test(result[insertLine - 2] ?? "")) insertLine--;
4951
4966
  const prevLine = insertLine > 0 ? result[insertLine - 1]?.trimStart() ?? "" : "";
4952
- const blockContent = /^import\s/.test(prevLine) || /^using\s/.test(prevLine) || /^from\s/.test(prevLine) || insertLine > 0 && prevLine === "" ? [...entry.lines] : ["", ...entry.lines];
4967
+ const blockContent = /^import\s/.test(prevLine) || /^from\s/.test(prevLine) || insertLine > 0 && prevLine === "" ? [...entry.lines] : ["", ...entry.lines];
4953
4968
  if ((result[insertLine]?.trimStart() ?? "").startsWith("///")) blockContent.push("");
4954
4969
  insertions.push({
4955
4970
  line: insertLine,
@@ -5044,6 +5059,139 @@ function findClassDeclarationStart(lines, className, beforeLine) {
5044
5059
  return -1;
5045
5060
  }
5046
5061
  /**
5062
+ * Conservative top-level declaration matcher for ignore-block content.
5063
+ * Matches column-0 declarations like `export interface X`, `type X = …`,
5064
+ * `const X = …`, `function X(…)`, `class X` (export optional). Names found
5065
+ * here are only acted on when the language's merge adapter also reports a
5066
+ * generated top-level statement with the same key, so false positives are
5067
+ * filtered by the real parser.
5068
+ */
5069
+ const REGION_DECLARATION_PATTERN = /^(?:export\s+)?(?:declare\s+)?(?:interface|type|enum|class|const|let|var|async\s+function|function)\s+([A-Za-z_$][\w$]*)/;
5070
+ /** Extract top-level symbol names declared inside an ignore block's lines. */
5071
+ function collectRegionDeclaredNames(blockLines) {
5072
+ const names = /* @__PURE__ */ new Set();
5073
+ for (const line of blockLines) {
5074
+ if (line.includes("@oagen-ignore")) continue;
5075
+ const m = line.match(REGION_DECLARATION_PATTERN);
5076
+ if (m) names.add(m[1]);
5077
+ }
5078
+ return names;
5079
+ }
5080
+ /** Inclusive line ranges of `@oagen-ignore-start`..`@oagen-ignore-end` regions. */
5081
+ function collectIgnoreRegionLineRanges(lines) {
5082
+ const ranges = [];
5083
+ let start = -1;
5084
+ for (let i = 0; i < lines.length; i++) {
5085
+ if (start === -1 && lines[i].includes("@oagen-ignore-start")) {
5086
+ start = i;
5087
+ continue;
5088
+ }
5089
+ if (start !== -1 && lines[i].includes("@oagen-ignore-end")) {
5090
+ ranges.push({
5091
+ start,
5092
+ end: i
5093
+ });
5094
+ start = -1;
5095
+ }
5096
+ }
5097
+ return ranges;
5098
+ }
5099
+ /**
5100
+ * Walk upward from a declaration's first line over its contiguous comment
5101
+ * block (doc comment), so the comment is dropped together with the
5102
+ * declaration. Stops at blank lines, region markers, and line 0 (a comment
5103
+ * on line 0 is the file header, not a docstring).
5104
+ */
5105
+ function extendOverPrecedingComments(lines, startLine) {
5106
+ let s = startLine;
5107
+ while (s > 1) {
5108
+ const prev = lines[s - 1].trim();
5109
+ if (prev === "" || prev.includes("@oagen-ignore")) break;
5110
+ if (!(prev.startsWith("//") || prev.startsWith("/*") || prev.startsWith("*") || prev.startsWith("#"))) break;
5111
+ s--;
5112
+ }
5113
+ return s;
5114
+ }
5115
+ /**
5116
+ * Drop generated top-level declarations that are redeclared inside an ignore
5117
+ * block ("region wins"). When `placeBlock` is set and at least one generated
5118
+ * declaration matches, the block's lines replace the first match in place
5119
+ * (mirroring the class-replacement behavior above); any further matches are
5120
+ * deleted. Mutates `result` in place.
5121
+ *
5122
+ * Returns `placed` (the block content now lives in `result` and must not be
5123
+ * inserted again) and `edited` (line numbers shifted).
5124
+ */
5125
+ async function applyRegionDeclarationOverride(result, blockLines, language, placeBlock) {
5126
+ const names = collectRegionDeclaredNames(blockLines);
5127
+ if (names.size === 0) return {
5128
+ placed: false,
5129
+ edited: false
5130
+ };
5131
+ const source = result.join("\n");
5132
+ let parsed;
5133
+ try {
5134
+ parsed = await extractStatements(source, language);
5135
+ } catch {
5136
+ return {
5137
+ placed: false,
5138
+ edited: false
5139
+ };
5140
+ }
5141
+ const regionRanges = collectIgnoreRegionLineRanges(result);
5142
+ const inRegion = (line) => regionRanges.some((r) => line >= r.start && line <= r.end);
5143
+ const spans = [];
5144
+ for (const stmt of parsed.statements) {
5145
+ if (stmt.kind !== "declaration" || !stmt.key || !names.has(stmt.key)) continue;
5146
+ let idx = source.indexOf(stmt.text);
5147
+ let startLine = -1;
5148
+ while (idx !== -1) {
5149
+ const line = source.slice(0, idx).split("\n").length - 1;
5150
+ if (!inRegion(line)) {
5151
+ startLine = line;
5152
+ break;
5153
+ }
5154
+ idx = source.indexOf(stmt.text, idx + 1);
5155
+ }
5156
+ if (startLine === -1) continue;
5157
+ const endLine = startLine + stmt.text.split("\n").length - 1;
5158
+ spans.push({
5159
+ start: extendOverPrecedingComments(result, startLine),
5160
+ end: endLine
5161
+ });
5162
+ }
5163
+ if (spans.length === 0) return {
5164
+ placed: false,
5165
+ edited: false
5166
+ };
5167
+ spans.sort((a, b) => a.start - b.start);
5168
+ for (let i = spans.length - 1; i >= 1; i--) {
5169
+ const span = spans[i];
5170
+ let deleteCount = span.end - span.start + 1;
5171
+ const prevBlank = span.start === 0 || (result[span.start - 1] ?? "").trim() === "";
5172
+ const nextBlank = (result[span.end + 1] ?? "").trim() === "";
5173
+ if (prevBlank && nextBlank && span.end + 1 < result.length) deleteCount++;
5174
+ result.splice(span.start, deleteCount);
5175
+ }
5176
+ const first = spans[0];
5177
+ if (placeBlock) {
5178
+ result.splice(first.start, first.end - first.start + 1, ...blockLines);
5179
+ return {
5180
+ placed: true,
5181
+ edited: true
5182
+ };
5183
+ }
5184
+ let deleteCount = first.end - first.start + 1;
5185
+ const prevBlank = first.start === 0 || (result[first.start - 1] ?? "").trim() === "";
5186
+ const nextBlank = (result[first.end + 1] ?? "").trim() === "";
5187
+ if (prevBlank && nextBlank && first.end + 1 < result.length) deleteCount++;
5188
+ result.splice(first.start, deleteCount);
5189
+ return {
5190
+ placed: false,
5191
+ edited: true
5192
+ };
5193
+ }
5194
+ /**
5047
5195
  * Collect `from X import Y, Z` identifiers grouped by module, handling
5048
5196
  * both single-line and multiline (parenthesized) forms.
5049
5197
  */