@nestia/sdk 4.4.0 → 4.4.1-dev.20241216

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 (109) 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 +8 -8
  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 +377 -377
  60. package/src/generates/internal/SdkTypeTagProgrammer.ts +120 -120
  61. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +363 -363
  62. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +265 -265
  63. package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
  64. package/src/generates/internal/SwaggerOperationComposer.ts +117 -117
  65. package/src/generates/internal/SwaggerOperationParameterComposer.ts +177 -177
  66. package/src/generates/internal/SwaggerOperationResponseComposer.ts +110 -110
  67. package/src/index.ts +4 -4
  68. package/src/module.ts +3 -3
  69. package/src/structures/INestiaProject.ts +13 -13
  70. package/src/structures/INestiaSdkInput.ts +20 -20
  71. package/src/structures/IReflectApplication.ts +8 -8
  72. package/src/structures/IReflectController.ts +15 -15
  73. package/src/structures/IReflectHttpOperation.ts +26 -26
  74. package/src/structures/IReflectHttpOperationException.ts +19 -19
  75. package/src/structures/IReflectHttpOperationParameter.ts +81 -81
  76. package/src/structures/IReflectHttpOperationSuccess.ts +22 -22
  77. package/src/structures/IReflectOperationError.ts +26 -26
  78. package/src/structures/IReflectType.ts +4 -4
  79. package/src/structures/IReflectTypeImport.ts +4 -4
  80. package/src/structures/IReflectWebSocketOperation.ts +17 -17
  81. package/src/structures/IReflectWebSocketOperationParameter.ts +38 -38
  82. package/src/structures/ITypedApplication.ts +11 -11
  83. package/src/structures/ITypedHttpRoute.ts +30 -30
  84. package/src/structures/ITypedHttpRouteException.ts +15 -15
  85. package/src/structures/ITypedHttpRouteParameter.ts +41 -41
  86. package/src/structures/ITypedHttpRouteSuccess.ts +22 -22
  87. package/src/structures/ITypedWebSocketRoute.ts +20 -20
  88. package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
  89. package/src/structures/MethodType.ts +5 -5
  90. package/src/structures/ParamCategory.ts +1 -1
  91. package/src/structures/TypeEntry.ts +22 -22
  92. package/src/transform.ts +9 -9
  93. package/src/transformers/IOperationMetadata.ts +44 -44
  94. package/src/transformers/ISdkOperationTransformerContext.ts +8 -8
  95. package/src/transformers/SdkOperationProgrammer.ts +209 -209
  96. package/src/transformers/SdkOperationTransformer.ts +253 -253
  97. package/src/transformers/TextPlainValidator.ts +17 -17
  98. package/src/typings/get-function-location.d.ts +7 -7
  99. package/src/utils/ArrayUtil.ts +26 -26
  100. package/src/utils/FileRetriever.ts +22 -22
  101. package/src/utils/MapUtil.ts +14 -14
  102. package/src/utils/MetadataUtil.ts +26 -26
  103. package/src/utils/PathUtil.ts +10 -10
  104. package/src/utils/SourceFinder.ts +66 -66
  105. package/src/utils/StringUtil.ts +17 -17
  106. package/src/utils/StripEnums.ts +5 -5
  107. package/src/utils/VersioningStrategy.ts +28 -28
  108. package/src/validators/HttpHeadersValidator.ts +34 -34
  109. package/src/validators/HttpQueryValidator.ts +34 -34
