@nestia/sdk 9.0.0-dev.20251107-2 → 9.0.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 (113) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +93 -93
  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/internal/NestiaSdkCommand.js +1 -1
  16. package/lib/executable/sdk.js +12 -12
  17. package/package.json +4 -4
  18. package/src/INestiaConfig.ts +267 -267
  19. package/src/NestiaSdkApplication.ts +307 -307
  20. package/src/NestiaSwaggerComposer.ts +138 -138
  21. package/src/analyses/AccessorAnalyzer.ts +67 -67
  22. package/src/analyses/ConfigAnalyzer.ts +155 -155
  23. package/src/analyses/DtoAnalyzer.ts +246 -246
  24. package/src/analyses/ExceptionAnalyzer.ts +154 -154
  25. package/src/analyses/GenericAnalyzer.ts +49 -49
  26. package/src/analyses/ImportAnalyzer.ts +126 -126
  27. package/src/analyses/PathAnalyzer.ts +69 -69
  28. package/src/analyses/ReflectControllerAnalyzer.ts +105 -105
  29. package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
  30. package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +71 -71
  31. package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +348 -348
  32. package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +127 -127
  33. package/src/analyses/ReflectMetadataAnalyzer.ts +44 -44
  34. package/src/analyses/ReflectWebSocketOperationAnalyzer.ts +172 -172
  35. package/src/analyses/SecurityAnalyzer.ts +25 -25
  36. package/src/analyses/TypedHttpRouteAnalyzer.ts +204 -204
  37. package/src/analyses/TypedWebSocketRouteAnalyzer.ts +33 -33
  38. package/src/decorators/OperationMetadata.ts +15 -15
  39. package/src/executable/internal/CommandParser.ts +15 -15
  40. package/src/executable/internal/NestiaConfigLoader.ts +78 -78
  41. package/src/executable/internal/NestiaSdkCommand.ts +103 -103
  42. package/src/executable/sdk.ts +75 -75
  43. package/src/generates/CloneGenerator.ts +66 -66
  44. package/src/generates/E2eGenerator.ts +32 -32
  45. package/src/generates/SdkGenerator.ts +160 -160
  46. package/src/generates/SwaggerGenerator.ts +284 -284
  47. package/src/generates/internal/E2eFileProgrammer.ts +197 -197
  48. package/src/generates/internal/FilePrinter.ts +53 -53
  49. package/src/generates/internal/ImportDictionary.ts +192 -192
  50. package/src/generates/internal/SdkAliasCollection.ts +261 -261
  51. package/src/generates/internal/SdkDistributionComposer.ts +103 -103
  52. package/src/generates/internal/SdkFileProgrammer.ts +110 -110
  53. package/src/generates/internal/SdkHttpCloneProgrammer.ts +124 -124
  54. package/src/generates/internal/SdkHttpCloneReferencer.ts +77 -77
  55. package/src/generates/internal/SdkHttpFunctionProgrammer.ts +279 -279
  56. package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +500 -500
  57. package/src/generates/internal/SdkHttpParameterProgrammer.ts +178 -178
  58. package/src/generates/internal/SdkHttpRouteProgrammer.ts +108 -108
  59. package/src/generates/internal/SdkHttpSimulationProgrammer.ts +310 -310
  60. package/src/generates/internal/SdkImportWizard.ts +62 -62
  61. package/src/generates/internal/SdkRouteDirectory.ts +18 -18
  62. package/src/generates/internal/SdkTypeProgrammer.ts +385 -385
  63. package/src/generates/internal/SdkTypeTagProgrammer.ts +104 -104
  64. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +381 -381
  65. package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +87 -87
  66. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +302 -302
  67. package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
  68. package/src/generates/internal/SwaggerOperationComposer.ts +119 -119
  69. package/src/generates/internal/SwaggerOperationParameterComposer.ts +177 -177
  70. package/src/generates/internal/SwaggerOperationResponseComposer.ts +110 -110
  71. package/src/index.ts +4 -4
  72. package/src/module.ts +3 -3
  73. package/src/structures/INestiaProject.ts +13 -13
  74. package/src/structures/INestiaSdkInput.ts +20 -20
  75. package/src/structures/IReflectApplication.ts +8 -8
  76. package/src/structures/IReflectController.ts +15 -15
  77. package/src/structures/IReflectHttpOperation.ts +26 -26
  78. package/src/structures/IReflectHttpOperationException.ts +19 -19
  79. package/src/structures/IReflectHttpOperationParameter.ts +77 -77
  80. package/src/structures/IReflectHttpOperationSuccess.ts +22 -22
  81. package/src/structures/IReflectImport.ts +6 -6
  82. package/src/structures/IReflectOperationError.ts +26 -26
  83. package/src/structures/IReflectType.ts +4 -4
  84. package/src/structures/IReflectWebSocketOperation.ts +17 -17
  85. package/src/structures/IReflectWebSocketOperationParameter.ts +36 -36
  86. package/src/structures/ITypedApplication.ts +11 -11
  87. package/src/structures/ITypedHttpRoute.ts +41 -41
  88. package/src/structures/ITypedHttpRouteException.ts +15 -15
  89. package/src/structures/ITypedHttpRouteParameter.ts +41 -41
  90. package/src/structures/ITypedHttpRouteSuccess.ts +22 -22
  91. package/src/structures/ITypedWebSocketRoute.ts +24 -24
  92. package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
  93. package/src/structures/MethodType.ts +5 -5
  94. package/src/structures/ParamCategory.ts +1 -1
  95. package/src/structures/TypeEntry.ts +22 -22
  96. package/src/transform.ts +9 -9
  97. package/src/transformers/IOperationMetadata.ts +44 -44
  98. package/src/transformers/ISdkOperationTransformerContext.ts +8 -8
  99. package/src/transformers/SdkOperationProgrammer.ts +238 -238
  100. package/src/transformers/SdkOperationTransformer.ts +252 -252
  101. package/src/transformers/TextPlainValidator.ts +17 -17
  102. package/src/typings/get-function-location.d.ts +7 -7
  103. package/src/utils/ArrayUtil.ts +26 -26
  104. package/src/utils/FileRetriever.ts +22 -22
  105. package/src/utils/MapUtil.ts +14 -14
  106. package/src/utils/MetadataUtil.ts +26 -26
  107. package/src/utils/PathUtil.ts +10 -10
  108. package/src/utils/SourceFinder.ts +63 -63
  109. package/src/utils/StringUtil.ts +17 -17
  110. package/src/utils/StripEnums.ts +5 -5
  111. package/src/utils/VersioningStrategy.ts +28 -28
  112. package/src/validators/HttpHeadersValidator.ts +34 -34
  113. package/src/validators/HttpQueryValidator.ts +34 -34
