@nestia/sdk 2.5.0-dev.20240130-7 → 2.5.0-dev.20240201

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 (45) hide show
  1. package/lib/NestiaSdkApplication.js +10 -8
  2. package/lib/NestiaSdkApplication.js.map +1 -1
  3. package/lib/analyses/ControllerAnalyzer.js +7 -2
  4. package/lib/analyses/ControllerAnalyzer.js.map +1 -1
  5. package/lib/analyses/ExceptionAnalyzer.js +3 -1
  6. package/lib/analyses/ExceptionAnalyzer.js.map +1 -1
  7. package/lib/generates/CloneGenerator.js +2 -0
  8. package/lib/generates/CloneGenerator.js.map +1 -1
  9. package/lib/generates/SdkGenerator.js +0 -8
  10. package/lib/generates/SdkGenerator.js.map +1 -1
  11. package/lib/generates/internal/E2eFileProgrammer.js +3 -1
  12. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  13. package/lib/generates/internal/SdkAliasCollection.js +5 -4
  14. package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
  15. package/lib/generates/internal/SdkCloneProgrammer.js +4 -1
  16. package/lib/generates/internal/SdkCloneProgrammer.js.map +1 -1
  17. package/lib/generates/internal/SdkFileProgrammer.js +0 -7
  18. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  19. package/lib/generates/internal/SdkFunctionProgrammer.d.ts +1 -1
  20. package/lib/generates/internal/SdkFunctionProgrammer.js +3 -3
  21. package/lib/generates/internal/SdkFunctionProgrammer.js.map +1 -1
  22. package/lib/generates/internal/SdkNamespaceProgrammer.js +1 -1
  23. package/lib/generates/internal/SdkNamespaceProgrammer.js.map +1 -1
  24. package/lib/generates/internal/SdkRouteProgrammer.js +2 -2
  25. package/lib/generates/internal/SdkRouteProgrammer.js.map +1 -1
  26. package/lib/generates/internal/SdkSimulationProgrammer.js +3 -3
  27. package/lib/generates/internal/SdkSimulationProgrammer.js.map +1 -1
  28. package/lib/generates/internal/SdkTypeProgrammer.js +4 -1
  29. package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -1
  30. package/package.json +3 -3
  31. package/src/NestiaSdkApplication.ts +15 -13
  32. package/src/analyses/ControllerAnalyzer.ts +399 -390
  33. package/src/analyses/ExceptionAnalyzer.ts +119 -115
  34. package/src/generates/CloneGenerator.ts +1 -0
  35. package/src/generates/SdkGenerator.ts +0 -12
  36. package/src/generates/internal/E2eFileProgrammer.ts +5 -1
  37. package/src/generates/internal/SdkAliasCollection.ts +4 -3
  38. package/src/generates/internal/SdkCloneProgrammer.ts +6 -1
  39. package/src/generates/internal/SdkFileProgrammer.ts +0 -9
  40. package/src/generates/internal/SdkFunctionProgrammer.ts +3 -3
  41. package/src/generates/internal/SdkNamespaceProgrammer.ts +1 -1
  42. package/src/generates/internal/SdkRouteProgrammer.ts +2 -2
  43. package/src/generates/internal/SdkSimulationProgrammer.ts +3 -3
  44. package/src/generates/internal/SdkTypeProgrammer.ts +6 -1
  45. package/assets/bundle/api/utils/NestiaSimulator.ts +0 -70
