@workos/oagen 0.1.0 → 0.2.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.
Files changed (47) hide show
  1. package/dist/cli/index.d.mts +2 -0
  2. package/dist/cli/index.mjs +6018 -0
  3. package/dist/cli/index.mjs.map +1 -0
  4. package/dist/compat/{index.d.ts → index.d.mts} +43 -114
  5. package/dist/compat/index.d.mts.map +1 -0
  6. package/dist/compat/index.mjs +4562 -0
  7. package/dist/compat/index.mjs.map +1 -0
  8. package/dist/diff-DyQF4sel.mjs +2250 -0
  9. package/dist/diff-DyQF4sel.mjs.map +1 -0
  10. package/dist/differ-BwelBJN0.mjs +1100 -0
  11. package/dist/differ-BwelBJN0.mjs.map +1 -0
  12. package/dist/index.d.mts +295 -0
  13. package/dist/index.d.mts.map +1 -0
  14. package/dist/index.mjs +1553 -0
  15. package/dist/index.mjs.map +1 -0
  16. package/dist/scripts/smoke/baseline.d.mts +1 -0
  17. package/dist/scripts/smoke/baseline.mjs +1916 -0
  18. package/dist/scripts/smoke/baseline.mjs.map +1 -0
  19. package/dist/scripts/smoke/sdk-test.d.mts +1 -0
  20. package/dist/scripts/smoke/sdk-test.mjs +111 -0
  21. package/dist/scripts/smoke/sdk-test.mjs.map +1 -0
  22. package/dist/smoke.d.mts +331 -0
  23. package/dist/smoke.d.mts.map +1 -0
  24. package/dist/smoke.mjs +2084 -0
  25. package/dist/smoke.mjs.map +1 -0
  26. package/dist/tree-sitter-BPNfbEzY.mjs +359 -0
  27. package/dist/tree-sitter-BPNfbEzY.mjs.map +1 -0
  28. package/dist/types-O25qZxMl.d.mts +397 -0
  29. package/dist/types-O25qZxMl.d.mts.map +1 -0
  30. package/dist/verify/index.d.mts +82 -0
  31. package/dist/verify/index.d.mts.map +1 -0
  32. package/dist/verify/index.mjs +327 -0
  33. package/dist/verify/index.mjs.map +1 -0
  34. package/package.json +19 -16
  35. package/dist/chunk-CIX5NOBF.js +0 -263
  36. package/dist/chunk-IJ5E5AE7.js +0 -1040
  37. package/dist/chunk-VUSRNIG2.js +0 -1661
  38. package/dist/cli/index.js +0 -5314
  39. package/dist/compat/index.js +0 -4491
  40. package/dist/index.d.ts +0 -277
  41. package/dist/index.js +0 -1565
  42. package/dist/scripts/smoke/baseline.js +0 -1813
  43. package/dist/scripts/smoke/sdk-test.js +0 -93
  44. package/dist/smoke.js +0 -2025
  45. package/dist/types-B5t9O9Wm.d.ts +0 -367
  46. package/dist/verify/index.d.ts +0 -76
  47. package/dist/verify/index.js +0 -336
