@nestia/sdk 4.2.0-dev.20241211 → 4.2.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.
Files changed (108) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +87 -87
  3. package/assets/bundle/api/HttpError.ts +1 -1
  4. package/assets/bundle/api/IConnection.ts +1 -1
  5. package/assets/bundle/api/Primitive.ts +1 -1
  6. package/assets/bundle/api/Resolved.ts +1 -1
  7. package/assets/bundle/api/index.ts +4 -4
  8. package/assets/bundle/api/module.ts +6 -6
  9. package/assets/bundle/distribute/README.md +37 -37
  10. package/assets/bundle/distribute/package.json +28 -28
  11. package/assets/bundle/distribute/tsconfig.json +109 -109
  12. package/assets/bundle/e2e/index.ts +42 -42
  13. package/assets/config/nestia.config.ts +97 -97
  14. package/lib/executable/internal/NestiaConfigLoader.js +4 -4
  15. package/lib/executable/sdk.js +12 -12
  16. package/package.json +5 -5
  17. package/src/INestiaConfig.ts +271 -271
  18. package/src/NestiaSdkApplication.ts +307 -307
  19. package/src/NestiaSwaggerComposer.ts +138 -138
  20. package/src/analyses/AccessorAnalyzer.ts +67 -67
  21. package/src/analyses/ConfigAnalyzer.ts +155 -155
  22. package/src/analyses/ExceptionAnalyzer.ts +154 -154
  23. package/src/analyses/GenericAnalyzer.ts +49 -49
  24. package/src/analyses/ImportAnalyzer.ts +171 -171
  25. package/src/analyses/PathAnalyzer.ts +69 -69
  26. package/src/analyses/ReflectControllerAnalyzer.ts +105 -105
  27. package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
  28. package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +71 -71
  29. package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +348 -348
  30. package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +127 -127
  31. package/src/analyses/ReflectMetadataAnalyzer.ts +44 -44
  32. package/src/analyses/ReflectWebSocketOperationAnalyzer.ts +172 -172
  33. package/src/analyses/SecurityAnalyzer.ts +25 -25
  34. package/src/analyses/TypedHttpRouteAnalyzer.ts +186 -186
  35. package/src/analyses/TypedWebSocketRouteAnalyzer.ts +18 -18
  36. package/src/decorators/OperationMetadata.ts +15 -15
  37. package/src/executable/internal/CommandParser.ts +15 -15
  38. package/src/executable/internal/NestiaConfigLoader.ts +78 -78
  39. package/src/executable/internal/NestiaSdkCommand.ts +103 -103
  40. package/src/executable/sdk.ts +75 -75
  41. package/src/generates/CloneGenerator.ts +66 -66
  42. package/src/generates/E2eGenerator.ts +32 -32
  43. package/src/generates/SdkGenerator.ts +159 -159
  44. package/src/generates/SwaggerGenerator.ts +292 -292
  45. package/src/generates/internal/E2eFileProgrammer.ts +183 -183
  46. package/src/generates/internal/FilePrinter.ts +53 -53
  47. package/src/generates/internal/ImportDictionary.ts +147 -147
  48. package/src/generates/internal/SdkAliasCollection.ts +185 -185
  49. package/src/generates/internal/SdkDistributionComposer.ts +103 -103
  50. package/src/generates/internal/SdkFileProgrammer.ts +116 -116
  51. package/src/generates/internal/SdkHttpCloneProgrammer.ts +124 -124
  52. package/src/generates/internal/SdkHttpCloneReferencer.ts +71 -71
  53. package/src/generates/internal/SdkHttpFunctionProgrammer.ts +301 -301
  54. package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +529 -529
  55. package/src/generates/internal/SdkHttpRouteProgrammer.ts +117 -117
  56. package/src/generates/internal/SdkHttpSimulationProgrammer.ts +362 -362
  57. package/src/generates/internal/SdkImportWizard.ts +55 -55
  58. package/src/generates/internal/SdkRouteDirectory.ts +18 -18
  59. package/src/generates/internal/SdkTypeProgrammer.ts +394 -394
  60. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +363 -363
  61. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +265 -265
  62. package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
  63. package/src/generates/internal/SwaggerOperationComposer.ts +117 -117
  64. package/src/generates/internal/SwaggerOperationParameterComposer.ts +177 -177
  65. package/src/generates/internal/SwaggerOperationResponseComposer.ts +110 -110
  66. package/src/index.ts +4 -4
  67. package/src/module.ts +3 -3
  68. package/src/structures/INestiaProject.ts +13 -13
  69. package/src/structures/INestiaSdkInput.ts +20 -20
  70. package/src/structures/IReflectApplication.ts +8 -8
  71. package/src/structures/IReflectController.ts +15 -15
  72. package/src/structures/IReflectHttpOperation.ts +26 -26
  73. package/src/structures/IReflectHttpOperationException.ts +19 -19
  74. package/src/structures/IReflectHttpOperationParameter.ts +81 -81
  75. package/src/structures/IReflectHttpOperationSuccess.ts +22 -22
  76. package/src/structures/IReflectOperationError.ts +26 -26
  77. package/src/structures/IReflectType.ts +4 -4
  78. package/src/structures/IReflectTypeImport.ts +4 -4
  79. package/src/structures/IReflectWebSocketOperation.ts +17 -17
  80. package/src/structures/IReflectWebSocketOperationParameter.ts +38 -38
  81. package/src/structures/ITypedApplication.ts +11 -11
  82. package/src/structures/ITypedHttpRoute.ts +30 -30
  83. package/src/structures/ITypedHttpRouteException.ts +15 -15
  84. package/src/structures/ITypedHttpRouteParameter.ts +41 -41
  85. package/src/structures/ITypedHttpRouteSuccess.ts +22 -22
  86. package/src/structures/ITypedWebSocketRoute.ts +20 -20
  87. package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
  88. package/src/structures/MethodType.ts +5 -5
  89. package/src/structures/ParamCategory.ts +1 -1
  90. package/src/structures/TypeEntry.ts +22 -22
  91. package/src/transform.ts +9 -9
  92. package/src/transformers/IOperationMetadata.ts +44 -44
  93. package/src/transformers/ISdkOperationTransformerContext.ts +8 -8
  94. package/src/transformers/SdkOperationProgrammer.ts +209 -209
  95. package/src/transformers/SdkOperationTransformer.ts +253 -253
  96. package/src/transformers/TextPlainValidator.ts +17 -17
  97. package/src/typings/get-function-location.d.ts +7 -7
  98. package/src/utils/ArrayUtil.ts +26 -26
  99. package/src/utils/FileRetriever.ts +22 -22
  100. package/src/utils/MapUtil.ts +14 -14
  101. package/src/utils/MetadataUtil.ts +26 -26
  102. package/src/utils/PathUtil.ts +10 -10
  103. package/src/utils/SourceFinder.ts +66 -66
  104. package/src/utils/StringUtil.ts +17 -17
  105. package/src/utils/StripEnums.ts +5 -5
  106. package/src/utils/VersioningStrategy.ts +28 -28
  107. package/src/validators/HttpHeadersValidator.ts +34 -34
  108. package/src/validators/HttpQueryValidator.ts +34 -34