@@ -1,115 +1,119 @@
1
- import path from "path";
2
- import ts from "typescript";
3
-
4
- import { IController } from "../structures/IController";
5
- import { INestiaProject } from "../structures/INestiaProject";
6
- import { IRoute } from "../structures/IRoute";
7
- import { ITypeTuple } from "../structures/ITypeTuple";
8
- import { GenericAnalyzer } from "./GenericAnalyzer";
9
- import { ImportAnalyzer } from "./ImportAnalyzer";
10
-
11
- export namespace ExceptionAnalyzer {
12
- export const analyze =
13
- (project: INestiaProject) =>
14
- (
15
- genericDict: GenericAnalyzer.Dictionary,
16
- importDict: ImportAnalyzer.Dictionary,
17
- ) =>
18
- (controller: IController, func: IController.IFunction) =>
19
- (
20
- declaration: ts.MethodDeclaration,
21
- ): Record<number | "2XX" | "3XX" | "4XX" | "5XX", IRoute.IOutput> => {
22
- const output: Record<
23
- number | "2XX" | "3XX" | "4XX" | "5XX",
24
- IRoute.IOutput
25
- > = {} as any;
26
- for (const decorator of declaration.modifiers ?? [])
27
- if (ts.isDecorator(decorator))
28
- analyzeTyped(project)(genericDict, importDict)(controller, func)(
29
- output,
30
- )(decorator);
31
- return output;
32
- };
33
-
34
- const analyzeTyped =
35
- (project: INestiaProject) =>
36
- (
37
- genericDict: GenericAnalyzer.Dictionary,
38
- importDict: ImportAnalyzer.Dictionary,
39
- ) =>
40
- (controller: IController, func: IController.IFunction) =>
41
- (output: Record<number | "2XX" | "3XX" | "4XX" | "5XX", IRoute.IOutput>) =>
42
- (decorator: ts.Decorator): boolean => {
43
- // CHECK DECORATOR
44
- if (!ts.isCallExpression(decorator.expression)) return false;
45
- else if ((decorator.expression.typeArguments ?? []).length !== 1)
46
- return false;
47
-
48
- // CHECK SIGNATURE
49
- const signature: ts.Signature | undefined =
50
- project.checker.getResolvedSignature(decorator.expression);
51
- if (!signature || !signature.declaration) return false;
52
- else if (
53
- path
54
- .resolve(signature.declaration.getSourceFile().fileName)
55
- .indexOf(TYPED_EXCEPTION_PATH) === -1
56
- )
57
- return false;
58
-
59
- // GET TYPE INFO
60
- const node: ts.TypeNode = decorator.expression.typeArguments![0];
61
- const type: ts.Type = project.checker.getTypeFromTypeNode(node);
62
- if (type.isTypeParameter()) {
63
- project.errors.push({
64
- file: controller.file,
65
- controller: controller.name,
66
- function: func.name,
67
- message: "TypedException() without generic argument specification.",
68
- });
69
- return false;
70
- }
71
-
72
- const tuple: ITypeTuple | null = ImportAnalyzer.analyze(
73
- project.checker,
74
- genericDict,
75
- importDict,
76
- type,
77
- );
78
- if (tuple === null || tuple.typeName === "__type") {
79
- project.errors.push({
80
- file: controller.file,
81
- controller: controller.name,
82
- function: func.name,
83
- message: "TypeException() with implicit (unnamed) type.",
84
- });
85
- return false;
86
- }
87
-
88
- // DO ASSIGN
89
- const matched: IController.IException[] = Object.entries(func.exceptions)
90
- .filter(([_key, value]) =>
91
- value.type.includes(" | ") && tuple.typeName.includes(" | ")
92
- ? value.type.split(" | ").sort().join(" | ") ===
93
- tuple.typeName.split(" | ").sort().join(" | ")
94
- : value.type === tuple.typeName,
95
- )
96
- .map(([_key, value]) => value);
97
- for (const m of matched)
98
- output[m.status] = {
99
- type: tuple.type,
100
- typeName: tuple.typeName,
101
- contentType: "application/json",
102
- description: m.description,
103
- };
104
- return true;
105
- };
106
- }
107
-
108
- const TYPED_EXCEPTION_PATH = path.join(
109
- "node_modules",
110
- "@nestia",
111
- "core",
112
- "lib",
113
- "decorators",
114
- "TypedException.d.ts",
115
- );
1
+ import path from "path";
2
+ import ts from "typescript";
3
+
4
+ import { IController } from "../structures/IController";
5
+ import { INestiaProject } from "../structures/INestiaProject";
6
+ import { IRoute } from "../structures/IRoute";
7
+ import { ITypeTuple } from "../structures/ITypeTuple";
8
+ import { GenericAnalyzer } from "./GenericAnalyzer";
9
+ import { ImportAnalyzer } from "./ImportAnalyzer";
10
+
11
+ export namespace ExceptionAnalyzer {
12
+ export const analyze =
13
+ (project: INestiaProject) =>
14
+ (
15
+ genericDict: GenericAnalyzer.Dictionary,
16
+ importDict: ImportAnalyzer.Dictionary,
17
+ ) =>
18
+ (controller: IController, func: IController.IFunction) =>
19
+ (
20
+ declaration: ts.MethodDeclaration,
21
+ ): Record<number | "2XX" | "3XX" | "4XX" | "5XX", IRoute.IOutput> => {
22
+ const output: Record<
23
+ number | "2XX" | "3XX" | "4XX" | "5XX",
24
+ IRoute.IOutput
25
+ > = {} as any;
26
+ for (const decorator of declaration.modifiers ?? [])
27
+ if (ts.isDecorator(decorator))
28
+ analyzeTyped(project)(genericDict, importDict)(controller, func)(
29
+ output,
30
+ )(decorator);
31
+ return output;
32
+ };
33
+
34
+ const analyzeTyped =
35
+ (project: INestiaProject) =>
36
+ (
37
+ genericDict: GenericAnalyzer.Dictionary,
38
+ importDict: ImportAnalyzer.Dictionary,
39
+ ) =>
40
+ (controller: IController, func: IController.IFunction) =>
41
+ (output: Record<number | "2XX" | "3XX" | "4XX" | "5XX", IRoute.IOutput>) =>
42
+ (decorator: ts.Decorator): boolean => {
43
+ // CHECK DECORATOR
44
+ if (!ts.isCallExpression(decorator.expression)) return false;
45
+ else if ((decorator.expression.typeArguments ?? []).length !== 1)
46
+ return false;
47
+
48
+ // CHECK SIGNATURE
49
+ const signature: ts.Signature | undefined =
50
+ project.checker.getResolvedSignature(decorator.expression);
51
+ if (!signature || !signature.declaration) return false;
52
+ else if (
53
+ path
54
+ .resolve(signature.declaration.getSourceFile().fileName)
55
+ .indexOf(TYPED_EXCEPTION_PATH) === -1
56
+ )
57
+ return false;
58
+
59
+ // GET TYPE INFO
60
+ const node: ts.TypeNode = decorator.expression.typeArguments![0];
61
+ const type: ts.Type = project.checker.getTypeFromTypeNode(node);
62
+ if (type.isTypeParameter()) {
63
+ project.errors.push({
64
+ file: controller.file,
65
+ controller: controller.name,
66
+ function: func.name,
67
+ message: "TypedException() without generic argument specification.",
68
+ });
69
+ return false;
70
+ }
71
+
72
+ const tuple: ITypeTuple | null = ImportAnalyzer.analyze(
73
+ project.checker,
74
+ genericDict,
75
+ importDict,
76
+ type,
77
+ );
78
+ if (
79
+ tuple === null ||
80
+ (project.config.clone !== true &&
81
+ (tuple.typeName === "__type" || tuple.typeName === "__object"))
82
+ ) {
83
+ project.errors.push({
84
+ file: controller.file,
85
+ controller: controller.name,
86
+ function: func.name,
87
+ message: "TypeException() with implicit (unnamed) type.",
88
+ });
89
+ return false;
90
+ }
91
+
92
+ // DO ASSIGN
93
+ const matched: IController.IException[] = Object.entries(func.exceptions)
94
+ .filter(([_key, value]) =>
95
+ value.type.includes(" | ") && tuple.typeName.includes(" | ")
96
+ ? value.type.split(" | ").sort().join(" | ") ===
97
+ tuple.typeName.split(" | ").sort().join(" | ")
98
+ : value.type === tuple.typeName,
99
+ )
100
+ .map(([_key, value]) => value);
101
+ for (const m of matched)
102
+ output[m.status] = {
103
+ type: tuple.type,
104
+ typeName: tuple.typeName,
105
+ contentType: "application/json",
106
+ description: m.description,
107
+ };
108
+ return true;
109
+ };
110
+ }
111
+
112
+ const TYPED_EXCEPTION_PATH = path.join(
113
+ "node_modules",
114
+ "@nestia",
115
+ "core",
116
+ "lib",
117
+ "decorators",
118
+ "TypedException.d.ts",
119
+ );
@@ -14,6 +14,7 @@ export namespace SdkCloneProgrammer {
14
14
  async (routes: IRoute[]): Promise<void> => {
15
15
  const dict: Map<string, SdkInterfaceProgrammer.IModule> =
16
16
  SdkInterfaceProgrammer.write(checker)(config)(routes);
17
+ if (dict.size === 0) return;
17
18
  try {
18
19
  await fs.promises.mkdir(`${config.output}/structures`);
19
20
  } catch {}
@@ -42,18 +42,6 @@ export namespace SdkGenerator {
42
42
  }
43
43
  }
44
44
  }