@@ -0,0 +1,327 @@
1
+ import { m as CommandError } from "../tree-sitter-BPNfbEzY.mjs";
2
+ import { i as generate, n as getEmitter, t as diffSpecs } from "../diff-DyQF4sel.mjs";
3
+ import { c as buildOverlayLookup, g as getExtractor, i as specDerivedFieldPaths, l as patchOverlay, n as filterSurface, o as specDerivedMethodPaths, r as specDerivedEnumValues, s as specDerivedNames, t as diffSurfaces } from "../differ-BwelBJN0.mjs";
4
+ import * as path$1 from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { closeSync, existsSync, openSync, readFileSync, readSync, readdirSync, statSync, unlinkSync } from "node:fs";
7
+ import { execFileSync } from "node:child_process";
8
+ //#region src/verify/run-compat-check.ts
9
+ async function runCompatCheck(baseline, outputDir, lang, spec) {
10
+ const extractor = getExtractor(lang);
11
+ const candidate = await extractor.extract(outputDir);
12
+ let scopedBaseline = baseline;
13
+ let scopedToSpec = false;
14
+ let scopedSymbolCount;
15
+ if (spec) {
16
+ scopedBaseline = filterSurface(baseline, specDerivedNames(spec, extractor.hints), {
17
+ fieldPaths: specDerivedFieldPaths(spec, extractor.hints),
18
+ methodPaths: specDerivedMethodPaths(spec),
19
+ enumValues: specDerivedEnumValues(spec)
20
+ });
21
+ scopedToSpec = true;
22
+ scopedSymbolCount = Object.keys(scopedBaseline.interfaces).length + Object.keys(scopedBaseline.classes).length + Object.keys(scopedBaseline.typeAliases).length + Object.keys(scopedBaseline.enums).length;
23
+ }
24
+ const diff = diffSurfaces(scopedBaseline, candidate, extractor.hints);
25
+ return {
26
+ passed: diff.violations.every((v) => v.severity !== "breaking"),
27
+ diff,
28
+ scopedToSpec,
29
+ scopedSymbolCount
30
+ };
31
+ }
32
+ //#endregion
33
+ //#region src/verify/run-overlay-retry-loop.ts
34
+ const PATCHABLE_CATEGORIES = new Set(["public-api", "export-structure"]);
35
+ /** Generated file header markers used by various emitters. */
36
+ const GENERATED_MARKERS = [
37
+ "Code generated by oagen",
38
+ "auto-generated by oagen",
39
+ "This file is auto-generated"
40
+ ];
41
+ /**
42
+ * Delete oagen-generated source files so the retry loop can regenerate them
43
+ * with overlay-corrected names. Only deletes files that contain an oagen
44
+ * generation marker, preserving hand-written files.
45
+ */
46
+ function cleanGeneratedFiles(dir) {
47
+ let entries;
48
+ try {
49
+ entries = readdirSync(dir);
50
+ } catch {
51
+ return;
52
+ }
53
+ for (const entry of entries) {
54
+ const full = path$1.join(dir, entry);
55
+ let stat;
56
+ try {
57
+ stat = statSync(full);
58
+ } catch {
59
+ continue;
60
+ }
61
+ if (stat.isDirectory()) {
62
+ cleanGeneratedFiles(full);
63
+ continue;
64
+ }
65
+ if (entry.endsWith(".json") || entry.endsWith(".lock")) continue;
66
+ try {
67
+ const buf = Buffer.alloc(200);
68
+ const fd = openSync(full, "r");
69
+ try {
70
+ readSync(fd, buf, 0, 200, 0);
71
+ } finally {
72
+ closeSync(fd);
73
+ }
74
+ const header = buf.toString("utf-8");
75
+ if (GENERATED_MARKERS.some((m) => header.includes(m))) unlinkSync(full);
76
+ } catch {}
77
+ }
78
+ }
79
+ /**
80
+ * Load a smoke-manifest.json from the generated SDK directory and convert
81
+ * it to ManifestEntry[] for the overlay builder.
82
+ */
83
+ function loadManifestFromDir(outputDir) {
84
+ const manifestPath = path$1.join(outputDir, "smoke-manifest.json");
85
+ if (!existsSync(manifestPath)) return void 0;
86
+ try {
87
+ const parsed = JSON.parse(readFileSync(manifestPath, "utf-8"));
88
+ if (Array.isArray(parsed)) return parsed;
89
+ if (typeof parsed === "object" && parsed !== null) return Object.entries(parsed).map(([httpKey, value]) => {
90
+ const spaceIdx = httpKey.indexOf(" ");
91
+ const httpMethod = httpKey.slice(0, spaceIdx);
92
+ const httpPath = httpKey.slice(spaceIdx + 1);
93
+ const v = value;
94
+ return {
95
+ operationId: "",
96
+ sdkResourceProperty: v.service ?? "",
97
+ sdkMethodName: v.sdkMethod ?? "",
98
+ httpMethod,
99
+ path: httpPath,
100
+ pathParams: [],
101
+ bodyFields: [],
102
+ queryFields: []
103
+ };
104
+ });
105
+ } catch {}
106
+ }
107
+ async function runOverlayRetryLoop(opts) {
108
+ const { baseline, parsedSpec, outputDir, lang, maxRetries, namespace, onRetry } = opts;
109
+ let overlay = buildOverlayLookup(baseline, loadManifestFromDir(outputDir), parsedSpec, getExtractor(lang).hints);
110
+ let prevPreserved = -1;
111
+ const patchedPerIteration = [];
112
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
113
+ const compatResult = await runCompatCheck(baseline, outputDir, lang, parsedSpec);
114
+ if (compatResult.passed) return {
115
+ status: "passed",
116
+ attempts: attempt,
117
+ patchedPerIteration,
118
+ compatResult
119
+ };
120
+ if (attempt === maxRetries) return {
121
+ status: "max-retries",
122
+ attempts: attempt,
123
+ patchedPerIteration,
124
+ compatResult
125
+ };
126
+ const patchable = compatResult.diff.violations.filter((v) => PATCHABLE_CATEGORIES.has(v.category));
127
+ if (patchable.length === 0) return {
128
+ status: "no-patchable",
129
+ attempts: attempt,
130
+ patchedPerIteration,
131
+ compatResult
132
+ };
133
+ const currentPreserved = compatResult.diff.preservedSymbols;
134
+ if (attempt > 0 && currentPreserved <= prevPreserved) return {
135
+ status: "stalled",
136
+ attempts: attempt,
137
+ patchedPerIteration,
138
+ compatResult
139
+ };
140
+ prevPreserved = currentPreserved;
141
+ patchedPerIteration.push(patchable.length);
142
+ onRetry?.(attempt + 1, maxRetries, patchable.length);
143
+ overlay = patchOverlay(overlay, patchable, baseline);
144
+ cleanGeneratedFiles(outputDir);
145
+ await generate(parsedSpec, getEmitter(lang), {
146
+ namespace: namespace ?? parsedSpec.name,
147
+ outputDir,
148
+ overlayLookup: overlay,
149
+ apiSurface: baseline
150
+ });
151
+ }
152
+ return {
153
+ status: "max-retries",
154
+ attempts: maxRetries,
155
+ patchedPerIteration,
156
+ compatResult: await runCompatCheck(baseline, outputDir, lang, parsedSpec)
157
+ };
158
+ }
159
+ //#endregion
160
+ //#region src/compat/staleness.ts
161
+ /**
162
+ * Detect symbols that exist in the live SDK surface but are no longer defined
163
+ * in the current OpenAPI spec. These are "stale" — they compile and run but
164
+ * represent dead code from a previous spec version.
165
+ *
166
+ * Requires both the old and new spec so we can distinguish hand-written SDK
167
+ * symbols (never in any spec) from genuinely removed spec symbols.
168
+ */
169
+ function detectStaleSymbols(liveSurface, oldSpec, newSpec, hints) {
170
+ const violations = [];
171
+ const oldNames = specDerivedNames(oldSpec, hints);
172
+ const newNames = specDerivedNames(newSpec, hints);
173
+ const removedNames = /* @__PURE__ */ new Set();
174
+ for (const name of oldNames) if (!newNames.has(name)) removedNames.add(name);
175
+ for (const name of removedNames) {
176
+ if (liveSurface.classes[name]) violations.push({
177
+ category: "staleness",
178
+ severity: "warning",
179
+ symbolPath: name,
180
+ baseline: name,
181
+ candidate: "(removed from spec)",
182
+ message: `Class "${name}" is no longer defined in the OpenAPI spec`
183
+ });
184
+ if (liveSurface.interfaces[name]) violations.push({
185
+ category: "staleness",
186
+ severity: "warning",
187
+ symbolPath: name,
188
+ baseline: name,
189
+ candidate: "(removed from spec)",
190
+ message: `Interface "${name}" is no longer defined in the OpenAPI spec`
191
+ });
192
+ if (liveSurface.typeAliases[name]) violations.push({
193
+ category: "staleness",
194
+ severity: "warning",
195
+ symbolPath: name,
196
+ baseline: name,
197
+ candidate: "(removed from spec)",
198
+ message: `Type alias "${name}" is no longer defined in the OpenAPI spec`
199
+ });
200
+ if (liveSurface.enums[name]) violations.push({
201
+ category: "staleness",
202
+ severity: "warning",
203
+ symbolPath: name,
204
+ baseline: name,
205
+ candidate: "(removed from spec)",
206
+ message: `Enum "${name}" is no longer defined in the OpenAPI spec`
207
+ });
208
+ }
209
+ const oldFieldPaths = specDerivedFieldPaths(oldSpec, hints);
210
+ const newFieldPaths = specDerivedFieldPaths(newSpec, hints);
211
+ for (const fieldPath of oldFieldPaths) {
212
+ if (newFieldPaths.has(fieldPath)) continue;
213
+ const [modelName, fieldName] = fieldPath.split(".");
214
+ if (removedNames.has(modelName)) continue;
215
+ if (liveSurface.interfaces[modelName]?.fields[fieldName]) violations.push({
216
+ category: "staleness",
217
+ severity: "warning",
218
+ symbolPath: fieldPath,
219
+ baseline: fieldName,
220
+ candidate: "(removed from spec)",
221
+ message: `Field "${fieldPath}" is no longer defined in the OpenAPI spec`
222
+ });
223
+ }
224
+ const diff = diffSpecs(oldSpec, newSpec);
225
+ for (const change of diff.changes) if (change.kind === "operation-removed") {
226
+ if (liveSurface.classes[change.serviceName]?.methods[change.operationName]) violations.push({
227
+ category: "staleness",
228
+ severity: "warning",
229
+ symbolPath: `${change.serviceName}.${change.operationName}`,
230
+ baseline: change.operationName,
231
+ candidate: "(removed from spec)",
232
+ message: `Method "${change.serviceName}.${change.operationName}" is no longer defined in the OpenAPI spec`
233
+ });
234
+ }
235
+ return violations;
236
+ }
237
+ //#endregion
238
+ //#region src/verify/run-staleness-check.ts
239
+ function runStalenessCheck(baseline, oldSpec, newSpec, lang) {
240
+ return { violations: detectStaleSymbols(baseline, oldSpec, newSpec, getExtractor(lang).hints) };
241
+ }
242
+ //#endregion
243
+ //#region src/verify/run-smoke-check.ts
244
+ const __filename = fileURLToPath(import.meta.url);
245
+ const __dirname = path$1.dirname(__filename);
246
+ function resolveScript(scriptRelPath) {
247
+ const distScript = path$1.resolve(__dirname, "..", "..", "dist", "scripts", scriptRelPath.replace(/\.ts$/, ".js"));
248
+ if (existsSync(distScript)) return {
249
+ bin: "node",
250
+ script: distScript
251
+ };
252
+ return {
253
+ bin: "npx",
254
+ script: path$1.resolve(__dirname, "..", "..", "scripts", scriptRelPath)
255
+ };
256
+ }
257
+ function runPackagedScript(scriptRelPath, args) {
258
+ const { bin, script } = resolveScript(scriptRelPath);
259
+ execFileSync(bin, bin === "npx" ? [
260
+ "tsx",
261
+ script,
262
+ ...args
263
+ ] : [script, ...args], {
264
+ stdio: "inherit",
265
+ env: process.env
266
+ });
267
+ }
268
+ function runSmokeCheck(opts) {
269
+ const { spec, lang, output, rawResults, smokeConfig, smokeRunner } = opts;
270
+ let baselinePath = rawResults ?? "smoke-results-raw.json";
271
+ let generatedBaseline = false;
272
+ if (!rawResults && !existsSync("smoke-results-raw.json")) {
273
+ if (!spec) throw new CommandError("error: --spec <path> or OPENAPI_SPEC_PATH env var is required when no raw baseline exists", "", 1);
274
+ try {
275
+ runPackagedScript("smoke/baseline.ts", ["--spec", spec]);
276
+ } catch {
277
+ throw new CommandError("Baseline generation failed", "", 1);
278
+ }
279
+ baselinePath = "smoke-results-spec-baseline.json";
280
+ generatedBaseline = true;
281
+ }
282
+ const smokeArgs = [
283
+ "--lang",
284
+ lang,
285
+ "--sdk-path",
286
+ output,
287
+ "--raw-results",
288
+ baselinePath
289
+ ];
290
+ if (spec) smokeArgs.push("--spec", spec);
291
+ if (smokeConfig) smokeArgs.push("--smoke-config", smokeConfig);
292
+ try {
293
+ if (smokeRunner) {
294
+ const bin = smokeRunner.endsWith(".ts") ? "npx" : "node";
295
+ execFileSync(bin, bin === "npx" ? [
296
+ "tsx",
297
+ smokeRunner,
298
+ ...smokeArgs
299
+ ] : [smokeRunner, ...smokeArgs], {
300
+ stdio: "inherit",
301
+ env: process.env
302
+ });
303
+ } else runPackagedScript("smoke/sdk-test.ts", smokeArgs);
304
+ return {
305
+ passed: true,
306
+ baselinePath,
307
+ generatedBaseline
308
+ };
309
+ } catch {
310
+ if (existsSync("smoke-compile-errors.json")) return {
311
+ passed: false,
312
+ compileErrors: true,
313
+ baselinePath,
314
+ generatedBaseline
315
+ };
316
+ return {
317
+ passed: false,
318
+ findingsCount: existsSync("smoke-diff-findings.json") ? JSON.parse(readFileSync("smoke-diff-findings.json", "utf-8")).length : void 0,
319
+ baselinePath,
320
+ generatedBaseline
321
+ };
322
+ }
323
+ }
324
+ //#endregion
325
+ export { runCompatCheck, runOverlayRetryLoop, runSmokeCheck, runStalenessCheck };
326
+
327
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["path","path"],"sources":["../../src/verify/run-compat-check.ts","../../src/verify/run-overlay-retry-loop.ts","../../src/compat/staleness.ts","../../src/verify/run-staleness-check.ts","../../src/verify/run-smoke-check.ts"],"sourcesContent":["import { getExtractor } from '../compat/extractor-registry.js';\nimport {\n diffSurfaces,\n specDerivedNames,\n specDerivedFieldPaths,\n specDerivedMethodPaths,\n specDerivedEnumValues,\n filterSurface,\n} from '../compat/differ.js';\nimport type { ApiSpec } from '../ir/types.js';\nimport type { ApiSurface } from '../compat/types.js';\nimport type { CompatCheckResult } from './types.js';\n\nexport async function runCompatCheck(\n baseline: ApiSurface,\n outputDir: string,\n lang: string,\n spec?: ApiSpec,\n): Promise<CompatCheckResult> {\n const extractor = getExtractor(lang);\n const candidate = await extractor.extract(outputDir);\n\n let scopedBaseline = baseline;\n let scopedToSpec = false;\n let scopedSymbolCount: number | undefined;\n if (spec) {\n const allowed = specDerivedNames(spec, extractor.hints);\n const fieldPaths = specDerivedFieldPaths(spec, extractor.hints);\n const methodPaths = specDerivedMethodPaths(spec);\n const enumVals = specDerivedEnumValues(spec);\n scopedBaseline = filterSurface(baseline, allowed, { fieldPaths, methodPaths, enumValues: enumVals });\n scopedToSpec = true;\n scopedSymbolCount =\n Object.keys(scopedBaseline.interfaces).length +\n Object.keys(scopedBaseline.classes).length +\n Object.keys(scopedBaseline.typeAliases).length +\n Object.keys(scopedBaseline.enums).length;\n }\n\n const diff = diffSurfaces(scopedBaseline, candidate, extractor.hints);\n const passed = diff.violations.every((v) => v.severity !== 'breaking');\n\n return { passed, diff, scopedToSpec, scopedSymbolCount };\n}\n","import { existsSync, readFileSync, readdirSync, unlinkSync, statSync, openSync, readSync, closeSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { buildOverlayLookup, patchOverlay } from '../compat/overlay.js';\nimport type { ManifestEntry } from '../compat/overlay.js';\nimport { getExtractor } from '../compat/extractor-registry.js';\nimport type { ApiSurface, ViolationCategory } from '../compat/types.js';\nimport type { ApiSpec } from '../ir/types.js';\nimport { getEmitter } from '../engine/registry.js';\nimport { generate } from '../engine/orchestrator.js';\nimport { runCompatCheck } from './run-compat-check.js';\nimport type { OverlayRetryResult } from './types.js';\n\nconst PATCHABLE_CATEGORIES: Set<ViolationCategory> = new Set(['public-api', 'export-structure']);\n\n/** Generated file header markers used by various emitters. */\nconst GENERATED_MARKERS = ['Code generated by oagen', 'auto-generated by oagen', 'This file is auto-generated'];\n\n/**\n * Delete oagen-generated source files so the retry loop can regenerate them\n * with overlay-corrected names. Only deletes files that contain an oagen\n * generation marker, preserving hand-written files.\n */\nfunction cleanGeneratedFiles(dir: string): void {\n let entries: string[];\n try {\n entries = readdirSync(dir);\n } catch {\n return;\n }\n for (const entry of entries) {\n const full = path.join(dir, entry);\n let stat;\n try {\n stat = statSync(full);\n } catch {\n continue;\n }\n if (stat.isDirectory()) {\n cleanGeneratedFiles(full);\n continue;\n }\n // Skip non-source files and manifests\n if (entry.endsWith('.json') || entry.endsWith('.lock')) continue;\n // Read only the first 200 bytes to check for generation marker\n try {\n const buf = Buffer.alloc(200);\n const fd = openSync(full, 'r');\n try {\n readSync(fd, buf, 0, 200, 0);\n } finally {\n closeSync(fd);\n }\n const header = buf.toString('utf-8');\n if (GENERATED_MARKERS.some((m) => header.includes(m))) {\n unlinkSync(full);\n }\n } catch {\n // ignore read/delete errors\n }\n }\n}\n\n/**\n * Load a smoke-manifest.json from the generated SDK directory and convert\n * it to ManifestEntry[] for the overlay builder.\n */\nfunction loadManifestFromDir(outputDir: string): ManifestEntry[] | undefined {\n const manifestPath = path.join(outputDir, 'smoke-manifest.json');\n if (!existsSync(manifestPath)) return undefined;\n try {\n const parsed = JSON.parse(readFileSync(manifestPath, 'utf-8'));\n if (Array.isArray(parsed)) return parsed as ManifestEntry[];\n if (typeof parsed === 'object' && parsed !== null) {\n return Object.entries(parsed).map(([httpKey, value]) => {\n const spaceIdx = httpKey.indexOf(' ');\n const httpMethod = httpKey.slice(0, spaceIdx);\n const httpPath = httpKey.slice(spaceIdx + 1);\n const v = value as { sdkMethod?: string; service?: string };\n return {\n operationId: '',\n sdkResourceProperty: v.service ?? '',\n sdkMethodName: v.sdkMethod ?? '',\n httpMethod,\n path: httpPath,\n pathParams: [],\n bodyFields: [],\n queryFields: [],\n };\n });\n }\n } catch {\n // ignore parse errors\n }\n return undefined;\n}\n\nexport async function runOverlayRetryLoop(opts: {\n baseline: ApiSurface;\n parsedSpec: ApiSpec;\n outputDir: string;\n lang: string;\n maxRetries: number;\n namespace?: string;\n onRetry?: (attemptNumber: number, maxRetries: number, patchableCount: number) => void;\n}): Promise<OverlayRetryResult> {\n const { baseline, parsedSpec, outputDir, lang, maxRetries, namespace, onRetry } = opts;\n\n const manifest = loadManifestFromDir(outputDir);\n // Pass language-specific hints so property-to-class matching works correctly\n // (e.g., Ruby snake_case \"api_keys\" → PascalCase \"ApiKeys\").\n const extractor = getExtractor(lang);\n let overlay = buildOverlayLookup(baseline, manifest, parsedSpec, extractor.hints);\n let prevPreserved = -1;\n const patchedPerIteration: number[] = [];\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const compatResult = await runCompatCheck(baseline, outputDir, lang, parsedSpec);\n if (compatResult.passed) {\n return {\n status: 'passed',\n attempts: attempt,\n patchedPerIteration,\n compatResult,\n };\n }\n\n if (attempt === maxRetries) {\n return {\n status: 'max-retries',\n attempts: attempt,\n patchedPerIteration,\n compatResult,\n };\n }\n\n const patchable = compatResult.diff.violations.filter((v) => PATCHABLE_CATEGORIES.has(v.category));\n if (patchable.length === 0) {\n return {\n status: 'no-patchable',\n attempts: attempt,\n patchedPerIteration,\n compatResult,\n };\n }\n\n const currentPreserved = compatResult.diff.preservedSymbols;\n if (attempt > 0 && currentPreserved <= prevPreserved) {\n return {\n status: 'stalled',\n attempts: attempt,\n patchedPerIteration,\n compatResult,\n };\n }\n prevPreserved = currentPreserved;\n patchedPerIteration.push(patchable.length);\n onRetry?.(attempt + 1, maxRetries, patchable.length);\n\n overlay = patchOverlay(overlay, patchable, baseline);\n\n // Pre-delete generated source files so the writer creates them fresh\n // with overlay-corrected names. Without this, the AST merger would preserve\n // old method/class names from the existing files.\n // Only delete files that look like oagen-generated source files (have the header).\n cleanGeneratedFiles(outputDir);\n\n const emitter = getEmitter(lang);\n await generate(parsedSpec, emitter, {\n namespace: namespace ?? parsedSpec.name,\n outputDir,\n overlayLookup: overlay,\n apiSurface: baseline,\n });\n }\n\n const compatResult = await runCompatCheck(baseline, outputDir, lang, parsedSpec);\n return {\n status: 'max-retries',\n attempts: maxRetries,\n patchedPerIteration,\n compatResult,\n };\n}\n","import type { ApiSurface, LanguageHints, Violation } from './types.js';\nimport type { ApiSpec } from '../ir/types.js';\nimport { specDerivedNames, specDerivedFieldPaths } from './spec-filter.js';\nimport { diffSpecs } from '../differ/diff.js';\n\n/**\n * Detect symbols that exist in the live SDK surface but are no longer defined\n * in the current OpenAPI spec. These are \"stale\" — they compile and run but\n * represent dead code from a previous spec version.\n *\n * Requires both the old and new spec so we can distinguish hand-written SDK\n * symbols (never in any spec) from genuinely removed spec symbols.\n */\nexport function detectStaleSymbols(\n liveSurface: ApiSurface,\n oldSpec: ApiSpec,\n newSpec: ApiSpec,\n hints: LanguageHints,\n): Violation[] {\n const violations: Violation[] = [];\n\n // Step 1-3: Name-set difference for top-level symbols\n const oldNames = specDerivedNames(oldSpec, hints);\n const newNames = specDerivedNames(newSpec, hints);\n\n const removedNames = new Set<string>();\n for (const name of oldNames) {\n if (!newNames.has(name)) removedNames.add(name);\n }\n\n for (const name of removedNames) {\n if (liveSurface.classes[name]) {\n violations.push({\n category: 'staleness',\n severity: 'warning',\n symbolPath: name,\n baseline: name,\n candidate: '(removed from spec)',\n message: `Class \"${name}\" is no longer defined in the OpenAPI spec`,\n });\n }\n if (liveSurface.interfaces[name]) {\n violations.push({\n category: 'staleness',\n severity: 'warning',\n symbolPath: name,\n baseline: name,\n candidate: '(removed from spec)',\n message: `Interface \"${name}\" is no longer defined in the OpenAPI spec`,\n });\n }\n if (liveSurface.typeAliases[name]) {\n violations.push({\n category: 'staleness',\n severity: 'warning',\n symbolPath: name,\n baseline: name,\n candidate: '(removed from spec)',\n message: `Type alias \"${name}\" is no longer defined in the OpenAPI spec`,\n });\n }\n if (liveSurface.enums[name]) {\n violations.push({\n category: 'staleness',\n severity: 'warning',\n symbolPath: name,\n baseline: name,\n candidate: '(removed from spec)',\n message: `Enum \"${name}\" is no longer defined in the OpenAPI spec`,\n });\n }\n }\n\n // Step 4-6: Field-level staleness for models that still exist in new spec\n const oldFieldPaths = specDerivedFieldPaths(oldSpec, hints);\n const newFieldPaths = specDerivedFieldPaths(newSpec, hints);\n\n for (const fieldPath of oldFieldPaths) {\n if (newFieldPaths.has(fieldPath)) continue;\n\n const [modelName, fieldName] = fieldPath.split('.');\n // Only flag field-level staleness for models that still exist in the new spec\n // (fully removed models are already caught above)\n if (removedNames.has(modelName)) continue;\n\n const iface = liveSurface.interfaces[modelName];\n if (iface?.fields[fieldName]) {\n violations.push({\n category: 'staleness',\n severity: 'warning',\n symbolPath: fieldPath,\n baseline: fieldName,\n candidate: '(removed from spec)',\n message: `Field \"${fieldPath}\" is no longer defined in the OpenAPI spec`,\n });\n }\n }\n\n // Step 7: Operation-level staleness via diffSpecs\n const diff = diffSpecs(oldSpec, newSpec);\n for (const change of diff.changes) {\n if (change.kind === 'operation-removed') {\n const cls = liveSurface.classes[change.serviceName];\n if (cls?.methods[change.operationName]) {\n violations.push({\n category: 'staleness',\n severity: 'warning',\n symbolPath: `${change.serviceName}.${change.operationName}`,\n baseline: change.operationName,\n candidate: '(removed from spec)',\n message: `Method \"${change.serviceName}.${change.operationName}\" is no longer defined in the OpenAPI spec`,\n });\n }\n }\n }\n\n return violations;\n}\n","import { getExtractor } from '../compat/extractor-registry.js';\nimport { detectStaleSymbols } from '../compat/staleness.js';\nimport type { ApiSpec } from '../ir/types.js';\nimport type { ApiSurface } from '../compat/types.js';\nimport type { StalenessCheckResult } from './types.js';\n\nexport function runStalenessCheck(\n baseline: ApiSurface,\n oldSpec: ApiSpec,\n newSpec: ApiSpec,\n lang: string,\n): StalenessCheckResult {\n const extractor = getExtractor(lang);\n const violations = detectStaleSymbols(baseline, oldSpec, newSpec, extractor.hints);\n return { violations };\n}\n","import { execFileSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport * as path from 'node:path';\nimport { CommandError } from '../errors.js';\nimport type { SmokeCheckResult } from './types.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nfunction resolveScript(scriptRelPath: string): { bin: string; script: string } {\n const distScript = path.resolve(__dirname, '..', '..', 'dist', 'scripts', scriptRelPath.replace(/\\.ts$/, '.js'));\n if (existsSync(distScript)) {\n return { bin: 'node', script: distScript };\n }\n const srcScript = path.resolve(__dirname, '..', '..', 'scripts', scriptRelPath);\n return { bin: 'npx', script: srcScript };\n}\n\nfunction runPackagedScript(scriptRelPath: string, args: string[]): void {\n const { bin, script } = resolveScript(scriptRelPath);\n const fullArgs = bin === 'npx' ? ['tsx', script, ...args] : [script, ...args];\n execFileSync(bin, fullArgs, { stdio: 'inherit', env: process.env });\n}\n\nexport function runSmokeCheck(opts: {\n spec?: string;\n lang: string;\n output: string;\n rawResults?: string;\n smokeConfig?: string;\n smokeRunner?: string;\n}): SmokeCheckResult {\n const { spec, lang, output, rawResults, smokeConfig, smokeRunner } = opts;\n\n let baselinePath = rawResults ?? 'smoke-results-raw.json';\n let generatedBaseline = false;\n\n if (!rawResults && !existsSync('smoke-results-raw.json')) {\n if (!spec) {\n throw new CommandError(\n 'error: --spec <path> or OPENAPI_SPEC_PATH env var is required when no raw baseline exists',\n '',\n 1,\n );\n }\n\n try {\n runPackagedScript('smoke/baseline.ts', ['--spec', spec]);\n } catch {\n throw new CommandError('Baseline generation failed', '', 1);\n }\n baselinePath = 'smoke-results-spec-baseline.json';\n generatedBaseline = true;\n }\n\n const smokeArgs = ['--lang', lang, '--sdk-path', output, '--raw-results', baselinePath];\n if (spec) smokeArgs.push('--spec', spec);\n if (smokeConfig) smokeArgs.push('--smoke-config', smokeConfig);\n\n try {\n if (smokeRunner) {\n const bin = smokeRunner.endsWith('.ts') ? 'npx' : 'node';\n const fullArgs = bin === 'npx' ? ['tsx', smokeRunner, ...smokeArgs] : [smokeRunner, ...smokeArgs];\n execFileSync(bin, fullArgs, { stdio: 'inherit', env: process.env });\n } else {\n runPackagedScript('smoke/sdk-test.ts', smokeArgs);\n }\n\n return { passed: true, baselinePath, generatedBaseline };\n } catch {\n if (existsSync('smoke-compile-errors.json')) {\n return { passed: false, compileErrors: true, baselinePath, generatedBaseline };\n }\n\n const findingsCount = existsSync('smoke-diff-findings.json')\n ? (JSON.parse(readFileSync('smoke-diff-findings.json', 'utf-8')) as unknown[]).length\n : undefined;\n\n return { passed: false, findingsCount, baselinePath, generatedBaseline };\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@workos/oagen",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Framework for building OpenAPI SDK emitters",
5
5
  "author": "WorkOS",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
- "main": "dist/index.js",
9
- "types": "dist/index.d.ts",
8
+ "main": "dist/index.mjs",
9
+ "types": "dist/index.d.mts",
10
10
  "engines": {
11
11
  "node": ">=24.10.0"
12
12
  },
@@ -27,24 +27,24 @@
27
27
  ],
