@openpkg-ts/extract 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -3
- package/dist/bin/tspec.js +1 -1
- package/dist/shared/{chunk-nymjpc96.js → chunk-y5d5qgyt.js} +601 -6
- package/dist/src/index.d.ts +63 -1
- package/dist/src/index.js +20 -164
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @openpkg-ts/extract
|
|
2
2
|
|
|
3
|
-
TypeScript API extraction library. Generates OpenPkg specs from TypeScript source code.
|
|
3
|
+
TypeScript API extraction library. Generates OpenPkg specs from TypeScript source code with **JSON Schema 2020-12** output.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -14,6 +14,9 @@ npm install @openpkg-ts/extract
|
|
|
14
14
|
# Extract API spec from entry point
|
|
15
15
|
tspec src/index.ts -o openpkg.json
|
|
16
16
|
|
|
17
|
+
# With runtime schema extraction (Zod, Valibot, etc.)
|
|
18
|
+
tspec src/index.ts --runtime
|
|
19
|
+
|
|
17
20
|
# With options
|
|
18
21
|
tspec src/index.ts --max-depth 4 --verbose
|
|
19
22
|
```
|
|
@@ -46,7 +49,107 @@ for (const diag of result.diagnostics) {
|
|
|
46
49
|
| `baseDir` | `string` | cwd | Base directory for resolution |
|
|
47
50
|
| `maxTypeDepth` | `number` | 4 | Max depth for type traversal |
|
|
48
51
|
| `resolveExternalTypes` | `boolean` | true | Resolve types from node_modules |
|
|
49
|
-
| `schemaExtraction` | `'static' \| 'hybrid'` | 'static' | Schema extraction mode |
|
|
52
|
+
| `schemaExtraction` | `'static' \| 'hybrid'` | `'static'` | Schema extraction mode |
|
|
53
|
+
| `schemaTarget` | `'draft-2020-12' \| 'draft-07' \| 'openapi-3.0'` | `'draft-2020-12'` | Target JSON Schema dialect |
|
|
54
|
+
| `only` | `string[]` | - | Only extract these exports (supports `*` wildcards) |
|
|
55
|
+
| `ignore` | `string[]` | - | Ignore these exports (supports `*` wildcards) |
|
|
56
|
+
|
|
57
|
+
## JSON Schema 2020-12 Output
|
|
58
|
+
|
|
59
|
+
All schema output is normalized to valid **JSON Schema 2020-12**. This ensures consistency between static TypeScript analysis and runtime schema extraction from libraries like Zod and Valibot.
|
|
60
|
+
|
|
61
|
+
### Interface/Class Output Format
|
|
62
|
+
|
|
63
|
+
Interfaces and classes include a `schema` property containing a JSON Schema object representation:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"kind": "interface",
|
|
68
|
+
"name": "User",
|
|
69
|
+
"schema": {
|
|
70
|
+
"type": "object",
|
|
71
|
+
"properties": {
|
|
72
|
+
"id": { "type": "string" },
|
|
73
|
+
"age": { "type": "number" },
|
|
74
|
+
"email": { "type": "string" }
|
|
75
|
+
},
|
|
76
|
+
"required": ["id", "email"]
|
|
77
|
+
},
|
|
78
|
+
"members": [...]
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### TypeScript Extension Fields (`x-ts-*`)
|
|
83
|
+
|
|
84
|
+
TypeScript constructs that don't map directly to JSON Schema are preserved using extension fields:
|
|
85
|
+
|
|
86
|
+
| Extension | Purpose | Example |
|
|
87
|
+
|-----------|---------|---------|
|
|
88
|
+
| `x-ts-type` | Preserves original TypeScript type | `{ "type": "integer", "x-ts-type": "bigint" }` |
|
|
89
|
+
| `x-ts-function` | Marks function types | `{ "x-ts-function": true, "x-ts-signatures": [...] }` |
|
|
90
|
+
| `x-ts-signatures` | Function/method signatures | Array of signature objects with parameters and returns |
|
|
91
|
+
| `x-ts-type-arguments` | Generic type arguments | `{ "$ref": "#/types/Promise", "x-ts-type-arguments": [{ "type": "string" }] }` |
|
|
92
|
+
| `x-ts-accessor` | Getter/setter markers | `{ "type": "string", "x-ts-accessor": "getter" }` |
|
|
93
|
+
|
|
94
|
+
### Type Mappings
|
|
95
|
+
|
|
96
|
+
| TypeScript Type | JSON Schema Output |
|
|
97
|
+
|-----------------|-------------------|
|
|
98
|
+
| `void` | `{ "type": "null" }` |
|
|
99
|
+
| `never` | `{ "not": {} }` |
|
|
100
|
+
| `any` | `{}` |
|
|
101
|
+
| `unknown` | `{}` |
|
|
102
|
+
| `undefined` | `{ "type": "null" }` |
|
|
103
|
+
| `bigint` | `{ "type": "integer", "x-ts-type": "bigint" }` |
|
|
104
|
+
| `symbol` | `{ "type": "string", "x-ts-type": "symbol" }` |
|
|
105
|
+
| `[T, U]` (tuple) | `{ "type": "array", "prefixedItems": [...], "minItems": 2, "maxItems": 2 }` |
|
|
106
|
+
| `() => T` (function) | `{ "x-ts-function": true, "x-ts-signatures": [...] }` |
|
|
107
|
+
| `Promise<T>` | `{ "$ref": "#/types/Promise", "x-ts-type-arguments": [<T schema>] }` |
|
|
108
|
+
|
|
109
|
+
### Example: Function Schema
|
|
110
|
+
|
|
111
|
+
```json
|
|
112
|
+
{
|
|
113
|
+
"kind": "function",
|
|
114
|
+
"name": "fetchUser",
|
|
115
|
+
"signatures": [{
|
|
116
|
+
"parameters": [{
|
|
117
|
+
"name": "id",
|
|
118
|
+
"schema": { "type": "string" },
|
|
119
|
+
"required": true
|
|
120
|
+
}],
|
|
121
|
+
"returns": {
|
|
122
|
+
"schema": {
|
|
123
|
+
"$ref": "#/types/Promise",
|
|
124
|
+
"x-ts-type-arguments": [{ "$ref": "#/types/User" }]
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}]
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Example: Interface with Methods
|
|
132
|
+
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"kind": "interface",
|
|
136
|
+
"name": "Repository",
|
|
137
|
+
"schema": {
|
|
138
|
+
"type": "object",
|
|
139
|
+
"properties": {
|
|
140
|
+
"id": { "type": "string" },
|
|
141
|
+
"find": {
|
|
142
|
+
"x-ts-function": true,
|
|
143
|
+
"x-ts-signatures": [{
|
|
144
|
+
"parameters": [{ "name": "query", "schema": { "type": "string" } }],
|
|
145
|
+
"returns": { "schema": { "type": "array", "items": { "$ref": "#/types/Item" } } }
|
|
146
|
+
}]
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"required": ["id", "find"]
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
50
153
|
|
|
51
154
|
## Exports
|
|
52
155
|
|
|
@@ -61,6 +164,12 @@ for (const diag of result.diagnostics) {
|
|
|
61
164
|
- `TypeRegistry` - Track and dedupe extracted types
|
|
62
165
|
- `serializeType` - Convert TS types to schema
|
|
63
166
|
|
|
167
|
+
### Schema Normalizer
|
|
168
|
+
- `normalizeSchema(schema, options)` - Convert SpecSchema to JSON Schema 2020-12
|
|
169
|
+
- `normalizeExport(exp, options)` - Normalize a SpecExport including nested schemas
|
|
170
|
+
- `normalizeType(type, options)` - Normalize a SpecType including nested schemas
|
|
171
|
+
- `normalizeMembers(members, options)` - Convert members array to JSON Schema properties
|
|
172
|
+
|
|
64
173
|
### Schema Adapters
|
|
65
174
|
- `ZodAdapter`, `ValibotAdapter` - Runtime schema extraction
|
|
66
175
|
|
|
@@ -70,7 +179,8 @@ for (const diag of result.diagnostics) {
|
|
|
70
179
|
2. Extracts all exported symbols
|
|
71
180
|
3. Serializes each export (functions, classes, types, variables)
|
|
72
181
|
4. Resolves type references and builds a type registry
|
|
73
|
-
5.
|
|
182
|
+
5. **Normalizes all schemas to JSON Schema 2020-12**
|
|
183
|
+
6. Outputs an OpenPkg-compliant JSON spec
|
|
74
184
|
|
|
75
185
|
## License
|
|
76
186
|
|
package/dist/bin/tspec.js
CHANGED
|
@@ -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
|
-
|
|
1512
|
-
const
|
|
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,6 +1693,7 @@ 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
|
}
|
|
@@ -1980,6 +2156,423 @@ async function extractStandardSchemasFromProject(entryFile, baseDir, options = {
|
|
|
1980
2156
|
};
|
|
1981
2157
|
}
|
|
1982
2158
|
|
|
2159
|
+
// src/types/schema-normalizer.ts
|
|
2160
|
+
var SCHEMA_DIALECT_URLS = {
|
|
2161
|
+
"draft-2020-12": "https://json-schema.org/draft/2020-12/schema",
|
|
2162
|
+
"draft-07": "http://json-schema.org/draft-07/schema#"
|
|
2163
|
+
};
|
|
2164
|
+
var TS_PRIMITIVE_NORMALIZATIONS = {
|
|
2165
|
+
void: () => ({ type: "null" }),
|
|
2166
|
+
never: () => ({ not: {} }),
|
|
2167
|
+
any: () => ({}),
|
|
2168
|
+
unknown: () => ({}),
|
|
2169
|
+
undefined: () => ({ type: "null" }),
|
|
2170
|
+
bigint: () => ({ type: "integer", "x-ts-type": "bigint" }),
|
|
2171
|
+
symbol: () => ({ type: "string", "x-ts-type": "symbol" })
|
|
2172
|
+
};
|
|
2173
|
+
function normalizeSchema(schema, options = {}) {
|
|
2174
|
+
const { includeSchemaField = false, dialect = "draft-2020-12" } = options;
|
|
2175
|
+
const normalized = normalizeSchemaInternal(schema, options);
|
|
2176
|
+
if (includeSchemaField && typeof normalized === "object") {
|
|
2177
|
+
return {
|
|
2178
|
+
$schema: SCHEMA_DIALECT_URLS[dialect],
|
|
2179
|
+
...normalized
|
|
2180
|
+
};
|
|
2181
|
+
}
|
|
2182
|
+
return normalized;
|
|
2183
|
+
}
|
|
2184
|
+
function normalizeSchemaInternal(schema, options) {
|
|
2185
|
+
if (typeof schema === "string") {
|
|
2186
|
+
return normalizeStringType(schema);
|
|
2187
|
+
}
|
|
2188
|
+
if (schema == null) {
|
|
2189
|
+
return {};
|
|
2190
|
+
}
|
|
2191
|
+
if (typeof schema !== "object") {
|
|
2192
|
+
return {};
|
|
2193
|
+
}
|
|
2194
|
+
if ("anyOf" in schema && Array.isArray(schema.anyOf)) {
|
|
2195
|
+
return normalizeCombinator("anyOf", schema.anyOf, schema, options);
|
|
2196
|
+
}
|
|
2197
|
+
if ("allOf" in schema && Array.isArray(schema.allOf)) {
|
|
2198
|
+
return normalizeCombinator("allOf", schema.allOf, schema, options);
|
|
2199
|
+
}
|
|
2200
|
+
if ("oneOf" in schema && Array.isArray(schema.oneOf)) {
|
|
2201
|
+
return normalizeCombinator("oneOf", schema.oneOf, schema, options);
|
|
2202
|
+
}
|
|
2203
|
+
if ("$ref" in schema && typeof schema.$ref === "string") {
|
|
2204
|
+
return normalizeRef(schema, options);
|
|
2205
|
+
}
|
|
2206
|
+
if ("type" in schema && typeof schema.type === "string") {
|
|
2207
|
+
return normalizeTypedSchema(schema, options);
|
|
2208
|
+
}
|
|
2209
|
+
return normalizeGenericObject(schema, options);
|
|
2210
|
+
}
|
|
2211
|
+
function normalizeStringType(type) {
|
|
2212
|
+
const specialNormalization = TS_PRIMITIVE_NORMALIZATIONS[type];
|
|
2213
|
+
if (specialNormalization) {
|
|
2214
|
+
return specialNormalization();
|
|
2215
|
+
}
|
|
2216
|
+
if (["string", "number", "boolean", "integer", "null", "object", "array"].includes(type)) {
|
|
2217
|
+
return { type };
|
|
2218
|
+
}
|
|
2219
|
+
return { "x-ts-type": type };
|
|
2220
|
+
}
|
|
2221
|
+
function normalizeTypedSchema(schema, options) {
|
|
2222
|
+
const { type } = schema;
|
|
2223
|
+
const specialNormalization = TS_PRIMITIVE_NORMALIZATIONS[type];
|
|
2224
|
+
if (specialNormalization) {
|
|
2225
|
+
const normalized = specialNormalization();
|
|
2226
|
+
return mergeSchemaFields(normalized, schema, ["type"]);
|
|
2227
|
+
}
|
|
2228
|
+
if (type === "function") {
|
|
2229
|
+
return normalizeFunctionType(schema, options);
|
|
2230
|
+
}
|
|
2231
|
+
if (type === "tuple") {
|
|
2232
|
+
return normalizeTupleType(schema, options);
|
|
2233
|
+
}
|
|
2234
|
+
if (type === "array") {
|
|
2235
|
+
return normalizeArrayType(schema, options);
|
|
2236
|
+
}
|
|
2237
|
+
if (type === "object") {
|
|
2238
|
+
return normalizeObjectType(schema, options);
|
|
2239
|
+
}
|
|
2240
|
+
if (["string", "number", "boolean", "integer", "null"].includes(type)) {
|
|
2241
|
+
return normalizeStandardType(schema, options);
|
|
2242
|
+
}
|
|
2243
|
+
const result = { "x-ts-type": type };
|
|
2244
|
+
return mergeSchemaFields(result, schema, ["type"]);
|
|
2245
|
+
}
|
|
2246
|
+
function normalizeFunctionType(schema, options) {
|
|
2247
|
+
const result = {
|
|
2248
|
+
"x-ts-function": true
|
|
2249
|
+
};
|
|
2250
|
+
if ("signatures" in schema && Array.isArray(schema.signatures)) {
|
|
2251
|
+
result["x-ts-signatures"] = schema.signatures.map((sig) => normalizeSignature(sig, options));
|
|
2252
|
+
}
|
|
2253
|
+
if ("description" in schema && schema.description) {
|
|
2254
|
+
result.description = schema.description;
|
|
2255
|
+
}
|
|
2256
|
+
return result;
|
|
2257
|
+
}
|
|
2258
|
+
function normalizeSignature(signature, options) {
|
|
2259
|
+
const result = {};
|
|
2260
|
+
if (signature.parameters) {
|
|
2261
|
+
result.parameters = signature.parameters.map((param) => ({
|
|
2262
|
+
name: param.name,
|
|
2263
|
+
schema: normalizeSchemaInternal(param.schema, options),
|
|
2264
|
+
...param.required !== undefined ? { required: param.required } : {},
|
|
2265
|
+
...param.description ? { description: param.description } : {},
|
|
2266
|
+
...param.default !== undefined ? { default: param.default } : {},
|
|
2267
|
+
...param.rest ? { rest: param.rest } : {}
|
|
2268
|
+
}));
|
|
2269
|
+
}
|
|
2270
|
+
if (signature.returns) {
|
|
2271
|
+
result.returns = {
|
|
2272
|
+
schema: normalizeSchemaInternal(signature.returns.schema, options),
|
|
2273
|
+
...signature.returns.description ? { description: signature.returns.description } : {}
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
if (signature.description) {
|
|
2277
|
+
result.description = signature.description;
|
|
2278
|
+
}
|
|
2279
|
+
if (signature.typeParameters) {
|
|
2280
|
+
result.typeParameters = signature.typeParameters;
|
|
2281
|
+
}
|
|
2282
|
+
return result;
|
|
2283
|
+
}
|
|
2284
|
+
function normalizeTupleType(schema, options) {
|
|
2285
|
+
const result = { type: "array" };
|
|
2286
|
+
if ("items" in schema && Array.isArray(schema.items)) {
|
|
2287
|
+
result.prefixedItems = schema.items.map((item) => normalizeSchemaInternal(item, options));
|
|
2288
|
+
result.minItems = schema.items.length;
|
|
2289
|
+
result.maxItems = schema.items.length;
|
|
2290
|
+
}
|
|
2291
|
+
if ("prefixedItems" in schema && Array.isArray(schema.prefixedItems)) {
|
|
2292
|
+
result.prefixedItems = schema.prefixedItems.map((item) => normalizeSchemaInternal(item, options));
|
|
2293
|
+
}
|
|
2294
|
+
if ("minItems" in schema && typeof schema.minItems === "number") {
|
|
2295
|
+
result.minItems = schema.minItems;
|
|
2296
|
+
}
|
|
2297
|
+
if ("maxItems" in schema && typeof schema.maxItems === "number") {
|
|
2298
|
+
result.maxItems = schema.maxItems;
|
|
2299
|
+
}
|
|
2300
|
+
if ("description" in schema && schema.description) {
|
|
2301
|
+
result.description = schema.description;
|
|
2302
|
+
}
|
|
2303
|
+
return result;
|
|
2304
|
+
}
|
|
2305
|
+
function normalizeArrayType(schema, options) {
|
|
2306
|
+
const result = { type: "array" };
|
|
2307
|
+
if ("items" in schema && schema.items && !Array.isArray(schema.items)) {
|
|
2308
|
+
result.items = normalizeSchemaInternal(schema.items, options);
|
|
2309
|
+
}
|
|
2310
|
+
if ("prefixedItems" in schema && Array.isArray(schema.prefixedItems)) {
|
|
2311
|
+
result.prefixedItems = schema.prefixedItems.map((item) => normalizeSchemaInternal(item, options));
|
|
2312
|
+
}
|
|
2313
|
+
if ("minItems" in schema && typeof schema.minItems === "number") {
|
|
2314
|
+
result.minItems = schema.minItems;
|
|
2315
|
+
}
|
|
2316
|
+
if ("maxItems" in schema && typeof schema.maxItems === "number") {
|
|
2317
|
+
result.maxItems = schema.maxItems;
|
|
2318
|
+
}
|
|
2319
|
+
if ("description" in schema && schema.description) {
|
|
2320
|
+
result.description = schema.description;
|
|
2321
|
+
}
|
|
2322
|
+
return result;
|
|
2323
|
+
}
|
|
2324
|
+
function normalizeObjectType(schema, options) {
|
|
2325
|
+
const result = { type: "object" };
|
|
2326
|
+
if ("properties" in schema && schema.properties) {
|
|
2327
|
+
const properties = schema.properties;
|
|
2328
|
+
result.properties = Object.fromEntries(Object.entries(properties).map(([key, value]) => [
|
|
2329
|
+
key,
|
|
2330
|
+
normalizeSchemaInternal(value, options)
|
|
2331
|
+
]));
|
|
2332
|
+
}
|
|
2333
|
+
if ("required" in schema && Array.isArray(schema.required)) {
|
|
2334
|
+
result.required = schema.required;
|
|
2335
|
+
}
|
|
2336
|
+
if ("additionalProperties" in schema) {
|
|
2337
|
+
if (typeof schema.additionalProperties === "boolean") {
|
|
2338
|
+
result.additionalProperties = schema.additionalProperties;
|
|
2339
|
+
} else if (schema.additionalProperties) {
|
|
2340
|
+
result.additionalProperties = normalizeSchemaInternal(schema.additionalProperties, options);
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
if ("description" in schema && schema.description) {
|
|
2344
|
+
result.description = schema.description;
|
|
2345
|
+
}
|
|
2346
|
+
return result;
|
|
2347
|
+
}
|
|
2348
|
+
function normalizeStandardType(schema, options) {
|
|
2349
|
+
const result = { type: schema.type };
|
|
2350
|
+
const validationKeywords = [
|
|
2351
|
+
"enum",
|
|
2352
|
+
"const",
|
|
2353
|
+
"format",
|
|
2354
|
+
"pattern",
|
|
2355
|
+
"minimum",
|
|
2356
|
+
"maximum",
|
|
2357
|
+
"exclusiveMinimum",
|
|
2358
|
+
"exclusiveMaximum",
|
|
2359
|
+
"multipleOf",
|
|
2360
|
+
"minLength",
|
|
2361
|
+
"maxLength",
|
|
2362
|
+
"description",
|
|
2363
|
+
"default",
|
|
2364
|
+
"examples",
|
|
2365
|
+
"title"
|
|
2366
|
+
];
|
|
2367
|
+
for (const keyword of validationKeywords) {
|
|
2368
|
+
if (keyword in schema && schema[keyword] !== undefined) {
|
|
2369
|
+
result[keyword] = schema[keyword];
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2372
|
+
return result;
|
|
2373
|
+
}
|
|
2374
|
+
function normalizeRef(schema, options) {
|
|
2375
|
+
const result = { $ref: schema.$ref };
|
|
2376
|
+
if (schema.typeArguments && schema.typeArguments.length > 0) {
|
|
2377
|
+
result["x-ts-type-arguments"] = schema.typeArguments.map((arg) => normalizeSchemaInternal(arg, options));
|
|
2378
|
+
}
|
|
2379
|
+
return result;
|
|
2380
|
+
}
|
|
2381
|
+
function normalizeCombinator(keyword, schemas, originalSchema, options) {
|
|
2382
|
+
const result = {
|
|
2383
|
+
[keyword]: schemas.map((s) => normalizeSchemaInternal(s, options))
|
|
2384
|
+
};
|
|
2385
|
+
if ((keyword === "anyOf" || keyword === "oneOf") && "discriminator" in originalSchema && originalSchema.discriminator) {
|
|
2386
|
+
result.discriminator = originalSchema.discriminator;
|
|
2387
|
+
}
|
|
2388
|
+
if ("description" in originalSchema && originalSchema.description) {
|
|
2389
|
+
result.description = originalSchema.description;
|
|
2390
|
+
}
|
|
2391
|
+
return result;
|
|
2392
|
+
}
|
|
2393
|
+
function normalizeGenericObject(schema, options) {
|
|
2394
|
+
const result = {};
|
|
2395
|
+
for (const [key, value] of Object.entries(schema)) {
|
|
2396
|
+
if (value == null)
|
|
2397
|
+
continue;
|
|
2398
|
+
if (isSchemaLike(value)) {
|
|
2399
|
+
result[key] = normalizeSchemaInternal(value, options);
|
|
2400
|
+
} else if (Array.isArray(value)) {
|
|
2401
|
+
result[key] = value.map((item) => isSchemaLike(item) ? normalizeSchemaInternal(item, options) : item);
|
|
2402
|
+
} else if (typeof value === "object") {
|
|
2403
|
+
result[key] = normalizeGenericObject(value, options);
|
|
2404
|
+
} else {
|
|
2405
|
+
result[key] = value;
|
|
2406
|
+
}
|
|
2407
|
+
}
|
|
2408
|
+
return result;
|
|
2409
|
+
}
|
|
2410
|
+
function isSchemaLike(value) {
|
|
2411
|
+
if (typeof value !== "object" || value == null)
|
|
2412
|
+
return false;
|
|
2413
|
+
if (typeof value === "string")
|
|
2414
|
+
return true;
|
|
2415
|
+
const obj = value;
|
|
2416
|
+
return "type" in obj || "$ref" in obj || "anyOf" in obj || "allOf" in obj || "oneOf" in obj || "properties" in obj || "items" in obj || "prefixedItems" in obj;
|
|
2417
|
+
}
|
|
2418
|
+
function mergeSchemaFields(target, source, excludeKeys) {
|
|
2419
|
+
if (typeof source !== "object" || source == null) {
|
|
2420
|
+
return target;
|
|
2421
|
+
}
|
|
2422
|
+
const excludeSet = new Set(excludeKeys);
|
|
2423
|
+
const result = { ...target };
|
|
2424
|
+
for (const [key, value] of Object.entries(source)) {
|
|
2425
|
+
if (!excludeSet.has(key) && value !== undefined) {
|
|
2426
|
+
if (!(key in result)) {
|
|
2427
|
+
result[key] = value;
|
|
2428
|
+
}
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
return result;
|
|
2432
|
+
}
|
|
2433
|
+
function normalizeExport(exp, options = {}) {
|
|
2434
|
+
const result = { ...exp };
|
|
2435
|
+
if (exp.schema) {
|
|
2436
|
+
result.schema = normalizeSchema(exp.schema, options);
|
|
2437
|
+
}
|
|
2438
|
+
if (exp.signatures) {
|
|
2439
|
+
result.signatures = exp.signatures.map((sig) => normalizeSignatureSpec(sig, options));
|
|
2440
|
+
}
|
|
2441
|
+
if (exp.members) {
|
|
2442
|
+
result.members = exp.members.map((member) => normalizeMember(member, options));
|
|
2443
|
+
}
|
|
2444
|
+
if (shouldGenerateMembersSchema(exp.kind) && exp.members && exp.members.length > 0) {
|
|
2445
|
+
result.schema = normalizeMembers(exp.members, options);
|
|
2446
|
+
}
|
|
2447
|
+
return result;
|
|
2448
|
+
}
|
|
2449
|
+
function normalizeType(type, options = {}) {
|
|
2450
|
+
const result = { ...type };
|
|
2451
|
+
if (type.schema) {
|
|
2452
|
+
result.schema = normalizeSchema(type.schema, options);
|
|
2453
|
+
}
|
|
2454
|
+
if (type.members) {
|
|
2455
|
+
result.members = type.members.map((member) => normalizeMember(member, options));
|
|
2456
|
+
}
|
|
2457
|
+
if (shouldGenerateMembersSchema(type.kind) && type.members && type.members.length > 0) {
|
|
2458
|
+
result.schema = normalizeMembers(type.members, options);
|
|
2459
|
+
}
|
|
2460
|
+
return result;
|
|
2461
|
+
}
|
|
2462
|
+
function shouldGenerateMembersSchema(kind) {
|
|
2463
|
+
return kind === "interface" || kind === "class";
|
|
2464
|
+
}
|
|
2465
|
+
function normalizeSignatureSpec(signature, options) {
|
|
2466
|
+
const result = { ...signature };
|
|
2467
|
+
if (signature.parameters) {
|
|
2468
|
+
result.parameters = signature.parameters.map((param) => ({
|
|
2469
|
+
...param,
|
|
2470
|
+
schema: normalizeSchema(param.schema, options)
|
|
2471
|
+
}));
|
|
2472
|
+
}
|
|
2473
|
+
if (signature.returns) {
|
|
2474
|
+
result.returns = {
|
|
2475
|
+
...signature.returns,
|
|
2476
|
+
schema: normalizeSchema(signature.returns.schema, options)
|
|
2477
|
+
};
|
|
2478
|
+
}
|
|
2479
|
+
return result;
|
|
2480
|
+
}
|
|
2481
|
+
function normalizeMember(member, options) {
|
|
2482
|
+
const result = { ...member };
|
|
2483
|
+
if (member.schema) {
|
|
2484
|
+
result.schema = normalizeSchema(member.schema, options);
|
|
2485
|
+
}
|
|
2486
|
+
if (member.signatures) {
|
|
2487
|
+
result.signatures = member.signatures.map((sig) => normalizeSignatureSpec(sig, options));
|
|
2488
|
+
}
|
|
2489
|
+
return result;
|
|
2490
|
+
}
|
|
2491
|
+
function normalizeMembers(members, options = {}) {
|
|
2492
|
+
const properties = {};
|
|
2493
|
+
const required = [];
|
|
2494
|
+
let additionalProperties;
|
|
2495
|
+
for (const member of members) {
|
|
2496
|
+
const { name, kind } = member;
|
|
2497
|
+
if (kind === "index" || kind === "index-signature") {
|
|
2498
|
+
additionalProperties = normalizeMemberToSchema(member, options);
|
|
2499
|
+
continue;
|
|
2500
|
+
}
|
|
2501
|
+
if (!name)
|
|
2502
|
+
continue;
|
|
2503
|
+
const memberSchema = normalizeMemberToSchema(member, options);
|
|
2504
|
+
properties[name] = memberSchema;
|
|
2505
|
+
if (!isOptionalMember(member)) {
|
|
2506
|
+
required.push(name);
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
const result = {
|
|
2510
|
+
type: "object",
|
|
2511
|
+
properties
|
|
2512
|
+
};
|
|
2513
|
+
if (required.length > 0) {
|
|
2514
|
+
result.required = required;
|
|
2515
|
+
}
|
|
2516
|
+
if (additionalProperties !== undefined) {
|
|
2517
|
+
result.additionalProperties = additionalProperties;
|
|
2518
|
+
}
|
|
2519
|
+
return result;
|
|
2520
|
+
}
|
|
2521
|
+
function normalizeMemberToSchema(member, options) {
|
|
2522
|
+
const { kind, schema, signatures, description } = member;
|
|
2523
|
+
if (kind === "method" || kind === "call-signature") {
|
|
2524
|
+
return normalizeMethodMember(member, options);
|
|
2525
|
+
}
|
|
2526
|
+
if (kind === "getter") {
|
|
2527
|
+
const baseSchema2 = schema ? normalizeSchemaInternal(schema, options) : {};
|
|
2528
|
+
return {
|
|
2529
|
+
...baseSchema2,
|
|
2530
|
+
"x-ts-accessor": "getter",
|
|
2531
|
+
...description ? { description } : {}
|
|
2532
|
+
};
|
|
2533
|
+
}
|
|
2534
|
+
if (kind === "setter") {
|
|
2535
|
+
const baseSchema2 = schema ? normalizeSchemaInternal(schema, options) : {};
|
|
2536
|
+
return {
|
|
2537
|
+
...baseSchema2,
|
|
2538
|
+
"x-ts-accessor": "setter",
|
|
2539
|
+
...description ? { description } : {}
|
|
2540
|
+
};
|
|
2541
|
+
}
|
|
2542
|
+
if (kind === "index" || kind === "index-signature") {
|
|
2543
|
+
if (schema && typeof schema === "object" && "additionalProperties" in schema) {
|
|
2544
|
+
return normalizeSchemaInternal(schema.additionalProperties, options);
|
|
2545
|
+
}
|
|
2546
|
+
return schema ? normalizeSchemaInternal(schema, options) : {};
|
|
2547
|
+
}
|
|
2548
|
+
if (signatures && signatures.length > 0) {
|
|
2549
|
+
return normalizeMethodMember(member, options);
|
|
2550
|
+
}
|
|
2551
|
+
const baseSchema = schema ? normalizeSchemaInternal(schema, options) : {};
|
|
2552
|
+
return description ? { ...baseSchema, description } : baseSchema;
|
|
2553
|
+
}
|
|
2554
|
+
function normalizeMethodMember(member, options) {
|
|
2555
|
+
const result = {
|
|
2556
|
+
"x-ts-function": true
|
|
2557
|
+
};
|
|
2558
|
+
if (member.signatures && member.signatures.length > 0) {
|
|
2559
|
+
result["x-ts-signatures"] = member.signatures.map((sig) => normalizeSignature(sig, options));
|
|
2560
|
+
}
|
|
2561
|
+
if (member.description) {
|
|
2562
|
+
result.description = member.description;
|
|
2563
|
+
}
|
|
2564
|
+
return result;
|
|
2565
|
+
}
|
|
2566
|
+
function isOptionalMember(member) {
|
|
2567
|
+
if (member.flags?.optional === true) {
|
|
2568
|
+
return true;
|
|
2569
|
+
}
|
|
2570
|
+
if (member.name?.endsWith("?")) {
|
|
2571
|
+
return true;
|
|
2572
|
+
}
|
|
2573
|
+
return false;
|
|
2574
|
+
}
|
|
2575
|
+
|
|
1983
2576
|
// src/builder/spec-builder.ts
|
|
1984
2577
|
import * as fs2 from "node:fs";
|
|
1985
2578
|
import * as path3 from "node:path";
|
|
@@ -2231,12 +2824,14 @@ async function extract(options) {
|
|
|
2231
2824
|
});
|
|
2232
2825
|
}
|
|
2233
2826
|
}
|
|
2827
|
+
const normalizedExports = exports.map((exp) => normalizeExport(exp, { dialect: "draft-2020-12" }));
|
|
2828
|
+
const normalizedTypes = types.map((t) => normalizeType(t, { dialect: "draft-2020-12" }));
|
|
2234
2829
|
const spec = {
|
|
2235
2830
|
...includeSchema ? { $schema: SCHEMA_URL } : {},
|
|
2236
2831
|
openpkg: SCHEMA_VERSION,
|
|
2237
2832
|
meta,
|
|
2238
|
-
exports,
|
|
2239
|
-
types,
|
|
2833
|
+
exports: normalizedExports,
|
|
2834
|
+
types: normalizedTypes,
|
|
2240
2835
|
generation: {
|
|
2241
2836
|
generator: "@openpkg-ts/extract",
|
|
2242
2837
|
timestamp: new Date().toISOString(),
|
|
@@ -2577,4 +3172,4 @@ async function getPackageMeta(entryFile, baseDir) {
|
|
|
2577
3172
|
} catch {}
|
|
2578
3173
|
return { name: path3.basename(searchDir) };
|
|
2579
3174
|
}
|
|
2580
|
-
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, detectTsRuntime, extractStandardSchemasFromTs, resolveCompiledPath, extractStandardSchemas, extractStandardSchemasFromProject, extract };
|
|
3175
|
+
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, normalizeSchema, normalizeExport, normalizeType, normalizeMembers, extract };
|
package/dist/src/index.d.ts
CHANGED
|
@@ -425,7 +425,69 @@ declare function deduplicateSchemas(schemas: SpecSchema[]): SpecSchema[];
|
|
|
425
425
|
* A valid discriminator has a unique literal value in each union member.
|
|
426
426
|
*/
|
|
427
427
|
declare function findDiscriminatorProperty(unionTypes: ts12.Type[], checker: ts12.TypeChecker): string | undefined;
|
|
428
|
+
import { SpecSchema as SpecSchema2, SpecExport as SpecExport8, SpecType as SpecType2, SpecMember } from "@openpkg-ts/spec";
|
|
429
|
+
/**
|
|
430
|
+
* Options for schema normalization
|
|
431
|
+
*/
|
|
432
|
+
interface NormalizeOptions {
|
|
433
|
+
/** Include $schema field in output */
|
|
434
|
+
includeSchemaField?: boolean;
|
|
435
|
+
/** Target JSON Schema dialect (default: 'draft-2020-12') */
|
|
436
|
+
dialect?: "draft-2020-12" | "draft-07";
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* JSON Schema 2020-12 compatible output type.
|
|
440
|
+
* Uses Record<string, unknown> for flexibility since JSON Schema is highly polymorphic.
|
|
441
|
+
*/
|
|
442
|
+
type JSONSchema = Record<string, unknown>;
|
|
443
|
+
/**
|
|
444
|
+
* Normalize a SpecSchema to JSON Schema 2020-12.
|
|
445
|
+
*
|
|
446
|
+
* @param schema - The SpecSchema to normalize
|
|
447
|
+
* @param options - Normalization options
|
|
448
|
+
* @returns JSON Schema 2020-12 compatible schema
|
|
449
|
+
*/
|
|
450
|
+
declare function normalizeSchema(schema: SpecSchema2, options?: NormalizeOptions): JSONSchema;
|
|
451
|
+
/**
|
|
452
|
+
* Normalize a SpecExport, normalizing its schema and nested schemas.
|
|
453
|
+
*
|
|
454
|
+
* For interfaces and classes, this function will:
|
|
455
|
+
* 1. Normalize any existing schema
|
|
456
|
+
* 2. Normalize member schemas
|
|
457
|
+
* 3. Generate a JSON Schema from members if members exist (populates `schema` field)
|
|
458
|
+
*/
|
|
459
|
+
declare function normalizeExport(exp: SpecExport8, options?: NormalizeOptions): SpecExport8;
|
|
460
|
+
/**
|
|
461
|
+
* Normalize a SpecType, normalizing its schema and nested schemas.
|
|
462
|
+
*
|
|
463
|
+
* For interfaces and classes, this function will:
|
|
464
|
+
* 1. Normalize any existing schema
|
|
465
|
+
* 2. Normalize member schemas
|
|
466
|
+
* 3. Generate a JSON Schema from members if members exist (populates `schema` field)
|
|
467
|
+
*/
|
|
468
|
+
declare function normalizeType(type: SpecType2, options?: NormalizeOptions): SpecType2;
|
|
469
|
+
/**
|
|
470
|
+
* Convert a members array to JSON Schema properties format.
|
|
471
|
+
*
|
|
472
|
+
* This function transforms the SpecMember[] array representation used by
|
|
473
|
+
* interfaces/classes into a JSON Schema 2020-12 object schema with properties,
|
|
474
|
+
* required array, and additionalProperties.
|
|
475
|
+
*
|
|
476
|
+
* Member Kind Mappings:
|
|
477
|
+
* | Member Kind | JSON Schema Output |
|
|
478
|
+
* |--------------------|-------------------------------------------------------|
|
|
479
|
+
* | property | Direct schema in properties |
|
|
480
|
+
* | method | { "x-ts-function": true, "x-ts-signatures": [...] } |
|
|
481
|
+
* | getter | Schema in properties (read-only via extension) |
|
|
482
|
+
* | setter | Schema in properties (write-only via extension) |
|
|
483
|
+
* | index | additionalProperties schema |
|
|
484
|
+
*
|
|
485
|
+
* @param members - The members array from an interface/class
|
|
486
|
+
* @param options - Normalization options
|
|
487
|
+
* @returns JSON Schema object with properties, required, and additionalProperties
|
|
488
|
+
*/
|
|
489
|
+
declare function normalizeMembers(members: SpecMember[], options?: NormalizeOptions): JSONSchema;
|
|
428
490
|
import ts13 from "typescript";
|
|
429
491
|
declare function isExported(node: ts13.Node): boolean;
|
|
430
492
|
declare function getNodeName(node: ts13.Node): string | undefined;
|
|
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 };
|
|
493
|
+
export { zodAdapter, withDescription, valibotAdapter, typeboxAdapter, serializeVariable, serializeTypeAlias, serializeInterface, serializeFunctionExport, serializeEnum, serializeClass, schemasAreEqual, schemaIsAny, resolveCompiledPath, registerReferencedTypes, registerAdapter, normalizeType, normalizeSchema, normalizeMembers, normalizeExport, 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, NormalizeOptions, JSONSchema, ForgottenExport, ExtractStandardSchemasOptions, ExtractResult, ExtractOptions, ExtractFromProjectOptions, Diagnostic, BUILTIN_TYPE_SCHEMAS };
|
package/dist/src/index.js
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BUILTIN_TYPE_SCHEMAS,
|
|
3
3
|
TypeRegistry,
|
|
4
|
+
arktypeAdapter,
|
|
4
5
|
buildSchema,
|
|
5
6
|
createProgram,
|
|
6
7
|
deduplicateSchemas,
|
|
7
8
|
detectTsRuntime,
|
|
8
9
|
extract,
|
|
9
10
|
extractParameters,
|
|
11
|
+
extractSchemaType,
|
|
10
12
|
extractStandardSchemas,
|
|
11
13
|
extractStandardSchemasFromProject,
|
|
12
14
|
extractStandardSchemasFromTs,
|
|
13
15
|
extractTypeParameters,
|
|
16
|
+
findAdapter,
|
|
14
17
|
findDiscriminatorProperty,
|
|
15
18
|
getJSDocComment,
|
|
19
|
+
getNonNullableType,
|
|
16
20
|
getParamDescription,
|
|
17
21
|
getSourceLocation,
|
|
18
22
|
isAnonymous,
|
|
19
23
|
isBuiltinGeneric,
|
|
20
24
|
isPrimitiveName,
|
|
21
25
|
isPureRefSchema,
|
|
26
|
+
isSchemaType,
|
|
22
27
|
isStandardJSONSchema,
|
|
23
28
|
isSymbolDeprecated,
|
|
29
|
+
isTypeReference,
|
|
30
|
+
normalizeExport,
|
|
31
|
+
normalizeMembers,
|
|
32
|
+
normalizeSchema,
|
|
33
|
+
normalizeType,
|
|
34
|
+
registerAdapter,
|
|
24
35
|
registerReferencedTypes,
|
|
25
36
|
resolveCompiledPath,
|
|
26
37
|
schemaIsAny,
|
|
@@ -31,170 +42,11 @@ import {
|
|
|
31
42
|
serializeInterface,
|
|
32
43
|
serializeTypeAlias,
|
|
33
44
|
serializeVariable,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
function getNonNullableType(type) {
|
|
41
|
-
if (type.isUnion()) {
|
|
42
|
-
const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
|
|
43
|
-
if (nonNullable.length === 1) {
|
|
44
|
-
return nonNullable[0];
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return type;
|
|
48
|
-
}
|
|
49
|
-
var adapters = [];
|
|
50
|
-
function registerAdapter(adapter) {
|
|
51
|
-
adapters.push(adapter);
|
|
52
|
-
}
|
|
53
|
-
function findAdapter(type, checker) {
|
|
54
|
-
return adapters.find((a) => a.matches(type, checker));
|
|
55
|
-
}
|
|
56
|
-
function isSchemaType(type, checker) {
|
|
57
|
-
return adapters.some((a) => a.matches(type, checker));
|
|
58
|
-
}
|
|
59
|
-
function extractSchemaType(type, checker) {
|
|
60
|
-
const adapter = findAdapter(type, checker);
|
|
61
|
-
if (!adapter)
|
|
62
|
-
return null;
|
|
63
|
-
const outputType = adapter.extractOutputType(type, checker);
|
|
64
|
-
if (!outputType)
|
|
65
|
-
return null;
|
|
66
|
-
const inputType = adapter.extractInputType?.(type, checker) ?? undefined;
|
|
67
|
-
return {
|
|
68
|
-
adapter,
|
|
69
|
-
outputType,
|
|
70
|
-
inputType
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// src/schema/adapters/arktype.ts
|
|
75
|
-
var ARKTYPE_TYPE_PATTERN = /^Type</;
|
|
76
|
-
var arktypeAdapter = {
|
|
77
|
-
id: "arktype",
|
|
78
|
-
packages: ["arktype"],
|
|
79
|
-
matches(type, checker) {
|
|
80
|
-
const typeName = checker.typeToString(type);
|
|
81
|
-
return ARKTYPE_TYPE_PATTERN.test(typeName);
|
|
82
|
-
},
|
|
83
|
-
extractOutputType(type, checker) {
|
|
84
|
-
if (!isTypeReference(type)) {
|
|
85
|
-
return null;
|
|
86
|
-
}
|
|
87
|
-
const args = checker.getTypeArguments(type);
|
|
88
|
-
if (args.length < 1) {
|
|
89
|
-
return null;
|
|
90
|
-
}
|
|
91
|
-
return args[0];
|
|
92
|
-
},
|
|
93
|
-
extractInputType(type, checker) {
|
|
94
|
-
if (!isTypeReference(type)) {
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
const args = checker.getTypeArguments(type);
|
|
98
|
-
if (args.length < 2) {
|
|
99
|
-
return null;
|
|
100
|
-
}
|
|
101
|
-
return args[1];
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// src/schema/adapters/typebox.ts
|
|
106
|
-
var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
|
|
107
|
-
var typeboxAdapter = {
|
|
108
|
-
id: "typebox",
|
|
109
|
-
packages: ["@sinclair/typebox"],
|
|
110
|
-
matches(type, checker) {
|
|
111
|
-
const typeName = checker.typeToString(type);
|
|
112
|
-
if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
const typeProperty = type.getProperty("type");
|
|
116
|
-
return typeProperty !== undefined;
|
|
117
|
-
},
|
|
118
|
-
extractOutputType(type, checker) {
|
|
119
|
-
const staticSymbol = type.getProperty("static");
|
|
120
|
-
if (staticSymbol) {
|
|
121
|
-
return checker.getTypeOfSymbol(staticSymbol);
|
|
122
|
-
}
|
|
123
|
-
return null;
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// src/schema/adapters/valibot.ts
|
|
128
|
-
var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
|
|
129
|
-
var valibotAdapter = {
|
|
130
|
-
id: "valibot",
|
|
131
|
-
packages: ["valibot"],
|
|
132
|
-
matches(type, checker) {
|
|
133
|
-
const typeName = checker.typeToString(type);
|
|
134
|
-
return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
|
|
135
|
-
},
|
|
136
|
-
extractOutputType(type, checker) {
|
|
137
|
-
const typesSymbol = type.getProperty("~types");
|
|
138
|
-
if (!typesSymbol) {
|
|
139
|
-
return null;
|
|
140
|
-
}
|
|
141
|
-
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
142
|
-
typesType = getNonNullableType(typesType);
|
|
143
|
-
const outputSymbol = typesType.getProperty("output");
|
|
144
|
-
if (!outputSymbol) {
|
|
145
|
-
return null;
|
|
146
|
-
}
|
|
147
|
-
return checker.getTypeOfSymbol(outputSymbol);
|
|
148
|
-
},
|
|
149
|
-
extractInputType(type, checker) {
|
|
150
|
-
const typesSymbol = type.getProperty("~types");
|
|
151
|
-
if (!typesSymbol) {
|
|
152
|
-
return null;
|
|
153
|
-
}
|
|
154
|
-
let typesType = checker.getTypeOfSymbol(typesSymbol);
|
|
155
|
-
typesType = getNonNullableType(typesType);
|
|
156
|
-
const inputSymbol = typesType.getProperty("input");
|
|
157
|
-
if (!inputSymbol) {
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
return checker.getTypeOfSymbol(inputSymbol);
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
// src/schema/adapters/zod.ts
|
|
165
|
-
var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
|
|
166
|
-
var zodAdapter = {
|
|
167
|
-
id: "zod",
|
|
168
|
-
packages: ["zod"],
|
|
169
|
-
matches(type, checker) {
|
|
170
|
-
const typeName = checker.typeToString(type);
|
|
171
|
-
return ZOD_TYPE_PATTERN.test(typeName);
|
|
172
|
-
},
|
|
173
|
-
extractOutputType(type, checker) {
|
|
174
|
-
const outputSymbol = type.getProperty("_output");
|
|
175
|
-
if (outputSymbol) {
|
|
176
|
-
return checker.getTypeOfSymbol(outputSymbol);
|
|
177
|
-
}
|
|
178
|
-
const typeSymbol = type.getProperty("_type");
|
|
179
|
-
if (typeSymbol) {
|
|
180
|
-
return checker.getTypeOfSymbol(typeSymbol);
|
|
181
|
-
}
|
|
182
|
-
return null;
|
|
183
|
-
},
|
|
184
|
-
extractInputType(type, checker) {
|
|
185
|
-
const inputSymbol = type.getProperty("_input");
|
|
186
|
-
if (inputSymbol) {
|
|
187
|
-
return checker.getTypeOfSymbol(inputSymbol);
|
|
188
|
-
}
|
|
189
|
-
return null;
|
|
190
|
-
}
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
// src/schema/adapters/index.ts
|
|
194
|
-
registerAdapter(zodAdapter);
|
|
195
|
-
registerAdapter(valibotAdapter);
|
|
196
|
-
registerAdapter(arktypeAdapter);
|
|
197
|
-
registerAdapter(typeboxAdapter);
|
|
45
|
+
typeboxAdapter,
|
|
46
|
+
valibotAdapter,
|
|
47
|
+
withDescription,
|
|
48
|
+
zodAdapter
|
|
49
|
+
} from "../shared/chunk-y5d5qgyt.js";
|
|
198
50
|
// src/types/utils.ts
|
|
199
51
|
function isExported(node) {
|
|
200
52
|
const modifiers = node.modifiers;
|
|
@@ -225,6 +77,10 @@ export {
|
|
|
225
77
|
resolveCompiledPath,
|
|
226
78
|
registerReferencedTypes,
|
|
227
79
|
registerAdapter,
|
|
80
|
+
normalizeType,
|
|
81
|
+
normalizeSchema,
|
|
82
|
+
normalizeMembers,
|
|
83
|
+
normalizeExport,
|
|
228
84
|
isTypeReference,
|
|
229
85
|
isSymbolDeprecated,
|
|
230
86
|
isStandardJSONSchema,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openpkg-ts/extract",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "TypeScript export extraction to OpenPkg spec",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openpkg",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"format": "biome format --write src/"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@openpkg-ts/spec": "^0.
|
|
43
|
+
"@openpkg-ts/spec": "^0.23.0",
|
|
44
44
|
"chalk": "^5.4.1",
|
|
45
45
|
"commander": "^12.0.0",
|
|
46
46
|
"tree-sitter-wasms": "^0.1.13",
|