@langchain/langgraph-api 0.0.10

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.
@@ -0,0 +1,1607 @@
1
+ // Copied from typescript-json-schema#70de093f2e148afab527aaf0d4dc38ce19de7715
2
+ //
3
+ // Copyright (c) 2016, typescript-json-schema contributors
4
+ // All rights reserved.
5
+ //
6
+ // Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
7
+ //
8
+ // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
9
+ //
10
+ // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
11
+ //
12
+ // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
13
+ //
14
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
+ import * as ts from "typescript";
16
+ import * as vm from "node:vm";
17
+ import * as fs from "node:fs/promises";
18
+ import * as path from "node:path";
19
+ import { createHash } from "node:crypto";
20
+ import dedent from "dedent";
21
+ const REGEX_FILE_NAME_OR_SPACE = /(\bimport\(".*?"\)|".*?")\.| /g;
22
+ const REGEX_TJS_JSDOC = /^-([\w]+)\s+(\S|\S[\s\S]*\S)\s*$/g;
23
+ const REGEX_GROUP_JSDOC = /^[.]?([\w]+)\s+(\S|\S[\s\S]*\S)\s*$/g;
24
+ /**
25
+ * Resolve required file, his path and a property name,
26
+ * pattern: require([file_path]).[property_name]
27
+ *
28
+ * the part ".[property_name]" is optional in the regex
29
+ *
30
+ * will match:
31
+ *
32
+ * require('./path.ts')
33
+ * require('./path.ts').objectName
34
+ * require("./path.ts")
35
+ * require("./path.ts").objectName
36
+ * require('@module-name')
37
+ *
38
+ * match[2] = file_path (a path to the file with quotes)
39
+ * match[3] = (optional) property_name (a property name, exported in the file)
40
+ *
41
+ * for more details, see tests/require.test.ts
42
+ */
43
+ const REGEX_REQUIRE = /^(\s+)?require\((\'@?[a-zA-Z0-9.\/_-]+\'|\"@?[a-zA-Z0-9.\/_-]+\")\)(\.([a-zA-Z0-9_$]+))?(\s+|$)/;
44
+ const NUMERIC_INDEX_PATTERN = "^[0-9]+$";
45
+ function pathEqual(actual, expected) {
46
+ return (actual === expected || normalizePath(actual) === normalizePath(expected));
47
+ }
48
+ function normalizePath(path) {
49
+ const replace = [
50
+ [/\\/g, "/"],
51
+ [/(\w):/, "/$1"],
52
+ [/(\w+)\/\.\.\/?/g, ""],
53
+ [/^\.\//, ""],
54
+ [/\/\.\//, "/"],
55
+ [/\/\.$/, ""],
56
+ [/\/$/, ""],
57
+ ];
58
+ replace.forEach((array) => {
59
+ while (array[0].test(path)) {
60
+ path = path.replace(array[0], array[1]);
61
+ }
62
+ });
63
+ return path;
64
+ }
65
+ function getDefaultArgs() {
66
+ return {
67
+ ref: true,
68
+ aliasRef: false,
69
+ topRef: false,
70
+ titles: false,
71
+ defaultProps: false,
72
+ noExtraProps: false,
73
+ propOrder: false,
74
+ typeOfKeyword: false,
75
+ required: false,
76
+ strictNullChecks: false,
77
+ esModuleInterop: false,
78
+ experimentalDecorators: true,
79
+ out: "",
80
+ validationKeywords: [],
81
+ include: [],
82
+ excludePrivate: false,
83
+ uniqueNames: false,
84
+ rejectDateType: false,
85
+ id: "",
86
+ defaultNumberType: "number",
87
+ constAsEnum: false,
88
+ };
89
+ }
90
+ function extend(target, ..._) {
91
+ if (target == null) {
92
+ // TypeError if undefined or null
93
+ throw new TypeError("Cannot convert undefined or null to object");
94
+ }
95
+ const to = Object(target);
96
+ for (var index = 1; index < arguments.length; index++) {
97
+ const nextSource = arguments[index];
98
+ if (nextSource != null) {
99
+ // Skip over if undefined or null
100
+ for (const nextKey in nextSource) {
101
+ // Avoid bugs when hasOwnProperty is shadowed
102
+ if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
103
+ to[nextKey] = nextSource[nextKey];
104
+ }
105
+ }
106
+ }
107
+ }
108
+ return to;
109
+ }
110
+ function unique(arr) {
111
+ const temp = {};
112
+ for (const e of arr) {
113
+ temp[e] = true;
114
+ }
115
+ const r = [];
116
+ for (const k in temp) {
117
+ // Avoid bugs when hasOwnProperty is shadowed
118
+ if (Object.prototype.hasOwnProperty.call(temp, k)) {
119
+ r.push(k);
120
+ }
121
+ }
122
+ return r;
123
+ }
124
+ /**
125
+ * Resolve required file
126
+ */
127
+ function resolveRequiredFile(symbol, key, fileName, objectName) {
128
+ const sourceFile = getSourceFile(symbol);
129
+ const requiredFilePath = /^[.\/]+/.test(fileName)
130
+ ? fileName === "."
131
+ ? path.resolve(sourceFile.fileName)
132
+ : path.resolve(path.dirname(sourceFile.fileName), fileName)
133
+ : fileName;
134
+ const requiredFile = require(requiredFilePath);
135
+ if (!requiredFile) {
136
+ throw Error("Required: File couldn't be loaded");
137
+ }
138
+ const requiredObject = objectName
139
+ ? requiredFile[objectName]
140
+ : requiredFile.default;
141
+ if (requiredObject === undefined) {
142
+ throw Error("Required: Variable is undefined");
143
+ }
144
+ if (typeof requiredObject === "function") {
145
+ throw Error("Required: Can't use function as a variable");
146
+ }
147
+ if (key === "examples" && !Array.isArray(requiredObject)) {
148
+ throw Error("Required: Variable isn't an array");
149
+ }
150
+ return requiredObject;
151
+ }
152
+ function regexRequire(value) {
153
+ return REGEX_REQUIRE.exec(value);
154
+ }
155
+ /**
156
+ * Try to parse a value and returns the string if it fails.
157
+ */
158
+ function parseValue(symbol, key, value) {
159
+ const match = regexRequire(value);
160
+ if (match) {
161
+ const fileName = match[2].substring(1, match[2].length - 2).trim();
162
+ const objectName = match[4];
163
+ return resolveRequiredFile(symbol, key, fileName, objectName);
164
+ }
165
+ try {
166
+ return JSON.parse(value);
167
+ }
168
+ catch (error) {
169
+ return value;
170
+ }
171
+ }
172
+ function extractLiteralValue(typ) {
173
+ let str = typ.value;
174
+ if (str === undefined) {
175
+ str = typ.text;
176
+ }
177
+ if (typ.flags & ts.TypeFlags.StringLiteral) {
178
+ return str;
179
+ }
180
+ else if (typ.flags & ts.TypeFlags.BooleanLiteral) {
181
+ return typ.intrinsicName === "true";
182
+ }
183
+ else if (typ.flags & ts.TypeFlags.EnumLiteral) {
184
+ // or .text for old TS
185
+ const num = parseFloat(str);
186
+ return isNaN(num) ? str : num;
187
+ }
188
+ else if (typ.flags & ts.TypeFlags.NumberLiteral) {
189
+ return parseFloat(str);
190
+ }
191
+ return undefined;
192
+ }
193
+ /**
194
+ * Checks whether a type is a tuple type.
195
+ */
196
+ function resolveTupleType(propertyType) {
197
+ if (!propertyType.getSymbol() &&
198
+ propertyType.getFlags() & ts.TypeFlags.Object &&
199
+ propertyType.objectFlags & ts.ObjectFlags.Reference) {
200
+ return propertyType.target;
201
+ }
202
+ if (!(propertyType.getFlags() & ts.TypeFlags.Object &&
203
+ propertyType.objectFlags & ts.ObjectFlags.Tuple)) {
204
+ return null;
205
+ }
206
+ return propertyType;
207
+ }
208
+ const simpleTypesAllowedProperties = {
209
+ type: true,
210
+ description: true,
211
+ };
212
+ function addSimpleType(def, type) {
213
+ for (const k in def) {
214
+ if (!simpleTypesAllowedProperties[k]) {
215
+ return false;
216
+ }
217
+ }
218
+ if (!def.type) {
219
+ def.type = type;
220
+ }
221
+ else if (typeof def.type !== "string") {
222
+ if (!def.type.every((val) => {
223
+ return typeof val === "string";
224
+ })) {
225
+ return false;
226
+ }
227
+ if (def.type.indexOf("null") === -1) {
228
+ def.type.push("null");
229
+ }
230
+ }
231
+ else {
232
+ if (typeof def.type !== "string") {
233
+ return false;
234
+ }
235
+ if (def.type !== "null") {
236
+ def.type = [def.type, "null"];
237
+ }
238
+ }
239
+ return true;
240
+ }
241
+ function makeNullable(def) {
242
+ if (!addSimpleType(def, "null")) {
243
+ const union = def.oneOf || def.anyOf;
244
+ if (union) {
245
+ union.push({ type: "null" });
246
+ }
247
+ else {
248
+ const subdef = {};
249
+ for (var k in def) {
250
+ if (def.hasOwnProperty(k)) {
251
+ subdef[k] = def[k];
252
+ delete def[k];
253
+ }
254
+ }
255
+ def.anyOf = [subdef, { type: "null" }];
256
+ }
257
+ }
258
+ return def;
259
+ }
260
+ /**
261
+ * Given a Symbol, returns a canonical Definition. That can be either:
262
+ * 1) The Symbol's valueDeclaration parameter if defined, or
263
+ * 2) The sole entry in the Symbol's declarations array, provided that array has a length of 1.
264
+ *
265
+ * valueDeclaration is listed as a required parameter in the definition of a Symbol, but I've
266
+ * experienced crashes when it's undefined at runtime, which is the reason for this function's
267
+ * existence. Not sure if that's a compiler API bug or what.
268
+ */
269
+ function getCanonicalDeclaration(sym) {
270
+ if (sym.valueDeclaration !== undefined) {
271
+ return sym.valueDeclaration;
272
+ }
273
+ else if (sym.declarations?.length === 1) {
274
+ return sym.declarations[0];
275
+ }
276
+ const declarationCount = sym.declarations?.length ?? 0;
277
+ throw new Error(`Symbol "${sym.name}" has no valueDeclaration and ${declarationCount} declarations.`);
278
+ }
279
+ /**
280
+ * Given a Symbol, finds the place it was declared and chases parent pointers until we find a
281
+ * node where SyntaxKind === SourceFile.
282
+ */
283
+ function getSourceFile(sym) {
284
+ let currentDecl = getCanonicalDeclaration(sym);
285
+ while (currentDecl.kind !== ts.SyntaxKind.SourceFile) {
286
+ if (currentDecl.parent === undefined) {
287
+ throw new Error(`Unable to locate source file for declaration "${sym.name}".`);
288
+ }
289
+ currentDecl = currentDecl.parent;
290
+ }
291
+ return currentDecl;
292
+ }
293
+ /**
294
+ * JSDoc keywords that should be used to annotate the JSON schema.
295
+ *
296
+ * Many of these validation keywords are defined here: http://json-schema.org/latest/json-schema-validation.html
297
+ */
298
+ // prettier-ignore
299
+ const validationKeywords = {
300
+ multipleOf: true, // 6.1.
301
+ maximum: true, // 6.2.
302
+ exclusiveMaximum: true, // 6.3.
303
+ minimum: true, // 6.4.
304
+ exclusiveMinimum: true, // 6.5.
305
+ maxLength: true, // 6.6.
306
+ minLength: true, // 6.7.
307
+ pattern: true, // 6.8.
308
+ items: true, // 6.9.
309
+ // additionalItems: true, // 6.10.
310
+ maxItems: true, // 6.11.
311
+ minItems: true, // 6.12.
312
+ uniqueItems: true, // 6.13.
313
+ contains: true, // 6.14.
314
+ maxProperties: true, // 6.15.
315
+ minProperties: true, // 6.16.
316
+ // required: true, // 6.17. This is not required. It is auto-generated.
317
+ // properties: true, // 6.18. This is not required. It is auto-generated.
318
+ // patternProperties: true, // 6.19.
319
+ additionalProperties: true, // 6.20.
320
+ // dependencies: true, // 6.21.
321
+ // propertyNames: true, // 6.22.
322
+ enum: true, // 6.23.
323
+ // const: true, // 6.24.
324
+ type: true, // 6.25.
325
+ // allOf: true, // 6.26.
326
+ // anyOf: true, // 6.27.
327
+ // oneOf: true, // 6.28.
328
+ // not: true, // 6.29.
329
+ examples: true, // Draft 6 (draft-handrews-json-schema-validation-01)
330
+ ignore: true,
331
+ description: true,
332
+ format: true,
333
+ default: true,
334
+ $ref: true,
335
+ id: true,
336
+ $id: true,
337
+ $comment: true,
338
+ title: true
339
+ };
340
+ /**
341
+ * Subset of descriptive, non-type keywords that are permitted alongside a $ref.
342
+ * Prior to JSON Schema draft 2019-09, $ref is a special keyword that doesn't
343
+ * permit keywords alongside it, and so AJV may raise warnings if it encounters
344
+ * any type-related keywords; see https://github.com/ajv-validator/ajv/issues/1121
345
+ */
346
+ const annotationKeywords = {
347
+ description: true,
348
+ default: true,
349
+ examples: true,
350
+ title: true,
351
+ // A JSDoc $ref annotation can appear as a $ref.
352
+ $ref: true,
353
+ };
354
+ const subDefinitions = {
355
+ items: true,
356
+ additionalProperties: true,
357
+ contains: true,
358
+ };
359
+ class JsonSchemaGenerator {
360
+ args;
361
+ tc;
362
+ /**
363
+ * Holds all symbols within a custom SymbolRef object, containing useful
364
+ * information.
365
+ */
366
+ symbols;
367
+ /**
368
+ * All types for declarations of classes, interfaces, enums, and type aliases
369
+ * defined in all TS files.
370
+ */
371
+ allSymbols;
372
+ /**
373
+ * All symbols for declarations of classes, interfaces, enums, and type aliases
374
+ * defined in non-default-lib TS files.
375
+ */
376
+ userSymbols;
377
+ /**
378
+ * Maps from the names of base types to the names of the types that inherit from
379
+ * them.
380
+ */
381
+ inheritingTypes;
382
+ /**
383
+ * This map holds references to all reffed definitions, including schema
384
+ * overrides and generated definitions.
385
+ */
386
+ reffedDefinitions = {};
387
+ /**
388
+ * This map only holds explicit schema overrides. This helps differentiate between
389
+ * user defined schema overrides and generated definitions.
390
+ */
391
+ schemaOverrides = new Map();
392
+ /**
393
+ * This is a set of all the user-defined validation keywords.
394
+ */
395
+ userValidationKeywords;
396
+ /**
397
+ * If true, this makes constants be defined as enums with a single value. This is useful
398
+ * for cases where constant values are not supported, such as OpenAPI.
399
+ */
400
+ constAsEnum;
401
+ /**
402
+ * Types are assigned names which are looked up by their IDs. This is the
403
+ * map from type IDs to type names.
404
+ */
405
+ typeNamesById = {};
406
+ /**
407
+ * Whenever a type is assigned its name, its entry in this dictionary is set,
408
+ * so that we don't give the same name to two separate types.
409
+ */
410
+ typeIdsByName = {};
411
+ constructor(symbols, allSymbols, userSymbols, inheritingTypes, tc, args = getDefaultArgs()) {
412
+ this.args = args;
413
+ this.symbols = symbols;
414
+ this.allSymbols = allSymbols;
415
+ this.userSymbols = userSymbols;
416
+ this.inheritingTypes = inheritingTypes;
417
+ this.tc = tc;
418
+ this.userValidationKeywords = args.validationKeywords.reduce((acc, word) => ({ ...acc, [word]: true }), {});
419
+ this.constAsEnum = args.constAsEnum;
420
+ }
421
+ get ReffedDefinitions() {
422
+ return this.reffedDefinitions;
423
+ }
424
+ isFromDefaultLib(symbol) {
425
+ const declarations = symbol.getDeclarations();
426
+ if (declarations && declarations.length > 0 && declarations[0].parent) {
427
+ return declarations[0].parent.getSourceFile().hasNoDefaultLib;
428
+ }
429
+ return false;
430
+ }
431
+ resetSchemaSpecificProperties(includeAllOverrides = false) {
432
+ this.reffedDefinitions = {};
433
+ this.typeIdsByName = {};
434
+ this.typeNamesById = {};
435
+ // restore schema overrides
436
+ if (includeAllOverrides) {
437
+ this.schemaOverrides.forEach((value, key) => {
438
+ this.reffedDefinitions[key] = value;
439
+ });
440
+ }
441
+ }
442
+ /**
443
+ * Parse the comments of a symbol into the definition and other annotations.
444
+ */
445
+ parseCommentsIntoDefinition(symbol, definition, otherAnnotations) {
446
+ if (!symbol) {
447
+ return;
448
+ }
449
+ if (!this.isFromDefaultLib(symbol)) {
450
+ // the comments for a symbol
451
+ const comments = symbol.getDocumentationComment(this.tc);
452
+ if (comments.length) {
453
+ definition.description = comments
454
+ .map((comment) => {
455
+ const newlineNormalizedComment = comment.text.replace(/\r\n/g, "\n");
456
+ // If a comment contains a "{@link XYZ}" inline tag that could not be
457
+ // resolved by the TS checker, then this comment will contain a trailing
458
+ // whitespace that we need to remove.
459
+ if (comment.kind === "linkText") {
460
+ return newlineNormalizedComment.trim();
461
+ }
462
+ return newlineNormalizedComment;
463
+ })
464
+ .join("")
465
+ .trim();
466
+ }
467
+ }
468
+ // jsdocs are separate from comments
469
+ const jsdocs = symbol.getJsDocTags();
470
+ jsdocs.forEach((doc) => {
471
+ // if we have @TJS-... annotations, we have to parse them
472
+ let name = doc.name;
473
+ const originalText = doc.text ? doc.text.map((t) => t.text).join("") : "";
474
+ let text = originalText;
475
+ // In TypeScript versions prior to 3.7, it stops parsing the annotation
476
+ // at the first non-alphanumeric character and puts the rest of the line as the
477
+ // "text" of the annotation, so we have a little hack to check for the name
478
+ // "TJS" and then we sort of re-parse the annotation to support prior versions
479
+ // of TypeScript.
480
+ if (name.startsWith("TJS-")) {
481
+ name = name.slice(4);
482
+ if (!text) {
483
+ text = "true";
484
+ }
485
+ }
486
+ else if (name === "TJS" && text.startsWith("-")) {
487
+ let match = new RegExp(REGEX_TJS_JSDOC).exec(originalText);
488
+ if (match) {
489
+ name = match[1];
490
+ text = match[2];
491
+ }
492
+ else {
493
+ // Treat empty text as boolean true
494
+ name = text.replace(/^[\s\-]+/, "");
495
+ text = "true";
496
+ }
497
+ }
498
+ // In TypeScript ~3.5, the annotation name splits at the dot character so we have
499
+ // to process the "." and beyond from the value
500
+ if (subDefinitions[name]) {
501
+ const match = new RegExp(REGEX_GROUP_JSDOC).exec(text);
502
+ if (match) {
503
+ const k = match[1];
504
+ const v = match[2];
505
+ definition[name] = {
506
+ ...definition[name],
507
+ [k]: v ? parseValue(symbol, k, v) : true,
508
+ };
509
+ return;
510
+ }
511
+ }
512
+ // In TypeScript 3.7+, the "." is kept as part of the annotation name
513
+ if (name.includes(".")) {
514
+ const parts = name.split(".");
515
+ const key = parts[0];
516
+ if (parts.length === 2 && subDefinitions[key]) {
517
+ definition[key] = {
518
+ ...definition[key],
519
+ [parts[1]]: text ? parseValue(symbol, name, text) : true,
520
+ };
521
+ }
522
+ }
523
+ if (validationKeywords[name] ||
524
+ this.userValidationKeywords[name]) {
525
+ definition[name] =
526
+ text === undefined ? "" : parseValue(symbol, name, text);
527
+ }
528
+ else {
529
+ // special annotations
530
+ otherAnnotations[doc.name] = true;
531
+ }
532
+ });
533
+ }
534
+ getDefinitionForRootType(propertyType, reffedType, definition, defaultNumberType = this.args.defaultNumberType, ignoreUndefined = false) {
535
+ const tupleType = resolveTupleType(propertyType);
536
+ if (tupleType) {
537
+ // tuple
538
+ const elemTypes = propertyType
539
+ .typeArguments;
540
+ const fixedTypes = elemTypes.map((elType) => this.getTypeDefinition(elType));
541
+ definition.type = "array";
542
+ if (fixedTypes.length > 0) {
543
+ definition.items = fixedTypes;
544
+ }
545
+ const targetTupleType = propertyType.target;
546
+ definition.minItems = targetTupleType.minLength;
547
+ if (targetTupleType.hasRestElement) {
548
+ definition.additionalItems = fixedTypes[fixedTypes.length - 1];
549
+ fixedTypes.splice(fixedTypes.length - 1, 1);
550
+ }
551
+ else {
552
+ definition.maxItems = targetTupleType.fixedLength;
553
+ }
554
+ }
555
+ else {
556
+ const propertyTypeString = this.tc.typeToString(propertyType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
557
+ const flags = propertyType.flags;
558
+ const arrayType = this.tc.getIndexTypeOfType(propertyType, ts.IndexKind.Number);
559
+ if (flags & ts.TypeFlags.String) {
560
+ definition.type = "string";
561
+ }
562
+ else if (flags & ts.TypeFlags.Number) {
563
+ const isInteger = definition.type === "integer" ||
564
+ reffedType?.getName() === "integer" ||
565
+ defaultNumberType === "integer";
566
+ definition.type = isInteger ? "integer" : "number";
567
+ }
568
+ else if (flags & ts.TypeFlags.Boolean) {
569
+ definition.type = "boolean";
570
+ }
571
+ else if (flags & ts.TypeFlags.ESSymbol) {
572
+ definition.type = "object";
573
+ }
574
+ else if (flags & ts.TypeFlags.Null) {
575
+ definition.type = "null";
576
+ }
577
+ else if (flags & ts.TypeFlags.Undefined ||
578
+ propertyTypeString === "void") {
579
+ if (!ignoreUndefined) {
580
+ throw new Error("Not supported: root type undefined");
581
+ }
582
+ // will be deleted
583
+ definition.type = "undefined";
584
+ }
585
+ else if (flags & ts.TypeFlags.Any || flags & ts.TypeFlags.Unknown) {
586
+ // no type restriction, so that anything will match
587
+ }
588
+ else if (propertyTypeString === "Date" && !this.args.rejectDateType) {
589
+ definition.type = "string";
590
+ definition.format = definition.format || "date-time";
591
+ }
592
+ else if (propertyTypeString === "object") {
593
+ definition.type = "object";
594
+ definition.properties = {};
595
+ definition.additionalProperties = true;
596
+ }
597
+ else if (propertyTypeString === "bigint") {
598
+ definition.type = "number";
599
+ definition.properties = {};
600
+ definition.additionalProperties = false;
601
+ }
602
+ else {
603
+ const value = extractLiteralValue(propertyType);
604
+ if (value !== undefined) {
605
+ // typeof value can be: "string", "boolean", "number", or "object" if value is null
606
+ const typeofValue = typeof value;
607
+ switch (typeofValue) {
608
+ case "string":
609
+ case "boolean":
610
+ definition.type = typeofValue;
611
+ break;
612
+ case "number":
613
+ definition.type = this.args.defaultNumberType;
614
+ break;
615
+ case "object":
616
+ definition.type = "null";
617
+ break;
618
+ default:
619
+ throw new Error(`Not supported: ${value} as a enum value`);
620
+ }
621
+ if (this.constAsEnum) {
622
+ definition.enum = [value];
623
+ }
624
+ else {
625
+ definition.const = value;
626
+ }
627
+ }
628
+ else if (arrayType !== undefined) {
629
+ if (propertyType.flags & ts.TypeFlags.Object &&
630
+ propertyType.objectFlags &
631
+ (ts.ObjectFlags.Anonymous |
632
+ ts.ObjectFlags.Interface |
633
+ ts.ObjectFlags.Mapped)) {
634
+ definition.type = "object";
635
+ definition.additionalProperties = false;
636
+ definition.patternProperties = {
637
+ [NUMERIC_INDEX_PATTERN]: this.getTypeDefinition(arrayType),
638
+ };
639
+ if (!!Array.from(propertyType.members)?.find((member) => member[0] !== "__index")) {
640
+ this.getClassDefinition(propertyType, definition);
641
+ }
642
+ }
643
+ else if (propertyType.flags & ts.TypeFlags.TemplateLiteral) {
644
+ definition.type = "string";
645
+ // @ts-ignore
646
+ const { texts, types } = propertyType;
647
+ const pattern = [];
648
+ for (let i = 0; i < texts.length; i++) {
649
+ const text = texts[i].replace(/[\\^$.*+?()[\]{}|]/g, "\\$&");
650
+ const type = types[i];
651
+ if (i === 0) {
652
+ pattern.push(`^`);
653
+ }
654
+ if (type) {
655
+ if (type.flags & ts.TypeFlags.String) {
656
+ pattern.push(`${text}.*`);
657
+ }
658
+ if (type.flags & ts.TypeFlags.Number ||
659
+ type.flags & ts.TypeFlags.BigInt) {
660
+ pattern.push(`${text}[0-9]*`);
661
+ }
662
+ if (type.flags & ts.TypeFlags.Undefined) {
663
+ pattern.push(`${text}undefined`);
664
+ }
665
+ if (type.flags & ts.TypeFlags.Null) {
666
+ pattern.push(`${text}null`);
667
+ }
668
+ }
669
+ if (i === texts.length - 1) {
670
+ pattern.push(`${text}$`);
671
+ }
672
+ }
673
+ definition.pattern = pattern.join("");
674
+ }
675
+ else {
676
+ definition.type = "array";
677
+ if (!definition.items) {
678
+ definition.items = this.getTypeDefinition(arrayType);
679
+ }
680
+ }
681
+ }
682
+ else {
683
+ // Report that type could not be processed
684
+ const error = new TypeError("Unsupported type: " + propertyTypeString);
685
+ error.type = propertyType;
686
+ throw error;
687
+ // definition = this.getTypeDefinition(propertyType, tc);
688
+ }
689
+ }
690
+ }
691
+ return definition;
692
+ }
693
+ getReferencedTypeSymbol(prop) {
694
+ const decl = prop.getDeclarations();
695
+ if (decl?.length) {
696
+ const type = decl[0].type;
697
+ if (type && type.kind & ts.SyntaxKind.TypeReference && type.typeName) {
698
+ const symbol = this.tc.getSymbolAtLocation(type.typeName);
699
+ if (symbol && symbol.flags & ts.SymbolFlags.Alias) {
700
+ return this.tc.getAliasedSymbol(symbol);
701
+ }
702
+ return symbol;
703
+ }
704
+ }
705
+ return undefined;
706
+ }
707
+ getDefinitionForProperty(prop, node) {
708
+ if (prop.flags & ts.SymbolFlags.Method) {
709
+ return null;
710
+ }
711
+ const propertyName = prop.getName();
712
+ const propertyType = this.tc.getTypeOfSymbolAtLocation(prop, node);
713
+ const reffedType = this.getReferencedTypeSymbol(prop);
714
+ const definition = this.getTypeDefinition(propertyType, undefined, undefined, prop, reffedType);
715
+ if (this.args.titles) {
716
+ definition.title = propertyName;
717
+ }
718
+ if (definition.hasOwnProperty("ignore")) {
719
+ return null;
720
+ }
721
+ // try to get default value
722
+ const valDecl = prop.valueDeclaration;
723
+ if (valDecl?.initializer) {
724
+ let initial = valDecl.initializer;
725
+ while (ts.isTypeAssertionExpression(initial)) {
726
+ initial = initial.expression;
727
+ }
728
+ if (initial.expression) {
729
+ // node
730
+ console.warn("initializer is expression for property " + propertyName);
731
+ }
732
+ else if (initial.kind &&
733
+ initial.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
734
+ definition.default = initial.getText();
735
+ }
736
+ else {
737
+ try {
738
+ const sandbox = { sandboxvar: null };
739
+ vm.runInNewContext("sandboxvar=" + initial.getText(), sandbox);
740
+ const val = sandbox.sandboxvar;
741
+ if (val === null ||
742
+ typeof val === "string" ||
743
+ typeof val === "number" ||
744
+ typeof val === "boolean" ||
745
+ Object.prototype.toString.call(val) === "[object Array]") {
746
+ definition.default = val;
747
+ }
748
+ else if (val) {
749
+ console.warn("unknown initializer for property " + propertyName + ": " + val);
750
+ }
751
+ }
752
+ catch (e) {
753
+ console.warn("exception evaluating initializer for property " + propertyName);
754
+ }
755
+ }
756
+ }
757
+ return definition;
758
+ }
759
+ getEnumDefinition(clazzType, definition) {
760
+ const node = clazzType.getSymbol().getDeclarations()[0];
761
+ const fullName = this.tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
762
+ const members = node.kind === ts.SyntaxKind.EnumDeclaration
763
+ ? node.members
764
+ : ts.factory.createNodeArray([node]);
765
+ var enumValues = [];
766
+ const enumTypes = [];
767
+ const addType = (type) => {
768
+ if (enumTypes.indexOf(type) === -1) {
769
+ enumTypes.push(type);
770
+ }
771
+ };
772
+ members.forEach((member) => {
773
+ const caseLabel = member.name.text;
774
+ const constantValue = this.tc.getConstantValue(member);
775
+ if (constantValue !== undefined) {
776
+ enumValues.push(constantValue);
777
+ addType(typeof constantValue); // can be only string or number;
778
+ }
779
+ else {
780
+ // try to extract the enums value; it will probably by a cast expression
781
+ const initial = member.initializer;
782
+ if (initial) {
783
+ if (initial.expression) {
784
+ // node
785
+ const exp = initial.expression;
786
+ const text = exp.text;
787
+ // if it is an expression with a text literal, chances are it is the enum convention:
788
+ // CASELABEL = 'literal' as any
789
+ if (text) {
790
+ enumValues.push(text);
791
+ addType("string");
792
+ }
793
+ else if (exp.kind === ts.SyntaxKind.TrueKeyword ||
794
+ exp.kind === ts.SyntaxKind.FalseKeyword) {
795
+ enumValues.push(exp.kind === ts.SyntaxKind.TrueKeyword);
796
+ addType("boolean");
797
+ }
798
+ else {
799
+ console.warn("initializer is expression for enum: " +
800
+ fullName +
801
+ "." +
802
+ caseLabel);
803
+ }
804
+ }
805
+ else if (initial.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
806
+ enumValues.push(initial.getText());
807
+ addType("string");
808
+ }
809
+ else if (initial.kind === ts.SyntaxKind.NullKeyword) {
810
+ enumValues.push(null);
811
+ addType("null");
812
+ }
813
+ }
814
+ }
815
+ });
816
+ if (enumTypes.length) {
817
+ definition.type = enumTypes.length === 1 ? enumTypes[0] : enumTypes;
818
+ }
819
+ if (enumValues.length > 0) {
820
+ if (enumValues.length > 1) {
821
+ definition.enum = enumValues;
822
+ }
823
+ else {
824
+ definition.const = enumValues[0];
825
+ }
826
+ }
827
+ return definition;
828
+ }
829
+ getUnionDefinition(unionType, unionModifier, definition) {
830
+ const enumValues = [];
831
+ const simpleTypes = [];
832
+ const schemas = [];
833
+ const pushSimpleType = (type) => {
834
+ if (simpleTypes.indexOf(type) === -1) {
835
+ simpleTypes.push(type);
836
+ }
837
+ };
838
+ const pushEnumValue = (val) => {
839
+ if (enumValues.indexOf(val) === -1) {
840
+ enumValues.push(val);
841
+ }
842
+ };
843
+ for (const valueType of unionType.types) {
844
+ const value = extractLiteralValue(valueType);
845
+ if (value !== undefined) {
846
+ pushEnumValue(value);
847
+ }
848
+ else {
849
+ const symbol = valueType.aliasSymbol;
850
+ const def = this.getTypeDefinition(valueType, undefined, undefined, symbol, symbol, undefined, undefined, true);
851
+ if (def.type === "undefined") {
852
+ continue;
853
+ }
854
+ const keys = Object.keys(def);
855
+ if (keys.length === 1 && keys[0] === "type") {
856
+ if (typeof def.type !== "string") {
857
+ console.error("Expected only a simple type.");
858
+ }
859
+ else {
860
+ pushSimpleType(def.type);
861
+ }
862
+ }
863
+ else {
864
+ schemas.push(def);
865
+ }
866
+ }
867
+ }
868
+ if (enumValues.length > 0) {
869
+ // If the values are true and false, just add "boolean" as simple type
870
+ const isOnlyBooleans = enumValues.length === 2 &&
871
+ typeof enumValues[0] === "boolean" &&
872
+ typeof enumValues[1] === "boolean" &&
873
+ enumValues[0] !== enumValues[1];
874
+ if (isOnlyBooleans) {
875
+ pushSimpleType("boolean");
876
+ }
877
+ else {
878
+ const enumSchema = enumValues.length > 1
879
+ ? { enum: enumValues.sort() }
880
+ : { const: enumValues[0] };
881
+ // If all values are of the same primitive type, add a "type" field to the schema
882
+ if (enumValues.every((x) => {
883
+ return typeof x === "string";
884
+ })) {
885
+ enumSchema.type = "string";
886
+ }
887
+ else if (enumValues.every((x) => {
888
+ return typeof x === "number";
889
+ })) {
890
+ enumSchema.type = "number";
891
+ }
892
+ else if (enumValues.every((x) => {
893
+ return typeof x === "boolean";
894
+ })) {
895
+ enumSchema.type = "boolean";
896
+ }
897
+ schemas.push(enumSchema);
898
+ }
899
+ }
900
+ if (simpleTypes.length > 0) {
901
+ schemas.push({
902
+ type: simpleTypes.length === 1 ? simpleTypes[0] : simpleTypes,
903
+ });
904
+ }
905
+ if (schemas.length === 1) {
906
+ for (const k in schemas[0]) {
907
+ if (schemas[0].hasOwnProperty(k)) {
908
+ if (k === "description" && definition.hasOwnProperty(k)) {
909
+ // If we already have a more specific description, don't overwrite it.
910
+ continue;
911
+ }
912
+ definition[k] =
913
+ schemas[0][k];
914
+ }
915
+ }
916
+ }
917
+ else {
918
+ definition[unionModifier] = schemas;
919
+ }
920
+ return definition;
921
+ }
922
+ getIntersectionDefinition(intersectionType, definition) {
923
+ const simpleTypes = [];
924
+ const schemas = [];
925
+ const pushSimpleType = (type) => {
926
+ if (simpleTypes.indexOf(type) === -1) {
927
+ simpleTypes.push(type);
928
+ }
929
+ };
930
+ for (const intersectionMember of intersectionType.types) {
931
+ const def = this.getTypeDefinition(intersectionMember);
932
+ const keys = Object.keys(def);
933
+ if (keys.length === 1 && keys[0] === "type") {
934
+ if (typeof def.type !== "string") {
935
+ console.error("Expected only a simple type.");
936
+ }
937
+ else {
938
+ pushSimpleType(def.type);
939
+ }
940
+ }
941
+ else {
942
+ schemas.push(def);
943
+ }
944
+ }
945
+ if (simpleTypes.length > 0) {
946
+ schemas.push({
947
+ type: simpleTypes.length === 1 ? simpleTypes[0] : simpleTypes,
948
+ });
949
+ }
950
+ if (schemas.length === 1) {
951
+ for (const k in schemas[0]) {
952
+ if (schemas[0].hasOwnProperty(k)) {
953
+ definition[k] =
954
+ schemas[0][k];
955
+ }
956
+ }
957
+ }
958
+ else {
959
+ definition.allOf = schemas;
960
+ }
961
+ return definition;
962
+ }
963
+ getClassDefinition(clazzType, definition) {
964
+ const node = clazzType.getSymbol().getDeclarations()[0];
965
+ // Example: typeof globalThis may not have any declaration
966
+ if (!node) {
967
+ definition.type = "object";
968
+ return definition;
969
+ }
970
+ if (this.args.typeOfKeyword && node.kind === ts.SyntaxKind.FunctionType) {
971
+ definition.typeof = "function";
972
+ return definition;
973
+ }
974
+ const clazz = node;
975
+ const props = this.tc.getPropertiesOfType(clazzType).filter((prop) => {
976
+ // filter never and undefined
977
+ const propertyFlagType = this.tc
978
+ .getTypeOfSymbolAtLocation(prop, node)
979
+ .getFlags();
980
+ if (ts.TypeFlags.Never === propertyFlagType ||
981
+ ts.TypeFlags.Undefined === propertyFlagType) {
982
+ return false;
983
+ }
984
+ if (!this.args.excludePrivate) {
985
+ return true;
986
+ }
987
+ const decls = prop.declarations;
988
+ return !(decls &&
989
+ decls.filter((decl) => {
990
+ const mods = decl.modifiers;
991
+ return (mods &&
992
+ mods.filter((mod) => mod.kind === ts.SyntaxKind.PrivateKeyword)
993
+ .length > 0);
994
+ }).length > 0);
995
+ });
996
+ const fullName = this.tc.typeToString(clazzType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
997
+ const modifierFlags = ts.getCombinedModifierFlags(node);
998
+ if (modifierFlags & ts.ModifierFlags.Abstract &&
999
+ this.inheritingTypes[fullName]) {
1000
+ const oneOf = this.inheritingTypes[fullName].map((typename) => {
1001
+ return this.getTypeDefinition(this.allSymbols[typename]);
1002
+ });
1003
+ definition.oneOf = oneOf;
1004
+ }
1005
+ else {
1006
+ if (clazz.members) {
1007
+ const indexSignatures = clazz.members == null
1008
+ ? []
1009
+ : clazz.members.filter((x) => x.kind === ts.SyntaxKind.IndexSignature);
1010
+ if (indexSignatures.length === 1) {
1011
+ // for case "array-types"
1012
+ const indexSignature = indexSignatures[0];
1013
+ if (indexSignature.parameters.length !== 1) {
1014
+ throw new Error("Not supported: IndexSignatureDeclaration parameters.length != 1");
1015
+ }
1016
+ const indexSymbol = indexSignature.parameters[0]
1017
+ .symbol;
1018
+ const indexType = this.tc.getTypeOfSymbolAtLocation(indexSymbol, node);
1019
+ const isIndexedObject = indexType.flags === ts.TypeFlags.String ||
1020
+ indexType.flags === ts.TypeFlags.Number;
1021
+ if (indexType.flags !== ts.TypeFlags.Number && !isIndexedObject) {
1022
+ throw new Error("Not supported: IndexSignatureDeclaration with index symbol other than a number or a string");
1023
+ }
1024
+ const typ = this.tc.getTypeAtLocation(indexSignature.type);
1025
+ let def;
1026
+ if (typ.flags & ts.TypeFlags.IndexedAccess) {
1027
+ const targetName = ts.escapeLeadingUnderscores(clazzType.mapper?.target?.value);
1028
+ const indexedAccessType = typ;
1029
+ const symbols = indexedAccessType.objectType.members;
1030
+ const targetSymbol = symbols?.get(targetName);
1031
+ if (targetSymbol) {
1032
+ const targetNode = targetSymbol.getDeclarations()[0];
1033
+ const targetDef = this.getDefinitionForProperty(targetSymbol, targetNode);
1034
+ if (targetDef) {
1035
+ def = targetDef;
1036
+ }
1037
+ }
1038
+ }
1039
+ if (!def) {
1040
+ def = this.getTypeDefinition(typ, undefined, "anyOf");
1041
+ }
1042
+ if (isIndexedObject) {
1043
+ definition.type = "object";
1044
+ if (!Object.keys(definition.patternProperties || {}).length) {
1045
+ definition.additionalProperties = def;
1046
+ }
1047
+ }
1048
+ else {
1049
+ definition.type = "array";
1050
+ if (!definition.items) {
1051
+ definition.items = def;
1052
+ }
1053
+ }
1054
+ }
1055
+ }
1056
+ const propertyDefinitions = props.reduce((all, prop) => {
1057
+ const propertyName = prop.getName();
1058
+ const propDef = this.getDefinitionForProperty(prop, node);
1059
+ if (propDef != null) {
1060
+ all[propertyName] = propDef;
1061
+ }
1062
+ return all;
1063
+ }, {});
1064
+ if (definition.type === undefined) {
1065
+ definition.type = "object";
1066
+ }
1067
+ if (definition.type === "object" &&
1068
+ Object.keys(propertyDefinitions).length > 0) {
1069
+ definition.properties = propertyDefinitions;
1070
+ }
1071
+ if (this.args.defaultProps) {
1072
+ definition.defaultProperties = [];
1073
+ }
1074
+ if (this.args.noExtraProps &&
1075
+ definition.additionalProperties === undefined) {
1076
+ definition.additionalProperties = false;
1077
+ }
1078
+ if (this.args.propOrder) {
1079
+ // propertyOrder is non-standard, but useful:
1080
+ // https://github.com/json-schema/json-schema/issues/87
1081
+ const propertyOrder = props.reduce((order, prop) => {
1082
+ order.push(prop.getName());
1083
+ return order;
1084
+ }, []);
1085
+ definition.propertyOrder = propertyOrder;
1086
+ }
1087
+ if (this.args.required) {
1088
+ const requiredProps = props.reduce((required, prop) => {
1089
+ const def = {};
1090
+ this.parseCommentsIntoDefinition(prop, def, {});
1091
+ const allUnionTypesFlags = prop.links?.type?.types?.map?.((t) => t.flags) ||
1092
+ [];
1093
+ if (!(prop.flags & ts.SymbolFlags.Optional) &&
1094
+ !(prop.flags & ts.SymbolFlags.Method) &&
1095
+ !allUnionTypesFlags.includes(ts.TypeFlags.Undefined) &&
1096
+ !allUnionTypesFlags.includes(ts.TypeFlags.Void) &&
1097
+ !def.hasOwnProperty("ignore")) {
1098
+ required.push(prop.getName());
1099
+ }
1100
+ return required;
1101
+ }, []);
1102
+ if (requiredProps.length > 0) {
1103
+ definition.required = unique(requiredProps).sort();
1104
+ }
1105
+ }
1106
+ }
1107
+ return definition;
1108
+ }
1109
+ /**
1110
+ * Gets/generates a globally unique type name for the given type
1111
+ */
1112
+ getTypeName(typ) {
1113
+ const id = typ.id;
1114
+ if (this.typeNamesById[id]) {
1115
+ // Name already assigned?
1116
+ return this.typeNamesById[id];
1117
+ }
1118
+ return this.makeTypeNameUnique(typ, this.tc
1119
+ .typeToString(typ, undefined, ts.TypeFormatFlags.NoTruncation |
1120
+ ts.TypeFormatFlags.UseFullyQualifiedType)
1121
+ .replace(REGEX_FILE_NAME_OR_SPACE, ""));
1122
+ }
1123
+ makeTypeNameUnique(typ, baseName) {
1124
+ const id = typ.id;
1125
+ let name = baseName;
1126
+ // If a type with same name exists
1127
+ // Try appending "_1", "_2", etc.
1128
+ for (let i = 1; this.typeIdsByName[name] !== undefined && this.typeIdsByName[name] !== id; ++i) {
1129
+ name = baseName + "_" + i;
1130
+ }
1131
+ this.typeNamesById[id] = name;
1132
+ this.typeIdsByName[name] = id;
1133
+ return name;
1134
+ }
1135
+ recursiveTypeRef = new Map();
1136
+ getTypeDefinition(typ, asRef = this.args.ref, unionModifier = "anyOf", prop, reffedType, pairedSymbol, forceNotRef = false, ignoreUndefined = false) {
1137
+ const definition = {}; // real definition
1138
+ // Ignore any number of Readonly and Mutable type wrappings, since they only add and remove readonly modifiers on fields and JSON Schema is not concerned with mutability
1139
+ while (typ.aliasSymbol &&
1140
+ (typ.aliasSymbol.escapedName === "Readonly" ||
1141
+ typ.aliasSymbol.escapedName === "Mutable") &&
1142
+ typ.aliasTypeArguments &&
1143
+ typ.aliasTypeArguments[0]) {
1144
+ typ = typ.aliasTypeArguments[0];
1145
+ reffedType = undefined;
1146
+ }
1147
+ if (this.args.typeOfKeyword &&
1148
+ typ.flags & ts.TypeFlags.Object &&
1149
+ typ.objectFlags & ts.ObjectFlags.Anonymous) {
1150
+ definition.typeof = "function";
1151
+ return definition;
1152
+ }
1153
+ let returnedDefinition = definition; // returned definition, may be a $ref
1154
+ // Parse property comments now to skip recursive if ignore.
1155
+ if (prop) {
1156
+ const defs = {};
1157
+ const others = {};
1158
+ this.parseCommentsIntoDefinition(prop, defs, others);
1159
+ if (defs.hasOwnProperty("ignore") || defs.hasOwnProperty("type")) {
1160
+ return defs;
1161
+ }
1162
+ }
1163
+ const symbol = typ.getSymbol();
1164
+ // FIXME: We can't just compare the name of the symbol - it ignores the namespace
1165
+ let isRawType = !symbol ||
1166
+ // Window is incorrectly marked as rawType here for some reason
1167
+ (this.tc.getFullyQualifiedName(symbol) !== "Window" &&
1168
+ (this.tc.getFullyQualifiedName(symbol) === "Date" ||
1169
+ symbol.name === "integer" ||
1170
+ this.tc.getIndexInfoOfType(typ, ts.IndexKind.Number) !== undefined));
1171
+ if (isRawType &&
1172
+ typ.aliasSymbol?.escapedName &&
1173
+ typ.types) {
1174
+ isRawType = false;
1175
+ }
1176
+ // special case: an union where all child are string literals -> make an enum instead
1177
+ let isStringEnum = false;
1178
+ if (typ.flags & ts.TypeFlags.Union) {
1179
+ const unionType = typ;
1180
+ isStringEnum = unionType.types.every((propType) => {
1181
+ return (propType.getFlags() & ts.TypeFlags.StringLiteral) !== 0;
1182
+ });
1183
+ }
1184
+ // aliased types must be handled slightly different
1185
+ const asTypeAliasRef = asRef && reffedType && (this.args.aliasRef || isStringEnum);
1186
+ if (!asTypeAliasRef) {
1187
+ if (isRawType ||
1188
+ (typ.getFlags() & ts.TypeFlags.Object &&
1189
+ typ.objectFlags & ts.ObjectFlags.Anonymous)) {
1190
+ asRef = false; // raw types and inline types cannot be reffed,
1191
+ // unless we are handling a type alias
1192
+ // or it is recursive type - see below
1193
+ }
1194
+ }
1195
+ let fullTypeName = "";
1196
+ if (asTypeAliasRef) {
1197
+ const typeName = this.tc
1198
+ .getFullyQualifiedName(reffedType.getFlags() & ts.SymbolFlags.Alias
1199
+ ? this.tc.getAliasedSymbol(reffedType)
1200
+ : reffedType)
1201
+ .replace(REGEX_FILE_NAME_OR_SPACE, "");
1202
+ if (this.args.uniqueNames && reffedType) {
1203
+ const sourceFile = getSourceFile(reffedType);
1204
+ const relativePath = path.relative(process.cwd(), sourceFile.fileName);
1205
+ fullTypeName = `${typeName}.${generateHashOfNode(getCanonicalDeclaration(reffedType), relativePath)}`;
1206
+ }
1207
+ else {
1208
+ fullTypeName = this.makeTypeNameUnique(typ, typeName);
1209
+ }
1210
+ }
1211
+ else {
1212
+ // typ.symbol can be undefined
1213
+ if (this.args.uniqueNames && typ.symbol) {
1214
+ const sym = typ.symbol;
1215
+ const sourceFile = getSourceFile(sym);
1216
+ const relativePath = path.relative(process.cwd(), sourceFile.fileName);
1217
+ fullTypeName = `${this.getTypeName(typ)}.${generateHashOfNode(getCanonicalDeclaration(sym), relativePath)}`;
1218
+ }
1219
+ else if (reffedType &&
1220
+ this.schemaOverrides.has(reffedType.escapedName)) {
1221
+ fullTypeName = reffedType.escapedName;
1222
+ }
1223
+ else {
1224
+ fullTypeName = this.getTypeName(typ);
1225
+ }
1226
+ }
1227
+ // Handle recursive types
1228
+ if (!isRawType || !!typ.aliasSymbol) {
1229
+ if (this.recursiveTypeRef.has(fullTypeName) && !forceNotRef) {
1230
+ asRef = true;
1231
+ }
1232
+ else {
1233
+ this.recursiveTypeRef.set(fullTypeName, definition);
1234
+ }
1235
+ }
1236
+ if (asRef) {
1237
+ // We don't return the full definition, but we put it into
1238
+ // reffedDefinitions below.
1239
+ returnedDefinition = {
1240
+ $ref: `${this.args.id}#/definitions/` + fullTypeName,
1241
+ };
1242
+ }
1243
+ // Parse comments
1244
+ const otherAnnotations = {};
1245
+ this.parseCommentsIntoDefinition(reffedType, definition, otherAnnotations); // handle comments in the type alias declaration
1246
+ this.parseCommentsIntoDefinition(symbol, definition, otherAnnotations);
1247
+ this.parseCommentsIntoDefinition(typ.aliasSymbol, definition, otherAnnotations);
1248
+ if (prop) {
1249
+ this.parseCommentsIntoDefinition(prop, returnedDefinition, otherAnnotations);
1250
+ }
1251
+ if (pairedSymbol && symbol && this.isFromDefaultLib(symbol)) {
1252
+ this.parseCommentsIntoDefinition(pairedSymbol, definition, otherAnnotations);
1253
+ }
1254
+ // Create the actual definition only if is an inline definition, or
1255
+ // if it will be a $ref and it is not yet created.
1256
+ // Prioritise overrides.
1257
+ const overrideDefinition = this.schemaOverrides.get(fullTypeName);
1258
+ if (overrideDefinition) {
1259
+ this.reffedDefinitions[fullTypeName] = overrideDefinition;
1260
+ }
1261
+ else if (!asRef || !this.reffedDefinitions[fullTypeName]) {
1262
+ if (asRef) {
1263
+ // must be here to prevent recursivity problems
1264
+ let reffedDefinition;
1265
+ if (asTypeAliasRef &&
1266
+ reffedType &&
1267
+ typ.symbol !== reffedType &&
1268
+ symbol) {
1269
+ reffedDefinition = this.getTypeDefinition(typ, true, undefined, symbol, symbol);
1270
+ }
1271
+ else {
1272
+ reffedDefinition = definition;
1273
+ }
1274
+ this.reffedDefinitions[fullTypeName] = reffedDefinition;
1275
+ if (this.args.titles && fullTypeName) {
1276
+ definition.title = fullTypeName;
1277
+ }
1278
+ }
1279
+ const node = symbol?.getDeclarations() !== undefined
1280
+ ? symbol.getDeclarations()[0]
1281
+ : null;
1282
+ if (definition.type === undefined) {
1283
+ // if users override the type, do not try to infer it
1284
+ if (typ.flags & ts.TypeFlags.Union &&
1285
+ (node === null || node.kind !== ts.SyntaxKind.EnumDeclaration)) {
1286
+ this.getUnionDefinition(typ, unionModifier, definition);
1287
+ }
1288
+ else if (typ.flags & ts.TypeFlags.Intersection) {
1289
+ if (this.args.noExtraProps) {
1290
+ // extend object instead of using allOf because allOf does not work well with additional properties. See #107
1291
+ if (this.args.noExtraProps) {
1292
+ definition.additionalProperties = false;
1293
+ }
1294
+ const types = typ.types;
1295
+ for (const member of types) {
1296
+ const other = this.getTypeDefinition(member, false, undefined, undefined, undefined, undefined, true);
1297
+ definition.type = other.type; // should always be object
1298
+ definition.properties = {
1299
+ ...definition.properties,
1300
+ ...other.properties,
1301
+ };
1302
+ if (Object.keys(other.default || {}).length > 0) {
1303
+ definition.default = extend(definition.default || {}, other.default);
1304
+ }
1305
+ if (other.required) {
1306
+ definition.required = unique((definition.required || []).concat(other.required)).sort();
1307
+ }
1308
+ }
1309
+ }
1310
+ else {
1311
+ this.getIntersectionDefinition(typ, definition);
1312
+ }
1313
+ }
1314
+ else if (isRawType) {
1315
+ if (pairedSymbol) {
1316
+ this.parseCommentsIntoDefinition(pairedSymbol, definition, {});
1317
+ }
1318
+ this.getDefinitionForRootType(typ, reffedType, definition, undefined, ignoreUndefined);
1319
+ }
1320
+ else if (node &&
1321
+ (node.kind === ts.SyntaxKind.EnumDeclaration ||
1322
+ node.kind === ts.SyntaxKind.EnumMember)) {
1323
+ this.getEnumDefinition(typ, definition);
1324
+ }
1325
+ else if (symbol &&
1326
+ symbol.flags & ts.SymbolFlags.TypeLiteral &&
1327
+ symbol.members.size === 0 &&
1328
+ !(node && node.kind === ts.SyntaxKind.MappedType)) {
1329
+ // {} is TypeLiteral with no members. Need special case because it doesn't have declarations.
1330
+ definition.type = "object";
1331
+ definition.properties = {};
1332
+ }
1333
+ else {
1334
+ this.getClassDefinition(typ, definition);
1335
+ }
1336
+ }
1337
+ }
1338
+ if (this.recursiveTypeRef.get(fullTypeName) === definition) {
1339
+ this.recursiveTypeRef.delete(fullTypeName);
1340
+ // If the type was recursive (there is reffedDefinitions) - lets replace it to reference
1341
+ if (this.reffedDefinitions[fullTypeName]) {
1342
+ const annotations = Object.entries(returnedDefinition).reduce((acc, [key, value]) => {
1343
+ if (annotationKeywords[key] &&
1344
+ typeof value !== undefined) {
1345
+ acc[key] = value;
1346
+ }
1347
+ return acc;
1348
+ }, {});
1349
+ returnedDefinition = {
1350
+ $ref: `${this.args.id}#/definitions/` + fullTypeName,
1351
+ ...annotations,
1352
+ };
1353
+ }
1354
+ }
1355
+ if (otherAnnotations["nullable"]) {
1356
+ makeNullable(returnedDefinition);
1357
+ }
1358
+ return returnedDefinition;
1359
+ }
1360
+ setSchemaOverride(symbolName, schema) {
1361
+ this.schemaOverrides.set(symbolName, schema);
1362
+ }
1363
+ getSchemaForSymbol(symbolName, includeReffedDefinitions = true, includeAllOverrides = false) {
1364
+ const overrideDefinition = this.schemaOverrides.get(symbolName);
1365
+ if (!this.allSymbols[symbolName] && !overrideDefinition) {
1366
+ throw new Error(`type ${symbolName} not found`);
1367
+ }
1368
+ this.resetSchemaSpecificProperties(includeAllOverrides);
1369
+ let def;
1370
+ if (overrideDefinition) {
1371
+ def = { ...overrideDefinition };
1372
+ }
1373
+ else {
1374
+ def = overrideDefinition
1375
+ ? overrideDefinition
1376
+ : this.getTypeDefinition(this.allSymbols[symbolName], this.args.topRef, undefined, undefined, undefined, this.userSymbols[symbolName] || undefined);
1377
+ }
1378
+ if (this.args.ref &&
1379
+ includeReffedDefinitions &&
1380
+ Object.keys(this.reffedDefinitions).length > 0) {
1381
+ def.definitions = this.reffedDefinitions;
1382
+ }
1383
+ def["$schema"] = "http://json-schema.org/draft-07/schema#";
1384
+ const id = this.args.id;
1385
+ if (id) {
1386
+ def["$id"] = this.args.id;
1387
+ }
1388
+ return def;
1389
+ }
1390
+ getSchemaForSymbols(symbolNames, includeReffedDefinitions = true, includeAllOverrides = false) {
1391
+ const root = {
1392
+ $schema: "http://json-schema.org/draft-07/schema#",
1393
+ definitions: {},
1394
+ };
1395
+ this.resetSchemaSpecificProperties(includeAllOverrides);
1396
+ const id = this.args.id;
1397
+ if (id) {
1398
+ root["$id"] = id;
1399
+ }
1400
+ for (const symbolName of symbolNames) {
1401
+ root.definitions[symbolName] = this.getTypeDefinition(this.allSymbols[symbolName], this.args.topRef, undefined, undefined, undefined, this.userSymbols[symbolName]);
1402
+ }
1403
+ if (this.args.ref &&
1404
+ includeReffedDefinitions &&
1405
+ Object.keys(this.reffedDefinitions).length > 0) {
1406
+ root.definitions = { ...root.definitions, ...this.reffedDefinitions };
1407
+ }
1408
+ return root;
1409
+ }
1410
+ getSymbols(name) {
1411
+ if (name === void 0) {
1412
+ return this.symbols;
1413
+ }
1414
+ return this.symbols.filter((symbol) => symbol.typeName === name);
1415
+ }
1416
+ getUserSymbols() {
1417
+ return Object.keys(this.userSymbols);
1418
+ }
1419
+ getMainFileSymbols(program, onlyIncludeFiles) {
1420
+ function includeFile(file) {
1421
+ if (onlyIncludeFiles === undefined) {
1422
+ return !file.isDeclarationFile;
1423
+ }
1424
+ return (onlyIncludeFiles.filter((f) => pathEqual(f, file.fileName)).length > 0);
1425
+ }
1426
+ const files = program.getSourceFiles().filter(includeFile);
1427
+ if (files.length) {
1428
+ return Object.keys(this.userSymbols).filter((key) => {
1429
+ const symbol = this.userSymbols[key];
1430
+ if (!symbol || !symbol.declarations || !symbol.declarations.length) {
1431
+ return false;
1432
+ }
1433
+ let node = symbol.declarations[0];
1434
+ while (node?.parent) {
1435
+ node = node.parent;
1436
+ }
1437
+ return files.indexOf(node.getSourceFile()) > -1;
1438
+ });
1439
+ }
1440
+ return [];
1441
+ }
1442
+ }
1443
+ function generateHashOfNode(node, relativePath) {
1444
+ return createHash("md5")
1445
+ .update(relativePath)
1446
+ .update(node.pos.toString())
1447
+ .digest("hex")
1448
+ .substring(0, 8);
1449
+ }
1450
+ export function buildGenerator(program, args = {}) {
1451
+ // Use defaults unless otherwise specified
1452
+ const settings = getDefaultArgs();
1453
+ for (const pref in args) {
1454
+ if (args.hasOwnProperty(pref)) {
1455
+ settings[pref] = args[pref];
1456
+ }
1457
+ }
1458
+ const typeChecker = program.getTypeChecker();
1459
+ const symbols = [];
1460
+ const allSymbols = {};
1461
+ const userSymbols = {};
1462
+ const inheritingTypes = {};
1463
+ const workingDir = program.getCurrentDirectory();
1464
+ program.getSourceFiles().forEach((sourceFile, _sourceFileIdx) => {
1465
+ const relativePath = path.relative(workingDir, sourceFile.fileName);
1466
+ function inspect(node, tc) {
1467
+ if (node.kind === ts.SyntaxKind.ClassDeclaration ||
1468
+ node.kind === ts.SyntaxKind.InterfaceDeclaration ||
1469
+ node.kind === ts.SyntaxKind.EnumDeclaration ||
1470
+ node.kind === ts.SyntaxKind.TypeAliasDeclaration) {
1471
+ const symbol = node.symbol;
1472
+ const nodeType = tc.getTypeAtLocation(node);
1473
+ const fullyQualifiedName = tc.getFullyQualifiedName(symbol);
1474
+ const typeName = fullyQualifiedName.replace(/".*"\./, "");
1475
+ const name = !args.uniqueNames
1476
+ ? typeName
1477
+ : `${typeName}.${generateHashOfNode(node, relativePath)}`;
1478
+ symbols.push({ name, typeName, fullyQualifiedName, symbol });
1479
+ if (!userSymbols[name]) {
1480
+ allSymbols[name] = nodeType;
1481
+ }
1482
+ const baseTypes = nodeType.getBaseTypes() || [];
1483
+ baseTypes.forEach((baseType) => {
1484
+ var baseName = tc.typeToString(baseType, undefined, ts.TypeFormatFlags.UseFullyQualifiedType);
1485
+ if (!inheritingTypes[baseName]) {
1486
+ inheritingTypes[baseName] = [];
1487
+ }
1488
+ inheritingTypes[baseName].push(name);
1489
+ });
1490
+ }
1491
+ else {
1492
+ ts.forEachChild(node, (n) => inspect(n, tc));
1493
+ }
1494
+ }
1495
+ inspect(sourceFile, typeChecker);
1496
+ });
1497
+ return new JsonSchemaGenerator(symbols, allSymbols, userSymbols, inheritingTypes, typeChecker, settings);
1498
+ }
1499
+ export async function extractGraphSchema(id, userPath, exportName) {
1500
+ const filePath = path.resolve(process.cwd(), userPath);
1501
+ const parentPath = path.dirname(filePath);
1502
+ const typePath = path.resolve(parentPath, `__$0${id}.mts`);
1503
+ const importPath = path.relative(parentPath, filePath);
1504
+ try {
1505
+ await fs.writeFile(typePath, dedent `
1506
+ import { ${exportName} as __graph } from "./${importPath}";
1507
+ import type { BaseMessage } from "@langchain/core/messages";
1508
+ import type {
1509
+ StateType,
1510
+ UpdateType,
1511
+ StateDefinition,
1512
+ } from "@langchain/langgraph";
1513
+
1514
+ type Wrap<T> = (a: T) => void;
1515
+ type MatchBaseMessage<T> = T extends BaseMessage ? BaseMessage : never;
1516
+ type MatchBaseMessageArray<T> =
1517
+ T extends Array<infer C>
1518
+ ? Wrap<MatchBaseMessage<C>> extends Wrap<BaseMessage>
1519
+ ? BaseMessage[]
1520
+ : never
1521
+ : never;
1522
+
1523
+ type Defactorify<T> = T extends (...args: any[]) => infer R
1524
+ ? Awaited<R>
1525
+ : Awaited<T>;
1526
+
1527
+ type Inspect<T> = T extends unknown
1528
+ ? {
1529
+ [K in keyof T]: 0 extends 1 & T[K]
1530
+ ? T[K]
1531
+ : Wrap<MatchBaseMessageArray<T[K]>> extends Wrap<BaseMessage[]>
1532
+ ? BaseMessage[]
1533
+ : Wrap<MatchBaseMessage<T[K]>> extends Wrap<BaseMessage>
1534
+ ? BaseMessage
1535
+ : Inspect<T[K]>;
1536
+ }
1537
+ : never;
1538
+
1539
+ type ReflectCompiled<T> = T extends { RunInput: infer S; RunOutput: infer U }
1540
+ ? { state: S; update: U }
1541
+ : never;
1542
+
1543
+ type Reflect<T> =
1544
+ Defactorify<T> extends infer DT
1545
+ ? DT extends {
1546
+ compile(...args: any[]): infer Compiled;
1547
+ }
1548
+ ? ReflectCompiled<Compiled>
1549
+ : ReflectCompiled<DT>
1550
+ : never;
1551
+
1552
+ type __reflect = Reflect<typeof __graph>;
1553
+ export type __state = Inspect<__reflect["state"]>;
1554
+ export type __update = Inspect<__reflect["update"]>;
1555
+
1556
+ type BuilderReflectCompiled<T> = T extends {
1557
+ builder: {
1558
+ _inputDefinition: infer I extends StateDefinition;
1559
+ _outputDefinition: infer O extends StateDefinition;
1560
+ _configSchema?: infer C extends StateDefinition | undefined;
1561
+ };
1562
+ }
1563
+ ? { input: UpdateType<I>; output: StateType<O>; config: UpdateType<C> }
1564
+ : never;
1565
+
1566
+ type BuilderReflect<T> =
1567
+ Defactorify<T> extends infer DT
1568
+ ? DT extends {
1569
+ compile(...args: any[]): infer Compiled;
1570
+ }
1571
+ ? BuilderReflectCompiled<Compiled>
1572
+ : BuilderReflectCompiled<DT>
1573
+ : never;
1574
+
1575
+ type __builder = BuilderReflect<typeof __graph>;
1576
+ type FilterAny<T> = 0 extends 1 & T ? never : T;
1577
+ export type __input = Inspect<FilterAny<__builder["input"]>>;
1578
+ export type __output = Inspect<FilterAny<__builder["output"]>>;
1579
+ export type __config = Inspect<FilterAny<__builder["config"]>>;
1580
+ `);
1581
+ const program = ts.createProgram([typePath], {
1582
+ noEmit: true,
1583
+ strict: true,
1584
+ allowUnusedLabels: true,
1585
+ });
1586
+ const schema = buildGenerator(program);
1587
+ const trySymbol = (schema, symbol) => {
1588
+ try {
1589
+ return schema?.getSchemaForSymbol(symbol) ?? undefined;
1590
+ }
1591
+ catch (e) {
1592
+ console.error(`Failed to obtain symbol "${symbol}":`, e?.message);
1593
+ }
1594
+ return undefined;
1595
+ };
1596
+ return {
1597
+ state: trySymbol(schema, "__state"),
1598
+ update: trySymbol(schema, "__update"),
1599
+ input: trySymbol(schema, "__input"),
1600
+ output: trySymbol(schema, "__output"),
1601
+ config: trySymbol(schema, "__config"),
1602
+ };
1603
+ }
1604
+ finally {
1605
+ await fs.unlink(typePath);
1606
+ }
1607
+ }