28
28
  "exports": {
29
29
  ".": {
30
- "types": "./dist/index.d.ts",
31
- "import": "./dist/index.js"
30
+ "types": "./dist/index.d.mts",
31
+ "import": "./dist/index.mjs"
32
32
  },
33
33
  "./compat": {
34
- "types": "./dist/compat/index.d.ts",
35
- "import": "./dist/compat/index.js"
34
+ "types": "./dist/compat/index.d.mts",
35
+ "import": "./dist/compat/index.mjs"
36
36
  },
37
37
  "./verify": {
38
- "types": "./dist/verify/index.d.ts",
39
- "import": "./dist/verify/index.js"
38
+ "types": "./dist/verify/index.d.mts",
39
+ "import": "./dist/verify/index.mjs"
40
40
  },
41
41
  "./smoke": {
42
- "types": "./dist/smoke.d.ts",
43
- "import": "./dist/smoke.js"
42
+ "types": "./dist/smoke.d.mts",
43
+ "import": "./dist/smoke.mjs"
44
44
  }
45
45
  },
46
46
  "bin": {
47
- "oagen": "dist/cli/index.js"
47
+ "oagen": "dist/cli/index.mjs"
48
48
  },
49
49
  "files": [
50
50
  "dist",
@@ -54,7 +54,7 @@
54
54
  "examples"
55
55
  ],
