@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/bin/tspec.js +1 -1
- package/dist/shared/{chunk-wddga8ye.js → chunk-v4cnenxs.js} +841 -96
- package/dist/src/index.d.ts +208 -47
- package/dist/src/index.js +361 -43
- package/package.json +2 -1
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
-
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
68
|
-
const
|
|
69
|
-
if (!
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return
|
|
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
|
-
|
|
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.
|
|
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": {
|