@openpkg-ts/extract 0.12.0 → 0.14.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/src/index.js CHANGED
@@ -1,78 +1,383 @@
1
1
  import {
2
+ BUILTIN_TYPE_SCHEMAS,
2
3
  TypeRegistry,
3
4
  buildSchema,
4
5
  createProgram,
6
+ deduplicateSchemas,
5
7
  extract,
6
8
  extractParameters,
9
+ extractTypeParameters,
10
+ findDiscriminatorProperty,
7
11
  getJSDocComment,
12
+ getParamDescription,
8
13
  getSourceLocation,
9
14
  isAnonymous,
10
15
  isBuiltinGeneric,
11
16
  isPrimitiveName,
17
+ isPureRefSchema,
18
+ isSymbolDeprecated,
12
19
  registerReferencedTypes,
20
+ schemaIsAny,
21
+ schemasAreEqual,
13
22
  serializeClass,
14
23
  serializeEnum,
15
24
  serializeFunctionExport,
16
25
  serializeInterface,
17
26
  serializeTypeAlias,
18
- serializeVariable
19
- } from "../shared/chunk-wddga8ye.js";
27
+ serializeVariable,
28
+ withDescription
29
+ } from "../shared/chunk-v4cnenxs.js";
30
+ // src/schema/registry.ts
31
+ function isTypeReference(type) {
32
+ return !!(type.flags & 524288 && type.objectFlags && type.objectFlags & 4);
33
+ }
34
+ function getNonNullableType(type) {
35
+ if (type.isUnion()) {
36
+ const nonNullable = type.types.filter((t) => !(t.flags & 32768) && !(t.flags & 65536));
37
+ if (nonNullable.length === 1) {
38
+ return nonNullable[0];
39
+ }
40
+ }
41
+ return type;
42
+ }
43
+ var adapters = [];
44
+ function registerAdapter(adapter) {
45
+ adapters.push(adapter);
46
+ }
47
+ function findAdapter(type, checker) {
48
+ return adapters.find((a) => a.matches(type, checker));
49
+ }
50
+ function isSchemaType(type, checker) {
51
+ return adapters.some((a) => a.matches(type, checker));
52
+ }
53
+ function extractSchemaType(type, checker) {
54
+ const adapter = findAdapter(type, checker);
55
+ if (!adapter)
56
+ return null;
57
+ const outputType = adapter.extractOutputType(type, checker);
58
+ if (!outputType)
59
+ return null;
60
+ const inputType = adapter.extractInputType?.(type, checker);
61
+ return {
62
+ adapter,
63
+ outputType,
64
+ inputType
65
+ };
66
+ }
67
+
20
68
  // src/schema/adapters/arktype.ts
69
+ var ARKTYPE_TYPE_PATTERN = /^Type</;
21
70
  var arktypeAdapter = {
22
- name: "arktype",
23
- detect: () => false,
24
- extract: () => null
71
+ id: "arktype",
72
+ packages: ["arktype"],
73
+ matches(type, checker) {
74
+ const typeName = checker.typeToString(type);
75
+ return ARKTYPE_TYPE_PATTERN.test(typeName);
76
+ },
77
+ extractOutputType(type, checker) {
78
+ if (!isTypeReference(type)) {
79
+ return null;
80
+ }
81
+ const args = checker.getTypeArguments(type);
82
+ if (args.length < 1) {
83
+ return null;
84
+ }
85
+ return args[0];
86
+ },
87
+ extractInputType(type, checker) {
88
+ if (!isTypeReference(type)) {
89
+ return null;
90
+ }
91
+ const args = checker.getTypeArguments(type);
92
+ if (args.length < 2) {
93
+ return null;
94
+ }
95
+ return args[1];
96
+ }
25
97
  };
98
+
26
99
  // src/schema/adapters/typebox.ts
100
+ var TYPEBOX_TYPE_PATTERN = /^T[A-Z]/;
27
101
  var typeboxAdapter = {
28
- name: "typebox",
29
- detect: () => false,
30
- extract: () => null
102
+ id: "typebox",
103
+ packages: ["@sinclair/typebox"],
104
+ matches(type, checker) {
105
+ const typeName = checker.typeToString(type);
106
+ if (!TYPEBOX_TYPE_PATTERN.test(typeName)) {
107
+ return false;
108
+ }
109
+ const typeProperty = type.getProperty("type");
110
+ return typeProperty !== undefined;
111
+ },
112
+ extractOutputType(type, checker) {
113
+ const staticSymbol = type.getProperty("static");
114
+ if (staticSymbol) {
115
+ return checker.getTypeOfSymbol(staticSymbol);
116
+ }
117
+ return null;
118
+ }
31
119
  };
120
+
32
121
  // src/schema/adapters/valibot.ts