@@ -1,529 +1,529 @@
1
- import ts from "typescript";
2
- import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory";
3
- import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
4
- import { LiteralFactory } from "typia/lib/factories/LiteralFactory";
5
- import { TypeFactory } from "typia/lib/factories/TypeFactory";
6
- import { Escaper } from "typia/lib/utils/Escaper";
7
-
8
- import { INestiaProject } from "../../structures/INestiaProject";
9
- import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute";
10
- import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter";
11
- import { FilePrinter } from "./FilePrinter";
12
- import { ImportDictionary } from "./ImportDictionary";
13
- import { SdkAliasCollection } from "./SdkAliasCollection";
14
- import { SdkHttpSimulationProgrammer } from "./SdkHttpSimulationProgrammer";
15
- import { SdkImportWizard } from "./SdkImportWizard";
16
-
17
- export namespace SdkHttpNamespaceProgrammer {
18
- export const write =
19
- (project: INestiaProject) =>
20
- (importer: ImportDictionary) =>
21
- (
22
- route: ITypedHttpRoute,
23
- props: {
24
- headers: ITypedHttpRouteParameter.IHeaders | undefined;
25
- query: ITypedHttpRouteParameter.IQuery | undefined;
26
- input: ITypedHttpRouteParameter.IBody | undefined;
27
- },
28
- ): ts.ModuleDeclaration => {
29
- const types = write_types(project)(importer)(route, props);
30
- return ts.factory.createModuleDeclaration(
31
- [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
32
- ts.factory.createIdentifier(route.name),
33
- ts.factory.createModuleBlock([
34
- ...types,
35
- ...(types.length ? [FilePrinter.enter()] : []),
36
- write_metadata(project)(importer)(route, props),
37
- FilePrinter.enter(),
38
- write_path(project)(importer)(route, props.query),
39
- ...(project.config.simulate
40
- ? [
41
- SdkHttpSimulationProgrammer.random(project)(importer)(route),
42
- SdkHttpSimulationProgrammer.simulate(project)(importer)(
43
- route,
44
- props,
45
- ),
46
- ]
47
- : []),
48
- ...(project.config.json &&
49
- props.input !== undefined &&
50
- (props.input.contentType === "application/json" ||
51
- props.input.encrypted === true)
52
- ? [write_stringify(project)(importer)]
53
- : []),
54
- ]),
55
- ts.NodeFlags.Namespace,
56
- );
57
- };
58
-
59
- const write_types =
60
- (project: INestiaProject) =>
61
- (importer: ImportDictionary) =>
62
- (
63
- route: ITypedHttpRoute,
64
- props: {
65
- headers: ITypedHttpRouteParameter.IHeaders | undefined;
66
- query: ITypedHttpRouteParameter.IQuery | undefined;
67
- input: ITypedHttpRouteParameter.IBody | undefined;
68
- },
69
- ): ts.TypeAliasDeclaration[] => {
70
- const array: ts.TypeAliasDeclaration[] = [];
71
- const declare = (name: string, type: ts.TypeNode) =>
72
- array.push(
73
- ts.factory.createTypeAliasDeclaration(
74
- [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
75
- name,
76
- undefined,
77
- type,
78
- ),
79
- );
80
- if (props.headers !== undefined)
81
- declare(
82
- "Headers",
83
- SdkAliasCollection.headers(project)(importer)(props.headers),
84
- );
85
- if (props.query !== undefined)
86
- declare(
87
- "Query",
88
- SdkAliasCollection.query(project)(importer)(props.query),
89
- );
90
- if (props.input !== undefined)
91
- declare(
92
- "Input",
93
- SdkAliasCollection.input(project)(importer)(props.input),
94
- );
95
- if (
96
- project.config.propagate === true ||
97
- route.success.metadata.size() !== 0
98
- )
99
- declare("Output", SdkAliasCollection.output(project)(importer)(route));
100
- return array;
101
- };
102
-
103
- const write_metadata =
104
- (project: INestiaProject) =>
105
- (importer: ImportDictionary) =>
106
- (
107
- route: ITypedHttpRoute,
108
- props: {
109
- headers: ITypedHttpRouteParameter.IHeaders | undefined;
110
- query: ITypedHttpRouteParameter.IQuery | undefined;
111
- input: ITypedHttpRouteParameter.IBody | undefined;
112
- },
113
- ): ts.VariableStatement =>
114
- constant("METADATA")(
115
- ts.factory.createAsExpression(
116
- ts.factory.createObjectLiteralExpression(
117
- [
118
- ts.factory.createPropertyAssignment(
119
- "method",
120
- ts.factory.createStringLiteral(route.method),
121
- ),
122
- ts.factory.createPropertyAssignment(
123
- "path",
124
- ts.factory.createStringLiteral(route.path),
125
- ),
126
- ts.factory.createPropertyAssignment(
127
- "request",
128
- props.input
129
- ? LiteralFactory.write(
130
- props.input !== undefined
131
- ? {
132
- type: props.input.contentType,
133
- encrypted: !!props.input.encrypted,
134
- }
135
- : {
136
- type: "application/json",
137
- encrypted: false,
138
- },
139
- )
140
- : ts.factory.createNull(),
141
- ),
142
- ts.factory.createPropertyAssignment(
143
- "response",
144
- route.method !== "HEAD"
145
- ? LiteralFactory.write({
146
- type: route.success.contentType,
147
- encrypted: !!route.success.encrypted,
148
- })
149
- : ts.factory.createNull(),
150
- ),
151
- ts.factory.createPropertyAssignment(
152
- "status",
153
- route.success.status !== null
154
- ? ExpressionFactory.number(route.success.status)
155
- : ts.factory.createNull(),
156
- ),
157
- ...(route.success.contentType ===
158
- "application/x-www-form-urlencoded"
159
- ? [
160
- ts.factory.createPropertyAssignment(
161
- "parseQuery",
162
- ts.factory.createCallExpression(
163
- ts.factory.createIdentifier(
164
- `${SdkImportWizard.typia(importer)}.http.createAssertQuery`,
165
- ),
166
- [
167
- project.config.clone === true
168
- ? SdkAliasCollection.from(project)(importer)(
169
- route.success.metadata,
170
- )
171
- : SdkAliasCollection.name(route.success),
172
- ],
173
- undefined,
174
- ),
175
- ),
176
- ]
177
- : []),
178
- ],
179
- true,
180
- ),
181
- ts.factory.createTypeReferenceNode(
182
- ts.factory.createIdentifier("const"),
183
- ),
184
- ),
185
- );
186
-
187
- const write_path =
188
- (project: INestiaProject) =>
189
- (importer: ImportDictionary) =>
190
- (
191
- route: ITypedHttpRoute,
192
- query: ITypedHttpRouteParameter.IQuery | undefined,
193
- ): ts.VariableStatement => {
194
- const g = {
195
- total: [
196
- ...route.parameters.filter(
197
- (param) => param.category === "param" || param.category === "query",
198
- ),
199
- ],
200
- query: route.parameters
201
- .filter((param) => param.category === "query")
202
- .filter((param) => param.field !== null),
203
- path: route.parameters.filter((param) => param.category === "param"),
204
- };
205
- const out = (body: ts.ConciseBody) =>
206
- constant("path")(
207
- ts.factory.createArrowFunction(
208
- [],
209
- [],
210
- g.total.map((p) =>
211
- IdentifierFactory.parameter(
212
- p.name,
213
- p === query
214
- ? p.metadata.isRequired() === false
215
- ? ts.factory.createUnionTypeNode([
216
- ts.factory.createTypeReferenceNode(
217
- `${route.name}.Query`,
218
- ),
219
- ts.factory.createTypeReferenceNode("undefined"),
220
- ])
221
- : ts.factory.createTypeReferenceNode(`${route.name}.Query`)
222
- : project.config.clone === true
223
- ? SdkAliasCollection.from(project)(importer)(p.metadata)
224
- : SdkAliasCollection.name(p),
225
- ),
226
- ),
227
- undefined,
228
- undefined,
229
- body,
230
- ),
231
- );
232
- if (g.total.length === 0)
233
- return out(ts.factory.createStringLiteral(route.path));
234
-
235
- const template = () => {
236
- const splitted: string[] = route.path.split(":");
237
- if (splitted.length === 1)
238
- return ts.factory.createStringLiteral(route.path);
239
- return ts.factory.createTemplateExpression(
240
- ts.factory.createTemplateHead(splitted[0]),
241
- splitted.slice(1).map((s, i, arr) => {
242
- const name: string = s.split("/")[0];
243
- return ts.factory.createTemplateSpan(
244
- ts.factory.createCallExpression(
245
- ts.factory.createIdentifier("encodeURIComponent"),
246
- undefined,
247
- [
248
- ts.factory.createBinaryExpression(
249
- ts.factory.createCallChain(
250
- ts.factory.createPropertyAccessChain(
251
- ts.factory.createIdentifier(
252
- g.path.find((p) => p.field === name)!.name,
253
- ),
254
- ts.factory.createToken(ts.SyntaxKind.QuestionDotToken),
255
- "toString",
256
- ),
257
- undefined,
258
- undefined,
259
- [],
260
- ),
261
- ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
262
- ts.factory.createStringLiteral("null"),
263
- ),
264
- ],
265
- ),
266
- (i !== arr.length - 1
267
- ? ts.factory.createTemplateMiddle
268
- : ts.factory.createTemplateTail)(s.substring(name.length)),
269
- );
270
- }),
271
- );
272
- };
273
- if (query === undefined && g.query.length === 0) return out(template());
274
-
275
- const block = (expr: ts.Expression) => {
276
- const computeName = (str: string): string =>
277
- g.total.find((p) => p.name === str) !== undefined
278
- ? computeName("_" + str)
279
- : str;
280
- const variables: string = computeName("variables");
281
- return ts.factory.createBlock(
282
- [
283
- local(variables)("URLSearchParams")(
284
- ts.factory.createNewExpression(
285
- ts.factory.createIdentifier("URLSearchParams"),
286
- [],
287
- [],
288
- ),
289
- ),
290
- ts.factory.createForOfStatement(
291
- undefined,
292
- ts.factory.createVariableDeclarationList(
293
- [
294
- ts.factory.createVariableDeclaration(
295
- ts.factory.createArrayBindingPattern([
296
- ts.factory.createBindingElement(
297
- undefined,
298
- undefined,
299
- ts.factory.createIdentifier("key"),
300
- undefined,
301
- ),
302
- ts.factory.createBindingElement(
303
- undefined,
304
- undefined,
305
- ts.factory.createIdentifier("value"),
306
- undefined,
307
- ),
308
- ]),
309
- undefined,
310
- undefined,
311
- undefined,
312
- ),
313
- ],
314
- ts.NodeFlags.Const,
315
- ),
316
- ts.factory.createCallExpression(
317
- ts.factory.createIdentifier("Object.entries"),
318
- undefined,
319
- [
320
- ts.factory.createAsExpression(
321
- expr,
322
- TypeFactory.keyword("any"),
323
- ),
324
- ],
325
- ),
326
- ts.factory.createIfStatement(
327
- ts.factory.createStrictEquality(
328
- ts.factory.createIdentifier("undefined"),
329
- ts.factory.createIdentifier("value"),
330
- ),
331
- ts.factory.createContinueStatement(),
332
- ts.factory.createIfStatement(
333
- ts.factory.createCallExpression(
334
- ts.factory.createIdentifier("Array.isArray"),
335
- undefined,
336
- [ts.factory.createIdentifier("value")],
337
- ),
338
- ts.factory.createExpressionStatement(
339
- ts.factory.createCallExpression(
340
- ts.factory.createPropertyAccessExpression(
341
- ts.factory.createIdentifier("value"),
342
- ts.factory.createIdentifier("forEach"),
343
- ),
344
- undefined,
345
- [
346
- ts.factory.createArrowFunction(
347
- undefined,
348
- undefined,
349
- [IdentifierFactory.parameter("elem")],
350
- undefined,
351
- undefined,
352
- ts.factory.createCallExpression(
353
- IdentifierFactory.access(
354
- ts.factory.createIdentifier(variables),
355
- "append",
356
- ),
357
- undefined,
358
- [
359
- ts.factory.createIdentifier("key"),
360
- ts.factory.createCallExpression(
361
- ts.factory.createIdentifier("String"),
362
- undefined,
363
- [ts.factory.createIdentifier("elem")],
364
- ),
365
- ],
366
- ),
367
- ),
368
- ],
369
- ),
370
- ),
371
- ts.factory.createExpressionStatement(
372
- ts.factory.createCallExpression(
373
- IdentifierFactory.access(
374
- ts.factory.createIdentifier(variables),
375
- "set",
376
- ),
377
- undefined,
378
- [
379
- ts.factory.createIdentifier("key"),
380
- ts.factory.createCallExpression(
381
- ts.factory.createIdentifier("String"),
382
- undefined,
383
- [ts.factory.createIdentifier("value")],
384
- ),
385
- ],
386
- ),
387
- ),
388
- ),
389
- ),
390
- ),
391
- local("location")("string")(template()),
392
- ts.factory.createReturnStatement(
393
- ts.factory.createConditionalExpression(
394
- ts.factory.createStrictEquality(
395
- ExpressionFactory.number(0),
396
- IdentifierFactory.access(
397
- ts.factory.createIdentifier(variables),
398
- "size",
399
- ),
400
- ),
401
- undefined,
402
- ts.factory.createIdentifier("location"),
403
- undefined,
404
- ts.factory.createTemplateExpression(
405
- ts.factory.createTemplateHead(""),
406
- [
407
- ts.factory.createTemplateSpan(
408
- ts.factory.createIdentifier("location"),
409
- ts.factory.createTemplateMiddle("?"),
410
- ),
411
- ts.factory.createTemplateSpan(
412
- ts.factory.createCallExpression(
413
- IdentifierFactory.access(
414
- ts.factory.createIdentifier(variables),
415
- "toString",
416
- ),
417
- undefined,
418
- undefined,
419
- ),
420
- ts.factory.createTemplateTail(""),
421
- ),
422
- ],
423
- ),
424
- ),
425
- ),
426
- ],
427
- true,
428
- );
429
- };
430
- if (query !== undefined && g.query.length === 0)
431
- return out(
432
- block(
433
- query.metadata.isRequired() === false
434
- ? ts.factory.createBinaryExpression(
435
- ts.factory.createIdentifier(query.name),
436
- ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
437
- ts.factory.createObjectLiteralExpression([], false),
438
- )
439
- : ts.factory.createIdentifier(query.name),
440
- ),
441
- );
442
- return out(
443
- block(
444
- ts.factory.createObjectLiteralExpression(
445
- [
446
- ...(query
447
- ? [
448
- ts.factory.createSpreadAssignment(
449
- ts.factory.createIdentifier(query.name),
450
- ),
451
- ]
452
- : []),
453
- ...g.query.map((q) =>
454
- q.name === q.field
455
- ? ts.factory.createShorthandPropertyAssignment(q.name)
456
- : ts.factory.createPropertyAssignment(
457
- Escaper.variable(q.field!)
458
- ? q.field!
459
- : ts.factory.createStringLiteral(q.field!),
460
- ts.factory.createIdentifier(q.name),
461
- ),
462
- ),
463
- ],
464
- true,
465
- ),
466
- ),
467
- );
468
- };
469
-
470
- const write_stringify =
471
- (project: INestiaProject) =>
472
- (importer: ImportDictionary): ts.VariableStatement =>
473
- constant("stringify")(
474
- ts.factory.createArrowFunction(
475
- [],
476
- undefined,
477
- [
478
- IdentifierFactory.parameter(
479
- "input",
480
- ts.factory.createTypeReferenceNode("Input"),
481
- ),
482
- ],
483
- undefined,
484
- undefined,
485
- ts.factory.createCallExpression(
486
- IdentifierFactory.access(
487
- IdentifierFactory.access(
488
- ts.factory.createIdentifier(SdkImportWizard.typia(importer)),
489
- "json",
490
- ),
491
- project.config.assert ? "stringify" : "assertStringify",
492
- ),
493
- undefined,
494
- [ts.factory.createIdentifier("input")],
495
- ),
496
- ),
497
- );
498
- }
499
-
500
- const local = (name: string) => (type: string) => (expression: ts.Expression) =>
501
- ts.factory.createVariableStatement(
502
- [],
503
- ts.factory.createVariableDeclarationList(
504
- [
505
- ts.factory.createVariableDeclaration(
506
- name,
507
- undefined,
508
- ts.factory.createTypeReferenceNode(type),
509
- expression,
510
- ),
511
- ],
512
- ts.NodeFlags.Const,
513
- ),
514
- );
515
- const constant = (name: string) => (expression: ts.Expression) =>
516
- ts.factory.createVariableStatement(
517
- [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
518
- ts.factory.createVariableDeclarationList(
519
- [
520
- ts.factory.createVariableDeclaration(
521
- name,
522
- undefined,
523
- undefined,
524
- expression,
525
- ),
526
- ],
527
- ts.NodeFlags.Const,
528
- ),
529
- );
1
+ import ts from "typescript";
2
+ import { ExpressionFactory } from "typia/lib/factories/ExpressionFactory";
3
+ import { IdentifierFactory } from "typia/lib/factories/IdentifierFactory";
4
+ import { LiteralFactory } from "typia/lib/factories/LiteralFactory";
5
+ import { TypeFactory } from "typia/lib/factories/TypeFactory";
6
+ import { Escaper } from "typia/lib/utils/Escaper";
7
+
8
+ import { INestiaProject } from "../../structures/INestiaProject";
9
+ import { ITypedHttpRoute } from "../../structures/ITypedHttpRoute";
10
+ import { ITypedHttpRouteParameter } from "../../structures/ITypedHttpRouteParameter";
11
+ import { FilePrinter } from "./FilePrinter";
12
+ import { ImportDictionary } from "./ImportDictionary";
13
+ import { SdkAliasCollection } from "./SdkAliasCollection";
14
+ import { SdkHttpSimulationProgrammer } from "./SdkHttpSimulationProgrammer";
15
+ import { SdkImportWizard } from "./SdkImportWizard";
16
+
17
+ export namespace SdkHttpNamespaceProgrammer {
18
+ export const write =
19
+ (project: INestiaProject) =>
20
+ (importer: ImportDictionary) =>
21
+ (
22
+ route: ITypedHttpRoute,
23
+ props: {
24
+ headers: ITypedHttpRouteParameter.IHeaders | undefined;
25
+ query: ITypedHttpRouteParameter.IQuery | undefined;
26
+ input: ITypedHttpRouteParameter.IBody | undefined;
27
+ },
28
+ ): ts.ModuleDeclaration => {
29
+ const types = write_types(project)(importer)(route, props);
30
+ return ts.factory.createModuleDeclaration(
31
+ [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
32
+ ts.factory.createIdentifier(route.name),
33
+ ts.factory.createModuleBlock([
34
+ ...types,
35
+ ...(types.length ? [FilePrinter.enter()] : []),
36
+ write_metadata(project)(importer)(route, props),
37
+ FilePrinter.enter(),
38
+ write_path(project)(importer)(route, props.query),
39
+ ...(project.config.simulate
40
+ ? [
41
+ SdkHttpSimulationProgrammer.random(project)(importer)(route),
42
+ SdkHttpSimulationProgrammer.simulate(project)(importer)(
43
+ route,
44
+ props,
45
+ ),
46
+ ]
47
+ : []),
48
+ ...(project.config.json &&
49
+ props.input !== undefined &&
50
+ (props.input.contentType === "application/json" ||
51
+ props.input.encrypted === true)
52
+ ? [write_stringify(project)(importer)]
53
+ : []),
54
+ ]),
55
+ ts.NodeFlags.Namespace,
56
+ );
57
+ };
58
+
59
+ const write_types =
60
+ (project: INestiaProject) =>
61
+ (importer: ImportDictionary) =>
62
+ (
63
+ route: ITypedHttpRoute,
64
+ props: {
65
+ headers: ITypedHttpRouteParameter.IHeaders | undefined;
66
+ query: ITypedHttpRouteParameter.IQuery | undefined;
67
+ input: ITypedHttpRouteParameter.IBody | undefined;
68
+ },
69
+ ): ts.TypeAliasDeclaration[] => {
70
+ const array: ts.TypeAliasDeclaration[] = [];
71
+ const declare = (name: string, type: ts.TypeNode) =>
72
+ array.push(
73
+ ts.factory.createTypeAliasDeclaration(
74
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
75
+ name,
76
+ undefined,
77
+ type,
78
+ ),
79
+ );
80
+ if (props.headers !== undefined)
81
+ declare(
82
+ "Headers",
83
+ SdkAliasCollection.headers(project)(importer)(props.headers),
84
+ );
85
+ if (props.query !== undefined)
86
+ declare(
87
+ "Query",
88
+ SdkAliasCollection.query(project)(importer)(props.query),
89
+ );
90
+ if (props.input !== undefined)
91
+ declare(
92
+ "Input",
93
+ SdkAliasCollection.input(project)(importer)(props.input),
94
+ );
95
+ if (
96
+ project.config.propagate === true ||
97
+ route.success.metadata.size() !== 0
98
+ )
99
+ declare("Output", SdkAliasCollection.output(project)(importer)(route));
100
+ return array;
101
+ };
102
+
103
+ const write_metadata =
104
+ (project: INestiaProject) =>
105
+ (importer: ImportDictionary) =>
106
+ (
107
+ route: ITypedHttpRoute,
108
+ props: {
109
+ headers: ITypedHttpRouteParameter.IHeaders | undefined;
110
+ query: ITypedHttpRouteParameter.IQuery | undefined;
111
+ input: ITypedHttpRouteParameter.IBody | undefined;
112
+ },
113
+ ): ts.VariableStatement =>
114
+ constant("METADATA")(
115
+ ts.factory.createAsExpression(
116
+ ts.factory.createObjectLiteralExpression(
117
+ [
118
+ ts.factory.createPropertyAssignment(
119
+ "method",
120
+ ts.factory.createStringLiteral(route.method),
121
+ ),
122
+ ts.factory.createPropertyAssignment(
123
+ "path",
124
+ ts.factory.createStringLiteral(route.path),
125
+ ),
126
+ ts.factory.createPropertyAssignment(
127
+ "request",
128
+ props.input
129
+ ? LiteralFactory.write(
130
+ props.input !== undefined
131
+ ? {
132
+ type: props.input.contentType,
133
+ encrypted: !!props.input.encrypted,
134
+ }
135
+ : {
136
+ type: "application/json",
137
+ encrypted: false,
138
+ },
139
+ )
140
+ : ts.factory.createNull(),
141
+ ),
142
+ ts.factory.createPropertyAssignment(
143
+ "response",
144
+ route.method !== "HEAD"
145
+ ? LiteralFactory.write({
146
+ type: route.success.contentType,
147
+ encrypted: !!route.success.encrypted,
148
+ })
149
+ : ts.factory.createNull(),
150
+ ),
151
+ ts.factory.createPropertyAssignment(
152
+ "status",
153
+ route.success.status !== null
154
+ ? ExpressionFactory.number(route.success.status)
155
+ : ts.factory.createNull(),
156
+ ),
157
+ ...(route.success.contentType ===
158
+ "application/x-www-form-urlencoded"
159
+ ? [
160
+ ts.factory.createPropertyAssignment(
161
+ "parseQuery",
162
+ ts.factory.createCallExpression(
163
+ ts.factory.createIdentifier(
164
+ `${SdkImportWizard.typia(importer)}.http.createAssertQuery`,
165
+ ),
166
+ [
167
+ project.config.clone === true
168
+ ? SdkAliasCollection.from(project)(importer)(
169
+ route.success.metadata,
170
+ )
171
+ : SdkAliasCollection.name(route.success),
172
+ ],
173
+ undefined,
174
+ ),
175
+ ),
176
+ ]
177
+ : []),
178
+ ],
179
+ true,
180
+ ),
181
+ ts.factory.createTypeReferenceNode(
182
+ ts.factory.createIdentifier("const"),
183
+ ),
184
+ ),
185
+ );
186
+
187
+ const write_path =
188
+ (project: INestiaProject) =>
189
+ (importer: ImportDictionary) =>
190
+ (
191
+ route: ITypedHttpRoute,
192
+ query: ITypedHttpRouteParameter.IQuery | undefined,
193
+ ): ts.VariableStatement => {
194
+ const g = {
195
+ total: [
196
+ ...route.parameters.filter(
197
+ (param) => param.category === "param" || param.category === "query",
198
+ ),
199
+ ],
200
+ query: route.parameters
201
+ .filter((param) => param.category === "query")
202
+ .filter((param) => param.field !== null),
203
+ path: route.parameters.filter((param) => param.category === "param"),
204
+ };
205
+ const out = (body: ts.ConciseBody) =>
206
+ constant("path")(
207
+ ts.factory.createArrowFunction(
208
+ [],
209
+ [],
210
+ g.total.map((p) =>
211
+ IdentifierFactory.parameter(
212
+ p.name,
213
+ p === query
214
+ ? p.metadata.isRequired() === false
215
+ ? ts.factory.createUnionTypeNode([
216
+ ts.factory.createTypeReferenceNode(
217
+ `${route.name}.Query`,
218
+ ),
219
+ ts.factory.createTypeReferenceNode("undefined"),
220
+ ])
221
+ : ts.factory.createTypeReferenceNode(`${route.name}.Query`)
222
+ : project.config.clone === true
223
+ ? SdkAliasCollection.from(project)(importer)(p.metadata)
224
+ : SdkAliasCollection.name(p),
225
+ ),
226
+ ),
227
+ undefined,
228
+ undefined,
229
+ body,
230
+ ),
231
+ );
232
+ if (g.total.length === 0)
233
+ return out(ts.factory.createStringLiteral(route.path));
234
+
235
+ const template = () => {
236
+ const splitted: string[] = route.path.split(":");
237
+ if (splitted.length === 1)
238
+ return ts.factory.createStringLiteral(route.path);
239
+ return ts.factory.createTemplateExpression(
240
+ ts.factory.createTemplateHead(splitted[0]),
241
+ splitted.slice(1).map((s, i, arr) => {
242
+ const name: string = s.split("/")[0];
243
+ return ts.factory.createTemplateSpan(
244
+ ts.factory.createCallExpression(
245
+ ts.factory.createIdentifier("encodeURIComponent"),
246
+ undefined,
247
+ [
248
+ ts.factory.createBinaryExpression(
249
+ ts.factory.createCallChain(
250
+ ts.factory.createPropertyAccessChain(
251
+ ts.factory.createIdentifier(
252
+ g.path.find((p) => p.field === name)!.name,
253
+ ),
254
+ ts.factory.createToken(ts.SyntaxKind.QuestionDotToken),
255
+ "toString",
256
+ ),
257
+ undefined,
258
+ undefined,
259
+ [],
260
+ ),
261
+ ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
262
+ ts.factory.createStringLiteral("null"),
263
+ ),
264
+ ],
265
+ ),
266
+ (i !== arr.length - 1
267
+ ? ts.factory.createTemplateMiddle
268
+ : ts.factory.createTemplateTail)(s.substring(name.length)),
269
+ );
270
+ }),
271
+ );
272
+ };
273
+ if (query === undefined && g.query.length === 0) return out(template());
274
+
275
+ const block = (expr: ts.Expression) => {
276
+ const computeName = (str: string): string =>
277
+ g.total.find((p) => p.name === str) !== undefined
278
+ ? computeName("_" + str)
279
+ : str;
280
+ const variables: string = computeName("variables");
281
+ return ts.factory.createBlock(
282
+ [
283
+ local(variables)("URLSearchParams")(
284
+ ts.factory.createNewExpression(
285
+ ts.factory.createIdentifier("URLSearchParams"),
286
+ [],
287
+ [],
288
+ ),
289
+ ),
290
+ ts.factory.createForOfStatement(
291
+ undefined,
292
+ ts.factory.createVariableDeclarationList(
293
+ [
294
+ ts.factory.createVariableDeclaration(
295
+ ts.factory.createArrayBindingPattern([
296
+ ts.factory.createBindingElement(
297
+ undefined,
298
+ undefined,
299
+ ts.factory.createIdentifier("key"),
300
+ undefined,
301
+ ),
302
+ ts.factory.createBindingElement(
303
+ undefined,
304
+ undefined,
305
+ ts.factory.createIdentifier("value"),
306
+ undefined,
307
+ ),
308
+ ]),
309
+ undefined,
310
+ undefined,
311
+ undefined,
312
+ ),
313
+ ],
314
+ ts.NodeFlags.Const,
315
+ ),
316
+ ts.factory.createCallExpression(
317
+ ts.factory.createIdentifier("Object.entries"),
318
+ undefined,
319
+ [
320
+ ts.factory.createAsExpression(
321
+ expr,
322
+ TypeFactory.keyword("any"),
323
+ ),
324
+ ],
325
+ ),
326
+ ts.factory.createIfStatement(
327
+ ts.factory.createStrictEquality(
328
+ ts.factory.createIdentifier("undefined"),
329
+ ts.factory.createIdentifier("value"),
330
+ ),
331
+ ts.factory.createContinueStatement(),
332
+ ts.factory.createIfStatement(
333
+ ts.factory.createCallExpression(
334
+ ts.factory.createIdentifier("Array.isArray"),
335
+ undefined,
336
+ [ts.factory.createIdentifier("value")],
337
+ ),
338
+ ts.factory.createExpressionStatement(
339
+ ts.factory.createCallExpression(
340
+ ts.factory.createPropertyAccessExpression(
341
+ ts.factory.createIdentifier("value"),
342
+ ts.factory.createIdentifier("forEach"),
343
+ ),
344
+ undefined,
345
+ [
346
+ ts.factory.createArrowFunction(
347
+ undefined,
348
+ undefined,
349
+ [IdentifierFactory.parameter("elem")],
350
+ undefined,
351
+ undefined,
352
+ ts.factory.createCallExpression(
353
+ IdentifierFactory.access(
354
+ ts.factory.createIdentifier(variables),
355
+ "append",
356
+ ),
357
+ undefined,
358
+ [
359
+ ts.factory.createIdentifier("key"),
360
+ ts.factory.createCallExpression(
361
+ ts.factory.createIdentifier("String"),
362
+ undefined,
363
+ [ts.factory.createIdentifier("elem")],
364
+ ),
365
+ ],
366
+ ),
367
+ ),
368
+ ],
369
+ ),
370
+ ),
371
+ ts.factory.createExpressionStatement(
372
+ ts.factory.createCallExpression(
373
+ IdentifierFactory.access(
374
+ ts.factory.createIdentifier(variables),
375
+ "set",
376
+ ),
377
+ undefined,
378
+ [
379
+ ts.factory.createIdentifier("key"),
380
+ ts.factory.createCallExpression(
381
+ ts.factory.createIdentifier("String"),
382
+ undefined,
383
+ [ts.factory.createIdentifier("value")],
384
+ ),
385
+ ],
386
+ ),
387
+ ),
388
+ ),
389
+ ),
390
+ ),
391
+ local("location")("string")(template()),
392
+ ts.factory.createReturnStatement(
393
+ ts.factory.createConditionalExpression(
394
+ ts.factory.createStrictEquality(
395
+ ExpressionFactory.number(0),
396
+ IdentifierFactory.access(
397
+ ts.factory.createIdentifier(variables),
398
+ "size",
399
+ ),
400
+ ),
401
+ undefined,
402
+ ts.factory.createIdentifier("location"),
403
+ undefined,
404
+ ts.factory.createTemplateExpression(
405
+ ts.factory.createTemplateHead(""),
406
+ [
407
+ ts.factory.createTemplateSpan(
408
+ ts.factory.createIdentifier("location"),
409
+ ts.factory.createTemplateMiddle("?"),
410
+ ),
411
+ ts.factory.createTemplateSpan(
412
+ ts.factory.createCallExpression(
413
+ IdentifierFactory.access(
414
+ ts.factory.createIdentifier(variables),
415
+ "toString",
416
+ ),
417
+ undefined,
418
+ undefined,
419
+ ),
420
+ ts.factory.createTemplateTail(""),
421
+ ),
422
+ ],
423
+ ),
424
+ ),
425
+ ),
426
+ ],
427
+ true,
428
+ );
429
+ };
430
+ if (query !== undefined && g.query.length === 0)
431
+ return out(
432
+ block(
433
+ query.metadata.isRequired() === false
434
+ ? ts.factory.createBinaryExpression(
435
+ ts.factory.createIdentifier(query.name),
436
+ ts.factory.createToken(ts.SyntaxKind.QuestionQuestionToken),
437
+ ts.factory.createObjectLiteralExpression([], false),
438
+ )
439
+ : ts.factory.createIdentifier(query.name),
440
+ ),
441
+ );
442
+ return out(
443
+ block(
444
+ ts.factory.createObjectLiteralExpression(
445
+ [
446
+ ...(query
447
+ ? [
448
+ ts.factory.createSpreadAssignment(
449
+ ts.factory.createIdentifier(query.name),
450
+ ),
451
+ ]
452
+ : []),
453
+ ...g.query.map((q) =>
454
+ q.name === q.field
455
+ ? ts.factory.createShorthandPropertyAssignment(q.name)
456
+ : ts.factory.createPropertyAssignment(
457
+ Escaper.variable(q.field!)
458
+ ? q.field!
459
+ : ts.factory.createStringLiteral(q.field!),
460
+ ts.factory.createIdentifier(q.name),
461
+ ),
462
+ ),
463
+ ],
464
+ true,
465
+ ),
466
+ ),
467
+ );
468
+ };
469
+
470
+ const write_stringify =
471
+ (project: INestiaProject) =>
472
+ (importer: ImportDictionary): ts.VariableStatement =>
473
+ constant("stringify")(
474
+ ts.factory.createArrowFunction(
475
+ [],
476
+ undefined,
477
+ [
478
+ IdentifierFactory.parameter(
479
+ "input",
480
+ ts.factory.createTypeReferenceNode("Input"),
481
+ ),
482
+ ],
483
+ undefined,
484
+ undefined,
485
+ ts.factory.createCallExpression(
486
+ IdentifierFactory.access(
487
+ IdentifierFactory.access(
488
+ ts.factory.createIdentifier(SdkImportWizard.typia(importer)),
489
+ "json",
490
+ ),
491
+ project.config.assert ? "stringify" : "assertStringify",
492
+ ),
493
+ undefined,
494
+ [ts.factory.createIdentifier("input")],
495
+ ),
496
+ ),
497
+ );
498
+ }
499
+
500
+ const local = (name: string) => (type: string) => (expression: ts.Expression) =>
501
+ ts.factory.createVariableStatement(
502
+ [],
503
+ ts.factory.createVariableDeclarationList(
504
+ [
505
+ ts.factory.createVariableDeclaration(
506
+ name,
507
+ undefined,
508
+ ts.factory.createTypeReferenceNode(type),
509
+ expression,
510
+ ),
511
+ ],
512
+ ts.NodeFlags.Const,
513
+ ),
514
+ );
515
+ const constant = (name: string) => (expression: ts.Expression) =>
516
+ ts.factory.createVariableStatement(
517
+ [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)],
518
+ ts.factory.createVariableDeclarationList(
519
+ [
520
+ ts.factory.createVariableDeclaration(
521
+ name,
522
+ undefined,
523
+ undefined,
524
+ expression,
525
+ ),
526
+ ],
527
+ ts.NodeFlags.Const,
528
+ ),
529
+ );