@@ -1,138 +1,138 @@
1
- import { INestApplication } from "@nestjs/common";
2
- import { OpenApi, OpenApiV3, SwaggerV2 } from "@samchon/openapi";
3
- import path from "path";
4
- import { TreeMap } from "tstl";
5
- import { IMetadataDictionary } from "typia/lib/schemas/metadata/IMetadataDictionary";
6
-
7
- import { INestiaConfig } from "./INestiaConfig";
8
- import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer";
9
- import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer";
10
- import { PathAnalyzer } from "./analyses/PathAnalyzer";
11
- import { ReflectControllerAnalyzer } from "./analyses/ReflectControllerAnalyzer";
12
- import { TypedHttpRouteAnalyzer } from "./analyses/TypedHttpRouteAnalyzer";
13
- import { SwaggerGenerator } from "./generates/SwaggerGenerator";
14
- import { INestiaProject } from "./structures/INestiaProject";
15
- import { INestiaSdkInput } from "./structures/INestiaSdkInput";
16
- import { IReflectController } from "./structures/IReflectController";
17
- import { IReflectOperationError } from "./structures/IReflectOperationError";
18
- import { ITypedHttpRoute } from "./structures/ITypedHttpRoute";
19
- import { IOperationMetadata } from "./transformers/IOperationMetadata";
20
- import { VersioningStrategy } from "./utils/VersioningStrategy";
21
-
22
- export namespace NestiaSwaggerComposer {
23
- export const document = async (
24
- app: INestApplication,
25
- config: Omit<INestiaConfig.ISwaggerConfig, "output">,
26
- ): Promise<OpenApi.IDocument | OpenApiV3.IDocument | SwaggerV2.IDocument> => {
27
- const input: INestiaSdkInput = await ConfigAnalyzer.application(app);
28
- const document: OpenApi.IDocument = await SwaggerGenerator.compose({
29
- config,
30
- routes: analyze(input),
31
- document: await SwaggerGenerator.initialize(config),
32
- });
33
- return config.openapi === "2.0"
34
- ? OpenApi.downgrade(document, "2.0")
35
- : config.openapi === "3.0"
36
- ? OpenApi.downgrade(document, "3.0")
37
- : document;
38
- };
39
-
40
- const analyze = (input: INestiaSdkInput): ITypedHttpRoute[] => {
41
- // GET REFLECT CONTROLLERS
42
- const unique: WeakSet<any> = new WeakSet();
43
- const project: Omit<INestiaProject, "config"> = {
44
- input,
45
- checker: null!,
46
- errors: [],
47
- warnings: [],
48
- };
49
- const controllers: IReflectController[] = project.input.controllers
50
- .map((c) =>
51
- ReflectControllerAnalyzer.analyze({ project, controller: c, unique }),
52
- )
53
- .filter((c): c is IReflectController => c !== null);
54
- if (project.errors.length)
55
- throw report({ type: "error", errors: project.errors });
56
-
57
- // METADATA COMPONENTS
58
- const collection: IMetadataDictionary =
59
- TypedHttpRouteAnalyzer.dictionary(controllers);
60
-
61
- // CONVERT TO TYPED OPERATIONS
62
- const globalPrefix: string = project.input.globalPrefix?.prefix ?? "";
63
- const routes: ITypedHttpRoute[] = [];
64
- for (const c of controllers)
65
- for (const o of c.operations) {
66
- const pathList: Set<string> = new Set();
67
- const versions: string[] = VersioningStrategy.merge(project)([
68
- ...(c.versions ?? []),
69
- ...(o.versions ?? []),
70
- ]);
71
- for (const v of versions)
72
- for (const prefix of wrapPaths(c.prefixes))
73
- for (const cPath of wrapPaths(c.paths))
74
- for (const filePath of wrapPaths(o.paths))
75
- pathList.add(
76
- PathAnalyzer.join(globalPrefix, v, prefix, cPath, filePath),
77
- );
78
- if (o.protocol === "http")
79
- routes.push(
80
- ...TypedHttpRouteAnalyzer.analyze({
81
- controller: c,
82
- errors: project.errors,
83
- dictionary: collection,
84
- operation: o,
85
- paths: Array.from(pathList),
86
- }),
87
- );
88
- }
89
- AccessorAnalyzer.analyze(routes);
90
- return routes;
91
- };
92
- }
93
-
94
- const report = (props: {
95
- type: "error" | "warning";
96
- errors: IReflectOperationError[];
97
- }): void => {
98
- const map: TreeMap<
99
- IReflectOperationError.Key,
100
- Array<string | IOperationMetadata.IError>
101
- > = new TreeMap();
102
- for (const e of props.errors)
103
- map.take(new IReflectOperationError.Key(e), () => []).push(...e.contents);
104
-
105
- const messages: string[] = [];
106
- for (const {
107
- first: { error },
108
- second: contents,
109
- } of map) {
110
- if (error.contents.length === 0) continue;
111
- const location: string = path.relative(process.cwd(), error.file);
112
- messages.push(
113
- [
114
- `${location} - `,
115
- error.class,
116
- ...(error.function !== null ? [`.${error.function}()`] : [""]),
117
- ...(error.from !== null ? [` from ${error.from}`] : [""]),
118
- ":\n",
119
- contents
120
- .map((c) => {
121
- if (typeof c === "string") return ` - ${c}`;
122
- else
123
- return [
124
- c.accessor
125
- ? ` - ${c.name}: `
126
- : ` - ${c.name} (${c.accessor}): `,
127
- ...c.messages.map((msg) => ` - ${msg}`),
128
- ].join("\n");
129
- })
130
- .join("\n"),
131
- ].join(""),
132
- );
133
- }
134
- throw new Error(`Error on NestiaSwaggerComposer.compose():\n${messages}`);
135
- };
136
-
137
- const wrapPaths = (paths: string[]): string[] =>
138
- paths.length === 0 ? [""] : paths;
1
+ import { INestApplication } from "@nestjs/common";
2
+ import { OpenApi, OpenApiV3, SwaggerV2 } from "@samchon/openapi";
3
+ import path from "path";
4
+ import { TreeMap } from "tstl";
5
+ import { IMetadataDictionary } from "typia/lib/schemas/metadata/IMetadataDictionary";
6
+
7
+ import { INestiaConfig } from "./INestiaConfig";
8
+ import { AccessorAnalyzer } from "./analyses/AccessorAnalyzer";
9
+ import { ConfigAnalyzer } from "./analyses/ConfigAnalyzer";
10
+ import { PathAnalyzer } from "./analyses/PathAnalyzer";
11
+ import { ReflectControllerAnalyzer } from "./analyses/ReflectControllerAnalyzer";
12
+ import { TypedHttpRouteAnalyzer } from "./analyses/TypedHttpRouteAnalyzer";
13
+ import { SwaggerGenerator } from "./generates/SwaggerGenerator";
14
+ import { INestiaProject } from "./structures/INestiaProject";
15
+ import { INestiaSdkInput } from "./structures/INestiaSdkInput";
16
+ import { IReflectController } from "./structures/IReflectController";
17
+ import { IReflectOperationError } from "./structures/IReflectOperationError";
18
+ import { ITypedHttpRoute } from "./structures/ITypedHttpRoute";
19
+ import { IOperationMetadata } from "./transformers/IOperationMetadata";
20
+ import { VersioningStrategy } from "./utils/VersioningStrategy";
21
+
22
+ export namespace NestiaSwaggerComposer {
23
+ export const document = async (
24
+ app: INestApplication,
25
+ config: Omit<INestiaConfig.ISwaggerConfig, "output">,
26
+ ): Promise<OpenApi.IDocument | OpenApiV3.IDocument | SwaggerV2.IDocument> => {
27
+ const input: INestiaSdkInput = await ConfigAnalyzer.application(app);
28
+ const document: OpenApi.IDocument = await SwaggerGenerator.compose({
29
+ config,
30
+ routes: analyze(input),
31
+ document: await SwaggerGenerator.initialize(config),
32
+ });
33
+ return config.openapi === "2.0"
34
+ ? OpenApi.downgrade(document, "2.0")
35
+ : config.openapi === "3.0"
36
+ ? OpenApi.downgrade(document, "3.0")
37
+ : document;
38
+ };
39
+
40
+ const analyze = (input: INestiaSdkInput): ITypedHttpRoute[] => {
41
+ // GET REFLECT CONTROLLERS
42
+ const unique: WeakSet<any> = new WeakSet();
43
+ const project: Omit<INestiaProject, "config"> = {
44
+ input,
45
+ checker: null!,
46
+ errors: [],
47
+ warnings: [],
48
+ };
49
+ const controllers: IReflectController[] = project.input.controllers
50
+ .map((c) =>
51
+ ReflectControllerAnalyzer.analyze({ project, controller: c, unique }),
52
+ )
53
+ .filter((c): c is IReflectController => c !== null);
54
+ if (project.errors.length)
55
+ throw report({ type: "error", errors: project.errors });
56
+
57
+ // METADATA COMPONENTS
58
+ const collection: IMetadataDictionary =
59
+ TypedHttpRouteAnalyzer.dictionary(controllers);
60
+
61
+ // CONVERT TO TYPED OPERATIONS
62
+ const globalPrefix: string = project.input.globalPrefix?.prefix ?? "";
63
+ const routes: ITypedHttpRoute[] = [];
64
+ for (const c of controllers)
65
+ for (const o of c.operations) {
66
+ const pathList: Set<string> = new Set();
67
+ const versions: string[] = VersioningStrategy.merge(project)([
68
+ ...(c.versions ?? []),
69
+ ...(o.versions ?? []),
70
+ ]);
71
+ for (const v of versions)
72
+ for (const prefix of wrapPaths(c.prefixes))
73
+ for (const cPath of wrapPaths(c.paths))
74
+ for (const filePath of wrapPaths(o.paths))
75
+ pathList.add(
76
+ PathAnalyzer.join(globalPrefix, v, prefix, cPath, filePath),
77
+ );
78
+ if (o.protocol === "http")
79
+ routes.push(
80
+ ...TypedHttpRouteAnalyzer.analyze({
81
+ controller: c,
82
+ errors: project.errors,
83
+ dictionary: collection,
84
+ operation: o,
85
+ paths: Array.from(pathList),
86
+ }),
87
+ );
88
+ }
89
+ AccessorAnalyzer.analyze(routes);
90
+ return routes;
91
+ };
92
+ }
93
+
94
+ const report = (props: {
95
+ type: "error" | "warning";
96
+ errors: IReflectOperationError[];
97
+ }): void => {
98
+ const map: TreeMap<
99
+ IReflectOperationError.Key,
100
+ Array<string | IOperationMetadata.IError>
101
+ > = new TreeMap();
102
+ for (const e of props.errors)
103
+ map.take(new IReflectOperationError.Key(e), () => []).push(...e.contents);
104
+
105
+ const messages: string[] = [];
106
+ for (const {
107
+ first: { error },
108
+ second: contents,
109
+ } of map) {
110
+ if (error.contents.length === 0) continue;
111
+ const location: string = path.relative(process.cwd(), error.file);
112
+ messages.push(
113
+ [
114
+ `${location} - `,
115
+ error.class,
116
+ ...(error.function !== null ? [`.${error.function}()`] : [""]),
117
+ ...(error.from !== null ? [` from ${error.from}`] : [""]),
118
+ ":\n",
119
+ contents
120
+ .map((c) => {
121
+ if (typeof c === "string") return ` - ${c}`;
122
+ else
123
+ return [
124
+ c.accessor
125
+ ? ` - ${c.name}: `
126
+ : ` - ${c.name} (${c.accessor}): `,
127
+ ...c.messages.map((msg) => ` - ${msg}`),
128
+ ].join("\n");
129
+ })
130
+ .join("\n"),
131
+ ].join(""),
132
+ );
133
+ }
134
+ throw new Error(`Error on NestiaSwaggerComposer.compose():\n${messages}`);
135
+ };
136
+
137
+ const wrapPaths = (paths: string[]): string[] =>
138
+ paths.length === 0 ? [""] : paths;
@@ -1,67 +1,67 @@
1
- import { Escaper } from "typia/lib/utils/Escaper";
2
-
3
- import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
4
- import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute";
5
-
6
- export namespace AccessorAnalyzer {
7
- export const analyze = (
8
- routes: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
9
- ) => {
10
- shrink(routes);
11
- variable(routes);
12
- shrink(routes);
13
- for (const r of routes) r.name = r.accessor.at(-1) ?? r.name;
14
- };
15
-
16
- const prepare = (
17
- routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
18
- ): Map<string, number> => {
19
- const dict: Map<string, number> = new Map();
20
- for (const route of routeList)
21
- route.accessor.forEach((_a, i) => {
22
- const key: string = route.accessor.slice(0, i + 1).join(".");
23
- dict.set(key, (dict.get(key) ?? 0) + 1);
24
- });
25
- return dict;
26
- };
27
-
28
- const variable = (
29
- routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
30
- ) => {
31
- const dict: Map<string, number> = prepare(routeList);
32
- for (const route of routeList) {
33
- const emended: string[] = route.accessor.slice();
34
- route.accessor.forEach((accessor, i) => {
35
- if (Escaper.variable(accessor)) return;
36
- while (true) {
37
- accessor = "$" + accessor;
38
- const partial: string = [
39
- ...route.accessor.slice(0, i),
40
- accessor,
41
- ].join(".");
42
- if (dict.has(partial) === false) {
43
- emended[i] = accessor;
44
- break;
45
- }
46
- }
47
- });
48
- route.accessor.splice(0, route.accessor.length, ...emended);
49
- }
50
- };
51
-
52
- const shrink = (routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>) => {
53
- const dict: Map<string, number> = prepare(routeList);
54
- for (const route of routeList) {
55
- if (
56
- route.accessor.length < 2 ||
57
- route.accessor.at(-1) !== route.accessor.at(-2)
58
- )
59
- continue;
60
-
61
- const cut: string[] = route.accessor.slice(0, -1);
62
- if ((dict.get(cut.join(".")) ?? 0) > 1) continue;
63
-
64
- route.accessor = cut;
65
- }
66
- };
67
- }
1
+ import { Escaper } from "typia/lib/utils/Escaper";
2
+
3
+ import { ITypedHttpRoute } from "../structures/ITypedHttpRoute";
4
+ import { ITypedWebSocketRoute } from "../structures/ITypedWebSocketRoute";
5
+
6
+ export namespace AccessorAnalyzer {
7
+ export const analyze = (
8
+ routes: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
9
+ ) => {
10
+ shrink(routes);
11
+ variable(routes);
12
+ shrink(routes);
13
+ for (const r of routes) r.name = r.accessor.at(-1) ?? r.name;
14
+ };
15
+
16
+ const prepare = (
17
+ routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
18
+ ): Map<string, number> => {
19
+ const dict: Map<string, number> = new Map();
20
+ for (const route of routeList)
21
+ route.accessor.forEach((_a, i) => {
22
+ const key: string = route.accessor.slice(0, i + 1).join(".");
23
+ dict.set(key, (dict.get(key) ?? 0) + 1);
24
+ });
25
+ return dict;
26
+ };
27
+
28
+ const variable = (
29
+ routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>,
30
+ ) => {
31
+ const dict: Map<string, number> = prepare(routeList);
32
+ for (const route of routeList) {
33
+ const emended: string[] = route.accessor.slice();
34
+ route.accessor.forEach((accessor, i) => {
35
+ if (Escaper.variable(accessor)) return;
36
+ while (true) {
37
+ accessor = "$" + accessor;
38
+ const partial: string = [
39
+ ...route.accessor.slice(0, i),
40
+ accessor,
41
+ ].join(".");
42
+ if (dict.has(partial) === false) {
43
+ emended[i] = accessor;
44
+ break;
45
+ }
46
+ }
47
+ });
48
+ route.accessor.splice(0, route.accessor.length, ...emended);
49
+ }
50
+ };
51
+
52
+ const shrink = (routeList: Array<ITypedHttpRoute | ITypedWebSocketRoute>) => {
53
+ const dict: Map<string, number> = prepare(routeList);
54
+ for (const route of routeList) {
55
+ if (
56
+ route.accessor.length < 2 ||
57
+ route.accessor.at(-1) !== route.accessor.at(-2)
58
+ )
59
+ continue;
60
+
61
+ const cut: string[] = route.accessor.slice(0, -1);
62
+ if ((dict.get(cut.join(".")) ?? 0) > 1) continue;
63
+
64
+ route.accessor = cut;
65
+ }
66
+ };
67
+ }