@nestia/sdk 12.0.0-dev.20260601.1 → 12.0.0-dev.20260612.2
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.
- package/LICENSE +21 -21
- package/README.md +93 -93
- package/assets/bundle/api/HttpError.ts +1 -1
- package/assets/bundle/api/IConnection.ts +1 -1
- package/assets/bundle/api/Primitive.ts +1 -1
- package/assets/bundle/api/Resolved.ts +1 -1
- package/assets/bundle/api/index.ts +4 -4
- package/assets/bundle/api/module.ts +6 -6
- package/assets/bundle/distribute/README.md +37 -37
- package/assets/bundle/distribute/package.json +28 -28
- package/assets/bundle/distribute/tsconfig.json +109 -109
- package/assets/bundle/e2e/index.ts +42 -42
- package/assets/config/nestia.config.ts +97 -97
- package/lib/NestiaSdkApplication.js +29 -7
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/NestiaSwaggerComposer.js +21 -13
- package/lib/NestiaSwaggerComposer.js.map +1 -1
- package/lib/analyses/AccessorAnalyzer.d.ts +4 -1
- package/lib/analyses/AccessorAnalyzer.js.map +1 -1
- package/lib/analyses/ConfigAnalyzer.js +1 -1
- package/lib/analyses/PathAnalyzer.d.ts +18 -3
- package/lib/analyses/PathAnalyzer.js +32 -0
- package/lib/analyses/PathAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectControllerAnalyzer.js +3 -2
- package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationAnalyzer.d.ts +1 -1
- package/lib/analyses/ReflectHttpOperationAnalyzer.js +1 -1
- package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.d.ts +1 -1
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +53 -20
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +14 -0
- package/lib/analyses/ReflectMcpOperationAnalyzer.js +79 -0
- package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -0
- package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +9 -0
- package/lib/analyses/TypedMcpRouteAnalyzer.js +31 -0
- package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -0
- package/lib/executable/internal/NestiaConfigLoader.js +5 -1
- package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
- package/lib/executable/internal/NestiaSdkCommand.js +30 -14
- package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
- package/lib/executable/internal/NestiaSdkWatcher.d.ts +10 -0
- package/lib/executable/internal/NestiaSdkWatcher.js +322 -0
- package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -0
- package/lib/executable/sdk.js +12 -12
- package/lib/executable/sdk.js.map +1 -1
- package/lib/generates/CloneGenerator.js +4 -2
- package/lib/generates/CloneGenerator.js.map +1 -1
- package/lib/generates/SdkGenerator.js +50 -1
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/SwaggerGenerator.js +18 -2
- package/lib/generates/SwaggerGenerator.js.map +1 -1
- package/lib/generates/internal/E2eFileProgrammer.js +3 -1
- package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
- package/lib/generates/internal/ImportDictionary.d.ts +1 -0
- package/lib/generates/internal/ImportDictionary.js +9 -4
- package/lib/generates/internal/ImportDictionary.js.map +1 -1
- package/lib/generates/internal/SdkAliasCollection.d.ts +2 -0
- package/lib/generates/internal/SdkAliasCollection.js +11 -2
- package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
- package/lib/generates/internal/SdkDistributionComposer.d.ts +1 -0
- package/lib/generates/internal/SdkDistributionComposer.js +3 -0
- package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
- package/lib/generates/internal/SdkFileProgrammer.js +4 -1
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpCloneReferencer.d.ts +1 -1
- package/lib/generates/internal/SdkHttpCloneReferencer.js +42 -9
- package/lib/generates/internal/SdkHttpCloneReferencer.js.map +1 -1
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js +3 -4
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +2 -1
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js +6 -3
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +15 -0
- package/lib/generates/internal/SdkMcpRouteProgrammer.js +148 -0
- package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkRouteDirectory.d.ts +2 -1
- package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketCloneProgrammer.d.ts +6 -0
- package/lib/generates/internal/SdkWebSocketCloneProgrammer.js +283 -0
- package/lib/generates/internal/SdkWebSocketCloneProgrammer.js.map +1 -0
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +11 -9
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationParameterComposer.js +10 -2
- package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +1 -1
- package/lib/generates/internal/SwaggerOperationResponseComposer.js +6 -1
- package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerReadonlyArrayEmender.d.ts +9 -0
- package/lib/generates/internal/SwaggerReadonlyArrayEmender.js +174 -0
- package/lib/generates/internal/SwaggerReadonlyArrayEmender.js.map +1 -0
- package/lib/structures/INestiaSdkInput.d.ts +9 -2
- package/lib/structures/IReflectController.d.ts +2 -1
- package/lib/structures/IReflectHttpOperationSuccess.d.ts +4 -2
- package/lib/structures/IReflectMcpOperation.d.ts +35 -0
- package/lib/structures/IReflectMcpOperation.js +3 -0
- package/lib/structures/IReflectMcpOperation.js.map +1 -0
- package/lib/structures/IReflectMcpOperationParameter.d.ts +19 -0
- package/lib/structures/IReflectMcpOperationParameter.js +3 -0
- package/lib/structures/IReflectMcpOperationParameter.js.map +1 -0
- package/lib/structures/ITypedApplication.d.ts +2 -1
- package/lib/structures/ITypedHttpRouteSuccess.d.ts +3 -1
- package/lib/structures/ITypedMcpRoute.d.ts +31 -0
- package/lib/structures/ITypedMcpRoute.js +3 -0
- package/lib/structures/ITypedMcpRoute.js.map +1 -0
- package/lib/utils/HttpResponseContentTypeUtil.d.ts +5 -0
- package/lib/utils/HttpResponseContentTypeUtil.js +22 -0
- package/lib/utils/HttpResponseContentTypeUtil.js.map +1 -0
- package/native/go.mod +52 -52
- package/native/go.sum +84 -54
- package/native/sdk/register.go +322 -165
- package/native/sdk/sdk.go +17 -17
- package/native/sdk/sdk_metadata_json.go +327 -327
- package/native/sdk/sdk_transform.go +1879 -1549
- package/package.json +11 -9
- package/src/INestiaConfig.ts +267 -267
- package/src/NestiaSdkApplication.ts +39 -8
- package/src/NestiaSwaggerComposer.ts +153 -142
- package/src/analyses/AccessorAnalyzer.ts +64 -67
- package/src/analyses/ConfigAnalyzer.ts +330 -330
- package/src/analyses/ImportAnalyzer.ts +92 -92
- package/src/analyses/PathAnalyzer.ts +130 -69
- package/src/analyses/ReflectControllerAnalyzer.ts +112 -105
- package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
- package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +90 -90
- package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +350 -350
- package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +163 -130
- package/src/analyses/ReflectMcpOperationAnalyzer.ts +124 -0
- package/src/analyses/ReflectMetadataAnalyzer.ts +44 -44
- package/src/analyses/SecurityAnalyzer.ts +25 -25
- package/src/analyses/TypedMcpRouteAnalyzer.ts +34 -0
- package/src/decorators/OperationMetadata.ts +29 -29
- package/src/executable/internal/CommandParser.ts +15 -15
- package/src/executable/internal/NestiaConfigLoader.ts +451 -446
- package/src/executable/internal/NestiaSdkCommand.ts +124 -106
- package/src/executable/internal/NestiaSdkWatcher.ts +342 -0
- package/src/executable/sdk.ts +90 -88
- package/src/generates/CloneGenerator.ts +73 -66
- package/src/generates/E2eGenerator.ts +32 -32
- package/src/generates/SdkGenerator.ts +176 -118
- package/src/generates/SwaggerGenerator.ts +342 -310
- package/src/generates/internal/E2eFileProgrammer.ts +240 -233
- package/src/generates/internal/FilePrinter.ts +65 -65
- package/src/generates/internal/ImportDictionary.ts +209 -204
- package/src/generates/internal/SdkAliasCollection.ts +274 -261
- package/src/generates/internal/SdkDistributionComposer.ts +123 -116
- package/src/generates/internal/SdkFileProgrammer.ts +116 -112
- package/src/generates/internal/SdkHttpCloneProgrammer.ts +126 -126
- package/src/generates/internal/SdkHttpCloneReferencer.ts +131 -77
- package/src/generates/internal/SdkHttpFunctionProgrammer.ts +301 -301
- package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +520 -510
- package/src/generates/internal/SdkHttpParameterProgrammer.ts +165 -165
- package/src/generates/internal/SdkHttpRouteProgrammer.ts +109 -109
- package/src/generates/internal/SdkHttpSimulationProgrammer.ts +331 -314
- package/src/generates/internal/SdkImportWizard.ts +62 -62
- package/src/generates/internal/SdkMcpRouteProgrammer.ts +452 -0
- package/src/generates/internal/SdkRouteDirectory.ts +21 -18
- package/src/generates/internal/SdkTypeTagProgrammer.ts +114 -114
- package/src/generates/internal/SdkWebSocketCloneProgrammer.ts +319 -0
- package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +389 -389
- package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +89 -89
- package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +331 -323
- package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
- package/src/generates/internal/SwaggerOperationComposer.ts +119 -119
- package/src/generates/internal/SwaggerOperationParameterComposer.ts +175 -162
- package/src/generates/internal/SwaggerOperationResponseComposer.ts +115 -110
- package/src/generates/internal/SwaggerReadonlyArrayEmender.ts +262 -0
- package/src/index.ts +4 -4
- package/src/internal/legacy.ts +492 -492
- package/src/module.ts +4 -4
- package/src/structures/INestiaProject.ts +10 -10
- package/src/structures/INestiaSdkInput.ts +27 -20
- package/src/structures/IOperationMetadata.ts +41 -41
- package/src/structures/IReflectController.ts +18 -15
- package/src/structures/IReflectHttpOperation.ts +26 -26
- package/src/structures/IReflectHttpOperationException.ts +18 -18
- package/src/structures/IReflectHttpOperationParameter.ts +79 -79
- package/src/structures/IReflectHttpOperationSuccess.ts +18 -21
- package/src/structures/IReflectImport.ts +6 -6
- package/src/structures/IReflectMcpOperation.ts +38 -0
- package/src/structures/IReflectMcpOperationParameter.ts +27 -0
- package/src/structures/IReflectOperationError.ts +26 -26
- package/src/structures/IReflectType.ts +4 -4
- package/src/structures/IReflectWebSocketOperation.ts +17 -17
- package/src/structures/ITypedApplication.ts +12 -11
- package/src/structures/ITypedHttpRoute.ts +41 -41
- package/src/structures/ITypedHttpRouteException.ts +15 -15
- package/src/structures/ITypedHttpRouteParameter.ts +41 -41
- package/src/structures/ITypedHttpRouteSuccess.ts +18 -22
- package/src/structures/ITypedMcpRoute.ts +33 -0
- package/src/structures/ITypedWebSocketRoute.ts +24 -24
- package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
- package/src/transform.ts +59 -59
- package/src/typings/get-function-location.d.ts +7 -7
- package/src/utils/ArrayUtil.ts +26 -26
- package/src/utils/EmittedJavaScriptPatcher.ts +88 -88
- package/src/utils/FileRetriever.ts +22 -22
- package/src/utils/HttpResponseContentTypeUtil.ts +30 -0
- package/src/utils/MapUtil.ts +14 -14
- package/src/utils/PathUtil.ts +10 -10
- package/src/utils/SourceFinder.ts +63 -63
- package/src/utils/StringUtil.ts +17 -17
- package/src/utils/TsConfigReader.ts +108 -108
- package/src/utils/TtscExecutor.ts +68 -68
- package/src/utils/VersioningStrategy.ts +28 -28
- package/src/validators/HttpHeadersValidator.ts +11 -11
- package/src/validators/HttpQueryValidator.ts +11 -11
- package/src/validators/TextPlainValidator.ts +17 -17
|
@@ -1,106 +1,124 @@
|
|
|
1
|
-
import { INestiaConfig } from "../../INestiaConfig";
|
|
2
|
-
import { NestiaSdkApplication } from "../../NestiaSdkApplication";
|
|
3
|
-
import { NestiaConfigLoader } from "./NestiaConfigLoader";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
1
|
+
import { INestiaConfig } from "../../INestiaConfig";
|
|
2
|
+
import { NestiaSdkApplication } from "../../NestiaSdkApplication";
|
|
3
|
+
import { NestiaConfigLoader } from "./NestiaConfigLoader";
|
|
4
|
+
import { NestiaSdkWatcher } from "./NestiaSdkWatcher";
|
|
5
|
+
|
|
6
|
+
export namespace NestiaSdkCommand {
|
|
7
|
+
export const sdk = () =>
|
|
8
|
+
main({
|
|
9
|
+
title: "SDK library",
|
|
10
|
+
generate: (app) => app.sdk(),
|
|
11
|
+
validate: (config) => !!config.output,
|
|
12
|
+
solution: "configure INestiaConfig.output property.",
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const swagger = () =>
|
|
16
|
+
main({
|
|
17
|
+
title: "Swagger Document",
|
|
18
|
+
generate: (app) => app.swagger(),
|
|
19
|
+
validate: (config) => !!config.swagger?.output,
|
|
20
|
+
solution: "configure INestiaConfig.swagger property.",
|
|
21
|
+
watch: hasFlagArgument("watch"),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export const e2e = () =>
|
|
25
|
+
main({
|
|
26
|
+
title: "E2E Functions",
|
|
27
|
+
generate: (app) => app.e2e(),
|
|
28
|
+
validate: (config) => !!config.e2e,
|
|
29
|
+
solution: [
|
|
30
|
+
"configure two properties:",
|
|
31
|
+
"",
|
|
32
|
+
" - INestiaConfig.output",
|
|
33
|
+
" - INestiaConfig.e2e",
|
|
34
|
+
].join("\n"),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const all = () =>
|
|
38
|
+
main({
|
|
39
|
+
title: "everything",
|
|
40
|
+
generate: (app) => app.all(),
|
|
41
|
+
validate: () => true,
|
|
42
|
+
solution: [
|
|
43
|
+
"configure at least one property of below:",
|
|
44
|
+
"",
|
|
45
|
+
" - INestiaConfig.output",
|
|
46
|
+
" - INestiaConfig.swagger.output",
|
|
47
|
+
].join("\n"),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const main = async (props: {
|
|
51
|
+
title: string;
|
|
52
|
+
solution: string;
|
|
53
|
+
generate: (app: NestiaSdkApplication) => Promise<void>;
|
|
54
|
+
validate: (config: INestiaConfig) => boolean;
|
|
55
|
+
watch?: boolean;
|
|
56
|
+
}) => {
|
|
57
|
+
// LOAD CONFIG INFO
|
|
58
|
+
const project: string =
|
|
59
|
+
getFileArgument({
|
|
60
|
+
type: "project",
|
|
61
|
+
extension: "json",
|
|
62
|
+
}) ?? "tsconfig.json";
|
|
63
|
+
process.env.NESTIA_PROJECT = project;
|
|
64
|
+
const configFile: string =
|
|
65
|
+
getFileArgument({
|
|
66
|
+
type: "config",
|
|
67
|
+
extension: "ts",
|
|
68
|
+
}) ?? "nestia.config.ts";
|
|
69
|
+
const configurations = async (): Promise<INestiaConfig[]> => {
|
|
70
|
+
const command: NestiaConfigLoader.ICompilerOptions =
|
|
71
|
+
await NestiaConfigLoader.compilerOptions(project);
|
|
72
|
+
return NestiaConfigLoader.configurations(
|
|
73
|
+
configFile,
|
|
74
|
+
command.raw.compilerOptions ?? {},
|
|
75
|
+
);
|
|
76
|
+
};
|
|
77
|
+
const generate = async (configurations: INestiaConfig[]): Promise<void> => {
|
|
78
|
+
if (
|
|
79
|
+
configurations.length > 1 &&
|
|
80
|
+
configurations.some(props.validate) === false
|
|
81
|
+
)
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Every configurations are invalid to generate ${props.title}, ${props.solution}`,
|
|
84
|
+
);
|
|
85
|
+
for (const config of configurations) {
|
|
86
|
+
if (configurations.length > 1 && props.validate(config) === false)
|
|
87
|
+
continue;
|
|
88
|
+
const app: NestiaSdkApplication = new NestiaSdkApplication(config);
|
|
89
|
+
await props.generate(app);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (props.watch === true)
|
|
94
|
+
return NestiaSdkWatcher.watch({
|
|
95
|
+
configFile,
|
|
96
|
+
configurations,
|
|
97
|
+
generate,
|
|
98
|
+
projectFile: project,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await generate(await configurations());
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const getFileArgument = (props: {
|
|
105
|
+
type: string;
|
|
106
|
+
extension: string;
|
|
107
|
+
}): string | null => {
|
|
108
|
+
const argv: string[] = process.argv.slice(3);
|
|
109
|
+
if (argv.length === 0) return null;
|
|
110
|
+
|
|
111
|
+
const index: number = argv.findIndex((str) => str === `--${props.type}`);
|
|
112
|
+
if (index === -1) return null;
|
|
113
|
+
else if (argv.length === 1)
|
|
114
|
+
throw new Error(`${props.type} file must be provided`);
|
|
115
|
+
|
|
116
|
+
const file: string = argv[index + 1]!;
|
|
117
|
+
if (file.endsWith(props.extension) === false)
|
|
118
|
+
throw new Error(`${props.type} file must be ${props.extension} file`);
|
|
119
|
+
return file;
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const hasFlagArgument = (type: string): boolean =>
|
|
123
|
+
process.argv.slice(3).some((str) => str === `--${type}`);
|
|
124
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import { glob } from "glob";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
import { INestiaConfig } from "../../INestiaConfig";
|
|
6
|
+
|
|
7
|
+
export namespace NestiaSdkWatcher {
|
|
8
|
+
export interface IProps {
|
|
9
|
+
configFile: string;
|
|
10
|
+
configurations: () => Promise<INestiaConfig[]>;
|
|
11
|
+
generate: (configurations: INestiaConfig[]) => Promise<void>;
|
|
12
|
+
projectFile: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const watch = async (props: IProps): Promise<void> => {
|
|
16
|
+
const session = new WatchSession(props);
|
|
17
|
+
session.registerSignals();
|
|
18
|
+
await session.regenerate("initial generation");
|
|
19
|
+
await new Promise<void>(() => {});
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
class WatchSession {
|
|
24
|
+
private readonly watchers: Map<string, fs.FSWatcher> = new Map();
|
|
25
|
+
private debounce: NodeJS.Timeout | null = null;
|
|
26
|
+
private ignored: string[] = [];
|
|
27
|
+
private pending: string | null = null;
|
|
28
|
+
private running: boolean = false;
|
|
29
|
+
private stopped: boolean = false;
|
|
30
|
+
|
|
31
|
+
public constructor(private readonly props: NestiaSdkWatcher.IProps) {}
|
|
32
|
+
|
|
33
|
+
public registerSignals(): void {
|
|
34
|
+
const stop = (): void => {
|
|
35
|
+
this.dispose();
|
|
36
|
+
process.exit(0);
|
|
37
|
+
};
|
|
38
|
+
process.once("SIGINT", stop);
|
|
39
|
+
process.once("SIGTERM", stop);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public async regenerate(reason: string): Promise<void> {
|
|
43
|
+
if (this.stopped) return;
|
|
44
|
+
if (this.running) {
|
|
45
|
+
this.pending = reason;
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
this.running = true;
|
|
50
|
+
let nextReason: string | null = reason;
|
|
51
|
+
do {
|
|
52
|
+
const current: string = nextReason;
|
|
53
|
+
nextReason = null;
|
|
54
|
+
this.pending = null;
|
|
55
|
+
let configurations: INestiaConfig[] = [];
|
|
56
|
+
try {
|
|
57
|
+
if (current === "initial generation")
|
|
58
|
+
console.log("Starting nestia swagger watch mode");
|
|
59
|
+
else console.log(`Change detected (${current}); regenerating swagger`);
|
|
60
|
+
|
|
61
|
+
configurations = await this.props.configurations();
|
|
62
|
+
await this.props.generate(configurations);
|
|
63
|
+
await this.refresh(configurations);
|
|
64
|
+
console.log(
|
|
65
|
+
`Watching ${this.watchers.size} directories. Press Ctrl+C to stop.`,
|
|
66
|
+
);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
await this.refresh(configurations);
|
|
69
|
+
console.error("Nestia swagger watch generation failed:");
|
|
70
|
+
printError(error);
|
|
71
|
+
}
|
|
72
|
+
nextReason = this.pending;
|
|
73
|
+
} while (nextReason !== null && !this.stopped);
|
|
74
|
+
this.running = false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private schedule(reason: string): void {
|
|
78
|
+
if (this.stopped) return;
|
|
79
|
+
if (this.debounce !== null) clearTimeout(this.debounce);
|
|
80
|
+
this.debounce = setTimeout(() => {
|
|
81
|
+
this.debounce = null;
|
|
82
|
+
void this.regenerate(reason);
|
|
83
|
+
}, 250);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
private async refresh(configurations: INestiaConfig[]): Promise<void> {
|
|
87
|
+
const next = await collectWatchPlan({
|
|
88
|
+
configurations,
|
|
89
|
+
configFile: this.props.configFile,
|
|
90
|
+
projectFile: this.props.projectFile,
|
|
91
|
+
});
|
|
92
|
+
this.ignored = next.ignored;
|
|
93
|
+
|
|
94
|
+
const directories: Set<string> = new Set();
|
|
95
|
+
for (const target of next.targets)
|
|
96
|
+
await collectDirectories({
|
|
97
|
+
directories,
|
|
98
|
+
ignored: next.ignored,
|
|
99
|
+
target,
|
|
100
|
+
});
|
|
101
|
+
this.sync(directories);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
private sync(next: Set<string>): void {
|
|
105
|
+
for (const [directory, watcher] of this.watchers)
|
|
106
|
+
if (next.has(directory) === false) {
|
|
107
|
+
watcher.close();
|
|
108
|
+
this.watchers.delete(directory);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
for (const directory of next) {
|
|
112
|
+
if (this.watchers.has(directory)) continue;
|
|
113
|
+
try {
|
|
114
|
+
const watcher = fs.watch(directory, (event, filename) => {
|
|
115
|
+
const target =
|
|
116
|
+
filename === null || filename === undefined
|
|
117
|
+
? directory
|
|
118
|
+
: path.resolve(directory, String(filename));
|
|
119
|
+
if (shouldIgnore(target, this.ignored)) return;
|
|
120
|
+
if (shouldReact({ event, target }) === false) return;
|
|
121
|
+
this.schedule(relative(target));
|
|
122
|
+
});
|
|
123
|
+
watcher.on("error", () => {
|
|
124
|
+
watcher.close();
|
|
125
|
+
this.watchers.delete(directory);
|
|
126
|
+
this.schedule(relative(directory));
|
|
127
|
+
});
|
|
128
|
+
this.watchers.set(directory, watcher);
|
|
129
|
+
} catch {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private dispose(): void {
|
|
136
|
+
this.stopped = true;
|
|
137
|
+
if (this.debounce !== null) clearTimeout(this.debounce);
|
|
138
|
+
for (const watcher of this.watchers.values()) watcher.close();
|
|
139
|
+
this.watchers.clear();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
interface IWatchPlan {
|
|
144
|
+
ignored: string[];
|
|
145
|
+
targets: string[];
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const collectWatchPlan = async (props: {
|
|
149
|
+
configurations: INestiaConfig[];
|
|
150
|
+
configFile: string;
|
|
151
|
+
projectFile: string;
|
|
152
|
+
}): Promise<IWatchPlan> => {
|
|
153
|
+
const targets: Set<string> = new Set([
|
|
154
|
+
path.resolve(props.configFile),
|
|
155
|
+
path.resolve(props.projectFile),
|
|
156
|
+
]);
|
|
157
|
+
const ignored: Set<string> = new Set([
|
|
158
|
+
path.resolve("node_modules"),
|
|
159
|
+
path.resolve(".git"),
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
for (const config of props.configurations) {
|
|
163
|
+
for (const target of await inputTargets(config.input)) targets.add(target);
|
|
164
|
+
for (const target of outputTargets(config)) ignored.add(target);
|
|
165
|
+
}
|
|
166
|
+
return {
|
|
167
|
+
ignored: [...ignored],
|
|
168
|
+
targets: [...targets],
|
|
169
|
+
};
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
const inputTargets = async (
|
|
173
|
+
input: INestiaConfig["input"],
|
|
174
|
+
): Promise<string[]> => {
|
|
175
|
+
if (typeof input === "function") {
|
|
176
|
+
const fallback: string = path.resolve("src");
|
|
177
|
+
return [fs.existsSync(fallback) ? fallback : process.cwd()];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const patterns: string[] = Array.isArray(input)
|
|
181
|
+
? input
|
|
182
|
+
: typeof input === "object"
|
|
183
|
+
? input.include
|
|
184
|
+
: [input];
|
|
185
|
+
const output: Set<string> = new Set();
|
|
186
|
+
for (const pattern of patterns) {
|
|
187
|
+
for (const target of await patternTargets(pattern)) output.add(target);
|
|
188
|
+
}
|
|
189
|
+
return [...output];
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const patternTargets = async (pattern: string): Promise<string[]> => {
|
|
193
|
+
const output: Set<string> = new Set();
|
|
194
|
+
const direct: string = path.resolve(pattern);
|
|
195
|
+
if (fs.existsSync(direct)) output.add(direct);
|
|
196
|
+
|
|
197
|
+
if (hasGlobMagic(pattern)) {
|
|
198
|
+
const root: string = staticRoot(pattern);
|
|
199
|
+
if (fs.existsSync(root)) output.add(root);
|
|
200
|
+
for (const match of await glob(pattern)) output.add(path.resolve(match));
|
|
201
|
+
}
|
|
202
|
+
return [...output];
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const outputTargets = (config: INestiaConfig): string[] => {
|
|
206
|
+
const output: string[] = [];
|
|
207
|
+
for (const value of [config.output, config.e2e, config.distribute])
|
|
208
|
+
if (typeof value === "string") output.push(path.resolve(value));
|
|
209
|
+
if (config.swagger?.output !== undefined) {
|
|
210
|
+
const parsed: path.ParsedPath = path.parse(config.swagger.output);
|
|
211
|
+
output.push(
|
|
212
|
+
parsed.ext
|
|
213
|
+
? path.resolve(config.swagger.output)
|
|
214
|
+
: path.resolve(config.swagger.output, "swagger.json"),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
return output;
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
const collectDirectories = async (props: {
|
|
221
|
+
directories: Set<string>;
|
|
222
|
+
ignored: string[];
|
|
223
|
+
target: string;
|
|
224
|
+
}): Promise<void> => {
|
|
225
|
+
const stats: fs.Stats | null = await safeStat(props.target);
|
|
226
|
+
if (stats === null) return;
|
|
227
|
+
const root: string = stats.isDirectory()
|
|
228
|
+
? props.target
|
|
229
|
+
: path.dirname(props.target);
|
|
230
|
+
await iterateDirectories({
|
|
231
|
+
directories: props.directories,
|
|
232
|
+
ignored: props.ignored,
|
|
233
|
+
root,
|
|
234
|
+
});
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
const iterateDirectories = async (props: {
|
|
238
|
+
directories: Set<string>;
|
|
239
|
+
ignored: string[];
|
|
240
|
+
root: string;
|
|
241
|
+
}): Promise<void> => {
|
|
242
|
+
const location: string = path.resolve(props.root);
|
|
243
|
+
if (shouldIgnore(location, props.ignored)) return;
|
|
244
|
+
props.directories.add(location);
|
|
245
|
+
|
|
246
|
+
let entries: fs.Dirent[];
|
|
247
|
+
try {
|
|
248
|
+
entries = await fs.promises.readdir(location, { withFileTypes: true });
|
|
249
|
+
} catch {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
for (const entry of entries) {
|
|
254
|
+
if (entry.isDirectory() === false) continue;
|
|
255
|
+
if (SKIPPED_DIRECTORIES.has(entry.name)) continue;
|
|
256
|
+
await iterateDirectories({
|
|
257
|
+
directories: props.directories,
|
|
258
|
+
ignored: props.ignored,
|
|
259
|
+
root: path.join(location, entry.name),
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const safeStat = async (location: string): Promise<fs.Stats | null> => {
|
|
265
|
+
try {
|
|
266
|
+
return await fs.promises.stat(location);
|
|
267
|
+
} catch {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const shouldReact = (props: { event: string; target: string }): boolean => {
|
|
273
|
+
const ext: string = path.extname(props.target).toLowerCase();
|
|
274
|
+
if (SOURCE_EXTENSIONS.has(ext)) return true;
|
|
275
|
+
return props.event === "rename" && ext.length === 0;
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const shouldIgnore = (location: string, ignored: string[]): boolean =>
|
|
279
|
+
ignored.some((elem) => isSameOrInside(location, elem));
|
|
280
|
+
|
|
281
|
+
const isSameOrInside = (child: string, parent: string): boolean => {
|
|
282
|
+
const left: string = comparable(child);
|
|
283
|
+
const right: string = comparable(parent);
|
|
284
|
+
return left === right || left.startsWith(`${right}${path.sep}`);
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const comparable = (location: string): string => {
|
|
288
|
+
const resolved: string = path.resolve(location);
|
|
289
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
const staticRoot = (pattern: string): string => {
|
|
293
|
+
const absolute: string = path.resolve(pattern);
|
|
294
|
+
const parsed: path.ParsedPath = path.parse(absolute);
|
|
295
|
+
const segments: string[] = absolute
|
|
296
|
+
.slice(parsed.root.length)
|
|
297
|
+
.split(/[\\/]+/)
|
|
298
|
+
.filter((segment) => segment.length !== 0);
|
|
299
|
+
const stable: string[] = [];
|
|
300
|
+
for (const segment of segments) {
|
|
301
|
+
if (hasGlobMagic(segment)) break;
|
|
302
|
+
stable.push(segment);
|
|
303
|
+
}
|
|
304
|
+
return stable.length === 0 ? parsed.root : path.join(parsed.root, ...stable);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const hasGlobMagic = (pattern: string): boolean => /[*?[\]{}]/.test(pattern);
|
|
308
|
+
|
|
309
|
+
const relative = (location: string): string => {
|
|
310
|
+
const output: string = path.relative(process.cwd(), location);
|
|
311
|
+
return output.length === 0 ? "." : output.split(path.sep).join("/");
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const printError = (error: unknown): void => {
|
|
315
|
+
let current: unknown = error;
|
|
316
|
+
let depth: number = 0;
|
|
317
|
+
while (current !== undefined && current !== null && depth < 10) {
|
|
318
|
+
const message: string =
|
|
319
|
+
current instanceof Error ? current.message : String(current);
|
|
320
|
+
console.error(
|
|
321
|
+
`${" ".repeat(depth)}${depth === 0 ? "" : "caused by: "}${message}`,
|
|
322
|
+
);
|
|
323
|
+
if (current instanceof Error && current.cause !== undefined) {
|
|
324
|
+
current = current.cause;
|
|
325
|
+
++depth;
|
|
326
|
+
} else break;
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const SOURCE_EXTENSIONS: Set<string> = new Set([
|
|
331
|
+
".cts",
|
|
332
|
+
".json",
|
|
333
|
+
".mts",
|
|
334
|
+
".ts",
|
|
335
|
+
".tsx",
|
|
336
|
+
]);
|
|
337
|
+
|
|
338
|
+
const SKIPPED_DIRECTORIES: Set<string> = new Set([
|
|
339
|
+
".git",
|
|
340
|
+
".nestia",
|
|
341
|
+
"node_modules",
|
|
342
|
+
]);
|