122
+ var VALIBOT_TYPE_PATTERN = /Schema(<|$)/;
33
123
  var valibotAdapter = {
34
- name: "valibot",
35
- detect: () => false,
36
- extract: () => null
124
+ id: "valibot",
125
+ packages: ["valibot"],
126
+ matches(type, checker) {
127
+ const typeName = checker.typeToString(type);
128
+ return VALIBOT_TYPE_PATTERN.test(typeName) && !typeName.includes("Zod");
129
+ },
130
+ extractOutputType(type, checker) {
131
+ const typesSymbol = type.getProperty("~types");
132
+ if (!typesSymbol) {
133
+ return null;
134
+ }
135
+ let typesType = checker.getTypeOfSymbol(typesSymbol);
136
+ typesType = getNonNullableType(typesType);
137
+ const outputSymbol = typesType.getProperty("output");
138
+ if (!outputSymbol) {
139
+ return null;
140
+ }
141
+ return checker.getTypeOfSymbol(outputSymbol);
142
+ },
143
+ extractInputType(type, checker) {
144
+ const typesSymbol = type.getProperty("~types");
145
+ if (!typesSymbol) {
146
+ return null;
147
+ }
148
+ let typesType = checker.getTypeOfSymbol(typesSymbol);
149
+ typesType = getNonNullableType(typesType);
150
+ const inputSymbol = typesType.getProperty("input");
151
+ if (!inputSymbol) {
152
+ return null;
153
+ }
154
+ return checker.getTypeOfSymbol(inputSymbol);
155
+ }
37
156
  };
157
+
38
158
  // src/schema/adapters/zod.ts
159
+ var ZOD_TYPE_PATTERN = /^Zod[A-Z]/;
39
160
  var zodAdapter = {
40
- name: "zod",
41
- detect: () => false,
42
- extract: () => null
161
+ id: "zod",
162
+ packages: ["zod"],
163
+ matches(type, checker) {
164
+ const typeName = checker.typeToString(type);
165
+ return ZOD_TYPE_PATTERN.test(typeName);
166
+ },
167
+ extractOutputType(type, checker) {
168
+ const outputSymbol = type.getProperty("_output");
169
+ if (outputSymbol) {
170
+ return checker.getTypeOfSymbol(outputSymbol);
171
+ }
172
+ const typeSymbol = type.getProperty("_type");
173
+ if (typeSymbol) {
174
+ return checker.getTypeOfSymbol(typeSymbol);
175
+ }
176
+ return null;
177
+ },
178
+ extractInputType(type, checker) {
179
+ const inputSymbol = type.getProperty("_input");
180
+ if (inputSymbol) {
181
+ return checker.getTypeOfSymbol(inputSymbol);
182
+ }
183
+ return null;
184
+ }
43
185
  };