@@ -1,394 +1,394 @@
1
- import ts from "typescript";
2
- import { IJsDocTagInfo } from "typia";
3
- import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory";
4
- import { TypeFactory } from "typia/lib/factories/TypeFactory";
5
- import { IMetadataTypeTag } from "typia/lib/schemas/metadata/IMetadataTypeTag";
6
- import { Metadata } from "typia/lib/schemas/metadata/Metadata";
7
- import { MetadataAliasType } from "typia/lib/schemas/metadata/MetadataAliasType";
8
- import { MetadataArray } from "typia/lib/schemas/metadata/MetadataArray";
9
- import { MetadataAtomic } from "typia/lib/schemas/metadata/MetadataAtomic";
10
- import { MetadataConstantValue } from "typia/lib/schemas/metadata/MetadataConstantValue";
11
- import { MetadataEscaped } from "typia/lib/schemas/metadata/MetadataEscaped";
12
- import { MetadataObjectType } from "typia/lib/schemas/metadata/MetadataObjectType";
13
- import { MetadataProperty } from "typia/lib/schemas/metadata/MetadataProperty";
14
- import { MetadataTuple } from "typia/lib/schemas/metadata/MetadataTuple";
15
- import { Escaper } from "typia/lib/utils/Escaper";
16
-
17
- import { INestiaProject } from "../../structures/INestiaProject";
18
- import { FilePrinter } from "./FilePrinter";
19
- import { ImportDictionary } from "./ImportDictionary";
20
-
21
- export namespace SdkTypeProgrammer {
22
- /* -----------------------------------------------------------
23
- FACADE
24
- ----------------------------------------------------------- */
25
- export const write =
26
- (project: INestiaProject) =>
27
- (importer: ImportDictionary) =>
28
- (meta: Metadata, parentEscaped: boolean = false): ts.TypeNode => {
29
- const union: ts.TypeNode[] = [];
30
-
31
- // COALESCES
32
- if (meta.any) union.push(TypeFactory.keyword("any"));
33
- if (meta.nullable) union.push(writeNode("null"));
34
- if (meta.isRequired() === false) union.push(writeNode("undefined"));
35
- if (parentEscaped === false && meta.escaped)
36
- union.push(write_escaped(project)(importer)(meta.escaped));
37
-
38
- // ATOMIC TYPES
39
- for (const c of meta.constants)
40
- for (const value of c.values) union.push(write_constant(value));
41
- for (const tpl of meta.templates)
42
- union.push(write_template(project)(importer)(tpl.row ?? tpl));
43
- for (const atom of meta.atomics) union.push(write_atomic(importer)(atom));
44
-
45
- // OBJECT TYPES
46
- for (const tuple of meta.tuples)
47
- union.push(write_tuple(project)(importer)(tuple));
48
- for (const array of meta.arrays)
49
- union.push(write_array(project)(importer)(array));
50
- for (const object of meta.objects)
51
- if (
52
- object.type.name === "object" ||
53
- object.type.name === "__type" ||
54
- object.type.name.startsWith("__type.") ||
55
- object.type.name === "__object" ||
56
- object.type.name.startsWith("__object.")
57
- )
58
- union.push(write_object(project)(importer)(object.type));
59
- else union.push(write_alias(project)(importer)(object.type));
60
- for (const alias of meta.aliases)
61
- union.push(write_alias(project)(importer)(alias.type));
62
- for (const native of meta.natives)
63
- if (native.name === "Blob" || native.name === "File")
64
- union.push(write_native(native.name));
65
-
66
- return union.length === 1
67
- ? union[0]
68
- : ts.factory.createUnionTypeNode(union);
69
- };
70
-
71
- export const write_object =
72
- (project: INestiaProject) =>
73
- (importer: ImportDictionary) =>
74
- (object: MetadataObjectType): ts.TypeNode => {
75
- const regular = object.properties.filter((p) => p.key.isSoleLiteral());
76
- const dynamic = object.properties.filter((p) => !p.key.isSoleLiteral());
77
- return FilePrinter.description(
78
- regular.length && dynamic.length
79
- ? ts.factory.createIntersectionTypeNode([
80
- write_regular_property(project)(importer)(regular),
81
- ...dynamic.map(write_dynamic_property(project)(importer)),
82
- ])
83
- : dynamic.length
84
- ? ts.factory.createIntersectionTypeNode(
85
- dynamic.map(write_dynamic_property(project)(importer)),
86
- )
87
- : write_regular_property(project)(importer)(regular),
88
- writeComment([])(object.description ?? null, object.jsDocTags),
89
- );
90
- };
91
-
92
- const write_escaped =
93
- (project: INestiaProject) =>
94
- (importer: ImportDictionary) =>
95
- (meta: MetadataEscaped): ts.TypeNode => {
96
- if (
97
- meta.original.size() === 1 &&
98
- meta.original.natives.length === 1 &&
99
- meta.original.natives[0].name === "Date"
100
- )
101
- return ts.factory.createIntersectionTypeNode([
102
- TypeFactory.keyword("string"),
103
- writeTag(importer)({
104
- name: "Format",
105
- value: "date-time",
106
- } as IMetadataTypeTag),
107
- ]);
108
- return write(project)(importer)(meta.returns, true);
109
- };
110
-
111
- /* -----------------------------------------------------------
112
- ATOMICS
113
- ----------------------------------------------------------- */
114
- const write_constant = (value: MetadataConstantValue) => {
115
- if (typeof value.value === "boolean")
116
- return ts.factory.createLiteralTypeNode(
117
- value ? ts.factory.createTrue() : ts.factory.createFalse(),
118
- );
119
- else if (typeof value.value === "bigint")
120
- return ts.factory.createLiteralTypeNode(
121
- value.value < BigInt(0)
122
- ? ts.factory.createPrefixUnaryExpression(
123
- ts.SyntaxKind.MinusToken,
124
- ts.factory.createBigIntLiteral((-value).toString()),
125
- )
126
- : ts.factory.createBigIntLiteral(value.toString()),
127
- );
128
- else if (typeof value.value === "number")
129
- return ts.factory.createLiteralTypeNode(
130
- ExpressionFactory.number(value.value),
131
- );
132
- return ts.factory.createLiteralTypeNode(
133
- ts.factory.createStringLiteral(value.value as string),
134
- );
135
- };
136
-
137
- const write_template =
138
- (project: INestiaProject) =>
139
- (importer: ImportDictionary) =>
140
- (meta: Metadata[]): ts.TypeNode => {
141
- const head: boolean = meta[0].isSoleLiteral();
142
- const spans: [ts.TypeNode | null, string | null][] = [];
143
- for (const elem of meta.slice(head ? 1 : 0)) {
144
- const last =
145
- spans.at(-1) ??
146
- (() => {
147
- const tuple = [null!, null!] as [ts.TypeNode | null, string | null];
148
- spans.push(tuple);
149
- return tuple;
150
- })();
151
- if (elem.isSoleLiteral())
152
- if (last[1] === null)
153
- last[1] = String(elem.constants[0].values[0].value);
154
- else
155
- spans.push([
156
- ts.factory.createLiteralTypeNode(
157
- ts.factory.createStringLiteral(
158
- String(elem.constants[0].values[0].value),
159
- ),
160
- ),
161
- null,
162
- ]);
163
- else if (last[0] === null) last[0] = write(project)(importer)(elem);
164
- else spans.push([write(project)(importer)(elem), null]);
165
- }
166
- return ts.factory.createTemplateLiteralType(
167
- ts.factory.createTemplateHead(
168
- head ? (meta[0].constants[0].values[0].value as string) : "",
169
- ),
170
- spans
171
- .filter(([node]) => node !== null)
172
- .map(([node, str], i, array) =>
173
- ts.factory.createTemplateLiteralTypeSpan(
174
- node!,
175
- (i !== array.length - 1
176
- ? ts.factory.createTemplateMiddle
177
- : ts.factory.createTemplateTail)(str ?? ""),
178
- ),
179
- ),
180
- );
181
- };
182
-
183
- const write_atomic =
184
- (importer: ImportDictionary) =>
185
- (meta: MetadataAtomic): ts.TypeNode =>
186
- write_type_tag_matrix(importer)(
187
- ts.factory.createKeywordTypeNode(
188
- meta.type === "boolean"
189
- ? ts.SyntaxKind.BooleanKeyword
190
- : meta.type === "bigint"
191
- ? ts.SyntaxKind.BigIntKeyword
192
- : meta.type === "number"
193
- ? ts.SyntaxKind.NumberKeyword
194
- : ts.SyntaxKind.StringKeyword,
195
- ),
196
- meta.tags,
197
- );
198
-
199
- /* -----------------------------------------------------------
200
- INSTANCES
201
- ----------------------------------------------------------- */
202
- const write_array =
203
- (project: INestiaProject) =>
204
- (importer: ImportDictionary) =>
205
- (meta: MetadataArray): ts.TypeNode =>
206
- write_type_tag_matrix(importer)(
207
- ts.factory.createArrayTypeNode(
208
- write(project)(importer)(meta.type.value),
209
- ),
210
- meta.tags,
211
- );
212
-
213
- const write_tuple =
214
- (project: INestiaProject) =>
215
- (importer: ImportDictionary) =>
216
- (meta: MetadataTuple): ts.TypeNode =>
217
- ts.factory.createTupleTypeNode(
218
- meta.type.elements.map((elem) =>
219
- elem.rest
220
- ? ts.factory.createRestTypeNode(
221
- ts.factory.createArrayTypeNode(
222
- write(project)(importer)(elem.rest),
223
- ),
224
- )
225
- : elem.optional
226
- ? ts.factory.createOptionalTypeNode(
227
- write(project)(importer)(elem),
228
- )
229
- : write(project)(importer)(elem),
230
- ),
231
- );
232
-
233
- const write_regular_property =
234
- (project: INestiaProject) =>
235
- (importer: ImportDictionary) =>
236
- (properties: MetadataProperty[]): ts.TypeLiteralNode =>
237
- ts.factory.createTypeLiteralNode(
238
- properties.map((p) =>
239
- FilePrinter.description(
240
- ts.factory.createPropertySignature(
241
- undefined,
242
- Escaper.variable(String(p.key.constants[0].values[0].value))
243
- ? ts.factory.createIdentifier(
244
- String(p.key.constants[0].values[0].value),
245
- )
246
- : ts.factory.createStringLiteral(
247
- String(p.key.constants[0].values[0].value),
248
- ),
249
- p.value.isRequired() === false
250
- ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
251
- : undefined,
252
- SdkTypeProgrammer.write(project)(importer)(p.value),
253
- ),
254
- writeComment(p.value.atomics)(p.description, p.jsDocTags),
255
- ),
256
- ),
257
- );
258
-
259
- const write_dynamic_property =
260
- (project: INestiaProject) =>
261
- (importer: ImportDictionary) =>
262
- (property: MetadataProperty): ts.TypeLiteralNode =>
263
- ts.factory.createTypeLiteralNode([
264
- FilePrinter.description(
265
- ts.factory.createIndexSignature(
266
- undefined,
267
- [
268
- ts.factory.createParameterDeclaration(
269
- undefined,
270
- undefined,
271
- ts.factory.createIdentifier("key"),
272
- undefined,
273
- SdkTypeProgrammer.write(project)(importer)(property.key),
274
- ),
275
- ],
276
- SdkTypeProgrammer.write(project)(importer)(property.value),
277
- ),
278
- writeComment(property.value.atomics)(
279
- property.description,
280
- property.jsDocTags,
281
- ),
282
- ),
283
- ]);
284
-
285
- const write_alias =
286
- (project: INestiaProject) =>
287
- (importer: ImportDictionary) =>
288
- (meta: MetadataAliasType | MetadataObjectType): ts.TypeNode => {
289
- importInternalFile(project)(importer)(meta.name);
290
- return ts.factory.createTypeReferenceNode(meta.name);
291
- };
292
-
293
- const write_native = (name: string): ts.TypeNode =>
294
- ts.factory.createTypeReferenceNode(name);
295
-
296
- /* -----------------------------------------------------------
297
- MISCELLANEOUS
298
- ----------------------------------------------------------- */
299
- const write_type_tag_matrix =
300
- (importer: ImportDictionary) =>
301
- (base: ts.TypeNode, matrix: IMetadataTypeTag[][]): ts.TypeNode => {
302
- matrix = matrix.filter((row) => row.length !== 0);
303
- if (matrix.length === 0) return base;
304
- else if (matrix.length === 1)
305
- return ts.factory.createIntersectionTypeNode([
306
- base,
307
- ...matrix[0].map((tag) => writeTag(importer)(tag)),
308
- ]);
309
- return ts.factory.createIntersectionTypeNode([
310
- base,
311
- ts.factory.createUnionTypeNode(
312
- matrix.map((row) =>
313
- row.length === 1
314
- ? writeTag(importer)(row[0])
315
- : ts.factory.createIntersectionTypeNode(
316
- row.map((tag) => writeTag(importer)(tag)),
317
- ),
318
- ),
319
- ),
320
- ]);
321
- };
322
- }
323
-
324
- const writeNode = (text: string) => ts.factory.createTypeReferenceNode(text);
325
- const writeTag = (importer: ImportDictionary) => (tag: IMetadataTypeTag) => {
326
- const instance: string = tag.name.split("<")[0];
327
- return ts.factory.createTypeReferenceNode(
328
- importer.external({
329
- type: true,
330
- library: `typia/lib/tags/${instance}`,
331
- instance,
332
- }),
333
- [
334
- ts.factory.createLiteralTypeNode(
335
- typeof tag.value === "boolean"
336
- ? tag.value
337
- ? ts.factory.createTrue()
338
- : ts.factory.createFalse()
339
- : typeof tag.value === "bigint"
340
- ? tag.value < BigInt(0)
341
- ? ts.factory.createPrefixUnaryExpression(
342
- ts.SyntaxKind.MinusToken,
343
- ts.factory.createBigIntLiteral((-tag.value).toString()),
344
- )
345
- : ts.factory.createBigIntLiteral(tag.value.toString())
346
- : typeof tag.value === "number"
347
- ? ExpressionFactory.number(tag.value)
348
- : ts.factory.createStringLiteral(tag.value),
349
- ),
350
- ],
351
- );
352
- };
353
- const writeComment =
354
- (atomics: MetadataAtomic[]) =>
355
- (description: string | null, jsDocTags: IJsDocTagInfo[]): string => {
356
- const lines: string[] = [];
357
- if (description?.length)
358
- lines.push(...description.split("\n").map((s) => `${s}`));
359
-
360
- const filtered: IJsDocTagInfo[] =
361
- !!atomics.length && !!jsDocTags?.length
362
- ? jsDocTags.filter(
363
- (tag) =>
364
- !atomics.some((a) =>
365
- a.tags.some((r) => r.some((t) => t.kind === tag.name)),
366
- ),
367
- )
368
- : (jsDocTags ?? []);
369
-
370
- if (description?.length && filtered.length) lines.push("");
371
- if (filtered.length)
372
- lines.push(
373
- ...filtered.map((t) =>
374
- t.text?.length
375
- ? `@${t.name} ${t.text.map((e) => e.text).join("")}`
376
- : `@${t.name}`,
377
- ),
378
- );
379
- return lines.join("\n");
380
- };
381
-
382
- const importInternalFile =
383
- (project: INestiaProject) =>
384
- (importer: ImportDictionary) =>
385
- (name: string) => {
386
- const top = name.split(".")[0];
387
- if (importer.file === `${project.config.output}/structures/${top}.ts`)
388
- return;
389
- importer.internal({
390
- type: true,
391
- file: `${project.config.output}/structures/${name.split(".")[0]}`,
392
- instance: top,
393
- });
394
- };
1
+ import ts from "typescript";
2
+ import { IJsDocTagInfo } from "typia";
3
+ import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory";
4
+ import { TypeFactory } from "typia/lib/factories/TypeFactory";
5
+ import { IMetadataTypeTag } from "typia/lib/schemas/metadata/IMetadataTypeTag";
6
+ import { Metadata } from "typia/lib/schemas/metadata/Metadata";
7
+ import { MetadataAliasType } from "typia/lib/schemas/metadata/MetadataAliasType";
8
+ import { MetadataArray } from "typia/lib/schemas/metadata/MetadataArray";
9
+ import { MetadataAtomic } from "typia/lib/schemas/metadata/MetadataAtomic";
10
+ import { MetadataConstantValue } from "typia/lib/schemas/metadata/MetadataConstantValue";
11
+ import { MetadataEscaped } from "typia/lib/schemas/metadata/MetadataEscaped";
12
+ import { MetadataObjectType } from "typia/lib/schemas/metadata/MetadataObjectType";
13
+ import { MetadataProperty } from "typia/lib/schemas/metadata/MetadataProperty";
14
+ import { MetadataTuple } from "typia/lib/schemas/metadata/MetadataTuple";
15
+ import { Escaper } from "typia/lib/utils/Escaper";
16
+
17
+ import { INestiaProject } from "../../structures/INestiaProject";
18
+ import { FilePrinter } from "./FilePrinter";
19
+ import { ImportDictionary } from "./ImportDictionary";
20
+
21
+ export namespace SdkTypeProgrammer {
22
+ /* -----------------------------------------------------------
23
+ FACADE
24
+ ----------------------------------------------------------- */
25
+ export const write =
26
+ (project: INestiaProject) =>
27
+ (importer: ImportDictionary) =>
28
+ (meta: Metadata, parentEscaped: boolean = false): ts.TypeNode => {
29
+ const union: ts.TypeNode[] = [];
30
+
31
+ // COALESCES
32
+ if (meta.any) union.push(TypeFactory.keyword("any"));
33
+ if (meta.nullable) union.push(writeNode("null"));
34
+ if (meta.isRequired() === false) union.push(writeNode("undefined"));
35
+ if (parentEscaped === false && meta.escaped)
36
+ union.push(write_escaped(project)(importer)(meta.escaped));
37
+
38
+ // ATOMIC TYPES
39
+ for (const c of meta.constants)
40
+ for (const value of c.values) union.push(write_constant(value));
41
+ for (const tpl of meta.templates)
42
+ union.push(write_template(project)(importer)(tpl.row ?? tpl));
43
+ for (const atom of meta.atomics) union.push(write_atomic(importer)(atom));
44
+
45
+ // OBJECT TYPES
46
+ for (const tuple of meta.tuples)
47
+ union.push(write_tuple(project)(importer)(tuple));
48
+ for (const array of meta.arrays)
49
+ union.push(write_array(project)(importer)(array));
50
+ for (const object of meta.objects)
51
+ if (
52
+ object.type.name === "object" ||
53
+ object.type.name === "__type" ||
54
+ object.type.name.startsWith("__type.") ||
55
+ object.type.name === "__object" ||
56
+ object.type.name.startsWith("__object.")
57
+ )
58
+ union.push(write_object(project)(importer)(object.type));
59
+ else union.push(write_alias(project)(importer)(object.type));
60
+ for (const alias of meta.aliases)
61
+ union.push(write_alias(project)(importer)(alias.type));
62
+ for (const native of meta.natives)
63
+ if (native.name === "Blob" || native.name === "File")
64
+ union.push(write_native(native.name));
65
+
66
+ return union.length === 1
67
+ ? union[0]
68
+ : ts.factory.createUnionTypeNode(union);
69
+ };
70
+
71
+ export const write_object =
72
+ (project: INestiaProject) =>
73
+ (importer: ImportDictionary) =>
74
+ (object: MetadataObjectType): ts.TypeNode => {
75
+ const regular = object.properties.filter((p) => p.key.isSoleLiteral());
76
+ const dynamic = object.properties.filter((p) => !p.key.isSoleLiteral());
77
+ return FilePrinter.description(
78
+ regular.length && dynamic.length
79
+ ? ts.factory.createIntersectionTypeNode([
80
+ write_regular_property(project)(importer)(regular),
81
+ ...dynamic.map(write_dynamic_property(project)(importer)),
82
+ ])
83
+ : dynamic.length
84
+ ? ts.factory.createIntersectionTypeNode(
85
+ dynamic.map(write_dynamic_property(project)(importer)),
86
+ )
87
+ : write_regular_property(project)(importer)(regular),
88
+ writeComment([])(object.description ?? null, object.jsDocTags),
89
+ );
90
+ };
91
+
92
+ const write_escaped =
93
+ (project: INestiaProject) =>
94
+ (importer: ImportDictionary) =>
95
+ (meta: MetadataEscaped): ts.TypeNode => {
96
+ if (
97
+ meta.original.size() === 1 &&
98
+ meta.original.natives.length === 1 &&
99
+ meta.original.natives[0].name === "Date"
100
+ )
101
+ return ts.factory.createIntersectionTypeNode([
102
+ TypeFactory.keyword("string"),
103
+ writeTag(importer)({
104
+ name: "Format",
105
+ value: "date-time",
106
+ } as IMetadataTypeTag),
107
+ ]);
108
+ return write(project)(importer)(meta.returns, true);
109
+ };
110
+
111
+ /* -----------------------------------------------------------
112
+ ATOMICS
113
+ ----------------------------------------------------------- */
114
+ const write_constant = (value: MetadataConstantValue) => {
115
+ if (typeof value.value === "boolean")
116
+ return ts.factory.createLiteralTypeNode(
117
+ value ? ts.factory.createTrue() : ts.factory.createFalse(),
118
+ );
119
+ else if (typeof value.value === "bigint")
120
+ return ts.factory.createLiteralTypeNode(
121
+ value.value < BigInt(0)
122
+ ? ts.factory.createPrefixUnaryExpression(
123
+ ts.SyntaxKind.MinusToken,
124
+ ts.factory.createBigIntLiteral((-value).toString()),
125
+ )
126
+ : ts.factory.createBigIntLiteral(value.toString()),
127
+ );
128
+ else if (typeof value.value === "number")
129
+ return ts.factory.createLiteralTypeNode(
130
+ ExpressionFactory.number(value.value),
131
+ );
132
+ return ts.factory.createLiteralTypeNode(
133
+ ts.factory.createStringLiteral(value.value as string),
134
+ );
135
+ };
136
+
137
+ const write_template =
138
+ (project: INestiaProject) =>
139
+ (importer: ImportDictionary) =>
140
+ (meta: Metadata[]): ts.TypeNode => {
141
+ const head: boolean = meta[0].isSoleLiteral();
142
+ const spans: [ts.TypeNode | null, string | null][] = [];
143
+ for (const elem of meta.slice(head ? 1 : 0)) {
144
+ const last =
145
+ spans.at(-1) ??
146
+ (() => {
147
+ const tuple = [null!, null!] as [ts.TypeNode | null, string | null];
148
+ spans.push(tuple);
149
+ return tuple;
150
+ })();
151
+ if (elem.isSoleLiteral())
152
+ if (last[1] === null)
153
+ last[1] = String(elem.constants[0].values[0].value);
154
+ else
155
+ spans.push([
156
+ ts.factory.createLiteralTypeNode(
157
+ ts.factory.createStringLiteral(
158
+ String(elem.constants[0].values[0].value),
159
+ ),
160
+ ),
161
+ null,
162
+ ]);
163
+ else if (last[0] === null) last[0] = write(project)(importer)(elem);
164
+ else spans.push([write(project)(importer)(elem), null]);
165
+ }
166
+ return ts.factory.createTemplateLiteralType(
167
+ ts.factory.createTemplateHead(
168
+ head ? (meta[0].constants[0].values[0].value as string) : "",
169
+ ),
170
+ spans
171
+ .filter(([node]) => node !== null)
172
+ .map(([node, str], i, array) =>
173
+ ts.factory.createTemplateLiteralTypeSpan(
174
+ node!,
175
+ (i !== array.length - 1
176
+ ? ts.factory.createTemplateMiddle
177
+ : ts.factory.createTemplateTail)(str ?? ""),
178
+ ),
179
+ ),
180
+ );
181
+ };
182
+
183
+ const write_atomic =
184
+ (importer: ImportDictionary) =>
185
+ (meta: MetadataAtomic): ts.TypeNode =>
186
+ write_type_tag_matrix(importer)(
187
+ ts.factory.createKeywordTypeNode(
188
+ meta.type === "boolean"
189
+ ? ts.SyntaxKind.BooleanKeyword
190
+ : meta.type === "bigint"
191
+ ? ts.SyntaxKind.BigIntKeyword
192
+ : meta.type === "number"
193
+ ? ts.SyntaxKind.NumberKeyword
194
+ : ts.SyntaxKind.StringKeyword,
195
+ ),
196
+ meta.tags,
197
+ );
198
+
199
+ /* -----------------------------------------------------------
200
+ INSTANCES
201
+ ----------------------------------------------------------- */
202
+ const write_array =
203
+ (project: INestiaProject) =>
204
+ (importer: ImportDictionary) =>
205
+ (meta: MetadataArray): ts.TypeNode =>
206
+ write_type_tag_matrix(importer)(
207
+ ts.factory.createArrayTypeNode(
208
+ write(project)(importer)(meta.type.value),
209
+ ),
210
+ meta.tags,
211
+ );
212
+
213
+ const write_tuple =
214
+ (project: INestiaProject) =>
215
+ (importer: ImportDictionary) =>
216
+ (meta: MetadataTuple): ts.TypeNode =>
217
+ ts.factory.createTupleTypeNode(
218
+ meta.type.elements.map((elem) =>
219
+ elem.rest
220
+ ? ts.factory.createRestTypeNode(
221
+ ts.factory.createArrayTypeNode(
222
+ write(project)(importer)(elem.rest),
223
+ ),
224
+ )
225
+ : elem.optional
226
+ ? ts.factory.createOptionalTypeNode(
227
+ write(project)(importer)(elem),
228
+ )
229
+ : write(project)(importer)(elem),
230
+ ),
231
+ );
232
+
233
+ const write_regular_property =
234
+ (project: INestiaProject) =>
235
+ (importer: ImportDictionary) =>
236
+ (properties: MetadataProperty[]): ts.TypeLiteralNode =>
237
+ ts.factory.createTypeLiteralNode(
238
+ properties.map((p) =>
239
+ FilePrinter.description(
240
+ ts.factory.createPropertySignature(
241
+ undefined,
242
+ Escaper.variable(String(p.key.constants[0].values[0].value))
243
+ ? ts.factory.createIdentifier(
244
+ String(p.key.constants[0].values[0].value),
245
+ )
246
+ : ts.factory.createStringLiteral(
247
+ String(p.key.constants[0].values[0].value),
248
+ ),
249
+ p.value.isRequired() === false
250
+ ? ts.factory.createToken(ts.SyntaxKind.QuestionToken)
251
+ : undefined,
252
+ SdkTypeProgrammer.write(project)(importer)(p.value),
253
+ ),
254
+ writeComment(p.value.atomics)(p.description, p.jsDocTags),
255
+ ),
256
+ ),
257
+ );
258
+
259
+ const write_dynamic_property =
260
+ (project: INestiaProject) =>
261
+ (importer: ImportDictionary) =>
262
+ (property: MetadataProperty): ts.TypeLiteralNode =>
263
+ ts.factory.createTypeLiteralNode([
264
+ FilePrinter.description(
265
+ ts.factory.createIndexSignature(
266
+ undefined,
267
+ [
268
+ ts.factory.createParameterDeclaration(
269
+ undefined,
270
+ undefined,
271
+ ts.factory.createIdentifier("key"),
272
+ undefined,
273
+ SdkTypeProgrammer.write(project)(importer)(property.key),
274
+ ),
275
+ ],
276
+ SdkTypeProgrammer.write(project)(importer)(property.value),
277
+ ),
278
+ writeComment(property.value.atomics)(
279
+ property.description,
280
+ property.jsDocTags,
281
+ ),
282
+ ),
283
+ ]);
284
+
285
+ const write_alias =
286
+ (project: INestiaProject) =>
287
+ (importer: ImportDictionary) =>
288
+ (meta: MetadataAliasType | MetadataObjectType): ts.TypeNode => {
289
+ importInternalFile(project)(importer)(meta.name);
290
+ return ts.factory.createTypeReferenceNode(meta.name);
291
+ };
292
+
293
+ const write_native = (name: string): ts.TypeNode =>
294
+ ts.factory.createTypeReferenceNode(name);
295
+
296
+ /* -----------------------------------------------------------
297
+ MISCELLANEOUS
298
+ ----------------------------------------------------------- */
299
+ const write_type_tag_matrix =
300
+ (importer: ImportDictionary) =>
301
+ (base: ts.TypeNode, matrix: IMetadataTypeTag[][]): ts.TypeNode => {
302
+ matrix = matrix.filter((row) => row.length !== 0);
303
+ if (matrix.length === 0) return base;
304
+ else if (matrix.length === 1)
305
+ return ts.factory.createIntersectionTypeNode([
306
+ base,
307
+ ...matrix[0].map((tag) => writeTag(importer)(tag)),
308
+ ]);
309
+ return ts.factory.createIntersectionTypeNode([
310
+ base,
311
+ ts.factory.createUnionTypeNode(
312
+ matrix.map((row) =>
313
+ row.length === 1
314
+ ? writeTag(importer)(row[0])
315
+ : ts.factory.createIntersectionTypeNode(
316
+ row.map((tag) => writeTag(importer)(tag)),
317
+ ),
318
+ ),
319
+ ),
320
+ ]);
321
+ };
322
+ }
323
+
324
+ const writeNode = (text: string) => ts.factory.createTypeReferenceNode(text);
325
+ const writeTag = (importer: ImportDictionary) => (tag: IMetadataTypeTag) => {
326
+ const instance: string = tag.name.split("<")[0];
327
+ return ts.factory.createTypeReferenceNode(
328
+ importer.external({
329
+ type: true,
330
+ library: `typia/lib/tags/${instance}`,
331
+ instance,
332
+ }),
333
+ [
334
+ ts.factory.createLiteralTypeNode(
335
+ typeof tag.value === "boolean"
336
+ ? tag.value
337
+ ? ts.factory.createTrue()
338
+ : ts.factory.createFalse()
339
+ : typeof tag.value === "bigint"
340
+ ? tag.value < BigInt(0)
341
+ ? ts.factory.createPrefixUnaryExpression(
342
+ ts.SyntaxKind.MinusToken,
343
+ ts.factory.createBigIntLiteral((-tag.value).toString()),
344
+ )
345
+ : ts.factory.createBigIntLiteral(tag.value.toString())
346
+ : typeof tag.value === "number"
347
+ ? ExpressionFactory.number(tag.value)
348
+ : ts.factory.createStringLiteral(tag.value),
349
+ ),
350
+ ],
351
+ );
352
+ };
353
+ const writeComment =
354
+ (atomics: MetadataAtomic[]) =>
355
+ (description: string | null, jsDocTags: IJsDocTagInfo[]): string => {
356
+ const lines: string[] = [];
357
+ if (description?.length)
358
+ lines.push(...description.split("\n").map((s) => `${s}`));
359
+
360
+ const filtered: IJsDocTagInfo[] =
361
+ !!atomics.length && !!jsDocTags?.length
362
+ ? jsDocTags.filter(
363
+ (tag) =>
364
+ !atomics.some((a) =>
365
+ a.tags.some((r) => r.some((t) => t.kind === tag.name)),
366
+ ),
367
+ )
368
+ : (jsDocTags ?? []);
369
+
370
+ if (description?.length && filtered.length) lines.push("");
371
+ if (filtered.length)
372
+ lines.push(
373
+ ...filtered.map((t) =>
374
+ t.text?.length
375
+ ? `@${t.name} ${t.text.map((e) => e.text).join("")}`
376
+ : `@${t.name}`,
377
+ ),
378
+ );
379
+ return lines.join("\n");
380
+ };
381
+
382
+ const importInternalFile =
383
+ (project: INestiaProject) =>
384
+ (importer: ImportDictionary) =>
385
+ (name: string) => {
386
+ const top = name.split(".")[0];
387
+ if (importer.file === `${project.config.output}/structures/${top}.ts`)
388
+ return;
389
+ importer.internal({
390
+ type: true,
391
+ file: `${project.config.output}/structures/${name.split(".")[0]}`,
392
+ instance: top,
393
+ });
394
+ };