@openpkg-ts/extract 0.21.0 → 0.22.1

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/bin/tspec.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  extract
4
- } from "../shared/chunk-yh8v9dbt.js";
4
+ } from "../shared/chunk-7fp2zqf8.js";
5
5
 
6
6
  // src/cli/spec.ts
7
7
  import * as fs from "node:fs";
@@ -452,8 +452,9 @@ function createProgram() {
452
452
  fs.writeFileSync(options.output, JSON.stringify(normalized, null, 2));
453
453
  spin.success(`Extracted to ${options.output}`);
454
454
  if (result.runtimeSchemas) {
455
- const { extracted, merged, vendors } = result.runtimeSchemas;
456
- console.log(`ℹ Runtime schemas: ${merged}/${extracted} merged (${vendors.join(", ")})`);
455
+ const { extracted, merged, vendors, method } = result.runtimeSchemas;
456
+ const via = method ? ` via ${method}` : "";
457
+ console.log(`ℹ Runtime schemas: ${merged}/${extracted} merged (${vendors.join(", ")})${via}`);
457
458
  }
458
459
  for (const diag of result.diagnostics) {
459
460
  if (diag.severity === "info" && !options.verbose)
@@ -125,9 +125,15 @@ function buildSchema(type, checker, ctx, _depth = 0) {
125
125
  return buildFunctionSchema(callSignatures, checker, ctx);
126
126
  }
127
127
  const symbol2 = type.getSymbol() || type.aliasSymbol;
128
- if (symbol2) {
128
+ if (symbol2 && !isAnonymous(type)) {
129
129
  return { $ref: `#/types/${symbol2.getName()}` };
130
130
  }
131
+ if (type.flags & ts.TypeFlags.Object) {
132
+ const properties = type.getProperties();
133
+ if (properties.length > 0) {
134
+ return buildObjectSchema(properties, checker, ctx);
135
+ }
136
+ }
131
137
  return { type: checker.typeToString(type) };
132
138
  }
133
139
  if (ctx && type.flags & ts.TypeFlags.Object) {
@@ -1498,6 +1504,169 @@ function serializeTypeAlias(node, ctx) {
1498
1504
  };
1499
1505
  }
1500
1506
 
1507
+ // src/schema/registry.ts
1508
+ function isTypeReference(type) {
1509
+ return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
1510
+ }
1511
+ function getNonNullableType(type) {
1512
+ if (type.isUnion()) {
1513
+ const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
1514
+ if (nonNullable.length === 1) {
1515
+ return nonNullable[0];
1516
+ }
1517
+ }
1518
+ return type;
1519
+ }
1520
+ var adapters = [];
1521
+ function registerAdapter(adapter) {
1522
+ adapters.push(adapter);
1523
+ }
1524
+ function findAdapter(type, checker) {
1525
+ return adapters.find((a) => a.matches(type, checker));
1526
+ }
1527
+ function isSchemaType(type, checker) {
1528
+ return adapters.some((a) => a.matches(type, checker));
1529
+ }
1530
+ function extractSchemaType(type, checker) {
1531
+ const adapter = findAdapter(type, checker);
1532
+ if (!adapter)
1533
+ return null;
1534
+ const outputType = adapter.extractOutputType(type, checker);
1535
+ if (!outputType)
1536
+ return null;
1537
+ const inputType = adapter.extractInputType?.(type, checker) ?? undefined;
1538
+ return {
1539
+ adapter,
1540
+ outputType,
1541
+ inputType
1542
+ };
1543
+ }
1544
+
1545
+ // src/schema/adapters/arktype.ts
1546
+ var ARKTYPE_TYPE_PATTERN = /^Type</;
1547
+ var arktypeAdapter = {
1548
+ id: "arktype",
1549
+ packages: ["arktype"],
1550
+ matches(type, checker) {
1551
+ const typeName = checker.typeToString(type);
1552
+ return ARKTYPE_TYPE_PATTERN.test(typeName);
1553
+ },
1554
+ extractOutputType(type, checker) {
1555
+ if (!isTypeReference(type)) {
1556
+ return null;
1557
+ }
1558
+ const args = checker.getTypeArguments(type);
1559
+ if (args.length < 1) {
1560
+ return null;
1561
+ }
1562
+ return args[0];
1563
+ },
1564
+ extractInputType(type, checker) {
1565
+ if (!isTypeReference(type)) {
1566
+ return null;
1567
+ }
1568
+ const args = checker.getTypeArguments(type);
1569
+ if (args.length < 2) {
1570
+ return null;
1571
+ }
1572
+ return args[1];
1573
+ }
1574
+ };
1575
+
1576
+ // src/schema/adapters/typebox.ts
1577
+ var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
1578
+ var typeboxAdapter = {
1579
+ id: "typebox",
1580
+ packages: ["@sinclair/typebox"],
1581
+ matches(type, checker) {
1582
+ const typeName = checker.typeToString(type);
1583
+ if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
1584
+ return false;
1585
+ }
1586
+ const typeProperty = type.getProperty("type");
1587
+ return typeProperty !== undefined;
1588
+ },
1589
+ extractOutputType(type, checker) {
1590
+ const staticSymbol = type.getProperty("static");
1591
+ if (staticSymbol) {
1592
+ return checker.getTypeOfSymbol(staticSymbol);
1593
+ }
1594
+ return null;
1595
+ }
1596
+ };
1597
+
1598
+ // src/schema/adapters/valibot.ts
1599
+ var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
1600
+ var valibotAdapter = {
1601
+ id: "valibot",
1602
+ packages: ["valibot"],
1603
+ matches(type, checker) {
1604
+ const typeName = checker.typeToString(type);
1605
+ return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
1606
+ },
1607
+ extractOutputType(type, checker) {
1608
+ const typesSymbol = type.getProperty("~types");
1609
+ if (!typesSymbol) {
1610
+ return null;
1611
+ }
1612
+ let typesType = checker.getTypeOfSymbol(typesSymbol);
1613
+ typesType = getNonNullableType(typesType);
1614
+ const outputSymbol = typesType.getProperty("output");
1615
+ if (!outputSymbol) {
1616
+ return null;
1617
+ }
1618
+ return checker.getTypeOfSymbol(outputSymbol);
1619
+ },
1620
+ extractInputType(type, checker) {
1621
+ const typesSymbol = type.getProperty("~types");
1622
+ if (!typesSymbol) {
1623
+ return null;
1624
+ }
1625
+ let typesType = checker.getTypeOfSymbol(typesSymbol);
1626
+ typesType = getNonNullableType(typesType);
1627
+ const inputSymbol = typesType.getProperty("input");
1628
+ if (!inputSymbol) {
1629
+ return null;
1630
+ }
1631
+ return checker.getTypeOfSymbol(inputSymbol);
1632
+ }
1633
+ };
1634
+
1635
+ // src/schema/adapters/zod.ts
1636
+ var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
1637
+ var zodAdapter = {
1638
+ id: "zod",
1639
+ packages: ["zod"],
1640
+ matches(type, checker) {
1641
+ const typeName = checker.typeToString(type);
1642
+ return ZOD_TYPE_PATTERN.test(typeName);
1643
+ },
1644
+ extractOutputType(type, checker) {
1645
+ const outputSymbol = type.getProperty("_output");
1646
+ if (outputSymbol) {
1647
+ return checker.getTypeOfSymbol(outputSymbol);
1648
+ }
1649
+ const typeSymbol = type.getProperty("_type");
1650
+ if (typeSymbol) {
1651
+ return checker.getTypeOfSymbol(typeSymbol);
1652
+ }
1653
+ return null;
1654
+ },
1655
+ extractInputType(type, checker) {
1656
+ const inputSymbol = type.getProperty("_input");
1657
+ if (inputSymbol) {
1658
+ return checker.getTypeOfSymbol(inputSymbol);
1659
+ }
1660
+ return null;
1661
+ }
1662
+ };
1663
+
1664
+ // src/schema/adapters/index.ts
1665
+ registerAdapter(zodAdapter);
1666
+ registerAdapter(valibotAdapter);
1667
+ registerAdapter(arktypeAdapter);
1668
+ registerAdapter(typeboxAdapter);
1669
+
1501
1670
  // src/serializers/variables.ts
1502
1671
  function serializeVariable(node, statement, ctx) {
1503
1672
  const symbol = ctx.typeChecker.getSymbolAtLocation(node.name);
@@ -1508,8 +1677,14 @@ function serializeVariable(node, statement, ctx) {
1508
1677
  const { description, tags, examples } = getJSDocComment(statement);
1509
1678
  const source = getSourceLocation(node, declSourceFile);
1510
1679
  const type = ctx.typeChecker.getTypeAtLocation(node);
1511
- registerReferencedTypes(type, ctx);
1512
- const schema = buildSchema(type, ctx.typeChecker, ctx);
1680
+ const schemaExtraction = extractSchemaType(type, ctx.typeChecker);
1681
+ const typeToSerialize = schemaExtraction?.outputType ?? type;
1682
+ registerReferencedTypes(typeToSerialize, ctx);
1683
+ const schema = buildSchema(typeToSerialize, ctx.typeChecker, ctx);
1684
+ const flags = schemaExtraction ? {
1685
+ schemaLibrary: schemaExtraction.adapter.id,
1686
+ ...schemaExtraction.inputType && schemaExtraction.inputType !== schemaExtraction.outputType ? { hasTransform: true } : {}
1687
+ } : undefined;
1513
1688
  return {
1514
1689
  id: name,
1515
1690
  name,
@@ -1518,13 +1693,15 @@ function serializeVariable(node, statement, ctx) {
1518
1693
  tags,
1519
1694
  source,
1520
1695
  schema,
1696
+ ...flags ? { flags } : {},
1521
1697
  ...examples.length > 0 ? { examples } : {}
1522
1698
  };
1523
1699
  }
1524
1700
 
1525
1701
  // src/schema/standard-schema.ts
1526
- import { spawn } from "node:child_process";
1702
+ import { spawn, spawnSync } from "node:child_process";
1527
1703
  import * as fs from "node:fs";
1704
+ import * as os from "node:os";
1528
1705
  import * as path2 from "node:path";
1529
1706
  function isStandardJSONSchema(obj) {
1530
1707
  if (typeof obj !== "object" || obj === null)
@@ -1543,6 +1720,57 @@ function isStandardJSONSchema(obj) {
1543
1720
  const jsObj = jsonSchema;
1544
1721
  return typeof jsObj.output === "function" && typeof jsObj.input === "function";
1545
1722
  }
1723
+ var cachedRuntime;
1724
+ function commandExists(cmd) {
1725
+ try {
1726
+ const result = spawnSync(process.platform === "win32" ? "where" : "which", [cmd], {
1727
+ stdio: "ignore"
1728
+ });
1729
+ return result.status === 0;
1730
+ } catch {
1731
+ return false;
1732
+ }
1733
+ }
1734
+ function detectTsRuntime() {
1735
+ if (cachedRuntime !== undefined) {
1736
+ return cachedRuntime;
1737
+ }
1738
+ const nodeVersion = parseInt(process.versions.node.split(".")[0], 10);
1739
+ if (nodeVersion >= 22) {
1740
+ cachedRuntime = {
1741
+ cmd: "node",
1742
+ args: ["--experimental-strip-types", "--no-warnings"],
1743
+ name: "node (native)"
1744
+ };
1745
+ return cachedRuntime;
1746
+ }
1747
+ if (commandExists("bun")) {
1748
+ cachedRuntime = {
1749
+ cmd: "bun",
1750
+ args: ["run"],
1751
+ name: "bun"
1752
+ };
1753
+ return cachedRuntime;
1754
+ }
1755
+ if (commandExists("tsx")) {
1756
+ cachedRuntime = {
1757
+ cmd: "tsx",
1758
+ args: [],
1759
+ name: "tsx"
1760
+ };
1761
+ return cachedRuntime;
1762
+ }
1763
+ if (commandExists("ts-node")) {
1764
+ cachedRuntime = {
1765
+ cmd: "ts-node",
1766
+ args: ["--transpile-only"],
1767
+ name: "ts-node"
1768
+ };
1769
+ return cachedRuntime;
1770
+ }
1771
+ cachedRuntime = null;
1772
+ return null;
1773
+ }
1546
1774
  var WORKER_SCRIPT = `
1547
1775
  const path = require('path');
1548
1776
  const { pathToFileURL } = require('url');
@@ -1634,6 +1862,161 @@ async function extract() {
1634
1862
 
1635
1863
  extract();
1636
1864
  `;
1865
+ var TS_WORKER_SCRIPT = `
1866
+ import * as path from 'path';
1867
+ import { pathToFileURL } from 'url';
1868
+
1869
+ // TypeBox detection
1870
+ const TYPEBOX_KIND = Symbol.for('TypeBox.Kind');
1871
+
1872
+ function isTypeBoxSchema(obj: unknown): boolean {
1873
+ if (!obj || typeof obj !== 'object') return false;
1874
+ const o = obj as Record<string | symbol, unknown>;
1875
+ if (!o[TYPEBOX_KIND]) return false;
1876
+ return typeof o.type === 'string' || 'anyOf' in o || 'oneOf' in o || 'allOf' in o;
1877
+ }
1878
+
1879
+ function sanitizeTypeBoxSchema(schema: unknown): unknown {
1880
+ return JSON.parse(JSON.stringify(schema));
1881
+ }
1882
+
1883
+ async function extract() {
1884
+ const [,, modulePath, optionsJson] = process.argv;
1885
+ const { target, libraryOptions } = JSON.parse(optionsJson || '{}');
1886
+
1887
+ try {
1888
+ const absPath = path.resolve(modulePath);
1889
+ const mod = await import(pathToFileURL(absPath).href);
1890
+ const results: Array<{exportName: string; vendor: string; outputSchema: unknown; inputSchema?: unknown}> = [];
1891
+
1892
+ // Build exports map
1893
+ const exports: Record<string, unknown> = {};
1894
+ for (const [name, value] of Object.entries(mod)) {
1895
+ if (name === 'default' && typeof value === 'object' && value !== null) {
1896
+ Object.assign(exports, value);
1897
+ } else if (name !== 'default') {
1898
+ exports[name] = value;
1899
+ }
1900
+ }
1901
+
1902
+ // Check each export
1903
+ for (const [name, value] of Object.entries(exports)) {
1904
+ if (name.startsWith('_')) continue;
1905
+ if (typeof value !== 'object' || value === null) continue;
1906
+
1907
+ const v = value as Record<string, unknown>;
1908
+ const std = v['~standard'] as Record<string, unknown> | undefined;
1909
+
1910
+ // Standard JSON Schema
1911
+ if (std && typeof std === 'object' && std.version === 1 && typeof std.vendor === 'string') {
1912
+ const jsonSchema = std.jsonSchema as Record<string, unknown> | undefined;
1913
+ if (jsonSchema && typeof jsonSchema.output === 'function') {
1914
+ try {
1915
+ const options = { target: target || 'draft-2020-12', ...(libraryOptions && { libraryOptions }) };
1916
+ const outputSchema = (jsonSchema.output as Function)(options);
1917
+ const inputSchema = typeof jsonSchema.input === 'function' ? (jsonSchema.input as Function)(options) : undefined;
1918
+ results.push({ exportName: name, vendor: std.vendor as string, outputSchema, inputSchema });
1919
+ } catch {}
1920
+ continue;
1921
+ }
1922
+ }
1923
+
1924
+ // TypeBox
1925
+ if (isTypeBoxSchema(value)) {
1926
+ try {
1927
+ results.push({ exportName: name, vendor: 'typebox', outputSchema: sanitizeTypeBoxSchema(value) });
1928
+ } catch {}
1929
+ }
1930
+ }
1931
+
1932
+ console.log(JSON.stringify({ success: true, results }));
1933
+ } catch (e) {
1934
+ console.log(JSON.stringify({ success: false, error: (e as Error).message }));
1935
+ }
1936
+ }
1937
+
1938
+ extract();
1939
+ `;
1940
+ async function extractStandardSchemasFromTs(tsFilePath, options = {}) {
1941
+ const { timeout = 1e4, target = "draft-2020-12", libraryOptions } = options;
1942
+ const result = {
1943
+ schemas: new Map,
1944
+ errors: []
1945
+ };
1946
+ const runtime = detectTsRuntime();
1947
+ if (!runtime) {
1948
+ result.errors.push("No TypeScript runtime available. Install bun, tsx, or ts-node, or use Node 22+.");
1949
+ return result;
1950
+ }
1951
+ if (!fs.existsSync(tsFilePath)) {
1952
+ result.errors.push(`TypeScript file not found: ${tsFilePath}`);
1953
+ return result;
1954
+ }
1955
+ const tempDir = os.tmpdir();
1956
+ const workerPath = path2.join(tempDir, `openpkg-extract-worker-${Date.now()}.ts`);
1957
+ try {
1958
+ fs.writeFileSync(workerPath, TS_WORKER_SCRIPT);
1959
+ const optionsJson = JSON.stringify({ target, libraryOptions });
1960
+ const args = [...runtime.args, workerPath, tsFilePath, optionsJson];
1961
+ return await new Promise((resolve) => {
1962
+ const child = spawn(runtime.cmd, args, {
1963
+ timeout,
1964
+ stdio: ["ignore", "pipe", "pipe"],
1965
+ cwd: path2.dirname(tsFilePath)
1966
+ });
1967
+ let stdout = "";
1968
+ let stderr = "";
1969
+ child.stdout.on("data", (data) => {
1970
+ stdout += data.toString();
1971
+ });
1972
+ child.stderr.on("data", (data) => {
1973
+ stderr += data.toString();
1974
+ });
1975
+ child.on("close", (code) => {
1976
+ try {
1977
+ fs.unlinkSync(workerPath);
1978
+ } catch {}
1979
+ if (code !== 0) {
1980
+ result.errors.push(`Extraction failed (${runtime.name}): ${stderr || `exit code ${code}`}`);
1981
+ resolve(result);
1982
+ return;
1983
+ }
1984
+ try {
1985
+ const parsed = JSON.parse(stdout);
1986
+ if (!parsed.success) {
1987
+ result.errors.push(`Extraction failed: ${parsed.error}`);
1988
+ resolve(result);
1989
+ return;
1990
+ }
1991
+ for (const item of parsed.results) {
1992
+ result.schemas.set(item.exportName, {
1993
+ exportName: item.exportName,
1994
+ vendor: item.vendor,
1995
+ outputSchema: item.outputSchema,
1996
+ inputSchema: item.inputSchema
1997
+ });
1998
+ }
1999
+ } catch (e) {
2000
+ result.errors.push(`Failed to parse extraction output: ${e}`);
2001
+ }
2002
+ resolve(result);
2003
+ });
2004
+ child.on("error", (err) => {
2005
+ try {
2006
+ fs.unlinkSync(workerPath);
2007
+ } catch {}
2008
+ result.errors.push(`Subprocess error: ${err.message}`);
2009
+ resolve(result);
2010
+ });
2011
+ });
2012
+ } catch (e) {
2013
+ try {
2014
+ fs.unlinkSync(workerPath);
2015
+ } catch {}
2016
+ result.errors.push(`Failed to create worker script: ${e}`);
2017
+ return result;
2018
+ }
2019
+ }
1637
2020
  function readTsconfigOutDir(baseDir) {
1638
2021
  const tsconfigPath = path2.join(baseDir, "tsconfig.json");
1639
2022
  try {
@@ -1743,14 +2126,34 @@ async function extractStandardSchemas(compiledJsPath, options = {}) {
1743
2126
  });
1744
2127
  }
1745
2128
  async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
1746
- const compiledPath = resolveCompiledPath(entryFile, baseDir);
1747
- if (!compiledPath) {
1748
- return {
1749
- schemas: new Map,
1750
- errors: [`Could not find compiled JS for ${entryFile}. Build the project first.`]
1751
- };
2129
+ const { preferDirectTs, ...extractOptions } = options;
2130
+ const isTypeScript = /\.tsx?$/.test(entryFile);
2131
+ if (!preferDirectTs) {
2132
+ const compiledPath = resolveCompiledPath(entryFile, baseDir);
2133
+ if (compiledPath) {
2134
+ const result = await extractStandardSchemas(compiledPath, extractOptions);
2135
+ return {
2136
+ ...result,
2137
+ info: { method: "compiled", path: compiledPath }
2138
+ };
2139
+ }
1752
2140
  }
1753
- return extractStandardSchemas(compiledPath, options);
2141
+ if (isTypeScript) {
2142
+ const runtime2 = detectTsRuntime();
2143
+ if (runtime2) {
2144
+ const result = await extractStandardSchemasFromTs(entryFile, extractOptions);
2145
+ return {
2146
+ ...result,
2147
+ info: { method: "direct-ts", runtime: runtime2.name, path: entryFile }
2148
+ };
2149
+ }
2150
+ }
2151
+ const runtime = detectTsRuntime();
2152
+ const hint = isTypeScript && !runtime ? " Install bun, tsx, or ts-node for direct TS execution." : "";
2153
+ return {
2154
+ schemas: new Map,
2155
+ errors: [`Could not find compiled JS for ${entryFile}.${hint}`]
2156
+ };
1754
2157
  }
1755
2158
 
1756
2159
  // src/builder/spec-builder.ts
@@ -1980,35 +2383,27 @@ async function extract(options) {
1980
2383
  let runtimeMetadata;
1981
2384
  if (options.schemaExtraction === "hybrid") {
1982
2385
  const projectBaseDir = baseDir || path3.dirname(entryFile);
1983
- const compiledPath = resolveCompiledPath(entryFile, projectBaseDir);
1984
- if (compiledPath) {
1985
- const runtimeResult = await extractStandardSchemas(compiledPath, {
1986
- target: options.schemaTarget || "draft-2020-12",
1987
- timeout: 15000
1988
- });
1989
- if (runtimeResult.schemas.size > 0) {
1990
- const mergeResult = mergeRuntimeSchemas(exports, runtimeResult.schemas);
1991
- exports = mergeResult.exports;
1992
- runtimeMetadata = {
1993
- extracted: runtimeResult.schemas.size,
1994
- merged: mergeResult.merged,
1995
- vendors: [...new Set([...runtimeResult.schemas.values()].map((s) => s.vendor))],
1996
- errors: runtimeResult.errors
1997
- };
1998
- }
1999
- for (const error of runtimeResult.errors) {
2000
- diagnostics.push({
2001
- message: `Runtime schema extraction: ${error}`,
2002
- severity: "warning",
2003
- code: "RUNTIME_SCHEMA_ERROR"
2004
- });
2005
- }
2006
- } else {
2386
+ const runtimeResult = await extractStandardSchemasFromProject(entryFile, projectBaseDir, {
2387
+ target: options.schemaTarget || "draft-2020-12",
2388
+ timeout: 15000
2389
+ });
2390
+ if (runtimeResult.schemas.size > 0) {
2391
+ const mergeResult = mergeRuntimeSchemas(exports, runtimeResult.schemas);
2392
+ exports = mergeResult.exports;
2393
+ const method = runtimeResult.info?.method === "direct-ts" ? `direct-ts (${runtimeResult.info.runtime})` : "compiled";
2394
+ runtimeMetadata = {
2395
+ extracted: runtimeResult.schemas.size,
2396
+ merged: mergeResult.merged,
2397
+ vendors: [...new Set([...runtimeResult.schemas.values()].map((s) => s.vendor))],
2398
+ errors: runtimeResult.errors,
2399
+ method
2400
+ };
2401
+ }
2402
+ for (const error of runtimeResult.errors) {
2007
2403
  diagnostics.push({
2008
- message: "Hybrid mode: Could not find compiled JS. Build the project first.",
2404
+ message: `Runtime schema extraction: ${error}`,
2009
2405
  severity: "warning",
2010
- code: "NO_COMPILED_JS",
2011
- suggestion: "Run `npm run build` or `tsc` before extraction"
2406
+ code: "RUNTIME_SCHEMA_ERROR"
2012
2407
  });
2013
2408
  }
2014
2409
  }
@@ -2358,4 +2753,4 @@ async function getPackageMeta(entryFile, baseDir) {
2358
2753
  } catch {}
2359
2754
  return { name: path3.basename(searchDir) };
2360
2755
  }
2361
- export { BUILTIN_TYPE_SCHEMAS, isPrimitiveName, isBuiltinGeneric, isAnonymous, buildSchema, isPureRefSchema, withDescription, schemaIsAny, schemasAreEqual, deduplicateSchemas, findDiscriminatorProperty, TypeRegistry, getJSDocComment, getSourceLocation, getParamDescription, extractTypeParameters, isSymbolDeprecated, createProgram, extractParameters, registerReferencedTypes, serializeClass, serializeEnum, serializeFunctionExport, serializeInterface, serializeTypeAlias, serializeVariable, isStandardJSONSchema, resolveCompiledPath, extractStandardSchemas, extractStandardSchemasFromProject, extract };
2756
+ export { BUILTIN_TYPE_SCHEMAS, isPrimitiveName, isBuiltinGeneric, isAnonymous, buildSchema, isPureRefSchema, withDescription, schemaIsAny, schemasAreEqual, deduplicateSchemas, findDiscriminatorProperty, TypeRegistry, getJSDocComment, getSourceLocation, getParamDescription, extractTypeParameters, isSymbolDeprecated, createProgram, extractParameters, registerReferencedTypes, serializeClass, serializeEnum, serializeFunctionExport, serializeInterface, serializeTypeAlias, isTypeReference, getNonNullableType, registerAdapter, findAdapter, isSchemaType, extractSchemaType, arktypeAdapter, typeboxAdapter, valibotAdapter, zodAdapter, serializeVariable, isStandardJSONSchema, detectTsRuntime, extractStandardSchemasFromTs, resolveCompiledPath, extractStandardSchemas, extractStandardSchemasFromProject, extract };
@@ -96,6 +96,8 @@ interface ExtractResult {
96
96
  vendors: string[];
97
97
  /** Any errors encountered during runtime extraction */
98
98
  errors: string[];
99
+ /** Extraction method used: 'compiled' or 'direct-ts (runtime)' */
100
+ method?: string;
99
101
  };
100
102
  }
101
103
  interface Diagnostic {
@@ -199,6 +201,32 @@ interface StandardSchemaExtractionOutput {
199
201
  */
200
202
  declare function isStandardJSONSchema(obj: unknown): obj is StandardJSONSchemaV1;
201
203
  /**
204
+ * TypeScript runtime configuration.
205
+ */
206
+ interface TsRuntime {
207
+ /** Command to execute */
208
+ cmd: string;
209
+ /** Arguments to pass before the script path */
210
+ args: string[];
211
+ /** Human-readable name */
212
+ name: string;
213
+ }
214
+ /**
215
+ * Detect available TypeScript runtime.
216
+ * Checks in order: Node 22+ native, bun, tsx, ts-node.
217
+ * Returns null if no TS runtime is available.
218
+ */
219
+ declare function detectTsRuntime(): TsRuntime | null;
220
+ /**
221
+ * Extract Standard Schema from a TypeScript file directly.
222
+ * Uses detected TS runtime (bun, tsx, ts-node, or node 22+).
223
+ *
224
+ * @param tsFilePath - Path to TypeScript file
225
+ * @param options - Extraction options
226
+ * @returns Extraction results
227
+ */
228
+ declare function extractStandardSchemasFromTs(tsFilePath: string, options?: ExtractStandardSchemasOptions): Promise<StandardSchemaExtractionOutput>;
229
+ /**
202
230
  * Resolve compiled JS path from TypeScript source.
203
231
  * Reads tsconfig.json for outDir and tries multiple output patterns.
204
232
  * Supports .js, .mjs, and .cjs extensions.
@@ -216,15 +244,42 @@ declare function resolveCompiledPath(tsPath: string, baseDir: string): string |
216
244
  */
217
245
  declare function extractStandardSchemas(compiledJsPath: string, options?: ExtractStandardSchemasOptions): Promise<StandardSchemaExtractionOutput>;
218
246
  /**
247
+ * Result info from extractStandardSchemasFromProject
248
+ */
249
+ interface ProjectExtractionInfo {
250
+ /** How schemas were extracted */
251
+ method: "compiled" | "direct-ts";
252
+ /** Runtime used (for direct-ts) */
253
+ runtime?: string;
254
+ /** Path that was used */
255
+ path: string;
256
+ }
257
+ /**
258
+ * Extended options for project extraction
259
+ */
260
+ interface ExtractFromProjectOptions extends ExtractStandardSchemasOptions {
261
+ /** Prefer direct TS execution even if compiled JS exists */
262
+ preferDirectTs?: boolean;
263
+ }
264
+ /**
265
+ * Extended result for project extraction
266
+ */
267
+ interface ProjectExtractionOutput extends StandardSchemaExtractionOutput {
268
+ /** Info about how extraction was performed */
269
+ info?: ProjectExtractionInfo;
270
+ }
271
+ /**
219
272
  * Extract Standard Schema from a TypeScript project.
220
273
  *
221
- * Convenience function that resolves compiled JS and extracts schemas.
274
+ * Tries in order:
275
+ * 1. Compiled JS (if found)
276
+ * 2. Direct TypeScript execution (if TS runtime available)
222
277
  *
223
278
  * @param entryFile - TypeScript entry file path
224
279
  * @param baseDir - Project base directory
225
280
  * @param options - Extraction options
226
281
  */
227
- declare function extractStandardSchemasFromProject(entryFile: string, baseDir: string, options?: ExtractStandardSchemasOptions): Promise<StandardSchemaExtractionOutput>;
282
+ declare function extractStandardSchemasFromProject(entryFile: string, baseDir: string, options?: ExtractFromProjectOptions): Promise<ProjectExtractionOutput>;
228
283
  import ts4 from "typescript";
229
284
  interface ProgramOptions {
230
285
  entryFile: string;
@@ -373,4 +428,4 @@ declare function findDiscriminatorProperty(unionTypes: ts12.Type[], checker: ts1
373
428
  import ts13 from "typescript";
374
429
  declare function isExported(node: ts13.Node): boolean;
375
430
  declare function getNodeName(node: ts13.Node): string | undefined;
376
- export { zodAdapter, withDescription, valibotAdapter, typeboxAdapter, serializeVariable, serializeTypeAlias, serializeInterface, serializeFunctionExport, serializeEnum, serializeClass, schemasAreEqual, schemaIsAny, resolveCompiledPath, registerReferencedTypes, registerAdapter, isTypeReference, isSymbolDeprecated, isStandardJSONSchema, isSchemaType, isPureRefSchema, isPrimitiveName, isExported, isBuiltinGeneric, isAnonymous, getSourceLocation, getParamDescription, getNonNullableType, getNodeName, getJSDocComment, findDiscriminatorProperty, findAdapter, extractTypeParameters, extractStandardSchemasFromProject, extractStandardSchemas, extractSchemaType, extractParameters, extract, deduplicateSchemas, createProgram, buildSchema, arktypeAdapter, TypeRegistry, TypeReference2 as TypeReference, StandardSchemaExtractionResult, StandardSchemaExtractionOutput, StandardJSONSchemaV1, StandardJSONSchemaTarget, StandardJSONSchemaOptions, SerializerContext, SchemaExtractionResult, SchemaAdapter, ProgramResult, ProgramOptions, ForgottenExport, ExtractStandardSchemasOptions, ExtractResult, ExtractOptions, Diagnostic, BUILTIN_TYPE_SCHEMAS };
431
+ export { zodAdapter, withDescription, valibotAdapter, typeboxAdapter, serializeVariable, serializeTypeAlias, serializeInterface, serializeFunctionExport, serializeEnum, serializeClass, schemasAreEqual, schemaIsAny, resolveCompiledPath, registerReferencedTypes, registerAdapter, isTypeReference, isSymbolDeprecated, isStandardJSONSchema, isSchemaType, isPureRefSchema, isPrimitiveName, isExported, isBuiltinGeneric, isAnonymous, getSourceLocation, getParamDescription, getNonNullableType, getNodeName, getJSDocComment, findDiscriminatorProperty, findAdapter, extractTypeParameters, extractStandardSchemasFromTs, extractStandardSchemasFromProject, extractStandardSchemas, extractSchemaType, extractParameters, extract, detectTsRuntime, deduplicateSchemas, createProgram, buildSchema, arktypeAdapter, TypeRegistry, TypeReference2 as TypeReference, TsRuntime, StandardSchemaExtractionResult, StandardSchemaExtractionOutput, StandardJSONSchemaV1, StandardJSONSchemaTarget, StandardJSONSchemaOptions, SerializerContext, SchemaExtractionResult, SchemaAdapter, ProjectExtractionOutput, ProjectExtractionInfo, ProgramResult, ProgramOptions, ForgottenExport, ExtractStandardSchemasOptions, ExtractResult, ExtractOptions, ExtractFromProjectOptions, Diagnostic, BUILTIN_TYPE_SCHEMAS };
package/dist/src/index.js CHANGED
@@ -1,24 +1,33 @@
1
1
  import {
2
2
  BUILTIN_TYPE_SCHEMAS,
3
3
  TypeRegistry,
4
+ arktypeAdapter,
4
5
  buildSchema,
5
6
  createProgram,
6
7
  deduplicateSchemas,
8
+ detectTsRuntime,
7
9
  extract,
8
10
  extractParameters,
11
+ extractSchemaType,
9
12
  extractStandardSchemas,
10
13
  extractStandardSchemasFromProject,
14
+ extractStandardSchemasFromTs,
11
15
  extractTypeParameters,
16
+ findAdapter,
12
17
  findDiscriminatorProperty,
13
18
  getJSDocComment,
19
+ getNonNullableType,
14
20
  getParamDescription,
15
21
  getSourceLocation,
16
22
  isAnonymous,
17
23
  isBuiltinGeneric,
18
24
  isPrimitiveName,
19
25
  isPureRefSchema,
26
+ isSchemaType,
20
27
  isStandardJSONSchema,
21
28
  isSymbolDeprecated,
29
+ isTypeReference,
30
+ registerAdapter,
22
31
  registerReferencedTypes,
23
32
  resolveCompiledPath,
24
33
  schemaIsAny,
@@ -29,170 +38,11 @@ import {
29
38
  serializeInterface,
30
39
  serializeTypeAlias,
31
40
  serializeVariable,
32
- withDescription
33
- } from "../shared/chunk-yh8v9dbt.js";
34
- // src/schema/registry.ts
35
- function isTypeReference(type) {
36
- return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
37
- }
38
- function getNonNullableType(type) {
39
- if (type.isUnion()) {
40
- const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
41
- if (nonNullable.length === 1) {
42
- return nonNullable[0];
43
- }
44
- }
45
- return type;
46
- }
47
- var adapters = [];
48
- function registerAdapter(adapter) {
49
- adapters.push(adapter);
50
- }
51
- function findAdapter(type, checker) {
52
- return adapters.find((a) => a.matches(type, checker));
53
- }
54
- function isSchemaType(type, checker) {
55
- return adapters.some((a) => a.matches(type, checker));
56
- }
57
- function extractSchemaType(type, checker) {
58
- const adapter = findAdapter(type, checker);
59
- if (!adapter)
60
- return null;
61
- const outputType = adapter.extractOutputType(type, checker);
62
- if (!outputType)
63
- return null;
64
- const inputType = adapter.extractInputType?.(type, checker) ?? undefined;
65
- return {
66
- adapter,
67
- outputType,
68
- inputType
69
- };
70
- }
71
-
72
- // src/schema/adapters/arktype.ts
73
- var ARKTYPE_TYPE_PATTERN = /^Type</;
74
- var arktypeAdapter = {
75
- id: "arktype",
76
- packages: ["arktype"],
77
- matches(type, checker) {
78
- const typeName = checker.typeToString(type);
79
- return ARKTYPE_TYPE_PATTERN.test(typeName);
80
- },
81
- extractOutputType(type, checker) {
82
- if (!isTypeReference(type)) {
83
- return null;
84
- }
85
- const args = checker.getTypeArguments(type);
86
- if (args.length < 1) {
87
- return null;
88
- }
89
- return args[0];
90
- },
91
- extractInputType(type, checker) {
92
- if (!isTypeReference(type)) {
93
- return null;
94
- }
95
- const args = checker.getTypeArguments(type);
96
- if (args.length < 2) {
97
- return null;
98
- }
99
- return args[1];
100
- }
101
- };
102
-
103
- // src/schema/adapters/typebox.ts
104
- var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
105
- var typeboxAdapter = {
106
- id: "typebox",
107
- packages: ["@sinclair/typebox"],
108
- matches(type, checker) {
109
- const typeName = checker.typeToString(type);
110
- if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
111
- return false;
112
- }
113
- const typeProperty = type.getProperty("type");
114
- return typeProperty !== undefined;
115
- },
116
- extractOutputType(type, checker) {
117
- const staticSymbol = type.getProperty("static");
118
- if (staticSymbol) {
119
- return checker.getTypeOfSymbol(staticSymbol);
120
- }
121
- return null;
122
- }
123
- };
124
-
125
- // src/schema/adapters/valibot.ts
126
- var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
127
- var valibotAdapter = {
128
- id: "valibot",
129
- packages: ["valibot"],
130
- matches(type, checker) {
131
- const typeName = checker.typeToString(type);
132
- return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
133
- },
134
- extractOutputType(type, checker) {
135
- const typesSymbol = type.getProperty("~types");
136
- if (!typesSymbol) {
137
- return null;
138
- }
139
- let typesType = checker.getTypeOfSymbol(typesSymbol);
140
- typesType = getNonNullableType(typesType);
141
- const outputSymbol = typesType.getProperty("output");
142
- if (!outputSymbol) {
143
- return null;
144
- }
145
- return checker.getTypeOfSymbol(outputSymbol);
146
- },
147
- extractInputType(type, checker) {
148
- const typesSymbol = type.getProperty("~types");
149
- if (!typesSymbol) {
150
- return null;
151
- }
152
- let typesType = checker.getTypeOfSymbol(typesSymbol);
153
- typesType = getNonNullableType(typesType);
154
- const inputSymbol = typesType.getProperty("input");
155
- if (!inputSymbol) {
156
- return null;
157
- }
158
- return checker.getTypeOfSymbol(inputSymbol);
159
- }
160
- };
161
-
162
- // src/schema/adapters/zod.ts
163
- var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
164
- var zodAdapter = {
165
- id: "zod",
166
- packages: ["zod"],
167
- matches(type, checker) {
168
- const typeName = checker.typeToString(type);
169
- return ZOD_TYPE_PATTERN.test(typeName);
170
- },
171
- extractOutputType(type, checker) {
172
- const outputSymbol = type.getProperty("_output");
173
- if (outputSymbol) {
174
- return checker.getTypeOfSymbol(outputSymbol);
175
- }
176
- const typeSymbol = type.getProperty("_type");
177
- if (typeSymbol) {
178
- return checker.getTypeOfSymbol(typeSymbol);
179
- }
180
- return null;
181
- },
182
- extractInputType(type, checker) {
183
- const inputSymbol = type.getProperty("_input");
184
- if (inputSymbol) {
185
- return checker.getTypeOfSymbol(inputSymbol);
186
- }
187
- return null;
188
- }
189
- };
190
-
191
- // src/schema/adapters/index.ts
192
- registerAdapter(zodAdapter);
193
- registerAdapter(valibotAdapter);
194
- registerAdapter(arktypeAdapter);
195
- registerAdapter(typeboxAdapter);
41
+ typeboxAdapter,
42
+ valibotAdapter,
43
+ withDescription,
44
+ zodAdapter
45
+ } from "../shared/chunk-7fp2zqf8.js";
196
46
  // src/types/utils.ts
197
47
  function isExported(node) {
198
48
  const modifiers = node.modifiers;
@@ -240,11 +90,13 @@ export {
240
90
  findDiscriminatorProperty,
241
91
  findAdapter,
242
92
  extractTypeParameters,
93
+ extractStandardSchemasFromTs,
243
94
  extractStandardSchemasFromProject,
244
95
  extractStandardSchemas,
245
96
  extractSchemaType,
246
97
  extractParameters,
247
98
  extract,
99
+ detectTsRuntime,
248
100
  deduplicateSchemas,
249
101
  createProgram,
250
102
  buildSchema,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/extract",
3
- "version": "0.21.0",
3
+ "version": "0.22.1",
4
4
  "description": "TypeScript export extraction to OpenPkg spec",
5
5
  "keywords": [
6
6
  "openpkg",