44
- // src/schema/registry.ts
45
- var adapters = [];
46
- function registerAdapter(adapter) {
47
- adapters.push(adapter);
186
+
187
+ // src/schema/adapters/index.ts
188
+ registerAdapter(zodAdapter);
189
+ registerAdapter(valibotAdapter);
190
+ registerAdapter(arktypeAdapter);
191
+ registerAdapter(typeboxAdapter);
192
+ // src/schema/standard-schema.ts
193
+ import { spawn } from "node:child_process";
194
+ import * as fs from "node:fs";
195
+ import * as path from "node:path";
196
+ function isStandardJSONSchema(obj) {
197
+ if (typeof obj !== "object" || obj === null)
198
+ return false;
199
+ const std = obj["~standard"];
200
+ if (typeof std !== "object" || std === null)
201
+ return false;
202
+ const stdObj = std;
203
+ if (typeof stdObj.version !== "number")
204
+ return false;
205
+ if (typeof stdObj.vendor !== "string")
206
+ return false;
207
+ const jsonSchema = stdObj.jsonSchema;
208
+ if (typeof jsonSchema !== "object" || jsonSchema === null)
209
+ return false;
210
+ const jsObj = jsonSchema;
211
+ return typeof jsObj.output === "function";
48
212
  }
49
- function findAdapter(node, checker) {
50
- return adapters.find((a) => a.detect(node, checker));
213
+ var WORKER_SCRIPT = `
214
+ const path = require('path');
215
+ const { pathToFileURL } = require('url');
216
+
217
+ // TypeBox detection: schemas have Symbol.for('TypeBox.Kind') and are JSON Schema
218
+ const TYPEBOX_KIND = Symbol.for('TypeBox.Kind');
219
+
220
+ function isTypeBoxSchema(obj) {
221
+ if (!obj || typeof obj !== 'object') return false;
222
+ // TypeBox schemas always have Kind symbol (Union, Object, String, etc.)
223
+ // Also check for common JSON Schema props to avoid false positives
224
+ if (!obj[TYPEBOX_KIND]) return false;
225
+ return typeof obj.type === 'string' || 'anyOf' in obj || 'oneOf' in obj || 'allOf' in obj;
51
226
  }
52
- function isSchemaType(node, checker) {
53
- return adapters.some((a) => a.detect(node, checker));
227
+
228
+ function sanitizeTypeBoxSchema(schema) {
229
+ // JSON.stringify removes symbol keys, keeping only JSON Schema props
230
+ return JSON.parse(JSON.stringify(schema));
54
231
  }
55
- function extractSchemaType(node, checker) {
56
- const adapter = findAdapter(node, checker);
57
- return adapter?.extract(node, checker) ?? null;
232
+
233
+ async function extract() {
234
+ // With node -e, argv is: [node, arg1, arg2, ...]
235
+ // (the -e script is NOT in argv)
236
+ const [modulePath, target] = process.argv.slice(1);
237
+
238
+ try {
239
+ // Import the module using dynamic import (works with ESM and CJS)
240
+ const absPath = path.resolve(modulePath);
241
+ const mod = await import(pathToFileURL(absPath).href);
242
+ const results = [];
243
+
244
+ // Build exports map - handle both ESM and CJS (where exports are in mod.default)
245
+ const exports = {};
246
+ for (const [name, value] of Object.entries(mod)) {
247
+ if (name === 'default' && typeof value === 'object' && value !== null) {
248
+ // CJS module: spread default exports
249
+ Object.assign(exports, value);
250
+ } else if (name !== 'default') {
251
+ exports[name] = value;
252
+ }
253
+ }
254
+
255
+ // Check each export
256
+ for (const [name, value] of Object.entries(exports)) {
257
+ if (name.startsWith('_')) continue;
258
+ if (typeof value !== 'object' || value === null) continue;
259
+
260
+ // Priority 1: Standard Schema (Zod 4.2+, ArkType, etc.)
261
+ const std = value['~standard'];
262
+ if (std && typeof std === 'object' && typeof std.version === 'number' && typeof std.vendor === 'string' && std.jsonSchema && typeof std.jsonSchema.output === 'function') {
263
+ try {
264
+ const outputSchema = std.jsonSchema.output(target);
265
+ const inputSchema = std.jsonSchema.input ? std.jsonSchema.input(target) : undefined;
266
+ results.push({
267
+ exportName: name,
268
+ vendor: std.vendor,
269
+ outputSchema,
270
+ inputSchema
271
+ });
272
+ } catch (e) {
273
+ // Skip schemas that fail to extract
274
+ }
275
+ continue;
276
+ }
277
+
278
+ // Priority 2: TypeBox (schema IS JSON Schema)
279
+ if (isTypeBoxSchema(value)) {
280
+ try {
281
+ results.push({
282
+ exportName: name,
283
+ vendor: 'typebox',
284
+ outputSchema: sanitizeTypeBoxSchema(value)
285
+ });
286
+ } catch (e) {
287
+ // Skip schemas that fail to extract
288
+ }
289
+ continue;
290
+ }
291
+ }
292
+
293
+ console.log(JSON.stringify({ success: true, results }));
294
+ } catch (e) {
295
+ console.log(JSON.stringify({ success: false, error: e.message }));
296
+ }
58
297
  }
59
- // src/schema/standard-schema.ts
60
- function extractStandardSchemas(_program, _entryFile) {
61
- return [];
298
+
299
+ extract();
300
+ `;
301
+ function resolveCompiledPath(tsPath, baseDir) {
302
+ const relativePath = path.relative(baseDir, tsPath);
303
+ const withoutExt = relativePath.replace(/\.tsx?$/, "");
304
+ const candidates = [
305
+ path.join(baseDir, `${withoutExt}.js`),
306
+ path.join(baseDir, "dist", `${withoutExt.replace(/^src\//, "")}.js`),
307
+ path.join(baseDir, "build", `${withoutExt.replace(/^src\//, "")}.js`),
308
+ path.join(baseDir, "lib", `${withoutExt.replace(/^src\//, "")}.js`)
309
+ ];
310
+ for (const candidate of candidates) {
311
+ if (fs.existsSync(candidate)) {
312
+ return candidate;
313
+ }
314
+ }
315
+ return null;
62
316
  }
63
- // src/types/formatter.ts
64
- function formatTypeReference(type, checker) {
65
- return checker.typeToString(type);
317
+ async function extractStandardSchemas(compiledJsPath, options = {}) {
318
+ const { timeout = 1e4, target = "draft-2020-12" } = options;
319
+ const result = {
320
+ schemas: new Map,
321
+ errors: []
322
+ };
323
+ if (!fs.existsSync(compiledJsPath)) {
324
+ result.errors.push(`Compiled JS not found: ${compiledJsPath}`);
325
+ return result;
326
+ }
327
+ return new Promise((resolve) => {
328
+ const child = spawn("node", ["-e", WORKER_SCRIPT, compiledJsPath, target], {
329
+ timeout,
330
+ stdio: ["ignore", "pipe", "pipe"]
331
+ });
332
+ let stdout = "";
333
+ let stderr = "";
334
+ child.stdout.on("data", (data) => {
335
+ stdout += data.toString();
336
+ });
337
+ child.stderr.on("data", (data) => {
338
+ stderr += data.toString();
339
+ });
340
+ child.on("close", (code) => {
341
+ if (code !== 0) {
342
+ result.errors.push(`Extraction process failed: ${stderr || `exit code ${code}`}`);
343
+ resolve(result);
344
+ return;
345
+ }
346
+ try {
347
+ const parsed = JSON.parse(stdout);
348
+ if (!parsed.success) {
349
+ result.errors.push(`Extraction failed: ${parsed.error}`);
350
+ resolve(result);
351
+ return;
352
+ }
353
+ for (const item of parsed.results) {
354
+ result.schemas.set(item.exportName, {
355
+ exportName: item.exportName,
356
+ vendor: item.vendor,
357
+ outputSchema: item.outputSchema,
358
+ inputSchema: item.inputSchema
359
+ });
360
+ }
361
+ } catch (e) {
362
+ result.errors.push(`Failed to parse extraction output: ${e}`);
363
+ }
364
+ resolve(result);
365
+ });
366
+ child.on("error", (err) => {
367
+ result.errors.push(`Subprocess error: ${err.message}`);
368
+ resolve(result);
369
+ });
370
+ });
66
371
  }
67
- function collectReferencedTypes(type, checker, visited = new Set) {
68
- const symbol = type.getSymbol();
69
- if (!symbol)
70
- return [];
71
- const name = symbol.getName();
72
- if (visited.has(name))
73
- return [];
74
- visited.add(name);
75
- return [name];
372
+ async function extractStandardSchemasFromProject(entryFile, baseDir, options = {}) {
373
+ const compiledPath = resolveCompiledPath(entryFile, baseDir);
374
+ if (!compiledPath) {
375
+ return {
376
+ schemas: new Map,
377
+ errors: [`Could not find compiled JS for ${entryFile}. Build the project first.`]
378
+ };
379
+ }
380
+ return extractStandardSchemas(compiledPath, options);
76
381
  }
77
382
  // src/types/utils.ts
78
383
  function isExported(node) {
@@ -90,6 +395,7 @@ function getNodeName(node) {
90
395
  }
91
396
  export {
92
397
  zodAdapter,
398
+ withDescription,
93
399
  valibotAdapter,
94
400
  typeboxAdapter,
95
401
  serializeVariable,
@@ -98,25 +404,37 @@ export {
98
404
  serializeFunctionExport,
99
405
  serializeEnum,
100
406
  serializeClass,
407
+ schemasAreEqual,
408
+ schemaIsAny,
409
+ resolveCompiledPath,
101
410
  registerReferencedTypes,
102
411
  registerAdapter,
412
+ isTypeReference,
413
+ isSymbolDeprecated,
414
+ isStandardJSONSchema,
103
415
  isSchemaType,
416
+ isPureRefSchema,
104
417
  isPrimitiveName,
105
418
  isExported,
106
419
  isBuiltinGeneric,
107
420
  isAnonymous,
108
421
  getSourceLocation,
422
+ getParamDescription,
423
+ getNonNullableType,
109
424
  getNodeName,
110
425
  getJSDocComment,
111
- formatTypeReference,
426
+ findDiscriminatorProperty,
112
427
  findAdapter,
428
+ extractTypeParameters,
429
+ extractStandardSchemasFromProject,
113
430
  extractStandardSchemas,
114
431
  extractSchemaType,
115
432
  extractParameters,
116
433
  extract,
434
+ deduplicateSchemas,
117
435
  createProgram,
118
- collectReferencedTypes,
119
436
  buildSchema,
120
437
  arktypeAdapter,
121
- TypeRegistry
438
+ TypeRegistry,
439
+ BUILTIN_TYPE_SCHEMAS
122
440
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openpkg-ts/extract",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "TypeScript export extraction to OpenPkg spec",
5
5
  "keywords": [
6
6
  "openpkg",
@@ -42,6 +42,7 @@
42
42
  "dependencies": {
43
43
  "@openpkg-ts/spec": "^0.12.0",
44
44
  "commander": "^12.0.0",
45
+ "tree-sitter-wasms": "^0.1.13",
45
46
  "typescript": "^5.0.0"
46
47
  },
47
48
  "devDependencies": {