@workos/oagen 0.2.0 → 0.3.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/dist/cli/index.mjs +238 -23
- package/dist/cli/index.mjs.map +1 -1
- package/dist/compat/index.mjs +2 -2
- package/dist/{diff-DyQF4sel.mjs → diff-Cn6S74vV.mjs} +194 -12
- package/dist/diff-Cn6S74vV.mjs.map +1 -0
- package/dist/{differ-BwelBJN0.mjs → differ-B6472Yef.mjs} +2 -2
- package/dist/{differ-BwelBJN0.mjs.map → differ-B6472Yef.mjs.map} +1 -1
- package/dist/index.d.mts +30 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3 -3
- package/dist/scripts/smoke/baseline.mjs +37 -2
- package/dist/scripts/smoke/baseline.mjs.map +1 -1
- package/dist/smoke.d.mts.map +1 -1
- package/dist/smoke.mjs +37 -2
- package/dist/smoke.mjs.map +1 -1
- package/dist/{tree-sitter-BPNfbEzY.mjs → tree-sitter-DFRXfWg9.mjs} +38 -3
- package/dist/{tree-sitter-BPNfbEzY.mjs.map → tree-sitter-DFRXfWg9.mjs.map} +1 -1
- package/dist/verify/index.mjs +4 -4
- package/package.json +1 -1
- package/dist/diff-DyQF4sel.mjs.map +0 -1
package/dist/compat/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as safeParse$1, v as ExtractorError } from "../tree-sitter-
|
|
2
|
-
import { _ as registerExtractor, a as specDerivedHttpKeys, c as buildOverlayLookup, d as defaultIsNullableOnlyDifference, f as namedTypeWordsOverlap, g as getExtractor, h as typeExistsInSurface, i as specDerivedFieldPaths, l as patchOverlay, m as resolveHints, n as filterSurface, o as specDerivedMethodPaths, p as nodeHints, r as specDerivedEnumValues, s as specDerivedNames, t as diffSurfaces, u as NAMED_TYPE_RE } from "../differ-
|
|
1
|
+
import { t as safeParse$1, v as ExtractorError } from "../tree-sitter-DFRXfWg9.mjs";
|
|
2
|
+
import { _ as registerExtractor, a as specDerivedHttpKeys, c as buildOverlayLookup, d as defaultIsNullableOnlyDifference, f as namedTypeWordsOverlap, g as getExtractor, h as typeExistsInSurface, i as specDerivedFieldPaths, l as patchOverlay, m as resolveHints, n as filterSurface, o as specDerivedMethodPaths, p as nodeHints, r as specDerivedEnumValues, s as specDerivedNames, t as diffSurfaces, u as NAMED_TYPE_RE } from "../differ-B6472Yef.mjs";
|
|
3
3
|
import path, { basename, dirname, join, relative, resolve } from "node:path";
|
|
4
4
|
import Parser from "tree-sitter";
|
|
5
5
|
import ts from "typescript";
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { C as assertNever, f as toSnakeCase, i as normalizeJsExtension, t as safeParse, x as RegistryError } from "./tree-sitter-
|
|
1
|
+
import { A as walkTypeRef, C as assertNever, f as toSnakeCase, i as normalizeJsExtension, t as safeParse, x as RegistryError } from "./tree-sitter-DFRXfWg9.mjs";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path$1 from "node:path";
|
|
4
|
+
import path from "node:path";
|
|
4
5
|
import Parser from "tree-sitter";
|
|
6
|
+
import { execFile } from "node:child_process";
|
|
7
|
+
import { promisify } from "node:util";
|
|
5
8
|
//#region src/engine/merge-adapters/url-fingerprint.ts
|
|
6
9
|
/**
|
|
7
10
|
* Extract a URL path fingerprint from a method body using tree-sitter AST traversal.
|
|
@@ -1558,11 +1561,27 @@ async function mergeIntoExisting(existingContent, generatedContent, language, he
|
|
|
1558
1561
|
const addedParts = [...toAppend];
|
|
1559
1562
|
if (insertions) for (const ins of insertions) addedParts.push(ins.text);
|
|
1560
1563
|
const addedCodeText = addedParts.join("\n");
|
|
1561
|
-
const filteredImports =
|
|
1564
|
+
const filteredImports = [];
|
|
1565
|
+
for (const imp of newImports) {
|
|
1562
1566
|
const braceMatch = imp.text.match(/\{([^}]+)\}/);
|
|
1563
|
-
if (!braceMatch)
|
|
1564
|
-
|
|
1565
|
-
|
|
1567
|
+
if (!braceMatch) {
|
|
1568
|
+
filteredImports.push(imp);
|
|
1569
|
+
continue;
|
|
1570
|
+
}
|
|
1571
|
+
const names = braceMatch[1].split(",").map((n) => n.replace(/\btype\b/, "").trim()).filter(Boolean);
|
|
1572
|
+
const usedNames = names.filter((name) => addedCodeText.includes(name));
|
|
1573
|
+
if (usedNames.length === 0) continue;
|
|
1574
|
+
if (usedNames.length === names.length) filteredImports.push(imp);
|
|
1575
|
+
else {
|
|
1576
|
+
const prefix = imp.text.trimStart().startsWith("import type") ? "import type" : "import";
|
|
1577
|
+
const sourceMatch = imp.text.match(/from\s+(['"][^'"]+['"]);?/);
|
|
1578
|
+
if (sourceMatch) filteredImports.push({
|
|
1579
|
+
key: imp.key,
|
|
1580
|
+
text: `${prefix} { ${usedNames.join(", ")} } from ${sourceMatch[1]};`
|
|
1581
|
+
});
|
|
1582
|
+
else filteredImports.push(imp);
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1566
1585
|
if (filteredImports.length > 0) {
|
|
1567
1586
|
const renderedImports = adapter.renderImports ? adapter.renderImports(filteredImports) : filteredImports.map((entry) => entry.text);
|
|
1568
1587
|
const lines = result.split("\n");
|
|
@@ -1806,17 +1825,71 @@ function buildEmitterContext(spec, options) {
|
|
|
1806
1825
|
overlayLookup: options.overlayLookup
|
|
1807
1826
|
};
|
|
1808
1827
|
}
|
|
1828
|
+
/**
|
|
1829
|
+
* Collect model and enum names transitively referenced by service operations.
|
|
1830
|
+
* Walks operation parameters, request bodies, responses, and pagination item
|
|
1831
|
+
* types, then chases model field references until the set stabilizes.
|
|
1832
|
+
*/
|
|
1833
|
+
function collectReferencedNames(services, models) {
|
|
1834
|
+
const referencedModels = /* @__PURE__ */ new Set();
|
|
1835
|
+
const referencedEnums = /* @__PURE__ */ new Set();
|
|
1836
|
+
const collectFromTypeRef = (ref) => {
|
|
1837
|
+
walkTypeRef(ref, {
|
|
1838
|
+
model: (r) => referencedModels.add(r.name),
|
|
1839
|
+
enum: (r) => referencedEnums.add(r.name)
|
|
1840
|
+
});
|
|
1841
|
+
};
|
|
1842
|
+
for (const service of services) for (const op of service.operations) {
|
|
1843
|
+
for (const p of [
|
|
1844
|
+
...op.pathParams,
|
|
1845
|
+
...op.queryParams,
|
|
1846
|
+
...op.headerParams,
|
|
1847
|
+
...op.cookieParams ?? []
|
|
1848
|
+
]) collectFromTypeRef(p.type);
|
|
1849
|
+
if (op.requestBody) collectFromTypeRef(op.requestBody);
|
|
1850
|
+
collectFromTypeRef(op.response);
|
|
1851
|
+
if (op.pagination) collectFromTypeRef(op.pagination.itemType);
|
|
1852
|
+
for (const err of op.errors) if (err.type) collectFromTypeRef(err.type);
|
|
1853
|
+
if (op.successResponses) for (const sr of op.successResponses) collectFromTypeRef(sr.type);
|
|
1854
|
+
}
|
|
1855
|
+
const modelsByName = new Map(models.map((m) => [m.name, m]));
|
|
1856
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1857
|
+
const queue = [...referencedModels];
|
|
1858
|
+
while (queue.length > 0) {
|
|
1859
|
+
const name = queue.pop();
|
|
1860
|
+
if (visited.has(name)) continue;
|
|
1861
|
+
visited.add(name);
|
|
1862
|
+
const model = modelsByName.get(name);
|
|
1863
|
+
if (!model) continue;
|
|
1864
|
+
for (const field of model.fields) {
|
|
1865
|
+
collectFromTypeRef(field.type);
|
|
1866
|
+
for (const m of referencedModels) if (!visited.has(m)) queue.push(m);
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return {
|
|
1870
|
+
models: referencedModels,
|
|
1871
|
+
enums: referencedEnums
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1809
1874
|
/** Collect all generated files from an emitter (no headers, no path prefixes). */
|
|
1810
1875
|
function generateAllFiles(spec, emitter, ctx) {
|
|
1876
|
+
const referenced = collectReferencedNames(spec.services, spec.models);
|
|
1877
|
+
const reachableModels = spec.models.filter((m) => referenced.models.has(m.name));
|
|
1878
|
+
const reachableEnums = spec.enums.filter((e) => referenced.enums.has(e.name));
|
|
1879
|
+
const reachableSpec = {
|
|
1880
|
+
...spec,
|
|
1881
|
+
models: reachableModels,
|
|
1882
|
+
enums: reachableEnums
|
|
1883
|
+
};
|
|
1811
1884
|
return [
|
|
1812
|
-
...emitter.generateModels(
|
|
1813
|
-
...emitter.generateEnums(
|
|
1885
|
+
...emitter.generateModels(reachableModels, ctx),
|
|
1886
|
+
...emitter.generateEnums(reachableEnums, ctx),
|
|
1814
1887
|
...emitter.generateResources(spec.services, ctx),
|
|
1815
1888
|
...emitter.generateClient(spec, ctx),
|
|
1816
1889
|
...emitter.generateErrors(ctx),
|
|
1817
1890
|
...emitter.generateConfig(ctx),
|
|
1818
|
-
...emitter.generateTypeSignatures?.(
|
|
1819
|
-
...emitter.generateTests(
|
|
1891
|
+
...emitter.generateTypeSignatures?.(reachableSpec, ctx) ?? [],
|
|
1892
|
+
...emitter.generateTests(reachableSpec, ctx),
|
|
1820
1893
|
...emitter.generateManifest?.(spec, ctx) ?? []
|
|
1821
1894
|
];
|
|
1822
1895
|
}
|
|
@@ -1840,6 +1913,81 @@ function generateFiles(spec, emitter, options) {
|
|
|
1840
1913
|
}
|
|
1841
1914
|
//#endregion
|
|
1842
1915
|
//#region src/engine/integrate.ts
|
|
1916
|
+
/**
|
|
1917
|
+
* Resolve a relative import path against a source file path to get the
|
|
1918
|
+
* target file path. Handles implicit .ts extensions and /index.ts.
|
|
1919
|
+
*/
|
|
1920
|
+
function resolveImportPath(fromFilePath, importModulePath, knownPaths) {
|
|
1921
|
+
const cleaned = importModulePath.replace(/^['"]|['"]$/g, "");
|
|
1922
|
+
if (!cleaned.startsWith(".")) return void 0;
|
|
1923
|
+
const dir = path.dirname(fromFilePath);
|
|
1924
|
+
const resolved = path.normalize(path.join(dir, cleaned));
|
|
1925
|
+
for (const candidate of [
|
|
1926
|
+
resolved,
|
|
1927
|
+
`${resolved}.ts`,
|
|
1928
|
+
`${resolved}/index.ts`
|
|
1929
|
+
]) if (knownPaths.has(candidate)) return candidate;
|
|
1930
|
+
}
|
|
1931
|
+
/**
|
|
1932
|
+
* Determine whether a generated file is a "root" — a file that should always
|
|
1933
|
+
* be integrated into the target. Roots are entry points from which we trace
|
|
1934
|
+
* the import graph. Everything else must be reachable from a root.
|
|
1935
|
+
*
|
|
1936
|
+
* Roots: resource classes, client, config, errors, enums, barrel index files.
|
|
1937
|
+
* Non-roots: serializers, fixtures, standalone interfaces, test files.
|
|
1938
|
+
*/
|
|
1939
|
+
function isRootFile(filePath) {
|
|
1940
|
+
if (filePath.includes("/fixtures/")) return false;
|
|
1941
|
+
if (filePath.endsWith(".spec.ts") || filePath.endsWith(".test.ts")) return false;
|
|
1942
|
+
if (filePath.includes("/serializers/")) return false;
|
|
1943
|
+
if (filePath.includes("/interfaces/") && !filePath.endsWith("/index.ts")) return false;
|
|
1944
|
+
return true;
|
|
1945
|
+
}
|
|
1946
|
+
/**
|
|
1947
|
+
* Build a file-level dependency graph from generated files and return only
|
|
1948
|
+
* files reachable from root entry points. Uses tree-sitter to parse imports.
|
|
1949
|
+
*/
|
|
1950
|
+
async function treeShakeFiles(files, language) {
|
|
1951
|
+
if (!hasGrammar(language)) return files;
|
|
1952
|
+
const knownPaths = new Set(files.map((f) => f.path));
|
|
1953
|
+
const deps = /* @__PURE__ */ new Map();
|
|
1954
|
+
for (const file of files) {
|
|
1955
|
+
if (!file.path.endsWith(".ts") && !file.path.endsWith(".js")) {
|
|
1956
|
+
deps.set(file.path, /* @__PURE__ */ new Set());
|
|
1957
|
+
continue;
|
|
1958
|
+
}
|
|
1959
|
+
const parsed = await extractStatements(file.content, language);
|
|
1960
|
+
const fileDeps = /* @__PURE__ */ new Set();
|
|
1961
|
+
for (const imp of parsed.imports) {
|
|
1962
|
+
const resolved = resolveImportPath(file.path, imp.key, knownPaths);
|
|
1963
|
+
if (resolved) fileDeps.add(resolved);
|
|
1964
|
+
}
|
|
1965
|
+
for (const stmt of parsed.statements) if (stmt.kind === "reexport") {
|
|
1966
|
+
const match = stmt.text.match(/from\s+['"]([^'"]+)['"]/);
|
|
1967
|
+
if (match) {
|
|
1968
|
+
const resolved = resolveImportPath(file.path, match[1], knownPaths);
|
|
1969
|
+
if (resolved) fileDeps.add(resolved);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
deps.set(file.path, fileDeps);
|
|
1973
|
+
}
|
|
1974
|
+
const reachable = /* @__PURE__ */ new Set();
|
|
1975
|
+
const queue = [];
|
|
1976
|
+
for (const file of files) if (isRootFile(file.path)) {
|
|
1977
|
+
reachable.add(file.path);
|
|
1978
|
+
queue.push(file.path);
|
|
1979
|
+
}
|
|
1980
|
+
while (queue.length > 0) {
|
|
1981
|
+
const current = queue.pop();
|
|
1982
|
+
const fileDeps = deps.get(current);
|
|
1983
|
+
if (!fileDeps) continue;
|
|
1984
|
+
for (const dep of fileDeps) if (!reachable.has(dep)) {
|
|
1985
|
+
reachable.add(dep);
|
|
1986
|
+
queue.push(dep);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
return files.filter((f) => reachable.has(f.path));
|
|
1990
|
+
}
|
|
1843
1991
|
function mapFilesForTargetIntegration(files, language) {
|
|
1844
1992
|
const langPrefix = `${language}/`;
|
|
1845
1993
|
return files.filter((f) => f.integrateTarget !== false).map((f) => {
|
|
@@ -1852,12 +2000,44 @@ function mapFilesForTargetIntegration(files, language) {
|
|
|
1852
2000
|
});
|
|
1853
2001
|
}
|
|
1854
2002
|
async function integrateGeneratedFiles(opts) {
|
|
1855
|
-
return writeFiles(mapFilesForTargetIntegration(opts.files, opts.language), opts.targetDir, {
|
|
2003
|
+
return writeFiles(await treeShakeFiles(mapFilesForTargetIntegration(opts.files, opts.language), opts.language), opts.targetDir, {
|
|
1856
2004
|
language: opts.language,
|
|
1857
2005
|
header: opts.header
|
|
1858
2006
|
});
|
|
1859
2007
|
}
|
|
1860
2008
|
//#endregion
|
|
2009
|
+
//#region src/engine/formatter.ts
|
|
2010
|
+
/**
|
|
2011
|
+
* Post-integration formatter.
|
|
2012
|
+
*
|
|
2013
|
+
* After generating and merging files into a target directory, runs the
|
|
2014
|
+
* emitter-provided format command on all written/merged files so that
|
|
2015
|
+
* generated code matches the target project's style conventions.
|
|
2016
|
+
*/
|
|
2017
|
+
const execFileAsync = promisify(execFile);
|
|
2018
|
+
/**
|
|
2019
|
+
* Run the emitter's format command on the given target files.
|
|
2020
|
+
* Silently skips if the emitter has no formatCommand or returns null.
|
|
2021
|
+
*/
|
|
2022
|
+
async function formatTargetFiles(emitter, targetDir, filePaths) {
|
|
2023
|
+
if (filePaths.length === 0) return;
|
|
2024
|
+
if (!emitter.formatCommand) return;
|
|
2025
|
+
const formatCmd = emitter.formatCommand(targetDir);
|
|
2026
|
+
if (!formatCmd) return;
|
|
2027
|
+
const absolutePaths = filePaths.map((f) => path$1.resolve(targetDir, f));
|
|
2028
|
+
const batchSize = formatCmd.batchSize ?? 100;
|
|
2029
|
+
for (let i = 0; i < absolutePaths.length; i += batchSize) {
|
|
2030
|
+
const batch = absolutePaths.slice(i, i + batchSize);
|
|
2031
|
+
try {
|
|
2032
|
+
await execFileAsync(formatCmd.cmd, [...formatCmd.args, ...batch], {
|
|
2033
|
+
cwd: targetDir,
|
|
2034
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
2035
|
+
timeout: 12e4
|
|
2036
|
+
});
|
|
2037
|
+
} catch {}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
//#endregion
|
|
1861
2041
|
//#region src/engine/orchestrator.ts
|
|
1862
2042
|
async function generate(spec, emitter, options) {
|
|
1863
2043
|
const { files: withHeaders, header } = generateFiles(spec, emitter, options);
|
|
@@ -1884,6 +2064,8 @@ async function generate(spec, emitter, options) {
|
|
|
1884
2064
|
if (targetResult.written.length > 0) console.log(`Target: created ${targetResult.written.length} new files`);
|
|
1885
2065
|
if (targetResult.merged.length > 0) console.log(`Target: merged into ${targetResult.merged.length} existing files (additive only)`);
|
|
1886
2066
|
if (targetResult.skipped.length > 0) console.log(`Target: skipped ${targetResult.skipped.length} files (excluded or no grammar)`);
|
|
2067
|
+
const allTargetFiles = [...targetResult.written, ...targetResult.merged];
|
|
2068
|
+
if (allTargetFiles.length > 0) await formatTargetFiles(emitter, options.target, allTargetFiles);
|
|
1887
2069
|
}
|
|
1888
2070
|
return withHeaders;
|
|
1889
2071
|
}
|
|
@@ -2245,6 +2427,6 @@ function summarize(changes) {
|
|
|
2245
2427
|
};
|
|
2246
2428
|
}
|
|
2247
2429
|
//#endregion
|
|
2248
|
-
export { integrateGeneratedFiles as a,
|
|
2430
|
+
export { integrateGeneratedFiles as a, applyFileHeaders as c, generateAllFiles as d, generateFiles as f, generate as i, buildEmitterContext as l, getEmitter as n, mapFilesForTargetIntegration as o, registerEmitter as r, treeShakeFiles as s, diffSpecs as t, collectReferencedNames as u };
|
|
2249
2431
|
|
|
2250
|
-
//# sourceMappingURL=diff-
|
|
2432
|
+
//# sourceMappingURL=diff-Cn6S74vV.mjs.map
|