56
56
  "scripts": {
57
- "build": "tsup",
57
+ "build": "tsdown",
58
58
  "dev": "tsx src/cli/index.ts",
59
59
  "lint": "oxlint --deny-warnings",
60
60
  "lint:fix": "oxlint --fix",
@@ -76,27 +76,30 @@
76
76
  "dependencies": {
77
77
  "@redocly/openapi-core": "^1.34.3",
78
78
  "commander": "^13.1.0",
79
+ "dotenv": "^17.3.1",
79
80
  "tree-sitter": "^0.21.1",
81
+ "tree-sitter-c-sharp": "^0.23.1",
82
+ "tree-sitter-elixir": "^0.3.5",
80
83
  "tree-sitter-go": "^0.23.4",
84
+ "tree-sitter-kotlin": "^0.3.8",
81
85
  "tree-sitter-php": "^0.23.12",
82
86
  "tree-sitter-python": "^0.21.0",
83
87
  "tree-sitter-ruby": "^0.21.0",
84
88
  "tree-sitter-rust": "^0.21.0",
85
89
  "tree-sitter-typescript": "^0.23.2",
86
90
  "tsx": "^4.19.0",
87
- "typescript": "^5.7.0"
91
+ "typescript": "^6.0.0"
88
92
  },
89
93
  "devDependencies": {
90
94
  "@commitlint/cli": "^20.5.0",
91
95
  "@commitlint/config-conventional": "^20.5.0",
92
96
  "@types/node": "^25.3.3",
93
97
  "@vitest/coverage-v8": "^3.2.4",
94
- "dotenv": "^17.3.1",
95
98
  "husky": "^9.1.7",
96
99
  "oxfmt": "^0.36.0",
97
100
  "oxlint": "^1.51.0",
98
101
  "prettier": "^3.8.1",
99
- "tsup": "^8.4.0",
102
+ "tsdown": "^0.21.5",
100
103
  "vitest": "^3.0.0"
101
104
  }
102
105
  }