45
- if (
46
- config.simulate === true &&
47
- routes.some((r) => !!r.parameters.length)
48
- ) {
49
- try {
50
- await fs.promises.mkdir(`${config.output}/utils`);
51
- } catch {}
52
- await fs.promises.copyFile(
53
- `${BUNDLE_PATH}/utils/NestiaSimulator.ts`,
54
- `${config.output}/utils/NestiaSimulator.ts`,
55
- );
56
- }
57
45
 
58
46
  // STRUCTURES
59
47
  if (config.clone) await SdkCloneProgrammer.write(checker)(config)(routes);
@@ -153,7 +153,11 @@ export namespace E2eFileProgrammer {
153
153
  ts.factory.createVariableDeclaration(
154
154
  "output",
155
155
  undefined,
156
- SdkAliasCollection.output(checker)(config)(importer)(route),
156
+ config.propagate !== true && route.output.typeName === "void"
157
+ ? undefined
158
+ : SdkAliasCollection.output(checker)(config)(importer)(
159
+ route,
160
+ ),
157
161
  ts.factory.createAwaitExpression(caller),
158
162
  ),
159
163
  ],
@@ -51,7 +51,7 @@ export namespace SdkAliasCollection {
51
51
  (importer: ImportDictionary) =>
52
52
  (param: IRoute.IParameter): ts.TypeNode => {
53
53
  const type: ts.TypeNode = name(config)(importer)(param);
54
- if (config.primitive === false) return type;
54
+ if (config.clone === true || config.primitive === false) return type;
55
55
  return ts.factory.createTypeReferenceNode(
56
56
  importer.external({
57
57
  type: true,
@@ -73,11 +73,12 @@ export namespace SdkAliasCollection {
73
73
  const filter = (flag: ts.TypeFlags) => (type.getFlags() & flag) !== 0;
74
74
 
75
75
  if (
76
+ config.clone === true ||
77
+ config.primitive === false ||
76
78
  filter(ts.TypeFlags.Undefined) ||
77
79
  filter(ts.TypeFlags.Never) ||
78
80
  filter(ts.TypeFlags.Void) ||
79
- filter(ts.TypeFlags.VoidLike) ||
80
- config.primitive === false
81
+ filter(ts.TypeFlags.VoidLike)
81
82
  )
82
83
  return node;
83
84
  return ts.factory.createTypeReferenceNode(
@@ -61,7 +61,12 @@ export namespace SdkInterfaceProgrammer {
61
61
  write_alias(config)(importer)(alias),
62
62
  );
63
63
  for (const object of collection.objects())
64
- if (object.name !== "__type" && !object.name.startsWith("__type."))
64
+ if (
65
+ object.name !== "__type" &&
66
+ !object.name.startsWith("__type.") &&
67
+ object.name !== "__object" &&
68
+ !object.name.startsWith("__object.")
69
+ )
65
70
  prepare(dict)(object.name)((importer) =>
66
71
  write_object(config)(importer)(object),
67
72
  );
@@ -75,15 +75,6 @@ export namespace SdkFileProgrammer {
75
75
  const importer: ImportDictionary = new ImportDictionary(
76
76
  `${outDir}/index.ts`,
77
77
  );
78
- if (
79
- config.simulate === true &&
80
- directory.routes.some((r) => !!r.parameters.length)
81
- )
82
- importer.internal({
83
- file: `${config.output}/utils/NestiaSimulator.ts`,
84
- instance: "NestiaSimulator",
85
- type: false,
86
- });
87
78
  directory.routes.forEach((route, i) => {
88
79
  if (config.clone !== true)
89
80
  for (const tuple of route.imports)
@@ -10,7 +10,7 @@ import { SdkImportWizard } from "./SdkImportWizard";
10
10
  import { SdkTypeProgrammer } from "./SdkTypeProgrammer";
11
11
 
12
12
  export namespace SdkFunctionProgrammer {
13
- export const generate =
13
+ export const write =
14
14
  (config: INestiaConfig) =>
15
15
  (importer: ImportDictionary) =>
16
16
  (
@@ -66,13 +66,13 @@ export namespace SdkFunctionProgrammer {
66
66
  ),
67
67
  ]),
68
68
  ts.factory.createBlock(
69
- generate_body(config)(importer)(route, props),
69
+ write_body(config)(importer)(route, props),
70
70
  true,
71
71
  ),
72
72
  );
73
73
  };
74
74
 
75
- const generate_body =
75
+ const write_body =
76
76
  (config: INestiaConfig) =>
77
77
  (importer: ImportDictionary) =>
78
78
  (
@@ -9,12 +9,12 @@ import { Escaper } from "typia/lib/utils/Escaper";
9
9
  import { INestiaConfig } from "../../INestiaConfig";
10
10
  import { IController } from "../../structures/IController";
11
11
  import { IRoute } from "../../structures/IRoute";
12
+ import { FilePrinter } from "./FilePrinter";
12
13
  import { ImportDictionary } from "./ImportDictionary";
13
14
  import { SdkAliasCollection } from "./SdkAliasCollection";
14
15
  import { SdkImportWizard } from "./SdkImportWizard";
15
16
  import { SdkSimulationProgrammer } from "./SdkSimulationProgrammer";
16
17
  import { SdkTypeProgrammer } from "./SdkTypeProgrammer";
17
- import { FilePrinter } from "./FilePrinter";
18
18
 
19
19
  export namespace SdkNamespaceProgrammer {
20
20
  export const write =
@@ -3,10 +3,10 @@ import { IJsDocTagInfo } from "typia";
3
3
 
4
4
  import { INestiaConfig } from "../../INestiaConfig";
5
5
  import { IRoute } from "../../structures/IRoute";
6
+ import { FilePrinter } from "./FilePrinter";
6
7
  import { ImportDictionary } from "./ImportDictionary";
7
8
  import { SdkFunctionProgrammer } from "./SdkFunctionProgrammer";
8
9
  import { SdkNamespaceProgrammer } from "./SdkNamespaceProgrammer";
9
- import { FilePrinter } from "./FilePrinter";
10
10
 
11
11
  export namespace SdkRouteProgrammer {
12
12
  export const generate =
@@ -25,7 +25,7 @@ export namespace SdkRouteProgrammer {
25
25
  };
26
26
  return [
27
27
  FilePrinter.description(
28
- SdkFunctionProgrammer.generate(config)(importer)(route, props),
28
+ SdkFunctionProgrammer.write(config)(importer)(route, props),
29
29
  describe(route),
30
30
  ),
31
31
  SdkNamespaceProgrammer.write(checker)(config)(importer)(route, props),
@@ -182,10 +182,10 @@ export namespace SdkSimulationProgrammer {
182
182
  ts.factory.createCallExpression(
183
183
  IdentifierFactory.access(
184
184
  ts.factory.createIdentifier(
185
- importer.internal({
186
- file: `${config.output}/utils/NestiaSimulator.ts`,
187
- instance: "NestiaSimulator",
185
+ importer.external({
188
186
  type: false,
187
+ library: `@nestia/fetcher/lib/NestiaSimulator`,
188
+ instance: "NestiaSimulator",
189
189
  }),
190
190
  ),
191
191
  )("assert"),
@@ -47,7 +47,12 @@ export namespace SdkTypeProgrammer {
47
47
  for (const array of meta.arrays)
48
48
  union.push(write_array(config)(importer)(array));
49
49
  for (const object of meta.objects)
50
- if (object.name === "__type" || object.name.startsWith("__type."))
50
+ if (
51
+ object.name === "__type" ||
52
+ object.name.startsWith("__type.") ||
53
+ object.name === "__object" ||
54
+ object.name.startsWith("__object.")
55
+ )
51
56
  union.push(write_object(config)(importer)(object));
52
57
  else union.push(write_alias(config)(importer)(object));
53
58
  for (const alias of meta.aliases)
@@ -1,70 +0,0 @@
1
- import { HttpError } from "@nestia/fetcher";
2
-
3
- import typia from "typia";
4
-
5
- export namespace NestiaSimulator {
6
- export interface IProps {
7
- host: string;
8
- path: string;
9
- method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
10
- contentType: string;
11
- }
12
-
13
- export const assert = (props: IProps) => {
14
- return {
15
- param: param(props),
16
- query: query(props),
17
- body: body(props),
18
- };
19
- };
20
- const param =
21
- (props: IProps) =>
22
- (name: string) =>
23
- <T>(task: () => T): void => {
24
- validate(
25
- (exp) => `URL parameter "${name}" is not ${exp.expected} type.`,
26
- )(props)(task);
27
- };
28
-
29
- const query =
30
- (props: IProps) =>
31
- <T>(task: () => T): void =>
32
- validate(
33
- () =>
34
- "Request query parameters are not following the promised type.",
35
- )(props)(task);
36
-
37
- const body =
38
- (props: IProps) =>
39
- <T>(task: () => T): void =>
40
- validate(() => "Request body is not following the promised type.")(
41
- props,
42
- )(task);
43
-
44
- const validate =
45
- (message: (exp: typia.TypeGuardError) => string, path?: string) =>
46
- (props: IProps) =>
47
- <T>(task: () => T): void => {
48
- try {
49
- task();
50
- } catch (exp) {
51
- if (typia.is<typia.TypeGuardError>(exp))
52
- throw new HttpError(
53
- props.method,
54
- props.host + props.path,
55
- 400,
56
- {
57
- "Content-Type": props.contentType,
58
- },
59
- JSON.stringify({
60
- method: exp.method,
61
- path: path ?? exp.path,
62
- expected: exp.expected,
63
- value: exp.value,
64
- message: message(exp),
65
- }),
66
- );
67
- throw exp;
68
- }
69
- };
70
- }