@nestia/sdk 14.0.0 → 14.0.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/README.md +1 -1
- package/assets/bundle/distribute/build-sdk.cjs +28 -0
- package/assets/bundle/distribute/package.json +7 -3
- package/assets/bundle/distribute/tsconfig.json +15 -105
- package/lib/INestiaConfig.d.ts +74 -34
- package/lib/NestiaSdkApplication.d.ts +60 -0
- package/lib/NestiaSdkApplication.js +121 -32
- package/lib/NestiaSdkApplication.js.map +1 -1
- package/lib/NestiaSwaggerComposer.d.ts +21 -0
- package/lib/NestiaSwaggerComposer.js +32 -13
- package/lib/NestiaSwaggerComposer.js.map +1 -1
- package/lib/analyses/AccessorAnalyzer.d.ts +26 -0
- package/lib/analyses/AccessorAnalyzer.js +26 -0
- package/lib/analyses/AccessorAnalyzer.js.map +1 -1
- package/lib/analyses/ConfigAnalyzer.d.ts +41 -0
- package/lib/analyses/ConfigAnalyzer.js +52 -77
- package/lib/analyses/ConfigAnalyzer.js.map +1 -1
- package/lib/analyses/ImportAnalyzer.d.ts +25 -0
- package/lib/analyses/ImportAnalyzer.js +25 -0
- package/lib/analyses/ImportAnalyzer.js.map +1 -1
- package/lib/analyses/ParameterNameAnalyzer.d.ts +44 -0
- package/lib/analyses/ParameterNameAnalyzer.js +63 -0
- package/lib/analyses/ParameterNameAnalyzer.js.map +1 -0
- package/lib/analyses/PathAnalyzer.d.ts +93 -1
- package/lib/analyses/PathAnalyzer.js +160 -23
- package/lib/analyses/PathAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectControllerAnalyzer.d.ts +32 -0
- package/lib/analyses/ReflectControllerAnalyzer.js +24 -1
- package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationAnalyzer.d.ts +29 -0
- package/lib/analyses/ReflectHttpOperationAnalyzer.js +35 -24
- package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationExceptionAnalyzer.d.ts +31 -0
- package/lib/analyses/ReflectHttpOperationExceptionAnalyzer.js +21 -0
- package/lib/analyses/ReflectHttpOperationExceptionAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationParameterAnalyzer.d.ts +31 -0
- package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js +143 -23
- package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.d.ts +32 -0
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +27 -2
- package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +29 -0
- package/lib/analyses/ReflectMcpOperationAnalyzer.js +35 -11
- package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectMetadataAnalyzer.d.ts +47 -0
- package/lib/analyses/ReflectMetadataAnalyzer.js +47 -0
- package/lib/analyses/ReflectMetadataAnalyzer.js.map +1 -1
- package/lib/analyses/ReflectWebSocketOperationAnalyzer.d.ts +29 -0
- package/lib/analyses/ReflectWebSocketOperationAnalyzer.js +27 -5
- package/lib/analyses/ReflectWebSocketOperationAnalyzer.js.map +1 -1
- package/lib/analyses/SecurityAnalyzer.d.ts +25 -0
- package/lib/analyses/SecurityAnalyzer.js +39 -19
- package/lib/analyses/SecurityAnalyzer.js.map +1 -1
- package/lib/analyses/SwaggerExampleAnalyzer.d.ts +31 -0
- package/lib/analyses/SwaggerExampleAnalyzer.js +36 -0
- package/lib/analyses/SwaggerExampleAnalyzer.js.map +1 -0
- package/lib/analyses/TypedHttpRouteAnalyzer.d.ts +41 -3
- package/lib/analyses/TypedHttpRouteAnalyzer.js +196 -172
- package/lib/analyses/TypedHttpRouteAnalyzer.js.map +1 -1
- package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +29 -0
- package/lib/analyses/TypedMcpRouteAnalyzer.js +152 -20
- package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -1
- package/lib/analyses/TypedWebSocketRouteAnalyzer.d.ts +19 -0
- package/lib/analyses/TypedWebSocketRouteAnalyzer.js +20 -1
- package/lib/analyses/TypedWebSocketRouteAnalyzer.js.map +1 -1
- package/lib/decorators/OperationMetadata.d.ts +6 -0
- package/lib/decorators/OperationMetadata.js +6 -0
- package/lib/decorators/OperationMetadata.js.map +1 -1
- package/lib/executable/internal/CommandParser.d.ts +19 -0
- package/lib/executable/internal/CommandParser.js +19 -0
- package/lib/executable/internal/CommandParser.js.map +1 -1
- package/lib/executable/internal/NestiaConfigLoader.d.ts +56 -0
- package/lib/executable/internal/NestiaConfigLoader.js +48 -42
- package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
- package/lib/executable/internal/NestiaSdkCommand.d.ts +60 -0
- package/lib/executable/internal/NestiaSdkCommand.js +95 -3
- package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
- package/lib/executable/internal/NestiaSdkWatcher.d.ts +81 -0
- package/lib/executable/internal/NestiaSdkWatcher.js +125 -41
- package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -1
- package/lib/executable/internal/NestiaSwaggerWatch.d.ts +80 -0
- package/lib/executable/internal/NestiaSwaggerWatch.js +308 -0
- package/lib/executable/internal/NestiaSwaggerWatch.js.map +1 -0
- package/lib/executable/sdk.js +16 -1
- package/lib/executable/sdk.js.map +1 -1
- package/lib/factories/ExpressionFactory.d.ts +18 -0
- package/lib/factories/ExpressionFactory.js +21 -2
- package/lib/factories/ExpressionFactory.js.map +1 -1
- package/lib/factories/IdentifierFactory.d.ts +24 -0
- package/lib/factories/IdentifierFactory.js +24 -0
- package/lib/factories/IdentifierFactory.js.map +1 -1
- package/lib/factories/LiteralFactory.d.ts +22 -4
- package/lib/factories/LiteralFactory.js +33 -11
- package/lib/factories/LiteralFactory.js.map +1 -1
- package/lib/factories/StatementFactory.d.ts +28 -1
- package/lib/factories/StatementFactory.js +28 -1
- package/lib/factories/StatementFactory.js.map +1 -1
- package/lib/factories/TypeFactory.d.ts +24 -0
- package/lib/factories/TypeFactory.js +15 -0
- package/lib/factories/TypeFactory.js.map +1 -1
- package/lib/generates/CloneGenerator.d.ts +22 -0
- package/lib/generates/CloneGenerator.js +26 -5
- package/lib/generates/CloneGenerator.js.map +1 -1
- package/lib/generates/E2eGenerator.d.ts +19 -0
- package/lib/generates/E2eGenerator.js +19 -0
- package/lib/generates/E2eGenerator.js.map +1 -1
- package/lib/generates/SdkGenerator.d.ts +38 -0
- package/lib/generates/SdkGenerator.js +43 -5
- package/lib/generates/SdkGenerator.js.map +1 -1
- package/lib/generates/SwaggerGenerator.d.ts +36 -6
- package/lib/generates/SwaggerGenerator.js +190 -40
- package/lib/generates/SwaggerGenerator.js.map +1 -1
- package/lib/generates/internal/E2eFileProgrammer.d.ts +19 -0
- package/lib/generates/internal/E2eFileProgrammer.js +20 -1
- package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
- package/lib/generates/internal/FilePrinter.d.ts +57 -0
- package/lib/generates/internal/FilePrinter.js +74 -0
- package/lib/generates/internal/FilePrinter.js.map +1 -1
- package/lib/generates/internal/ImportDictionary.d.ts +93 -1
- package/lib/generates/internal/ImportDictionary.js +103 -2
- package/lib/generates/internal/ImportDictionary.js.map +1 -1
- package/lib/generates/internal/SdkAliasCollection.d.ts +121 -1
- package/lib/generates/internal/SdkAliasCollection.js +132 -11
- package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
- package/lib/generates/internal/SdkDistributionComposer.d.ts +19 -0
- package/lib/generates/internal/SdkDistributionComposer.js +149 -52
- package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
- package/lib/generates/internal/SdkFileProgrammer.d.ts +18 -0
- package/lib/generates/internal/SdkFileProgrammer.js +101 -12
- package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpCloneProgrammer.d.ts +29 -0
- package/lib/generates/internal/SdkHttpCloneProgrammer.js +33 -0
- package/lib/generates/internal/SdkHttpCloneProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpCloneReferencer.d.ts +19 -0
- package/lib/generates/internal/SdkHttpCloneReferencer.js +78 -25
- package/lib/generates/internal/SdkHttpCloneReferencer.js.map +1 -1
- package/lib/generates/internal/SdkHttpFunctionProgrammer.d.ts +19 -0
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js +40 -23
- package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.d.ts +19 -0
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +46 -34
- package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpParameterProgrammer.d.ts +142 -1
- package/lib/generates/internal/SdkHttpParameterProgrammer.js +175 -6
- package/lib/generates/internal/SdkHttpParameterProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpRouteProgrammer.d.ts +20 -0
- package/lib/generates/internal/SdkHttpRouteProgrammer.js +36 -32
- package/lib/generates/internal/SdkHttpRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkHttpSimulationProgrammer.d.ts +38 -0
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js +66 -18
- package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkImportWizard.d.ts +63 -0
- package/lib/generates/internal/SdkImportWizard.js +63 -0
- package/lib/generates/internal/SdkImportWizard.js.map +1 -1
- package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +17 -2
- package/lib/generates/internal/SdkMcpRouteProgrammer.js +72 -24
- package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkPathTemplate.d.ts +36 -0
- package/lib/generates/internal/SdkPathTemplate.js +65 -0
- package/lib/generates/internal/SdkPathTemplate.js.map +1 -0
- package/lib/generates/internal/SdkRouteDirectory.d.ts +10 -0
- package/lib/generates/internal/SdkRouteDirectory.js +10 -0
- package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
- package/lib/generates/internal/SdkTypeProgrammer.d.ts +54 -1
- package/lib/generates/internal/SdkTypeProgrammer.js +111 -27
- package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkTypeTagProgrammer.d.ts +53 -1
- package/lib/generates/internal/SdkTypeTagProgrammer.js +407 -67
- package/lib/generates/internal/SdkTypeTagProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketCloneProgrammer.d.ts +40 -0
- package/lib/generates/internal/SdkWebSocketCloneProgrammer.js +149 -142
- package/lib/generates/internal/SdkWebSocketCloneProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.d.ts +19 -0
- package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js +46 -36
- package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.d.ts +114 -1
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.js +120 -5
- package/lib/generates/internal/SdkWebSocketParameterProgrammer.js.map +1 -1
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.d.ts +18 -0
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +75 -33
- package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
- package/lib/generates/internal/SwaggerDescriptionComposer.d.ts +42 -0
- package/lib/generates/internal/SwaggerDescriptionComposer.js +44 -1
- package/lib/generates/internal/SwaggerDescriptionComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationComposer.d.ts +18 -0
- package/lib/generates/internal/SwaggerOperationComposer.js +37 -8
- package/lib/generates/internal/SwaggerOperationComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationParameterComposer.d.ts +40 -1
- package/lib/generates/internal/SwaggerOperationParameterComposer.js +230 -25
- package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
- package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +21 -0
- package/lib/generates/internal/SwaggerOperationResponseComposer.js +58 -7
- package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
- package/lib/internal/legacy.d.ts +342 -31
- package/lib/internal/legacy.js +207 -31
- package/lib/internal/legacy.js.map +1 -1
- package/lib/structures/INestiaProject.d.ts +10 -0
- package/lib/structures/INestiaSdkInput.d.ts +30 -0
- package/lib/structures/IOperationMetadata.d.ts +77 -0
- package/lib/structures/IReflectController.d.ts +10 -0
- package/lib/structures/IReflectHttpOperation.d.ts +10 -0
- package/lib/structures/IReflectHttpOperationException.d.ts +13 -2
- package/lib/structures/IReflectHttpOperationParameter.d.ts +55 -3
- package/lib/structures/IReflectHttpOperationSuccess.d.ts +15 -3
- package/lib/structures/IReflectImport.d.ts +10 -0
- package/lib/structures/IReflectMcpOperation.d.ts +19 -0
- package/lib/structures/IReflectMcpOperationParameter.d.ts +9 -0
- package/lib/structures/IReflectOperationError.d.ts +28 -0
- package/lib/structures/IReflectOperationError.js +22 -2
- package/lib/structures/IReflectOperationError.js.map +1 -1
- package/lib/structures/IReflectType.d.ts +9 -0
- package/lib/structures/IReflectWebSocketOperation.d.ts +10 -0
- package/lib/structures/IReflectWebSocketOperationParameter.d.ts +55 -0
- package/lib/structures/ITypedApplication.d.ts +10 -0
- package/lib/structures/ITypedHttpRoute.d.ts +18 -0
- package/lib/structures/ITypedHttpRouteException.d.ts +13 -1
- package/lib/structures/ITypedHttpRouteParameter.d.ts +48 -1
- package/lib/structures/ITypedHttpRouteSuccess.d.ts +13 -1
- package/lib/structures/ITypedMcpRoute.d.ts +11 -0
- package/lib/structures/ITypedWebSocketRoute.d.ts +18 -0
- package/lib/transform.d.ts +6 -0
- package/lib/transform.js +6 -0
- package/lib/transform.js.map +1 -1
- package/lib/utils/ArrayUtil.d.ts +31 -1
- package/lib/utils/ArrayUtil.js +32 -11
- package/lib/utils/ArrayUtil.js.map +1 -1
- package/lib/utils/EmittedJavaScriptPatcher.d.ts +20 -0
- package/lib/utils/EmittedJavaScriptPatcher.js +65 -46
- package/lib/utils/EmittedJavaScriptPatcher.js.map +1 -1
- package/lib/utils/FileRetriever.d.ts +29 -0
- package/lib/utils/FileRetriever.js +29 -0
- package/lib/utils/FileRetriever.js.map +1 -1
- package/lib/utils/HttpResponseContentTypeUtil.d.ts +41 -0
- package/lib/utils/HttpResponseContentTypeUtil.js +31 -0
- package/lib/utils/HttpResponseContentTypeUtil.js.map +1 -1
- package/lib/utils/MapUtil.d.ts +64 -1
- package/lib/utils/MapUtil.js +24 -3
- package/lib/utils/MapUtil.js.map +1 -1
- package/lib/utils/PathUtil.d.ts +31 -0
- package/lib/utils/PathUtil.js +38 -0
- package/lib/utils/PathUtil.js.map +1 -1
- package/lib/utils/SdkBundlePath.d.ts +2 -0
- package/lib/utils/SdkBundlePath.js +10 -0
- package/lib/utils/SdkBundlePath.js.map +1 -0
- package/lib/utils/SdkInputFilter.d.ts +31 -0
- package/lib/utils/SdkInputFilter.js +115 -0
- package/lib/utils/SdkInputFilter.js.map +1 -0
- package/lib/utils/SourceFinder.d.ts +45 -0
- package/lib/utils/SourceFinder.js +74 -16
- package/lib/utils/SourceFinder.js.map +1 -1
- package/lib/utils/StringUtil.d.ts +77 -2
- package/lib/utils/StringUtil.js +166 -4
- package/lib/utils/StringUtil.js.map +1 -1
- package/lib/utils/TemporaryDirectory.d.ts +60 -0
- package/lib/utils/TemporaryDirectory.js +112 -0
- package/lib/utils/TemporaryDirectory.js.map +1 -0
- package/lib/utils/TsConfigReader.d.ts +42 -0
- package/lib/utils/TsConfigReader.js +98 -14
- package/lib/utils/TsConfigReader.js.map +1 -1
- package/lib/utils/TtscExecutor.d.ts +23 -1
- package/lib/utils/TtscExecutor.js +22 -0
- package/lib/utils/TtscExecutor.js.map +1 -1
- package/lib/utils/VersioningStrategy.d.ts +39 -2
- package/lib/utils/VersioningStrategy.js +46 -13
- package/lib/utils/VersioningStrategy.js.map +1 -1
- package/lib/validators/TextPlainValidator.d.ts +19 -0
- package/lib/validators/TextPlainValidator.js +19 -0
- package/lib/validators/TextPlainValidator.js.map +1 -1
- package/native/go.mod +1 -1
- package/native/go.sum +2 -2
- package/native/sdk/register.go +12 -2
- package/native/sdk/sdk_http_rules.go +210 -0
- package/native/sdk/sdk_metadata_json.go +79 -4
- package/native/sdk/sdk_transform.go +1157 -414
- package/package.json +8 -6
- package/src/INestiaConfig.ts +74 -34
- package/src/NestiaSdkApplication.ts +129 -41
- package/src/NestiaSwaggerComposer.ts +32 -14
- package/src/analyses/AccessorAnalyzer.ts +26 -0
- package/src/analyses/ConfigAnalyzer.ts +52 -90
- package/src/analyses/ImportAnalyzer.ts +25 -0
- package/src/analyses/ParameterNameAnalyzer.ts +75 -0
- package/src/analyses/PathAnalyzer.ts +167 -23
- package/src/analyses/ReflectControllerAnalyzer.ts +34 -1
- package/src/analyses/ReflectHttpOperationAnalyzer.ts +36 -14
- package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +31 -0
- package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +153 -39
- package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +38 -7
- package/src/analyses/ReflectMcpOperationAnalyzer.ts +38 -3
- package/src/analyses/ReflectMetadataAnalyzer.ts +47 -0
- package/src/analyses/ReflectWebSocketOperationAnalyzer.ts +40 -7
- package/src/analyses/SecurityAnalyzer.ts +45 -20
- package/src/analyses/SwaggerExampleAnalyzer.ts +39 -0
- package/src/analyses/TypedHttpRouteAnalyzer.ts +263 -198
- package/src/analyses/TypedMcpRouteAnalyzer.ts +180 -20
- package/src/analyses/TypedWebSocketRouteAnalyzer.ts +20 -0
- package/src/decorators/OperationMetadata.ts +6 -0
- package/src/executable/internal/CommandParser.ts +19 -0
- package/src/executable/internal/NestiaConfigLoader.ts +66 -50
- package/src/executable/internal/NestiaSdkCommand.ts +96 -3
- package/src/executable/internal/NestiaSdkWatcher.ts +172 -44
- package/src/executable/internal/NestiaSwaggerWatch.ts +359 -0
- package/src/executable/sdk.ts +24 -1
- package/src/factories/ExpressionFactory.ts +23 -2
- package/src/factories/IdentifierFactory.ts +24 -0
- package/src/factories/LiteralFactory.ts +51 -13
- package/src/factories/StatementFactory.ts +28 -1
- package/src/factories/TypeFactory.ts +24 -0
- package/src/generates/CloneGenerator.ts +26 -5
- package/src/generates/E2eGenerator.ts +19 -0
- package/src/generates/SdkGenerator.ts +43 -12
- package/src/generates/SwaggerGenerator.ts +229 -46
- package/src/generates/internal/E2eFileProgrammer.ts +20 -0
- package/src/generates/internal/FilePrinter.ts +71 -0
- package/src/generates/internal/ImportDictionary.ts +120 -2
- package/src/generates/internal/SdkAliasCollection.ts +144 -15
- package/src/generates/internal/SdkDistributionComposer.ts +181 -56
- package/src/generates/internal/SdkFileProgrammer.ts +126 -16
- package/src/generates/internal/SdkHttpCloneProgrammer.ts +55 -0
- package/src/generates/internal/SdkHttpCloneReferencer.ts +97 -23
- package/src/generates/internal/SdkHttpFunctionProgrammer.ts +47 -30
- package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +53 -64
- package/src/generates/internal/SdkHttpParameterProgrammer.ts +295 -13
- package/src/generates/internal/SdkHttpRouteProgrammer.ts +37 -31
- package/src/generates/internal/SdkHttpSimulationProgrammer.ts +122 -18
- package/src/generates/internal/SdkImportWizard.ts +63 -0
- package/src/generates/internal/SdkMcpRouteProgrammer.ts +150 -33
- package/src/generates/internal/SdkPathTemplate.ts +88 -0
- package/src/generates/internal/SdkRouteDirectory.ts +10 -0
- package/src/generates/internal/SdkTypeProgrammer.ts +137 -36
- package/src/generates/internal/SdkTypeTagProgrammer.ts +536 -78
- package/src/generates/internal/SdkWebSocketCloneProgrammer.ts +185 -142
- package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +62 -68
- package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +205 -5
- package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +89 -48
- package/src/generates/internal/SwaggerDescriptionComposer.ts +47 -1
- package/src/generates/internal/SwaggerOperationComposer.ts +38 -6
- package/src/generates/internal/SwaggerOperationParameterComposer.ts +318 -36
- package/src/generates/internal/SwaggerOperationResponseComposer.ts +74 -11
- package/src/internal/legacy-augmentation.d.ts +16 -0
- package/src/internal/legacy.ts +407 -49
- package/src/structures/INestiaProject.ts +10 -0
- package/src/structures/INestiaSdkInput.ts +30 -0
- package/src/structures/IOperationMetadata.ts +83 -0
- package/src/structures/IReflectController.ts +10 -0
- package/src/structures/IReflectHttpOperation.ts +10 -0
- package/src/structures/IReflectHttpOperationException.ts +17 -2
- package/src/structures/IReflectHttpOperationParameter.ts +56 -2
- package/src/structures/IReflectHttpOperationSuccess.ts +19 -3
- package/src/structures/IReflectImport.ts +10 -0
- package/src/structures/IReflectMcpOperation.ts +19 -0
- package/src/structures/IReflectMcpOperationParameter.ts +9 -0
- package/src/structures/IReflectOperationError.ts +31 -2
- package/src/structures/IReflectType.ts +9 -0
- package/src/structures/IReflectWebSocketOperation.ts +10 -0
- package/src/structures/IReflectWebSocketOperationParameter.ts +55 -0
- package/src/structures/ITypedApplication.ts +10 -0
- package/src/structures/ITypedHttpRoute.ts +18 -0
- package/src/structures/ITypedHttpRouteException.ts +14 -1
- package/src/structures/ITypedHttpRouteParameter.ts +48 -1
- package/src/structures/ITypedHttpRouteSuccess.ts +14 -1
- package/src/structures/ITypedMcpRoute.ts +12 -0
- package/src/structures/ITypedWebSocketRoute.ts +18 -0
- package/src/transform.ts +6 -0
- package/src/utils/ArrayUtil.ts +32 -13
- package/src/utils/EmittedJavaScriptPatcher.ts +70 -47
- package/src/utils/FileRetriever.ts +29 -0
- package/src/utils/HttpResponseContentTypeUtil.ts +41 -0
- package/src/utils/MapUtil.ts +68 -3
- package/src/utils/PathUtil.ts +38 -0
- package/src/utils/SdkBundlePath.ts +11 -0
- package/src/utils/SdkInputFilter.ts +108 -0
- package/src/utils/SourceFinder.ts +76 -18
- package/src/utils/StringUtil.ts +182 -5
- package/src/utils/TemporaryDirectory.ts +108 -0
- package/src/utils/TsConfigReader.ts +124 -26
- package/src/utils/TtscExecutor.ts +23 -1
- package/src/utils/VersioningStrategy.ts +51 -16
- package/src/validators/TextPlainValidator.ts +19 -0
- package/lib/generates/internal/SwaggerReadonlyArrayEmender.d.ts +0 -9
- package/lib/generates/internal/SwaggerReadonlyArrayEmender.js +0 -178
- package/lib/generates/internal/SwaggerReadonlyArrayEmender.js.map +0 -1
- package/lib/validators/HttpHeadersValidator.d.ts +0 -11
- package/lib/validators/HttpHeadersValidator.js +0 -15
- package/lib/validators/HttpHeadersValidator.js.map +0 -1
- package/lib/validators/HttpQueryValidator.d.ts +0 -10
- package/lib/validators/HttpQueryValidator.js +0 -14
- package/lib/validators/HttpQueryValidator.js.map +0 -1
- package/src/generates/internal/SwaggerReadonlyArrayEmender.ts +0 -264
- package/src/validators/HttpHeadersValidator.ts +0 -12
- package/src/validators/HttpQueryValidator.ts +0 -11
|
@@ -1,18 +1,23 @@
|
|
|
1
1
|
package sdk
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
|
+
"encoding"
|
|
4
5
|
"encoding/json"
|
|
5
6
|
"fmt"
|
|
7
|
+
"math"
|
|
6
8
|
"os"
|
|
7
9
|
"path/filepath"
|
|
8
|
-
"
|
|
10
|
+
"reflect"
|
|
9
11
|
"sort"
|
|
12
|
+
"strconv"
|
|
10
13
|
"strings"
|
|
11
14
|
|
|
12
15
|
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
13
16
|
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
17
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
14
18
|
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
15
19
|
"github.com/samchon/nestia/packages/core/native/transform"
|
|
20
|
+
"github.com/samchon/nestia/packages/core/native/transform/schemaprojection"
|
|
16
21
|
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
17
22
|
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
18
23
|
nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
|
|
@@ -40,9 +45,10 @@ type nestiaSDKContext struct {
|
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
type nestiaSDKSchemaCacheKey struct {
|
|
43
|
-
Type
|
|
44
|
-
Text
|
|
45
|
-
Escape
|
|
48
|
+
Type *shimchecker.Type
|
|
49
|
+
Text string
|
|
50
|
+
Escape bool
|
|
51
|
+
Properties bool
|
|
46
52
|
}
|
|
47
53
|
|
|
48
54
|
func newNestiaSDKContext(prog *driver.Program) *nestiaSDKContext {
|
|
@@ -148,7 +154,16 @@ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile,
|
|
|
148
154
|
for index, param := range methodDecl.Parameters.Nodes {
|
|
149
155
|
typ := prog.Checker.GetTypeAtLocation(param)
|
|
150
156
|
name := nestiaSDKParameterName(param)
|
|
151
|
-
response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param))
|
|
157
|
+
response := nestiaSDKResponse(context, imports, typ, nestiaSDKParameterTypeNode(param), true)
|
|
158
|
+
if ref, refs, err := nestiaSDKWebSocketParameterType(context, param); err != nil {
|
|
159
|
+
return "", err
|
|
160
|
+
} else if ref != nil {
|
|
161
|
+
response["type"] = ref
|
|
162
|
+
response["imports"] = refs
|
|
163
|
+
}
|
|
164
|
+
if err := nestiaSDKWebSocketHeaderError(context.prog, param, typ); err != nil {
|
|
165
|
+
return "", err
|
|
166
|
+
}
|
|
152
167
|
parameters = append(parameters, map[string]any{
|
|
153
168
|
"name": name,
|
|
154
169
|
"index": index,
|
|
@@ -166,7 +181,7 @@ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile,
|
|
|
166
181
|
exceptions := nestiaSDKExceptionResponses(context, imports, method)
|
|
167
182
|
metadata := map[string]any{
|
|
168
183
|
"parameters": parameters,
|
|
169
|
-
"success": nestiaSDKResponse(context, imports, returnType, returnTypeNode),
|
|
184
|
+
"success": nestiaSDKResponse(context, imports, returnType, returnTypeNode, false),
|
|
170
185
|
"exceptions": exceptions,
|
|
171
186
|
"description": nestiaSDKNullableString(doc.Description),
|
|
172
187
|
"jsDocTags": doc.Tags,
|
|
@@ -174,16 +189,98 @@ func nestiaSDKMetadataText(context *nestiaSDKContext, file *shimast.SourceFile,
|
|
|
174
189
|
return nestiaSDKMetadataLiteralText(metadata)
|
|
175
190
|
}
|
|
176
191
|
|
|
177
|
-
|
|
192
|
+
// An explicit JSON null must have a distinct Go representation from authored
|
|
193
|
+
// strings, including strings that happen to resemble an internal marker.
|
|
194
|
+
var nestiaSDKLiteralNull = nativefactories.LiteralFactory_Null{}
|
|
178
195
|
|
|
179
196
|
func nestiaSDKMetadataLiteralText(metadata map[string]any) (string, error) {
|
|
180
|
-
data, err := json.Marshal(metadata)
|
|
197
|
+
data, err := json.Marshal(nestiaSDKFiniteLiteral(metadata))
|
|
181
198
|
if err != nil {
|
|
182
199
|
return "", err
|
|
183
200
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
201
|
+
return string(data), nil
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// nestiaSDKFiniteLiteral copies a metadata value for encoding/json, writing
|
|
205
|
+
// every non-finite number as null, the way JavaScript's `JSON.stringify` does.
|
|
206
|
+
//
|
|
207
|
+
// typia hands over NaN and positive or negative Infinity wherever a type or a comment spells one: a
|
|
208
|
+
// numeric literal type such as `1e999`, a type tag argument such as
|
|
209
|
+
// `tags.Minimum<1e999>`, and a JSDoc `@x-` extension whose text parses as
|
|
210
|
+
// a float, which `NaN`, `Infinity`, and `inf` all do. encoding/json
|
|
211
|
+
// refuses those values, so one of them anywhere in the types a route reaches
|
|
212
|
+
// used to abort the whole metadata pass. The metadata is marshaled only here,
|
|
213
|
+
// so walking it here gives every value, baked schema and metadata literal
|
|
214
|
+
// alike, the same rule.
|
|
215
|
+
//
|
|
216
|
+
// The walk rebuilds maps, slices, and typia's ordered objects, whose
|
|
217
|
+
// MarshalJSON would otherwise marshal a non-finite member itself. A null is
|
|
218
|
+
// written as typia's explicit `LiteralFactory_Null` marker rather than a Go
|
|
219
|
+
// nil, because an ordered object drops a nil member instead of printing it,
|
|
220
|
+
// while `JSON.stringify({ a: NaN })` keeps the key. Any other value that
|
|
221
|
+
// marshals itself is left as it is.
|
|
222
|
+
func nestiaSDKFiniteLiteral(input any) any {
|
|
223
|
+
switch value := input.(type) {
|
|
224
|
+
case nil:
|
|
225
|
+
return nil
|
|
226
|
+
case nativefactories.LiteralFactory_OrderedObject:
|
|
227
|
+
return nestiaSDKFiniteOrderedLiteral(value)
|
|
228
|
+
case *nativefactories.LiteralFactory_OrderedObject:
|
|
229
|
+
if value == nil {
|
|
230
|
+
return value
|
|
231
|
+
}
|
|
232
|
+
return nestiaSDKFiniteOrderedLiteral(*value)
|
|
233
|
+
case json.Marshaler, encoding.TextMarshaler:
|
|
234
|
+
return value
|
|
235
|
+
}
|
|
236
|
+
reflected := reflect.ValueOf(input)
|
|
237
|
+
switch reflected.Kind() {
|
|
238
|
+
case reflect.Float32, reflect.Float64:
|
|
239
|
+
// by kind, because the checker's numbers are typescript-go's named
|
|
240
|
+
// `jsnum.Number`, not a plain float64
|
|
241
|
+
if number := reflected.Float(); math.IsNaN(number) || math.IsInf(number, 0) {
|
|
242
|
+
return nativefactories.LiteralFactory_Null{}
|
|
243
|
+
}
|
|
244
|
+
return input
|
|
245
|
+
case reflect.Map:
|
|
246
|
+
if reflected.IsNil() || reflected.Type().Key().Kind() != reflect.String {
|
|
247
|
+
return input
|
|
248
|
+
}
|
|
249
|
+
output := make(map[string]any, reflected.Len())
|
|
250
|
+
iterator := reflected.MapRange()
|
|
251
|
+
for iterator.Next() {
|
|
252
|
+
output[iterator.Key().String()] = nestiaSDKFiniteLiteral(iterator.Value().Interface())
|
|
253
|
+
}
|
|
254
|
+
return output
|
|
255
|
+
case reflect.Slice, reflect.Array:
|
|
256
|
+
if (reflected.Kind() == reflect.Slice && reflected.IsNil()) || reflected.Type().Elem().Kind() == reflect.Uint8 {
|
|
257
|
+
return input
|
|
258
|
+
}
|
|
259
|
+
output := make([]any, reflected.Len())
|
|
260
|
+
for i := range output {
|
|
261
|
+
output[i] = nestiaSDKFiniteLiteral(reflected.Index(i).Interface())
|
|
262
|
+
}
|
|
263
|
+
return output
|
|
264
|
+
case reflect.Pointer, reflect.Interface:
|
|
265
|
+
if reflected.IsNil() {
|
|
266
|
+
return input
|
|
267
|
+
}
|
|
268
|
+
return nestiaSDKFiniteLiteral(reflected.Elem().Interface())
|
|
269
|
+
}
|
|
270
|
+
return input
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
func nestiaSDKFiniteOrderedLiteral(
|
|
274
|
+
input nativefactories.LiteralFactory_OrderedObject,
|
|
275
|
+
) nativefactories.LiteralFactory_OrderedObject {
|
|
276
|
+
output := nativefactories.LiteralFactory_OrderedObject{
|
|
277
|
+
Keys: input.Keys,
|
|
278
|
+
Values: make(map[string]any, len(input.Values)),
|
|
279
|
+
}
|
|
280
|
+
for key, value := range input.Values {
|
|
281
|
+
output.Values[key] = nestiaSDKFiniteLiteral(value)
|
|
282
|
+
}
|
|
283
|
+
return output
|
|
187
284
|
}
|
|
188
285
|
|
|
189
286
|
type nestiaSDKJSDoc struct {
|
|
@@ -205,34 +302,83 @@ func nestiaSDKMethodJSDoc(file *shimast.SourceFile, method *shimast.Node) nestia
|
|
|
205
302
|
if comment == "" {
|
|
206
303
|
return doc
|
|
207
304
|
}
|
|
305
|
+
// A tag runs until the next one, and each line loses the comment's margin
|
|
306
|
+
// as TypeScript takes it off: the `*`, then the indentation up to the
|
|
307
|
+
// column the text began at. That is the description's first line, the
|
|
308
|
+
// text after a tag's name, a `@param` description past the parameter's
|
|
309
|
+
// name, a `@returns` one past its type, or, when the tag's text starts on
|
|
310
|
+
// the next line, the tag itself, so an `@example` keeps its code's
|
|
311
|
+
// indentation while a wrapped `@param` description does not.
|
|
208
312
|
description := []string{}
|
|
209
|
-
|
|
313
|
+
descriptionMargin := -1
|
|
314
|
+
var tag *nestiaSDKPendingTag
|
|
315
|
+
flush := func() {
|
|
316
|
+
if tag == nil {
|
|
317
|
+
return
|
|
318
|
+
}
|
|
319
|
+
body := strings.TrimLeft(strings.Join(tag.lines, "\n"), "\n")
|
|
320
|
+
body = strings.TrimRight(body, " \t\n")
|
|
321
|
+
if tag.name == "param" {
|
|
322
|
+
param, desc := nestiaSDKParseParamTag(body)
|
|
323
|
+
doc.Tags = append(doc.Tags, nestiaSDKJSDocParamTag(param, desc))
|
|
324
|
+
if param != "" {
|
|
325
|
+
doc.Params[param] = desc
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
if nestiaSDKIsReturnTag(tag.name) {
|
|
329
|
+
// TypeScript's text leaves out a type the tag declares
|
|
330
|
+
body = strings.TrimLeft(body[nestiaSDKReturnTypeEnd(body):], "\n")
|
|
331
|
+
}
|
|
332
|
+
doc.Tags = append(doc.Tags, nestiaSDKJSDocTag(tag.name, body))
|
|
333
|
+
}
|
|
334
|
+
tag = nil
|
|
335
|
+
}
|
|
210
336
|
for _, line := range strings.Split(comment, "\n") {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
337
|
+
rest := strings.TrimLeft(line, " \t")
|
|
338
|
+
rest = strings.TrimPrefix(rest, "*")
|
|
339
|
+
rest = strings.TrimRight(rest, " \t\r")
|
|
340
|
+
trimmed := strings.TrimSpace(rest)
|
|
341
|
+
if strings.HasPrefix(trimmed, "@") {
|
|
342
|
+
flush()
|
|
343
|
+
name, body := nestiaSDKParseJSDocTag(trimmed)
|
|
344
|
+
margin := nestiaSDKJSDocIndent(rest)
|
|
345
|
+
// where the text begins in the body: a `@param` description after
|
|
346
|
+
// the parameter's name, a `@returns` one after its type
|
|
347
|
+
text := 0
|
|
348
|
+
if name == "param" {
|
|
349
|
+
_, text = nestiaSDKParamTagName(body)
|
|
350
|
+
} else if nestiaSDKIsReturnTag(name) {
|
|
351
|
+
text = nestiaSDKReturnTypeEnd(body)
|
|
352
|
+
}
|
|
353
|
+
if text < len(body) {
|
|
354
|
+
after := trimmed[1+len(name):]
|
|
355
|
+
margin += 1 + len(name) + len(after) - len(strings.TrimLeft(after, " \t")) + text
|
|
217
356
|
}
|
|
357
|
+
tag = &nestiaSDKPendingTag{name: name, lines: []string{body}, margin: margin}
|
|
218
358
|
continue
|
|
219
359
|
}
|
|
220
|
-
if
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
doc.Params[param] = desc
|
|
228
|
-
}
|
|
360
|
+
if tag != nil {
|
|
361
|
+
tag.lines = append(tag.lines, nestiaSDKJSDocOutdent(rest, tag.margin))
|
|
362
|
+
continue
|
|
363
|
+
}
|
|
364
|
+
if trimmed == "" {
|
|
365
|
+
if len(description) != 0 {
|
|
366
|
+
description = append(description, "")
|
|
229
367
|
}
|
|
230
368
|
continue
|
|
231
369
|
}
|
|
232
|
-
if
|
|
233
|
-
|
|
370
|
+
if descriptionMargin == -1 {
|
|
371
|
+
// text on the opening `/**` line has no `*` to measure from, so the
|
|
372
|
+
// lines after it keep the usual `* ` margin
|
|
373
|
+
if strings.HasPrefix(strings.TrimLeft(line, " \t"), "*") {
|
|
374
|
+
descriptionMargin = nestiaSDKJSDocIndent(rest)
|
|
375
|
+
} else {
|
|
376
|
+
descriptionMargin = 1
|
|
377
|
+
}
|
|
234
378
|
}
|
|
379
|
+
description = append(description, nestiaSDKJSDocOutdent(rest, descriptionMargin))
|
|
235
380
|
}
|
|
381
|
+
flush()
|
|
236
382
|
doc.Description = strings.TrimSpace(strings.Join(description, "\n"))
|
|
237
383
|
return doc
|
|
238
384
|
}
|
|
@@ -288,14 +434,108 @@ func nestiaSDKParseJSDocTag(text string) (string, string) {
|
|
|
288
434
|
return name, body
|
|
289
435
|
}
|
|
290
436
|
|
|
437
|
+
// nestiaSDKPendingTag is a JSDoc tag whose text may continue on the next
|
|
438
|
+
// lines.
|
|
439
|
+
type nestiaSDKPendingTag struct {
|
|
440
|
+
name string
|
|
441
|
+
lines []string
|
|
442
|
+
margin int
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// nestiaSDKJSDocIndent counts the spaces and tabs a JSDoc line starts with.
|
|
446
|
+
func nestiaSDKJSDocIndent(line string) int {
|
|
447
|
+
return len(line) - len(strings.TrimLeft(line, " \t"))
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// nestiaSDKJSDocOutdent takes up to margin spaces and tabs off a JSDoc line.
|
|
451
|
+
func nestiaSDKJSDocOutdent(line string, margin int) string {
|
|
452
|
+
indent := nestiaSDKJSDocIndent(line)
|
|
453
|
+
if indent > margin {
|
|
454
|
+
indent = margin
|
|
455
|
+
}
|
|
456
|
+
return line[indent:]
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// nestiaSDKParseParamTag splits a `@param` body into the parameter's name and
|
|
460
|
+
// its description, as TypeScript does. A description that starts on the next
|
|
461
|
+
// line keeps the indentation its margin leaves it.
|
|
291
462
|
func nestiaSDKParseParamTag(body string) (string, string) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
463
|
+
param, text := nestiaSDKParamTagName(body)
|
|
464
|
+
return param, strings.TrimLeft(body[text:], "\n")
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// nestiaSDKParamTagName reads the parameter a `@param` body names, as
|
|
468
|
+
// TypeScript does: a leading `{Type}` is not the name, and an optional
|
|
469
|
+
// parameter's brackets and default, `[name=value]`, are not part of it. It
|
|
470
|
+
// also returns where the description begins, past the spaces after the name.
|
|
471
|
+
func nestiaSDKParamTagName(body string) (string, int) {
|
|
472
|
+
offset := nestiaSDKSkipBlank(body, 0, true)
|
|
473
|
+
if end := nestiaSDKClosing(body, offset, '{', '}'); end != -1 {
|
|
474
|
+
offset = nestiaSDKSkipBlank(body, end+1, true)
|
|
295
475
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
476
|
+
name := ""
|
|
477
|
+
if end := nestiaSDKClosing(body, offset, '[', ']'); end != -1 {
|
|
478
|
+
// the bracket that closes the first, past any in a default value
|
|
479
|
+
name = body[offset+1 : end]
|
|
480
|
+
if equal := strings.Index(name, "="); equal != -1 {
|
|
481
|
+
name = name[:equal]
|
|
482
|
+
}
|
|
483
|
+
name = strings.TrimSpace(name)
|
|
484
|
+
offset = end + 1
|
|
485
|
+
} else {
|
|
486
|
+
start := offset
|
|
487
|
+
for offset < len(body) && strings.IndexByte(" \t\n", body[offset]) == -1 {
|
|
488
|
+
offset++
|
|
489
|
+
}
|
|
490
|
+
name = body[start:offset]
|
|
491
|
+
}
|
|
492
|
+
return name, nestiaSDKSkipBlank(body, offset, false)
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// nestiaSDKIsReturnTag tells a `@returns` tag, which TypeScript also reads as
|
|
496
|
+
// `@return`.
|
|
497
|
+
func nestiaSDKIsReturnTag(name string) bool {
|
|
498
|
+
return name == "returns" || name == "return"
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
// nestiaSDKReturnTypeEnd returns where a `@returns` body's text begins past a
|
|
502
|
+
// leading `{Type}`, which TypeScript reads as the type rather than the text,
|
|
503
|
+
// and the spaces after it; 0 without a type.
|
|
504
|
+
func nestiaSDKReturnTypeEnd(body string) int {
|
|
505
|
+
if end := nestiaSDKClosing(body, nestiaSDKSkipBlank(body, 0, true), '{', '}'); end != -1 {
|
|
506
|
+
return nestiaSDKSkipBlank(body, end+1, false)
|
|
507
|
+
}
|
|
508
|
+
return 0
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// nestiaSDKClosing finds the bracket closing the one text opens at start, or
|
|
512
|
+
// -1 when text opens none there or never closes it.
|
|
513
|
+
func nestiaSDKClosing(text string, start int, open byte, close byte) int {
|
|
514
|
+
if start >= len(text) || text[start] != open {
|
|
515
|
+
return -1
|
|
516
|
+
}
|
|
517
|
+
depth := 0
|
|
518
|
+
for i := start; i < len(text); i++ {
|
|
519
|
+
if text[i] == open {
|
|
520
|
+
depth++
|
|
521
|
+
} else if text[i] == close {
|
|
522
|
+
depth--
|
|
523
|
+
if depth == 0 {
|
|
524
|
+
return i
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
return -1
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// nestiaSDKSkipBlank moves offset past spaces and tabs, and line breaks too
|
|
532
|
+
// when newline is set.
|
|
533
|
+
func nestiaSDKSkipBlank(text string, offset int, newline bool) int {
|
|
534
|
+
for offset < len(text) &&
|
|
535
|
+
(text[offset] == ' ' || text[offset] == '\t' || (newline && text[offset] == '\n')) {
|
|
536
|
+
offset++
|
|
537
|
+
}
|
|
538
|
+
return offset
|
|
299
539
|
}
|
|
300
540
|
|
|
301
541
|
func nestiaSDKJSDocTag(name string, text string) map[string]any {
|
|
@@ -315,6 +555,33 @@ func nestiaSDKJSDocTag(name string, text string) map[string]any {
|
|
|
315
555
|
}
|
|
316
556
|
}
|
|
317
557
|
|
|
558
|
+
// nestiaSDKJSDocParamTag writes a `@param` tag the way TypeScript's
|
|
559
|
+
// `JSDocTagInfo` does, which the SDK generators read: the parameter's name
|
|
560
|
+
// as its own `parameterName` part, then a space and the description. Written
|
|
561
|
+
// as one text part, the name could not be matched, so a WebSocket route lost
|
|
562
|
+
// every `@param` line and the generators' tag fallbacks never applied.
|
|
563
|
+
func nestiaSDKJSDocParamTag(param string, desc string) map[string]any {
|
|
564
|
+
if param == "" {
|
|
565
|
+
return nestiaSDKJSDocTag("param", "")
|
|
566
|
+
}
|
|
567
|
+
text := []any{
|
|
568
|
+
map[string]any{
|
|
569
|
+
"text": param,
|
|
570
|
+
"kind": "parameterName",
|
|
571
|
+
},
|
|
572
|
+
}
|
|
573
|
+
if desc != "" {
|
|
574
|
+
text = append(text,
|
|
575
|
+
map[string]any{"text": " ", "kind": "space"},
|
|
576
|
+
map[string]any{"text": desc, "kind": "text"},
|
|
577
|
+
)
|
|
578
|
+
}
|
|
579
|
+
return map[string]any{
|
|
580
|
+
"name": "param",
|
|
581
|
+
"text": text,
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
318
585
|
func nestiaSDKNullableString(value string) any {
|
|
319
586
|
value = strings.TrimSpace(value)
|
|
320
587
|
if value == "" {
|
|
@@ -335,7 +602,7 @@ func nestiaSDKExceptionResponses(
|
|
|
335
602
|
if exception == nil {
|
|
336
603
|
continue
|
|
337
604
|
}
|
|
338
|
-
responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node))
|
|
605
|
+
responses = append(responses, nestiaSDKResponse(context, imports, exception.Type, exception.Node, false))
|
|
339
606
|
}
|
|
340
607
|
return responses
|
|
341
608
|
}
|
|
@@ -354,7 +621,9 @@ func nestiaSDKTypedExceptionInfo(prog *driver.Program, decorator *shimast.Node)
|
|
|
354
621
|
return nil
|
|
355
622
|
}
|
|
356
623
|
call := expression.AsCallExpression()
|
|
357
|
-
|
|
624
|
+
// by the export the import binds, as the runtime registers an aliased
|
|
625
|
+
// `TypedException` too; a lexical name lost it, misaligning the exceptions
|
|
626
|
+
segments := transform.NestiaCoreCanonicalDecoratorSegments(decorator)
|
|
358
627
|
if len(segments) == 0 || segments[len(segments)-1] != "TypedException" {
|
|
359
628
|
return nil
|
|
360
629
|
}
|
|
@@ -449,43 +718,67 @@ func nestiaSDKNormalizeImportPath(fileName string, module string) string {
|
|
|
449
718
|
return filepath.ToSlash(filepath.Join("node_modules", module))
|
|
450
719
|
}
|
|
451
720
|
|
|
452
|
-
|
|
453
|
-
|
|
721
|
+
// nestiaSDKReflectImports collects imports from actual type references and
|
|
722
|
+
// typeof queries. Literal values, property names and template text do not bind
|
|
723
|
+
// imports, and named library types are classified by declaration provenance.
|
|
724
|
+
func nestiaSDKReflectImports(prog *driver.Program, node *shimast.Node, imports []nestiaSDKImportInfo) []any {
|
|
454
725
|
output := []any{}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
726
|
+
add := func(entity *shimast.Node) {
|
|
727
|
+
if entity == nil {
|
|
728
|
+
return
|
|
729
|
+
}
|
|
730
|
+
root := entity
|
|
731
|
+
for root != nil && root.Kind == shimast.KindQualifiedName {
|
|
732
|
+
root = root.AsQualifiedName().Left
|
|
733
|
+
}
|
|
734
|
+
if root == nil || root.Kind != shimast.KindIdentifier {
|
|
735
|
+
return
|
|
736
|
+
}
|
|
737
|
+
prefix := root.Text()
|
|
738
|
+
if nestiaSDKIsTypeKeyword(prefix) {
|
|
739
|
+
return
|
|
740
|
+
}
|
|
741
|
+
if prog != nil && prog.Checker != nil {
|
|
742
|
+
symbol := prog.Checker.GetSymbolAtLocation(root)
|
|
743
|
+
if symbol != nil && symbol.Flags&shimast.SymbolFlagsAlias != 0 {
|
|
744
|
+
prefixes := map[string]bool{prefix: true}
|
|
745
|
+
for _, imp := range imports {
|
|
746
|
+
if nestiaSDKImportMatches(prefixes, imp) {
|
|
747
|
+
output = append(output, nestiaSDKImportLiteral(imp, prefixes))
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return
|
|
751
|
+
}
|
|
464
752
|
}
|
|
465
|
-
|
|
466
|
-
output = append(output, item)
|
|
753
|
+
output = append(output, nestiaSDKReflectTypeReferenceSymbolImport(prog, entity, nestiaSDKEntityNameText(entity))...)
|
|
467
754
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
output := map[string]bool{}
|
|
473
|
-
re := regexp.MustCompile(`[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)*`)
|
|
474
|
-
for _, match := range re.FindAllString(name, -1) {
|
|
475
|
-
prefix := strings.Split(match, ".")[0]
|
|
476
|
-
if nestiaSDKIsGlobalTypePrefix(prefix) == false {
|
|
477
|
-
output[prefix] = true
|
|
755
|
+
var walk func(*shimast.Node)
|
|
756
|
+
walk = func(current *shimast.Node) {
|
|
757
|
+
if current == nil {
|
|
758
|
+
return
|
|
478
759
|
}
|
|
760
|
+
switch current.Kind {
|
|
761
|
+
case shimast.KindTypeReference:
|
|
762
|
+
add(current.AsTypeReferenceNode().TypeName)
|
|
763
|
+
case shimast.KindTypeQuery:
|
|
764
|
+
add(current.AsTypeQueryNode().ExprName)
|
|
765
|
+
}
|
|
766
|
+
current.ForEachChild(func(child *shimast.Node) bool { walk(child); return false })
|
|
479
767
|
}
|
|
480
|
-
|
|
768
|
+
if node != nil && (node.Kind == shimast.KindIdentifier || node.Kind == shimast.KindQualifiedName) {
|
|
769
|
+
add(node)
|
|
770
|
+
} else {
|
|
771
|
+
walk(node)
|
|
772
|
+
}
|
|
773
|
+
return nestiaSDKMergeImportLiterals(output)
|
|
481
774
|
}
|
|
482
775
|
|
|
483
|
-
|
|
776
|
+
// nestiaSDKIsTypeKeyword reports type spellings that cannot name an imported
|
|
777
|
+
// declaration. Library names such as Date and Array can be shadowed in a module.
|
|
778
|
+
func nestiaSDKIsTypeKeyword(prefix string) bool {
|
|
484
779
|
switch prefix {
|
|
485
780
|
case "any", "unknown", "never", "void", "null", "undefined",
|
|
486
|
-
"string", "number", "boolean", "bigint", "symbol", "object"
|
|
487
|
-
"Array", "ReadonlyArray", "Promise", "Record", "Partial", "Pick", "Omit",
|
|
488
|
-
"Date", "File", "Blob", "Uint8Array", "ArrayBuffer", "Error":
|
|
781
|
+
"string", "number", "boolean", "bigint", "symbol", "object":
|
|
489
782
|
return true
|
|
490
783
|
default:
|
|
491
784
|
return false
|
|
@@ -538,11 +831,16 @@ func nestiaSDKImportLiteral(imp nestiaSDKImportInfo, prefixes map[string]bool) m
|
|
|
538
831
|
return output
|
|
539
832
|
}
|
|
540
833
|
|
|
834
|
+
// nestiaSDKResponse reflects one route input or output. `parameter` marks a
|
|
835
|
+
// method parameter: the Swagger generator reads query and headers parameters
|
|
836
|
+
// from the resolved schema and may decompose their object into one OpenAPI
|
|
837
|
+
// parameter per property, so only that schema bakes the property schemas.
|
|
541
838
|
func nestiaSDKResponse(
|
|
542
839
|
context *nestiaSDKContext,
|
|
543
840
|
imports []nestiaSDKImportInfo,
|
|
544
841
|
typ *shimchecker.Type,
|
|
545
842
|
typeNode *shimast.Node,
|
|
843
|
+
parameter bool,
|
|
546
844
|
) map[string]any {
|
|
547
845
|
prog := context.prog
|
|
548
846
|
refType, refImports := nestiaSDKReflectType(prog, imports, typ, typeNode)
|
|
@@ -552,17 +850,18 @@ func nestiaSDKResponse(
|
|
|
552
850
|
return map[string]any{
|
|
553
851
|
"type": refType,
|
|
554
852
|
"imports": refImports,
|
|
555
|
-
"primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true),
|
|
556
|
-
"resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false),
|
|
853
|
+
"primitive": nestiaSDKSchemaPipe(context, typ, typeNode, true, false),
|
|
854
|
+
"resolved": nestiaSDKSchemaPipe(context, typ, typeNode, false, parameter),
|
|
557
855
|
}
|
|
558
856
|
}
|
|
559
857
|
|
|
560
|
-
func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool) any {
|
|
858
|
+
func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeNode *shimast.Node, escape bool, properties bool) any {
|
|
561
859
|
prog := context.prog
|
|
562
860
|
key := nestiaSDKSchemaCacheKey{
|
|
563
|
-
Type:
|
|
564
|
-
Text:
|
|
565
|
-
Escape:
|
|
861
|
+
Type: typ,
|
|
862
|
+
Text: nestiaSDKTypeNodeText(typeNode),
|
|
863
|
+
Escape: escape,
|
|
864
|
+
Properties: properties,
|
|
566
865
|
}
|
|
567
866
|
if cached, ok := context.schemaCache[key]; ok {
|
|
568
867
|
context.schemaHits++
|
|
@@ -614,18 +913,23 @@ func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeN
|
|
|
614
913
|
// JSON-schema representation (e.g. a `void` route return or a parameter
|
|
615
914
|
// whose only members are functions). The legacy reader on the JS side
|
|
616
915
|
// already treats a missing `jsonSchema` as "skip", so swallow the panic
|
|
617
|
-
// and omit the field
|
|
916
|
+
// and omit the field; the SDK generator falls back to its own derived
|
|
618
917
|
// schema path. This must never mask a real bug, so re-raise anything we
|
|
619
918
|
// don't recognize as a transformer error from the typia runtime.
|
|
620
|
-
if baked := nestiaSDKTryBakeJsonSchema(prog,
|
|
919
|
+
if baked := nestiaSDKTryBakeJsonSchema(prog, typ, context.collection, result.Data, properties, escape); baked != nil {
|
|
621
920
|
metadataLiteral["jsonSchema"] = baked
|
|
622
921
|
}
|
|
922
|
+
data := map[string]any{
|
|
923
|
+
"components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
|
|
924
|
+
"metadata": metadataLiteral,
|
|
925
|
+
}
|
|
926
|
+
if properties {
|
|
927
|
+
// a route parameter's HTTP rule verdicts, from its own analysis
|
|
928
|
+
data["http"] = nestiaSDKHttpRules(prog.Checker, typ)
|
|
929
|
+
}
|
|
623
930
|
value := map[string]any{
|
|
624
931
|
"success": true,
|
|
625
|
-
"data":
|
|
626
|
-
"components": nestiaSDKMetadataComponentsLiteral(nestiaSDKVisitedMetadataComponents(context.collection, result.Data)),
|
|
627
|
-
"metadata": metadataLiteral,
|
|
628
|
-
},
|
|
932
|
+
"data": data,
|
|
629
933
|
}
|
|
630
934
|
context.schemaCache[key] = value
|
|
631
935
|
return value
|
|
@@ -634,12 +938,19 @@ func nestiaSDKSchemaPipe(context *nestiaSDKContext, typ *shimchecker.Type, typeN
|
|
|
634
938
|
// nestiaSDKTryBakeJsonSchema runs `JsonSchemasProgrammer.WriteSchemas` for a
|
|
635
939
|
// single metadata and returns the OpenAPI 3.1 schema literal. Returns nil
|
|
636
940
|
// when typia signals the metadata has no JSON-schema representation (e.g.
|
|
637
|
-
// `void` returns, function-only types)
|
|
941
|
+
// `void` returns, function-only types); the JS-side reader handles missing
|
|
638
942
|
// `jsonSchema` fields. Any other panic is re-raised so real bugs surface.
|
|
943
|
+
//
|
|
944
|
+
// With `properties`, the literal also carries the schema of each property of
|
|
945
|
+
// the metadata's first object type, for the Swagger generator to decompose into
|
|
946
|
+
// individual parameters (see `nestiaSDKPropertySchemas`).
|
|
639
947
|
func nestiaSDKTryBakeJsonSchema(
|
|
640
948
|
prog *driver.Program,
|
|
641
|
-
|
|
949
|
+
typ *shimchecker.Type,
|
|
950
|
+
registry *schemametadata.MetadataCollection,
|
|
642
951
|
metadata *schemametadata.MetadataSchema,
|
|
952
|
+
properties bool,
|
|
953
|
+
escape bool,
|
|
643
954
|
) (baked map[string]any) {
|
|
644
955
|
defer func() {
|
|
645
956
|
if r := recover(); r != nil {
|
|
@@ -650,6 +961,7 @@ func nestiaSDKTryBakeJsonSchema(
|
|
|
650
961
|
panic(r)
|
|
651
962
|
}
|
|
652
963
|
}()
|
|
964
|
+
metadata = schemaprojection.Project(metadata)
|
|
653
965
|
collection := nativejson.JsonSchemasProgrammer.WriteSchemas(struct {
|
|
654
966
|
Version string
|
|
655
967
|
Metadatas []*schemametadata.MetadataSchema
|
|
@@ -660,6 +972,12 @@ func nestiaSDKTryBakeJsonSchema(
|
|
|
660
972
|
if len(collection.Schemas) == 0 {
|
|
661
973
|
return nil
|
|
662
974
|
}
|
|
975
|
+
// Baked before the components are flattened: the property schemas share
|
|
976
|
+
// the parent's components, so any component they reach must be included.
|
|
977
|
+
var propertySchemas map[string]any
|
|
978
|
+
if properties && collection.Components != nil {
|
|
979
|
+
propertySchemas = nestiaSDKPropertySchemas(metadata, collection.Components)
|
|
980
|
+
}
|
|
663
981
|
// `iterate.OpenApi_IComponents` has no JSON tags on its Schemas field,
|
|
664
982
|
// so default Go marshaling would emit `"Schemas"` (capital). The JS
|
|
665
983
|
// side reads `components.schemas`, so flatten to a plain map here.
|
|
@@ -667,266 +985,412 @@ func nestiaSDKTryBakeJsonSchema(
|
|
|
667
985
|
if collection.Components != nil && collection.Components.Schemas != nil {
|
|
668
986
|
schemasLiteral := map[string]any{}
|
|
669
987
|
for key, val := range collection.Components.Schemas {
|
|
670
|
-
schemasLiteral[key] =
|
|
988
|
+
schemasLiteral[key] = nestiaSDKJsonSchemaLiteral(val)
|
|
671
989
|
}
|
|
672
990
|
componentsLiteral["schemas"] = schemasLiteral
|
|
673
991
|
}
|
|
674
992
|
baked = map[string]any{
|
|
675
993
|
"version": collection.Version,
|
|
676
994
|
"components": componentsLiteral,
|
|
677
|
-
"schema":
|
|
995
|
+
"schema": nestiaSDKJsonSchemaLiteral(collection.Schemas[0]),
|
|
678
996
|
}
|
|
679
|
-
|
|
997
|
+
if propertySchemas != nil {
|
|
998
|
+
baked["properties"] = propertySchemas
|
|
999
|
+
}
|
|
1000
|
+
nestiaSDKMarkReadonlyArrayJsonSchema(prog, typ, baked, registry, escape)
|
|
680
1001
|
return baked
|
|
681
1002
|
}
|
|
682
1003
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
) {
|
|
703
|
-
if
|
|
704
|
-
return
|
|
1004
|
+
// nestiaSDKPropertySchemas bakes the schema of each property of the metadata's
|
|
1005
|
+
// first object type, keyed by property name, for a decomposed query or headers
|
|
1006
|
+
// parameter. It is the property schema typia's object writer produces, minus
|
|
1007
|
+
// the fields the OpenAPI parameter carries in its own members: typia merges the
|
|
1008
|
+
// property's title, description, and deprecation into that schema, plus
|
|
1009
|
+
// readOnly, which has no meaning for a request parameter. What remains is the
|
|
1010
|
+
// value schema, written by typia's `Json_schema_station` exactly as
|
|
1011
|
+
// `WriteSchemas` writes one metadata but against the parent's components, so a
|
|
1012
|
+
// named type the value reaches resolves to the component the parent already
|
|
1013
|
+
// carries, and the property's `x-` JSDoc extensions.
|
|
1014
|
+
//
|
|
1015
|
+
// Only properties typia's object schema describes are baked: a property must
|
|
1016
|
+
// have a literal key and no `@hidden`, `@ignore`, or `@internal` tag (the
|
|
1017
|
+
// filter `json_schema_object` applies), and a value with no JSON form
|
|
1018
|
+
// (function-only or `never`) yields no schema. Skipping them here also keeps
|
|
1019
|
+
// their types out of the shared components.
|
|
1020
|
+
func nestiaSDKPropertySchemas(
|
|
1021
|
+
metadata *schemametadata.MetadataSchema,
|
|
1022
|
+
components *nativeiterate.OpenApi_IComponents,
|
|
1023
|
+
) map[string]any {
|
|
1024
|
+
if len(metadata.Objects) == 0 || metadata.Objects[0].Type == nil {
|
|
1025
|
+
return nil
|
|
705
1026
|
}
|
|
706
|
-
|
|
707
|
-
|
|
1027
|
+
output := map[string]any{}
|
|
1028
|
+
for _, property := range metadata.Objects[0].Type.Properties {
|
|
1029
|
+
if property == nil || property.Key == nil || property.Value == nil {
|
|
1030
|
+
continue
|
|
1031
|
+
}
|
|
1032
|
+
key := property.Key.GetSoleLiteral()
|
|
1033
|
+
if key == nil || nestiaSDKHasJSDocTag(property.JsDocTags, "hidden", "ignore", "internal") {
|
|
1034
|
+
continue
|
|
1035
|
+
}
|
|
1036
|
+
schema := nativeiterate.Json_schema_station(nativeiterate.Json_schema_station_props{
|
|
1037
|
+
BlockNever: true,
|
|
1038
|
+
Components: components,
|
|
1039
|
+
Attribute: nativeiterate.JsonSchema{},
|
|
1040
|
+
Metadata: property.Value,
|
|
1041
|
+
})
|
|
1042
|
+
if schema == nil {
|
|
1043
|
+
continue
|
|
1044
|
+
}
|
|
1045
|
+
nestiaSDKJsDocExtensions(schema, property.JsDocTags)
|
|
1046
|
+
output[*key] = nestiaSDKJsonSchemaLiteral(schema)
|
|
708
1047
|
}
|
|
709
|
-
|
|
710
|
-
|
|
1048
|
+
return output
|
|
1049
|
+
}
|
|
711
1050
|
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
1051
|
+
// nestiaSDKJsDocExtensions writes a property's `x-` JSDoc tags into its schema
|
|
1052
|
+
// the way typia's object writer does (`json_schema_jsDocTags`, unexported):
|
|
1053
|
+
// the first text part, trimmed, read as a boolean, a number, null, or else a
|
|
1054
|
+
// string.
|
|
1055
|
+
func nestiaSDKJsDocExtensions(schema nativeiterate.JsonSchema, tags []schemametadata.IJsDocTagInfo) {
|
|
1056
|
+
for _, tag := range tags {
|
|
1057
|
+
if strings.HasPrefix(tag.Name, "x-") == false {
|
|
1058
|
+
continue
|
|
1059
|
+
}
|
|
1060
|
+
for _, text := range tag.Text {
|
|
1061
|
+
if text.Kind != "text" {
|
|
1062
|
+
continue
|
|
1063
|
+
}
|
|
1064
|
+
value := strings.ReplaceAll(strings.TrimSpace(text.Text), "\r\n", "\n")
|
|
1065
|
+
if value == "true" || value == "false" {
|
|
1066
|
+
schema[tag.Name] = value == "true"
|
|
1067
|
+
} else if number, err := strconv.ParseFloat(value, 64); err == nil {
|
|
1068
|
+
schema[tag.Name] = number
|
|
1069
|
+
} else if value == "null" {
|
|
1070
|
+
schema[tag.Name] = nil
|
|
1071
|
+
} else {
|
|
1072
|
+
schema[tag.Name] = value
|
|
732
1073
|
}
|
|
1074
|
+
break
|
|
733
1075
|
}
|
|
734
|
-
case shimast.KindParenthesizedType:
|
|
735
|
-
nestiaSDKMarkReadonlyArraySchemaNode(
|
|
736
|
-
prog,
|
|
737
|
-
typeNode.AsParenthesizedTypeNode().Type,
|
|
738
|
-
schema,
|
|
739
|
-
components,
|
|
740
|
-
visiting,
|
|
741
|
-
)
|
|
742
|
-
case shimast.KindTypeOperator:
|
|
743
|
-
operator := typeNode.AsTypeOperatorNode()
|
|
744
|
-
if nestiaSDKTypeOperatorPrefix(typeNode, operator.Type) == "readonly" &&
|
|
745
|
-
nestiaSDKReadonlyArrayOperand(operator.Type) {
|
|
746
|
-
schema["x-readonly-array"] = true
|
|
747
|
-
}
|
|
748
|
-
nestiaSDKMarkReadonlyArraySchemaNode(
|
|
749
|
-
prog,
|
|
750
|
-
operator.Type,
|
|
751
|
-
schema,
|
|
752
|
-
components,
|
|
753
|
-
visiting,
|
|
754
|
-
)
|
|
755
|
-
case shimast.KindTypeReference:
|
|
756
|
-
nestiaSDKMarkReadonlyArrayTypeReference(
|
|
757
|
-
prog,
|
|
758
|
-
typeNode,
|
|
759
|
-
schema,
|
|
760
|
-
components,
|
|
761
|
-
visiting,
|
|
762
|
-
)
|
|
763
|
-
case shimast.KindTypeLiteral:
|
|
764
|
-
nestiaSDKMarkReadonlyArrayTypeElements(
|
|
765
|
-
prog,
|
|
766
|
-
typeNode.AsTypeLiteralNode().Members,
|
|
767
|
-
schema,
|
|
768
|
-
components,
|
|
769
|
-
visiting,
|
|
770
|
-
)
|
|
771
1076
|
}
|
|
772
1077
|
}
|
|
773
1078
|
|
|
774
|
-
func
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
) {
|
|
781
|
-
ref := typeNode.AsTypeReferenceNode()
|
|
782
|
-
name := nestiaSDKEntityNameText(ref.TypeName)
|
|
783
|
-
if name == "ReadonlyArray" {
|
|
784
|
-
schema["x-readonly-array"] = true
|
|
785
|
-
}
|
|
786
|
-
if (name == "Array" || name == "ReadonlyArray") && ref.TypeArguments != nil &&
|
|
787
|
-
len(ref.TypeArguments.Nodes) != 0 {
|
|
788
|
-
child := nestiaSDKSchemaMap(schema["items"])
|
|
789
|
-
nestiaSDKMarkReadonlyArraySchemaNode(
|
|
790
|
-
prog,
|
|
791
|
-
ref.TypeArguments.Nodes[0],
|
|
792
|
-
child,
|
|
793
|
-
components,
|
|
794
|
-
visiting,
|
|
795
|
-
)
|
|
796
|
-
}
|
|
797
|
-
for _, decl := range nestiaSDKTypeReferenceDeclarations(prog, ref.TypeName) {
|
|
798
|
-
switch decl.Kind {
|
|
799
|
-
case shimast.KindInterfaceDeclaration:
|
|
800
|
-
target := nestiaSDKReferencedSchema(schema, components, name)
|
|
801
|
-
nestiaSDKMarkReadonlyArrayTypeElements(
|
|
802
|
-
prog,
|
|
803
|
-
decl.AsInterfaceDeclaration().Members,
|
|
804
|
-
target,
|
|
805
|
-
components,
|
|
806
|
-
visiting,
|
|
807
|
-
)
|
|
808
|
-
case shimast.KindTypeAliasDeclaration:
|
|
809
|
-
target := nestiaSDKReferencedSchema(schema, components, name)
|
|
810
|
-
nestiaSDKMarkReadonlyArraySchemaNode(
|
|
811
|
-
prog,
|
|
812
|
-
decl.AsTypeAliasDeclaration().Type,
|
|
813
|
-
target,
|
|
814
|
-
components,
|
|
815
|
-
visiting,
|
|
816
|
-
)
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
func nestiaSDKMarkReadonlyArrayTypeElements(
|
|
822
|
-
prog *driver.Program,
|
|
823
|
-
members *shimast.TypeElementList,
|
|
824
|
-
schema map[string]any,
|
|
825
|
-
components map[string]any,
|
|
826
|
-
visiting map[*shimast.Node]bool,
|
|
827
|
-
) {
|
|
828
|
-
if members == nil || schema == nil {
|
|
829
|
-
return
|
|
830
|
-
}
|
|
831
|
-
properties := nestiaSDKSchemaMap(schema["properties"])
|
|
832
|
-
if properties == nil {
|
|
833
|
-
return
|
|
834
|
-
}
|
|
835
|
-
for _, member := range members.Nodes {
|
|
836
|
-
if member == nil || member.Kind != shimast.KindPropertySignature {
|
|
837
|
-
continue
|
|
1079
|
+
func nestiaSDKHasJSDocTag(tags []schemametadata.IJsDocTagInfo, names ...string) bool {
|
|
1080
|
+
for _, tag := range tags {
|
|
1081
|
+
for _, name := range names {
|
|
1082
|
+
if tag.Name == name {
|
|
1083
|
+
return true
|
|
1084
|
+
}
|
|
838
1085
|
}
|
|
839
|
-
property := member.AsPropertySignatureDeclaration()
|
|
840
|
-
name := nestiaSDKSchemaPropertyName(property.Name())
|
|
841
|
-
child := nestiaSDKSchemaMap(properties[name])
|
|
842
|
-
nestiaSDKMarkReadonlyArraySchemaNode(
|
|
843
|
-
prog,
|
|
844
|
-
property.Type,
|
|
845
|
-
child,
|
|
846
|
-
components,
|
|
847
|
-
visiting,
|
|
848
|
-
)
|
|
849
1086
|
}
|
|
1087
|
+
return false
|
|
850
1088
|
}
|
|
851
1089
|
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1090
|
+
// nestiaSDKJsonSchemaLiteral prepares one typia JSON schema for the
|
|
1091
|
+
// encoding/json serialization of the SDK metadata, dropping the members typia
|
|
1092
|
+
// leaves absent.
|
|
1093
|
+
//
|
|
1094
|
+
// typia's schema writer stores a Go nil for an absent member: a constant with
|
|
1095
|
+
// no `@title` gets a nil *string title and description, and `any` gets a nil
|
|
1096
|
+
// type. typia's own literal printer skips nil object members, but encoding/json
|
|
1097
|
+
// prints them as null, which JSON Schema rejects for those keywords. A nil
|
|
1098
|
+
// cannot be dropped everywhere, though: in an instance-valued keyword (`const`,
|
|
1099
|
+
// `default`, `enum`, `example`, `examples`) or a vendor extension it can be a
|
|
1100
|
+
// real null. typia marks the nulls a type declares, such as
|
|
1101
|
+
// `tags.Example<null>`, with `LiteralFactory_Null`, which is no nil and
|
|
1102
|
+
// marshals as null, but a JSDoc `@x-foo null` still reaches the schema as a
|
|
1103
|
+
// bare nil, from typia's object writer and from nestiaSDKJsDocExtensions alike.
|
|
1104
|
+
//
|
|
1105
|
+
// So the walk follows JSON Schema structure: it drops a nil member of a schema
|
|
1106
|
+
// object, recurses through the applicator keywords into subschemas, and leaves
|
|
1107
|
+
// every other value, instance data included, as it is.
|
|
1108
|
+
func nestiaSDKJsonSchemaLiteral(input any) any {
|
|
1109
|
+
reflected := reflect.ValueOf(input)
|
|
1110
|
+
if reflected.Kind() != reflect.Map || reflected.Type().Key().Kind() != reflect.String {
|
|
1111
|
+
return input // a boolean schema, or a value that is no schema object
|
|
1112
|
+
}
|
|
1113
|
+
output := make(map[string]any, reflected.Len())
|
|
1114
|
+
iterator := reflected.MapRange()
|
|
1115
|
+
for iterator.Next() {
|
|
1116
|
+
key := iterator.Key().String()
|
|
1117
|
+
member := iterator.Value().Interface()
|
|
1118
|
+
if nestiaSDKJsonSchemaInstanceKeyword(key) {
|
|
1119
|
+
output[key] = member
|
|
1120
|
+
} else if nestiaSDKIsNilLike(member) == false {
|
|
1121
|
+
output[key] = nestiaSDKJsonSchemaMember(key, member)
|
|
861
1122
|
}
|
|
862
1123
|
}
|
|
863
|
-
|
|
864
|
-
return nil
|
|
865
|
-
}
|
|
866
|
-
return symbol.Declarations
|
|
1124
|
+
return output
|
|
867
1125
|
}
|
|
868
1126
|
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1127
|
+
// nestiaSDKJsonSchemaMember recurses into the subschemas a JSON Schema 2020-12
|
|
1128
|
+
// applicator keyword holds, and returns any other keyword value unchanged.
|
|
1129
|
+
func nestiaSDKJsonSchemaMember(key string, member any) any {
|
|
1130
|
+
switch key {
|
|
1131
|
+
case "items", "additionalItems", "additionalProperties", "unevaluatedItems",
|
|
1132
|
+
"unevaluatedProperties", "contains", "propertyNames", "not", "if", "then",
|
|
1133
|
+
"else", "contentSchema":
|
|
1134
|
+
return nestiaSDKJsonSchemaLiteral(member)
|
|
1135
|
+
case "allOf", "anyOf", "oneOf", "prefixItems":
|
|
1136
|
+
reflected := reflect.ValueOf(member)
|
|
1137
|
+
if reflected.Kind() != reflect.Slice && reflected.Kind() != reflect.Array {
|
|
1138
|
+
return member
|
|
1139
|
+
}
|
|
1140
|
+
output := make([]any, reflected.Len())
|
|
1141
|
+
for i := range output {
|
|
1142
|
+
output[i] = nestiaSDKJsonSchemaLiteral(reflected.Index(i).Interface())
|
|
1143
|
+
}
|
|
1144
|
+
return output
|
|
1145
|
+
case "properties", "patternProperties", "dependentSchemas", "$defs", "definitions":
|
|
1146
|
+
return nestiaSDKJsonSchemaNamedLiteral(member)
|
|
881
1147
|
}
|
|
882
|
-
return
|
|
1148
|
+
return member
|
|
883
1149
|
}
|
|
884
1150
|
|
|
885
|
-
|
|
1151
|
+
// nestiaSDKJsonSchemaNamedLiteral normalizes each schema of a name-keyed schema
|
|
1152
|
+
// map, such as `properties`, keeping the order of typia's ordered objects.
|
|
1153
|
+
func nestiaSDKJsonSchemaNamedLiteral(input any) any {
|
|
886
1154
|
switch value := input.(type) {
|
|
887
|
-
case
|
|
888
|
-
return value
|
|
889
|
-
case
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
1155
|
+
case nativefactories.LiteralFactory_OrderedObject:
|
|
1156
|
+
return nestiaSDKJsonSchemaOrderedLiteral(value)
|
|
1157
|
+
case *nativefactories.LiteralFactory_OrderedObject:
|
|
1158
|
+
if value == nil {
|
|
1159
|
+
return nil
|
|
1160
|
+
}
|
|
1161
|
+
return nestiaSDKJsonSchemaOrderedLiteral(*value)
|
|
1162
|
+
}
|
|
1163
|
+
reflected := reflect.ValueOf(input)
|
|
1164
|
+
if reflected.Kind() != reflect.Map || reflected.Type().Key().Kind() != reflect.String {
|
|
1165
|
+
return input
|
|
893
1166
|
}
|
|
1167
|
+
output := make(map[string]any, reflected.Len())
|
|
1168
|
+
iterator := reflected.MapRange()
|
|
1169
|
+
for iterator.Next() {
|
|
1170
|
+
output[iterator.Key().String()] = nestiaSDKJsonSchemaLiteral(iterator.Value().Interface())
|
|
1171
|
+
}
|
|
1172
|
+
return output
|
|
894
1173
|
}
|
|
895
1174
|
|
|
896
|
-
func
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
1175
|
+
func nestiaSDKJsonSchemaOrderedLiteral(
|
|
1176
|
+
input nativefactories.LiteralFactory_OrderedObject,
|
|
1177
|
+
) nativefactories.LiteralFactory_OrderedObject {
|
|
1178
|
+
output := nativefactories.LiteralFactory_OrderedObject{
|
|
1179
|
+
Keys: make([]string, 0, len(input.Keys)),
|
|
1180
|
+
Values: make(map[string]any, len(input.Keys)),
|
|
1181
|
+
}
|
|
1182
|
+
for _, key := range input.Keys {
|
|
1183
|
+
value, ok := input.Values[key]
|
|
1184
|
+
if ok == false || nestiaSDKIsNilLike(value) {
|
|
1185
|
+
continue
|
|
904
1186
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
return nil
|
|
1187
|
+
output.Keys = append(output.Keys, key)
|
|
1188
|
+
output.Values[key] = nestiaSDKJsonSchemaLiteral(value)
|
|
908
1189
|
}
|
|
1190
|
+
return output
|
|
909
1191
|
}
|
|
910
1192
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
1193
|
+
// nestiaSDKJsonSchemaInstanceKeyword reports a keyword whose value is JSON
|
|
1194
|
+
// instance data, where null is a legitimate value: the instance keywords of
|
|
1195
|
+
// JSON Schema and OpenAPI, and `x-` vendor extensions.
|
|
1196
|
+
func nestiaSDKJsonSchemaInstanceKeyword(key string) bool {
|
|
1197
|
+
switch key {
|
|
1198
|
+
case "const", "default", "enum", "example", "examples":
|
|
1199
|
+
return true
|
|
914
1200
|
}
|
|
915
|
-
|
|
916
|
-
|
|
1201
|
+
return strings.HasPrefix(key, "x-")
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// nestiaSDKIsNilLike mirrors typia's `literalFactory_isNilLike`.
|
|
1205
|
+
func nestiaSDKIsNilLike(value any) bool {
|
|
1206
|
+
if value == nil {
|
|
917
1207
|
return true
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1208
|
+
}
|
|
1209
|
+
reflected := reflect.ValueOf(value)
|
|
1210
|
+
switch reflected.Kind() {
|
|
1211
|
+
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
|
1212
|
+
return reflected.IsNil()
|
|
922
1213
|
default:
|
|
923
1214
|
return false
|
|
924
1215
|
}
|
|
925
1216
|
}
|
|
926
1217
|
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
1218
|
+
// nestiaSDKMarkReadonlyArrayJsonSchema carries the producing checker's readonly
|
|
1219
|
+
// facts into the baked schema. Alias spelling is never an immutability test.
|
|
1220
|
+
func nestiaSDKMarkReadonlyArrayJsonSchema(prog *driver.Program, typ *shimchecker.Type, baked map[string]any, registry *schemametadata.MetadataCollection, escape bool) {
|
|
1221
|
+
registry = registry.Clone()
|
|
1222
|
+
components, _ := baked["components"].(map[string]any)
|
|
1223
|
+
schemas, _ := components["schemas"].(map[string]any)
|
|
1224
|
+
nestiaSDKMarkReadonlySchemaType(prog, typ, nestiaSDKSchemaMap(baked["schema"]), schemas, registry, escape, map[nestiaSDKReadonlyVisit]bool{}, false)
|
|
1225
|
+
for key, schema := range nestiaSDKSchemaMap(baked["properties"]) {
|
|
1226
|
+
nestiaSDKMarkReadonlySchemaType(prog, prog.Checker.GetTypeOfPropertyOfType(typ, key), nestiaSDKSchemaMap(schema), schemas, registry, escape, map[nestiaSDKReadonlyVisit]bool{}, false)
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// nestiaSDKMarkReadonlySchemaType pairs actual substituted checker types with
|
|
1231
|
+
// the schema positions the writer emitted, including inferred types and cycles.
|
|
1232
|
+
func nestiaSDKMarkReadonlySchemaType(prog *driver.Program, typ *shimchecker.Type, schema map[string]any, components map[string]any, registry *schemametadata.MetadataCollection, escape bool, visiting map[nestiaSDKReadonlyVisit]bool, skipEscape bool) {
|
|
1233
|
+
if prog == nil || prog.Checker == nil || typ == nil || schema == nil { return }
|
|
1234
|
+
// The cloned registry preserves the producer's type-to-component identity.
|
|
1235
|
+
// Public emplace lookups cannot mutate the analysis registry or its counters.
|
|
1236
|
+
if escape && !skipEscape {
|
|
1237
|
+
if method := prog.Checker.GetTypeOfPropertyOfType(typ, "toJSON"); method != nil {
|
|
1238
|
+
for _, signature := range prog.Checker.GetSignaturesOfType(method, shimchecker.SignatureKindCall) {
|
|
1239
|
+
if returned := prog.Checker.GetReturnTypeOfSignature(signature); returned != nil && returned != typ {
|
|
1240
|
+
nestiaSDKMarkReadonlySchemaType(prog, returned, schema, components, registry, escape, visiting, true)
|
|
1241
|
+
return
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
target := schema
|
|
1247
|
+
if ref, ok := schema["$ref"].(string); ok && strings.HasPrefix(ref, "#/components/schemas/") {
|
|
1248
|
+
name := strings.TrimPrefix(ref, "#/components/schemas/")
|
|
1249
|
+
if component := nestiaSDKSchemaMap(components[name]); component != nil {
|
|
1250
|
+
if component["type"] == "object" && typ.Flags() & shimchecker.TypeFlagsObject != 0 {
|
|
1251
|
+
owner, fresh := registry.Emplace(prog.Checker, typ)
|
|
1252
|
+
if fresh || owner.Name != name { return }
|
|
1253
|
+
}
|
|
1254
|
+
target = component
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
key := nestiaSDKReadonlyVisit{Type: typ, Schema: reflect.ValueOf(schema).Pointer()}
|
|
1258
|
+
if visiting[key] { return }
|
|
1259
|
+
visiting[key] = true
|
|
1260
|
+
if typ.Flags() & (shimchecker.TypeFlagsUnion | shimchecker.TypeFlagsIntersection) != 0 {
|
|
1261
|
+
for _, child := range typ.AsUnionOrIntersectionType().Types() {
|
|
1262
|
+
matched := false
|
|
1263
|
+
if typ.Flags() & shimchecker.TypeFlagsUnion != 0 {
|
|
1264
|
+
for _, keyword := range []string{"oneOf", "anyOf"} {
|
|
1265
|
+
if branches := nestiaSDKSchemaList(target[keyword]); len(branches) != 0 {
|
|
1266
|
+
matched = true
|
|
1267
|
+
analyzed := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
|
|
1268
|
+
Checker: prog.Checker,
|
|
1269
|
+
Options: nativefactories.MetadataFactory_IOptions{Escape: escape && !skipEscape, Constant: true, Absorb: true},
|
|
1270
|
+
Components: registry.Clone(), Type: child,
|
|
1271
|
+
})
|
|
1272
|
+
if analyzed.Success {
|
|
1273
|
+
// A union constituent can have no JSON value (undefined, void, never,
|
|
1274
|
+
// or function-only). Unlike the top-level writer, this branch probe
|
|
1275
|
+
// must return nil for those omissions without rejecting the parent.
|
|
1276
|
+
produced := nativeiterate.Json_schema_station(nativeiterate.Json_schema_station_props{
|
|
1277
|
+
BlockNever: false,
|
|
1278
|
+
Components: &nativeiterate.OpenApi_IComponents{Schemas: map[string]nativeiterate.JsonSchema{}},
|
|
1279
|
+
Attribute: nativeiterate.JsonSchema{}, Metadata: schemaprojection.Project(analyzed.Data),
|
|
1280
|
+
})
|
|
1281
|
+
if produced != nil {
|
|
1282
|
+
expected := nestiaSDKJsonSchemaLiteral(produced)
|
|
1283
|
+
for _, branch := range branches {
|
|
1284
|
+
if nestiaSDKReadonlySchemaEquivalent(expected, branch) {
|
|
1285
|
+
nestiaSDKMarkReadonlySchemaType(prog, child, nestiaSDKSchemaMap(branch), components, registry, escape, visiting, skipEscape)
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
if !matched { nestiaSDKMarkReadonlySchemaType(prog, child, schema, components, registry, escape, visiting, skipEscape) }
|
|
1294
|
+
}
|
|
1295
|
+
return
|
|
1296
|
+
}
|
|
1297
|
+
for _, keyword := range []string{"oneOf", "anyOf", "allOf"} {
|
|
1298
|
+
if branches := nestiaSDKSchemaList(target[keyword]); len(branches) != 0 {
|
|
1299
|
+
for _, branch := range branches {
|
|
1300
|
+
nestiaSDKMarkReadonlySchemaType(prog, typ, nestiaSDKSchemaMap(branch), components, registry, escape, visiting, skipEscape)
|
|
1301
|
+
}
|
|
1302
|
+
return
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
if target["type"] == "array" {
|
|
1306
|
+
readonly := false
|
|
1307
|
+
var element *shimchecker.Type
|
|
1308
|
+
if shimchecker.IsTupleType(typ) {
|
|
1309
|
+
readonly = typ.TargetTupleType().IsReadonly()
|
|
1310
|
+
} else {
|
|
1311
|
+
for _, index := range prog.Checker.GetIndexInfosOfType(typ) {
|
|
1312
|
+
if index.KeyType().Flags() & shimchecker.TypeFlagsNumber != 0 { element = index.ValueType(); readonly = index.IsReadonly() }
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
if !shimchecker.IsTupleType(typ) && element == nil { return }
|
|
1316
|
+
mutableKey := nestiaSDKReadonlyVisit{Schema: reflect.ValueOf(target).Pointer()}
|
|
1317
|
+
if !readonly {
|
|
1318
|
+
visiting[mutableKey] = true
|
|
1319
|
+
delete(schema, "x-readonly-array")
|
|
1320
|
+
delete(target, "x-readonly-array")
|
|
1321
|
+
} else if !visiting[mutableKey] { schema["x-readonly-array"] = true; target["x-readonly-array"] = true }
|
|
1322
|
+
if shimchecker.IsTupleType(typ) {
|
|
1323
|
+
arguments := prog.Checker.GetTypeArguments(typ)
|
|
1324
|
+
for index, flag := range typ.TargetTupleType().ElementFlags() {
|
|
1325
|
+
if flag == shimchecker.ElementFlagsRest && index < len(arguments) { nestiaSDKMarkReadonlySchemaType(prog, arguments[index], nestiaSDKSchemaMap(target["additionalItems"]), components, registry, escape, visiting, false) }
|
|
1326
|
+
}
|
|
1327
|
+
for index, child := range nestiaSDKSchemaList(target["prefixItems"]) {
|
|
1328
|
+
if index < len(arguments) { nestiaSDKMarkReadonlySchemaType(prog, arguments[index], nestiaSDKSchemaMap(child), components, registry, escape, visiting, false) }
|
|
1329
|
+
}
|
|
1330
|
+
} else if element != nil {
|
|
1331
|
+
nestiaSDKMarkReadonlySchemaType(prog, element, nestiaSDKSchemaMap(target["items"]), components, registry, escape, visiting, false)
|
|
1332
|
+
}
|
|
1333
|
+
return
|
|
1334
|
+
}
|
|
1335
|
+
for key, child := range nestiaSDKSchemaMap(target["properties"]) {
|
|
1336
|
+
nestiaSDKMarkReadonlySchemaType(prog, prog.Checker.GetTypeOfPropertyOfType(typ, key), nestiaSDKSchemaMap(child), components, registry, escape, visiting, false)
|
|
1337
|
+
}
|
|
1338
|
+
for _, index := range prog.Checker.GetIndexInfosOfType(typ) {
|
|
1339
|
+
nestiaSDKMarkReadonlySchemaType(prog, index.ValueType(), nestiaSDKSchemaMap(target["additionalProperties"]), components, registry, escape, visiting, false)
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
// nestiaSDKReadonlySchemaEquivalent compares the native writer's branch shape,
|
|
1344
|
+
// ignoring only the readonly facts this traversal has already attached.
|
|
1345
|
+
func nestiaSDKReadonlySchemaEquivalent(expected any, actual any) bool {
|
|
1346
|
+
left, right := nestiaSDKSchemaMap(expected), nestiaSDKSchemaMap(actual)
|
|
1347
|
+
if left != nil && right != nil {
|
|
1348
|
+
count := 0
|
|
1349
|
+
for key, value := range left {
|
|
1350
|
+
if key == "x-readonly-array" { continue }
|
|
1351
|
+
other, exists := right[key]
|
|
1352
|
+
if !exists || !nestiaSDKReadonlySchemaEquivalent(value, other) { return false }
|
|
1353
|
+
count++
|
|
1354
|
+
}
|
|
1355
|
+
for key := range right { if key != "x-readonly-array" { count-- } }
|
|
1356
|
+
return count == 0
|
|
1357
|
+
}
|
|
1358
|
+
leftList, rightList := nestiaSDKSchemaList(expected), nestiaSDKSchemaList(actual)
|
|
1359
|
+
if leftList != nil && rightList != nil {
|
|
1360
|
+
if len(leftList) != len(rightList) { return false }
|
|
1361
|
+
for i := range leftList { if !nestiaSDKReadonlySchemaEquivalent(leftList[i], rightList[i]) { return false } }
|
|
1362
|
+
return true
|
|
1363
|
+
}
|
|
1364
|
+
return reflect.DeepEqual(expected, actual)
|
|
1365
|
+
}
|
|
1366
|
+
// nestiaSDKReadonlyVisit identifies one checker-type/schema-map pair per bake.
|
|
1367
|
+
type nestiaSDKReadonlyVisit struct {
|
|
1368
|
+
Type *shimchecker.Type
|
|
1369
|
+
Schema uintptr
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// nestiaSDKSchemaMap exposes schema maps and the values of an ordered property
|
|
1373
|
+
// dictionary without copying or changing its key order.
|
|
1374
|
+
func nestiaSDKSchemaMap(input any) map[string]any {
|
|
1375
|
+
switch value := input.(type) {
|
|
1376
|
+
case map[string]any: return value
|
|
1377
|
+
case nativeiterate.JsonSchema: return map[string]any(value)
|
|
1378
|
+
case nativefactories.LiteralFactory_OrderedObject: return value.Values
|
|
1379
|
+
case *nativefactories.LiteralFactory_OrderedObject:
|
|
1380
|
+
if value != nil { return value.Values }
|
|
1381
|
+
}
|
|
1382
|
+
return nil
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
func nestiaSDKSchemaList(input any) []any {
|
|
1386
|
+
switch value := input.(type) {
|
|
1387
|
+
case []any: return value
|
|
1388
|
+
case []nativeiterate.JsonSchema:
|
|
1389
|
+
output := make([]any, len(value))
|
|
1390
|
+
for i, elem := range value { output[i] = elem }
|
|
1391
|
+
return output
|
|
1392
|
+
default: return nil
|
|
1393
|
+
}
|
|
930
1394
|
}
|
|
931
1395
|
|
|
932
1396
|
func nestiaSDKRestoreUnionOrder(typeNode *shimast.Node, metadata *schemametadata.MetadataSchema) {
|
|
@@ -1115,27 +1579,11 @@ func nestiaSDKIsTypeGuardError(prog *driver.Program, typ *shimchecker.Type, type
|
|
|
1115
1579
|
}
|
|
1116
1580
|
|
|
1117
1581
|
func nestiaSDKIsTypiaSourceFile(prog *driver.Program, source *shimast.SourceFile) bool {
|
|
1118
|
-
|
|
1119
|
-
return false
|
|
1120
|
-
}
|
|
1121
|
-
for directory := filepath.Dir(source.FileName()); ; {
|
|
1122
|
-
contents, ok := prog.FS.ReadFile(filepath.Join(directory, "package.json"))
|
|
1123
|
-
if ok {
|
|
1124
|
-
var pack struct {
|
|
1125
|
-
Name string `json:"name"`
|
|
1126
|
-
}
|
|
1127
|
-
return json.Unmarshal([]byte(contents), &pack) == nil && pack.Name == "typia"
|
|
1128
|
-
}
|
|
1129
|
-
parent := filepath.Dir(directory)
|
|
1130
|
-
if parent == directory {
|
|
1131
|
-
return false
|
|
1132
|
-
}
|
|
1133
|
-
directory = parent
|
|
1134
|
-
}
|
|
1582
|
+
return transform.SourceFilePackageName(prog, source) == "typia"
|
|
1135
1583
|
}
|
|
1136
1584
|
|
|
1137
1585
|
func nestiaSDKTypeGuardErrorSchemaPipe() any {
|
|
1138
|
-
// Synthetic metadata for `TypeGuardError` exception responses
|
|
1586
|
+
// Synthetic metadata for `TypeGuardError` exception responses; it does not
|
|
1139
1587
|
// flow through `nestiaSDKSchemaPipe`, so the pre-baked fields legacy.ts
|
|
1140
1588
|
// reads (size/name/empty/jsonSchema) have to be filled by hand.
|
|
1141
1589
|
metadata := nestiaSDKObjectReferenceSchema("TypeGuardErrorany")
|
|
@@ -1285,6 +1733,9 @@ func nestiaSDKReflectType(
|
|
|
1285
1733
|
if typeNode == nil {
|
|
1286
1734
|
return map[string]any{"name": "__type"}, []any{}
|
|
1287
1735
|
}
|
|
1736
|
+
if ref, refs, ok := nestiaSDKReflectAsyncAlias(prog, imports, typ, typeNode); ok {
|
|
1737
|
+
return ref, refs
|
|
1738
|
+
}
|
|
1288
1739
|
if typeNode != nil {
|
|
1289
1740
|
if ref, refs, ok := nestiaSDKReflectTypeNode(prog, imports, typeNode); ok {
|
|
1290
1741
|
return ref, refs
|
|
@@ -1300,7 +1751,120 @@ func nestiaSDKReflectType(
|
|
|
1300
1751
|
if name == "any" && prog != nil && prog.Checker != nil && typ != nil {
|
|
1301
1752
|
name = prog.Checker.TypeToString(typ)
|
|
1302
1753
|
}
|
|
1303
|
-
return map[string]any{"name": name}, nestiaSDKReflectImports(
|
|
1754
|
+
return map[string]any{"name": name}, nestiaSDKReflectImports(prog, typeNode, imports)
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
// nestiaSDKReflectAsyncAlias reflects the actual response payload when its
|
|
1758
|
+
// annotation aliases an asynchronous wrapper. The checker builds a concrete AST
|
|
1759
|
+
// in the annotation's scope, substituting generic arguments and defaults; its
|
|
1760
|
+
// identifier-symbol map preserves declaration identity without reparsing text.
|
|
1761
|
+
// Ordinary user wrappers keep their original annotation and imports.
|
|
1762
|
+
func nestiaSDKReflectAsyncAlias(
|
|
1763
|
+
prog *driver.Program,
|
|
1764
|
+
imports []nestiaSDKImportInfo,
|
|
1765
|
+
typ *shimchecker.Type,
|
|
1766
|
+
node *shimast.Node,
|
|
1767
|
+
) (map[string]any, []any, bool) {
|
|
1768
|
+
if prog == nil || prog.Checker == nil || typ == nil || node == nil {
|
|
1769
|
+
return nil, nil, false
|
|
1770
|
+
}
|
|
1771
|
+
reference := node
|
|
1772
|
+
for reference.Kind == shimast.KindParenthesizedType {
|
|
1773
|
+
reference = reference.AsParenthesizedTypeNode().Type
|
|
1774
|
+
}
|
|
1775
|
+
if reference.Kind != shimast.KindTypeReference || transform.NestiaCoreIsAsyncReturnWrapperReference(prog, reference.AsTypeReferenceNode().TypeName) {
|
|
1776
|
+
return nil, nil, false
|
|
1777
|
+
}
|
|
1778
|
+
declared := prog.Checker.GetTypeFromTypeNode(node)
|
|
1779
|
+
if declared == nil || declared == typ || declared.Symbol() == nil {
|
|
1780
|
+
return nil, nil, false
|
|
1781
|
+
}
|
|
1782
|
+
isAsync := func(current *shimchecker.Type) bool {
|
|
1783
|
+
if current == nil || current.Symbol() == nil {
|
|
1784
|
+
return false
|
|
1785
|
+
}
|
|
1786
|
+
for _, declaration := range current.Symbol().Declarations {
|
|
1787
|
+
if declaration != nil && declaration.Name() != nil && transform.NestiaCoreIsAsyncReturnWrapperReference(prog, declaration.Name()) {
|
|
1788
|
+
return true
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
return false
|
|
1792
|
+
}
|
|
1793
|
+
if !isAsync(declared) {
|
|
1794
|
+
return nil, nil, false
|
|
1795
|
+
}
|
|
1796
|
+
seen := map[*shimchecker.Type]bool{}
|
|
1797
|
+
for isAsync(typ) && !seen[typ] {
|
|
1798
|
+
seen[typ] = true
|
|
1799
|
+
arguments := prog.Checker.GetTypeArguments(typ)
|
|
1800
|
+
if len(arguments) != 1 {
|
|
1801
|
+
break
|
|
1802
|
+
}
|
|
1803
|
+
typ = arguments[0]
|
|
1804
|
+
}
|
|
1805
|
+
symbols := map[*shimast.IdentifierNode]*shimast.Symbol{}
|
|
1806
|
+
// TypeScript's nodebuilder.FlagsNoTruncation is the public flag bit 1 << 0.
|
|
1807
|
+
// The shim exposes TypeToTypeNode but no alias for that internal enum; a
|
|
1808
|
+
// response type is executable client syntax and cannot use display elision.
|
|
1809
|
+
const noTruncation = 1 << 0
|
|
1810
|
+
projected := prog.Checker.TypeToTypeNode(typ, node, noTruncation, symbols)
|
|
1811
|
+
if projected == nil {
|
|
1812
|
+
return nil, nil, false
|
|
1813
|
+
}
|
|
1814
|
+
ref, _, ok := nestiaSDKReflectTypeNode(nil, nil, projected)
|
|
1815
|
+
if !ok {
|
|
1816
|
+
return nil, nil, false
|
|
1817
|
+
}
|
|
1818
|
+
refs := []any{}
|
|
1819
|
+
add := func(entity *shimast.Node) {
|
|
1820
|
+
root := entity
|
|
1821
|
+
for root != nil && root.Kind == shimast.KindQualifiedName {
|
|
1822
|
+
root = root.AsQualifiedName().Left
|
|
1823
|
+
}
|
|
1824
|
+
if root == nil || root.Kind != shimast.KindIdentifier {
|
|
1825
|
+
return
|
|
1826
|
+
}
|
|
1827
|
+
symbol := symbols[root]
|
|
1828
|
+
if symbol == nil || symbol.Flags&shimast.SymbolFlagsTypeParameter != 0 {
|
|
1829
|
+
return
|
|
1830
|
+
}
|
|
1831
|
+
prefixes := map[string]bool{root.Text(): true}
|
|
1832
|
+
matched := false
|
|
1833
|
+
for _, imp := range imports {
|
|
1834
|
+
if nestiaSDKImportMatches(prefixes, imp) {
|
|
1835
|
+
refs = append(refs, nestiaSDKImportLiteral(imp, prefixes))
|
|
1836
|
+
matched = true
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
if matched {
|
|
1840
|
+
return
|
|
1841
|
+
}
|
|
1842
|
+
for _, declaration := range symbol.Declarations {
|
|
1843
|
+
source := shimast.GetSourceFileOfNode(declaration)
|
|
1844
|
+
if source != nil && (prog.TSProgram == nil || !prog.TSProgram.IsLibFile(source)) {
|
|
1845
|
+
refs = append(refs, map[string]any{
|
|
1846
|
+
"file": filepath.ToSlash(source.FileName()), "asterisk": nestiaSDKLiteralNull,
|
|
1847
|
+
"default": nestiaSDKLiteralNull, "elements": []string{root.Text()},
|
|
1848
|
+
})
|
|
1849
|
+
return
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
var walk func(*shimast.Node)
|
|
1854
|
+
walk = func(current *shimast.Node) {
|
|
1855
|
+
if current == nil {
|
|
1856
|
+
return
|
|
1857
|
+
}
|
|
1858
|
+
switch current.Kind {
|
|
1859
|
+
case shimast.KindTypeReference:
|
|
1860
|
+
add(current.AsTypeReferenceNode().TypeName)
|
|
1861
|
+
case shimast.KindTypeQuery:
|
|
1862
|
+
add(current.AsTypeQueryNode().ExprName)
|
|
1863
|
+
}
|
|
1864
|
+
current.ForEachChild(func(child *shimast.Node) bool { walk(child); return false })
|
|
1865
|
+
}
|
|
1866
|
+
walk(projected)
|
|
1867
|
+
return ref, nestiaSDKMergeImportLiterals(refs), true
|
|
1304
1868
|
}
|
|
1305
1869
|
|
|
1306
1870
|
func nestiaSDKReflectTypeNode(
|
|
@@ -1322,7 +1886,7 @@ func nestiaSDKReflectTypeNode(
|
|
|
1322
1886
|
element, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsArrayTypeNode().ElementType)
|
|
1323
1887
|
if ok == false {
|
|
1324
1888
|
element = map[string]any{"name": nestiaSDKTypeNodeText(node.AsArrayTypeNode().ElementType)}
|
|
1325
|
-
refs = nestiaSDKReflectImports(
|
|
1889
|
+
refs = nestiaSDKReflectImports(prog, node.AsArrayTypeNode().ElementType, imports)
|
|
1326
1890
|
}
|
|
1327
1891
|
return map[string]any{
|
|
1328
1892
|
"name": "Array",
|
|
@@ -1332,34 +1896,29 @@ func nestiaSDKReflectTypeNode(
|
|
|
1332
1896
|
child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, node.AsParenthesizedTypeNode().Type)
|
|
1333
1897
|
if ok == false {
|
|
1334
1898
|
child = map[string]any{"name": nestiaSDKTypeNodeText(node.AsParenthesizedTypeNode().Type)}
|
|
1335
|
-
refs = nestiaSDKReflectImports(
|
|
1899
|
+
refs = nestiaSDKReflectImports(prog, node.AsParenthesizedTypeNode().Type, imports)
|
|
1336
1900
|
}
|
|
1337
|
-
name
|
|
1338
|
-
return map[string]any{"name": "(" + name + ")"}, refs, true
|
|
1901
|
+
return map[string]any{"name": "(" + nestiaSDKReflectTypeText(child) + ")"}, refs, true
|
|
1339
1902
|
case shimast.KindTypeOperator:
|
|
1340
1903
|
operator := node.AsTypeOperatorNode()
|
|
1341
1904
|
prefix := nestiaSDKTypeOperatorPrefix(node, operator.Type)
|
|
1342
1905
|
if prefix == "" {
|
|
1343
1906
|
return nil, nil, false
|
|
1344
1907
|
}
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
}
|
|
1350
|
-
|
|
1351
|
-
return map[string]any{"name": prefix + " " + name}, refs, true
|
|
1908
|
+
// Operators retain their actual syntax: readonly requires an array or
|
|
1909
|
+
// tuple operand, and rendering the reflected Array<T> form here would
|
|
1910
|
+
// produce invalid TypeScript. Checker-projected nodes already contain
|
|
1911
|
+
// concrete alias arguments and are printed by the same text operation.
|
|
1912
|
+
return map[string]any{"name": nestiaSDKTypeNodeText(node)},
|
|
1913
|
+
nestiaSDKReflectImports(prog, operator.Type, imports), true
|
|
1352
1914
|
case shimast.KindTypeQuery:
|
|
1353
1915
|
return nil, nil, false
|
|
1354
1916
|
case shimast.KindTypeReference:
|
|
1355
1917
|
ref := node.AsTypeReferenceNode()
|
|
1356
1918
|
name := nestiaSDKEntityNameText(ref.TypeName)
|
|
1357
|
-
rootRefs := nestiaSDKReflectImports(
|
|
1358
|
-
if len(rootRefs) == 0 && nestiaSDKIsAsyncReturnWrapper(prog, ref.TypeName, name) == false {
|
|
1359
|
-
rootRefs = nestiaSDKReflectTypeReferenceSymbolImport(prog, ref.TypeName, name)
|
|
1360
|
-
}
|
|
1919
|
+
rootRefs := nestiaSDKReflectImports(prog, ref.TypeName, imports)
|
|
1361
1920
|
if ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) != 0 {
|
|
1362
|
-
if
|
|
1921
|
+
if transform.NestiaCoreIsAsyncReturnWrapperReference(prog, ref.TypeName) && len(ref.TypeArguments.Nodes) == 1 {
|
|
1363
1922
|
return nestiaSDKReflectTypeNode(prog, imports, ref.TypeArguments.Nodes[0])
|
|
1364
1923
|
}
|
|
1365
1924
|
args := make([]any, 0, len(ref.TypeArguments.Nodes))
|
|
@@ -1369,7 +1928,7 @@ func nestiaSDKReflectTypeNode(
|
|
|
1369
1928
|
if ok == false {
|
|
1370
1929
|
text := nestiaSDKTypeNodeText(child)
|
|
1371
1930
|
arg = map[string]any{"name": text}
|
|
1372
|
-
refs = nestiaSDKReflectImports(
|
|
1931
|
+
refs = nestiaSDKReflectImports(prog, child, imports)
|
|
1373
1932
|
}
|
|
1374
1933
|
args = append(args, arg)
|
|
1375
1934
|
groups = append(groups, refs)
|
|
@@ -1385,16 +1944,20 @@ func nestiaSDKReflectTypeNode(
|
|
|
1385
1944
|
if name == "" {
|
|
1386
1945
|
return nil, nil, false
|
|
1387
1946
|
}
|
|
1388
|
-
return map[string]any{"name": name}, nestiaSDKReflectImports(
|
|
1947
|
+
return map[string]any{"name": name}, nestiaSDKReflectImports(prog, node, imports), true
|
|
1389
1948
|
}
|
|
1390
1949
|
}
|
|
1391
1950
|
|
|
1951
|
+
// nestiaSDKReflectTypeReferenceSymbolImport supplies a declaration import when
|
|
1952
|
+
// no authored import binding describes the referenced type. The actual program's
|
|
1953
|
+
// library-file identity keeps global library declarations out of client imports.
|
|
1954
|
+
// Locally bound type parameters have no importable module declaration.
|
|
1392
1955
|
func nestiaSDKReflectTypeReferenceSymbolImport(prog *driver.Program, node *shimast.Node, name string) []any {
|
|
1393
1956
|
if prog == nil || prog.Checker == nil || node == nil {
|
|
1394
1957
|
return nil
|
|
1395
1958
|
}
|
|
1396
1959
|
prefix := strings.Split(name, ".")[0]
|
|
1397
|
-
if
|
|
1960
|
+
if nestiaSDKIsTypeKeyword(prefix) {
|
|
1398
1961
|
return nil
|
|
1399
1962
|
}
|
|
1400
1963
|
symbol := prog.Checker.GetSymbolAtLocation(node)
|
|
@@ -1404,17 +1967,18 @@ func nestiaSDKReflectTypeReferenceSymbolImport(prog *driver.Program, node *shima
|
|
|
1404
1967
|
symbol = typ.Symbol()
|
|
1405
1968
|
}
|
|
1406
1969
|
}
|
|
1407
|
-
if symbol == nil || len(symbol.Declarations) == 0 {
|
|
1970
|
+
if symbol == nil || symbol.Flags&shimast.SymbolFlagsTypeParameter != 0 || len(symbol.Declarations) == 0 {
|
|
1408
1971
|
return nil
|
|
1409
1972
|
}
|
|
1410
1973
|
sourceFile := shimast.GetSourceFileOfNode(symbol.Declarations[0])
|
|
1411
1974
|
if sourceFile == nil {
|
|
1412
1975
|
return nil
|
|
1413
1976
|
}
|
|
1414
|
-
|
|
1415
|
-
if
|
|
1977
|
+
// a default library type, whichever folder or replacement package supplies it
|
|
1978
|
+
if prog.TSProgram != nil && prog.TSProgram.IsLibFile(sourceFile) {
|
|
1416
1979
|
return nil
|
|
1417
1980
|
}
|
|
1981
|
+
file := filepath.ToSlash(sourceFile.FileName())
|
|
1418
1982
|
return []any{
|
|
1419
1983
|
map[string]any{
|
|
1420
1984
|
"file": file,
|
|
@@ -1441,7 +2005,7 @@ func nestiaSDKReflectJoinedTypeNode(
|
|
|
1441
2005
|
if ok == false {
|
|
1442
2006
|
text := nestiaSDKTypeNodeText(child)
|
|
1443
2007
|
names = append(names, text)
|
|
1444
|
-
groups = append(groups, nestiaSDKReflectImports(
|
|
2008
|
+
groups = append(groups, nestiaSDKReflectImports(prog, child, imports))
|
|
1445
2009
|
} else {
|
|
1446
2010
|
names = append(names, nestiaSDKReflectTypeText(ref))
|
|
1447
2011
|
groups = append(groups, refs)
|
|
@@ -1505,18 +2069,261 @@ func nestiaSDKParentClassName(node *shimast.Node) string {
|
|
|
1505
2069
|
return ""
|
|
1506
2070
|
}
|
|
1507
2071
|
|
|
2072
|
+
// nestiaSDKMethodName names a decorated method's site. The contributor visits
|
|
2073
|
+
// every decorated method of the program, controller or not, and a computed
|
|
2074
|
+
// name such as `[key]` has no identifier: it reads as its literal key when it
|
|
2075
|
+
// has one, and as its source otherwise. The metadata still reaches the method,
|
|
2076
|
+
// because a decorator attaches it under the key the runtime computes.
|
|
1508
2077
|
func nestiaSDKMethodName(node *shimast.Node) string {
|
|
1509
2078
|
if node == nil || node.Name() == nil {
|
|
1510
2079
|
return ""
|
|
1511
2080
|
}
|
|
1512
|
-
|
|
2081
|
+
name := node.Name()
|
|
2082
|
+
if name.Kind == shimast.KindComputedPropertyName {
|
|
2083
|
+
switch expression := name.AsComputedPropertyName().Expression; expression.Kind {
|
|
2084
|
+
case shimast.KindStringLiteral, shimast.KindNumericLiteral, shimast.KindNoSubstitutionTemplateLiteral:
|
|
2085
|
+
return expression.Text()
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
return strings.Trim(shimast.NodeText(name), "\"'")
|
|
2089
|
+
}
|
|
2090
|
+
|
|
2091
|
+
// nestiaSDKWebSocketHeaderError reports a WebSocket route whose handshake
|
|
2092
|
+
// header the generated SDK cannot carry: the header type of an
|
|
2093
|
+
// @WebSocketRoute.Acceptor() (its first type argument) or the type of an
|
|
2094
|
+
// @WebSocketRoute.Header() parameter must be an object type or undefined,
|
|
2095
|
+
// because the SDK function sends it as connection.headers, which @nestia/fetcher
|
|
2096
|
+
// types `IConnection<Headers extends object | undefined>`. tgrid itself leaves
|
|
2097
|
+
// the header unconstrained, so a null or primitive header serves, yet its SDK
|
|
2098
|
+
// would not compile.
|
|
2099
|
+
func nestiaSDKWebSocketHeaderError(
|
|
2100
|
+
prog *driver.Program,
|
|
2101
|
+
param *shimast.Node,
|
|
2102
|
+
typ *shimchecker.Type,
|
|
2103
|
+
) error {
|
|
2104
|
+
category := transform.NestiaCoreWebSocketParameterCategory(prog, param)
|
|
2105
|
+
var header *shimchecker.Type
|
|
2106
|
+
switch category {
|
|
2107
|
+
case "Acceptor":
|
|
2108
|
+
// only tgrid's acceptor has a header; the transform already rejects any
|
|
2109
|
+
// other type, and asking a non-reference type for its type arguments
|
|
2110
|
+
// would fault the checker
|
|
2111
|
+
if _, name := transform.NestiaCoreWebSocketTypeReference(prog, nestiaSDKParameterTypeNode(param)); name != "WebSocketAcceptor" {
|
|
2112
|
+
return nil
|
|
2113
|
+
}
|
|
2114
|
+
if typ == nil || typ.Flags()&shimchecker.TypeFlagsObject == 0 || typ.ObjectFlags()&shimchecker.ObjectFlagsReference == 0 {
|
|
2115
|
+
return nil
|
|
2116
|
+
}
|
|
2117
|
+
if args := shimchecker.Checker_getTypeArguments(prog.Checker, typ); len(args) != 0 {
|
|
2118
|
+
header = args[0]
|
|
2119
|
+
}
|
|
2120
|
+
case "Header":
|
|
2121
|
+
header = typ
|
|
2122
|
+
default:
|
|
2123
|
+
return nil
|
|
2124
|
+
}
|
|
2125
|
+
if header == nil || nestiaSDKIsConnectionHeader(header) {
|
|
2126
|
+
return nil
|
|
2127
|
+
}
|
|
2128
|
+
return fmt.Errorf(
|
|
2129
|
+
"@WebSocketRoute.%s() parameter %q has the header type %q, which the SDK cannot send: it carries the handshake header as connection.headers, which must be an object type or undefined. Use an object type, or undefined for no header.",
|
|
2130
|
+
category,
|
|
2131
|
+
nestiaSDKParameterName(param),
|
|
2132
|
+
prog.Checker.TypeToString(header),
|
|
2133
|
+
)
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
// nestiaSDKIsConnectionHeader reports whether typ is assignable to
|
|
2137
|
+
// `object | undefined`, the constraint of IConnection's Headers: any, never,
|
|
2138
|
+
// undefined, an object type, or a union of them; an intersection only when
|
|
2139
|
+
// every member is an object type, so `string & {}` stays a string.
|
|
2140
|
+
func nestiaSDKIsConnectionHeader(typ *shimchecker.Type) bool {
|
|
2141
|
+
flags := typ.Flags()
|
|
2142
|
+
if flags&(shimchecker.TypeFlagsAny|shimchecker.TypeFlagsNever|shimchecker.TypeFlagsUndefined|shimchecker.TypeFlagsObject|shimchecker.TypeFlagsNonPrimitive) != 0 {
|
|
2143
|
+
return true
|
|
2144
|
+
}
|
|
2145
|
+
if flags&shimchecker.TypeFlagsUnion != 0 {
|
|
2146
|
+
for _, elem := range typ.AsUnionOrIntersectionType().Types() {
|
|
2147
|
+
if nestiaSDKIsConnectionHeader(elem) == false {
|
|
2148
|
+
return false
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
return true
|
|
2152
|
+
}
|
|
2153
|
+
if flags&shimchecker.TypeFlagsIntersection != 0 {
|
|
2154
|
+
for _, elem := range typ.AsUnionOrIntersectionType().Types() {
|
|
2155
|
+
if elem.Flags()&(shimchecker.TypeFlagsObject|shimchecker.TypeFlagsNonPrimitive) == 0 {
|
|
2156
|
+
return false
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
return true
|
|
2160
|
+
}
|
|
2161
|
+
return false
|
|
1513
2162
|
}
|
|
1514
2163
|
|
|
2164
|
+
// nestiaSDKWebSocketParameterType reflects an @WebSocketRoute.Acceptor() or
|
|
2165
|
+
// .Driver() parameter annotated through a type alias or an import type as the
|
|
2166
|
+
// tgrid reference it spells, such as `WebSocketAcceptor<Header, Provider,
|
|
2167
|
+
// Listener>`, because the generated client needs those type arguments. Each
|
|
2168
|
+
// argument is reflected in the file that writes it, and one naming a type
|
|
2169
|
+
// parameter of a generic alias is replaced by what the annotation passes for
|
|
2170
|
+
// it. It returns nil for an annotation writing the tgrid reference itself as a
|
|
2171
|
+
// type reference, and an error for an alias using its type parameter inside an
|
|
2172
|
+
// argument, such as `IRoom<P>`, which no written node spells.
|
|
2173
|
+
func nestiaSDKWebSocketParameterType(
|
|
2174
|
+
context *nestiaSDKContext,
|
|
2175
|
+
param *shimast.Node,
|
|
2176
|
+
) (map[string]any, []any, error) {
|
|
2177
|
+
category := transform.NestiaCoreWebSocketParameterCategory(context.prog, param)
|
|
2178
|
+
switch category {
|
|
2179
|
+
case "Acceptor", "Driver":
|
|
2180
|
+
default:
|
|
2181
|
+
return nil, nil, nil
|
|
2182
|
+
}
|
|
2183
|
+
chain, name := transform.NestiaCoreWebSocketTypeReference(context.prog, nestiaSDKParameterTypeNode(param))
|
|
2184
|
+
// the annotation's own reflection serves a tgrid reference written as a
|
|
2185
|
+
// plain type reference, but reflects an import type without its arguments
|
|
2186
|
+
if len(chain) == 0 || (len(chain) == 1 && chain[0].Kind == shimast.KindTypeReference) {
|
|
2187
|
+
return nil, nil, nil
|
|
2188
|
+
}
|
|
2189
|
+
args := []any{}
|
|
2190
|
+
groups := [][]any{}
|
|
2191
|
+
if target := chain[len(chain)-1].TypeArgumentList(); target != nil {
|
|
2192
|
+
for _, node := range target.Nodes {
|
|
2193
|
+
argument := nestiaSDKWebSocketTypeArgument(context.prog, chain, node)
|
|
2194
|
+
if argument == nil {
|
|
2195
|
+
return nil, nil, fmt.Errorf(
|
|
2196
|
+
"@WebSocketRoute.%s() parameter %q is typed by a type alias whose %s type argument %q uses a type parameter of the alias inside it, which the SDK cannot write. Pass each type parameter as a whole type argument, or write the %s type directly.",
|
|
2197
|
+
category,
|
|
2198
|
+
nestiaSDKParameterName(param),
|
|
2199
|
+
name,
|
|
2200
|
+
nestiaSDKTypeNodeText(node),
|
|
2201
|
+
name,
|
|
2202
|
+
)
|
|
2203
|
+
}
|
|
2204
|
+
imports := context.imports(shimast.GetSourceFileOfNode(argument))
|
|
2205
|
+
arg, refs, ok := nestiaSDKReflectTypeNode(context.prog, imports, argument)
|
|
2206
|
+
if ok == false {
|
|
2207
|
+
text := nestiaSDKTypeNodeText(argument)
|
|
2208
|
+
arg = map[string]any{"name": text}
|
|
2209
|
+
refs = nestiaSDKReflectImports(context.prog, argument, imports)
|
|
2210
|
+
}
|
|
2211
|
+
args = append(args, arg)
|
|
2212
|
+
groups = append(groups, refs)
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
refs := nestiaSDKMergeImportLiterals(groups...)
|
|
2216
|
+
if refs == nil {
|
|
2217
|
+
refs = []any{}
|
|
2218
|
+
}
|
|
2219
|
+
return map[string]any{
|
|
2220
|
+
"name": name,
|
|
2221
|
+
"typeArguments": args,
|
|
2222
|
+
}, refs, nil
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
// nestiaSDKWebSocketTypeArgument follows a type argument of the last reference
|
|
2226
|
+
// in chain back through the aliases while it names a type parameter of the
|
|
2227
|
+
// alias it is written in, to what the reference before passes for it or else
|
|
2228
|
+
// to the parameter's default. It returns nil when the argument uses such a
|
|
2229
|
+
// type parameter any other way, which no written node spells.
|
|
2230
|
+
func nestiaSDKWebSocketTypeArgument(prog *driver.Program, chain []*shimast.Node, node *shimast.Node) *shimast.Node {
|
|
2231
|
+
for level := len(chain) - 1; level > 0; {
|
|
2232
|
+
alias := nestiaSDKEnclosingTypeAlias(chain[level])
|
|
2233
|
+
if alias == nil {
|
|
2234
|
+
return nil
|
|
2235
|
+
}
|
|
2236
|
+
parameters := alias.AsTypeAliasDeclaration().TypeParameters
|
|
2237
|
+
index := nestiaSDKTypeParameterIndex(prog, parameters, node)
|
|
2238
|
+
if index == -1 {
|
|
2239
|
+
if nestiaSDKUsesAliasTypeParameter(prog, node) {
|
|
2240
|
+
return nil
|
|
2241
|
+
}
|
|
2242
|
+
return node
|
|
2243
|
+
}
|
|
2244
|
+
// the reference naming the alias, a type reference or an import type
|
|
2245
|
+
reference := chain[level-1].TypeArgumentList()
|
|
2246
|
+
if reference != nil && index < len(reference.Nodes) {
|
|
2247
|
+
node = reference.Nodes[index]
|
|
2248
|
+
level--
|
|
2249
|
+
continue
|
|
2250
|
+
}
|
|
2251
|
+
// a default may name an earlier parameter of the same alias
|
|
2252
|
+
node = parameters.Nodes[index].AsTypeParameterDeclaration().DefaultType
|
|
2253
|
+
if node == nil {
|
|
2254
|
+
return nil
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
return node
|
|
2258
|
+
}
|
|
2259
|
+
|
|
2260
|
+
func nestiaSDKEnclosingTypeAlias(node *shimast.Node) *shimast.Node {
|
|
2261
|
+
for parent := node.Parent; parent != nil; parent = parent.Parent {
|
|
2262
|
+
if parent.Kind == shimast.KindTypeAliasDeclaration {
|
|
2263
|
+
return parent
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
return nil
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
// nestiaSDKTypeParameterIndex is the position of the type parameter node names
|
|
2270
|
+
// by itself, such as `P` or `(P)`, among parameters, or -1.
|
|
2271
|
+
func nestiaSDKTypeParameterIndex(prog *driver.Program, parameters *shimast.NodeList, node *shimast.Node) int {
|
|
2272
|
+
for node != nil && node.Kind == shimast.KindParenthesizedType {
|
|
2273
|
+
node = node.AsParenthesizedTypeNode().Type
|
|
2274
|
+
}
|
|
2275
|
+
if parameters == nil || node == nil || node.Kind != shimast.KindTypeReference {
|
|
2276
|
+
return -1
|
|
2277
|
+
}
|
|
2278
|
+
if arguments := node.AsTypeReferenceNode().TypeArguments; arguments != nil && len(arguments.Nodes) != 0 {
|
|
2279
|
+
return -1
|
|
2280
|
+
}
|
|
2281
|
+
symbol := prog.Checker.GetSymbolAtLocation(node.AsTypeReferenceNode().TypeName)
|
|
2282
|
+
if symbol == nil || symbol.Flags&shimast.SymbolFlagsTypeParameter == 0 {
|
|
2283
|
+
return -1
|
|
2284
|
+
}
|
|
2285
|
+
for index, parameter := range parameters.Nodes {
|
|
2286
|
+
for _, declaration := range symbol.Declarations {
|
|
2287
|
+
if declaration == parameter {
|
|
2288
|
+
return index
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
return -1
|
|
2293
|
+
}
|
|
2294
|
+
|
|
2295
|
+
// nestiaSDKUsesAliasTypeParameter reports whether node refers to a type
|
|
2296
|
+
// parameter a type alias declares anywhere inside it.
|
|
2297
|
+
func nestiaSDKUsesAliasTypeParameter(prog *driver.Program, node *shimast.Node) bool {
|
|
2298
|
+
if node == nil {
|
|
2299
|
+
return false
|
|
2300
|
+
}
|
|
2301
|
+
if node.Kind == shimast.KindTypeReference {
|
|
2302
|
+
symbol := prog.Checker.GetSymbolAtLocation(node.AsTypeReferenceNode().TypeName)
|
|
2303
|
+
if symbol != nil && symbol.Flags&shimast.SymbolFlagsTypeParameter != 0 {
|
|
2304
|
+
for _, declaration := range symbol.Declarations {
|
|
2305
|
+
if declaration != nil && declaration.Parent != nil && declaration.Parent.Kind == shimast.KindTypeAliasDeclaration {
|
|
2306
|
+
return true
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
found := false
|
|
2312
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
2313
|
+
found = nestiaSDKUsesAliasTypeParameter(prog, child)
|
|
2314
|
+
return found
|
|
2315
|
+
})
|
|
2316
|
+
return found
|
|
2317
|
+
}
|
|
2318
|
+
|
|
2319
|
+
// nestiaSDKParameterName reads a parameter's identifier, or "" for a
|
|
2320
|
+
// destructuring pattern such as `{ organizationId }`, which declares no name
|
|
2321
|
+
// the SDK could reuse; the SDK names such a parameter after its role.
|
|
1515
2322
|
func nestiaSDKParameterName(node *shimast.Node) string {
|
|
1516
|
-
if node == nil || node.Name() == nil {
|
|
2323
|
+
if node == nil || node.Name() == nil || node.Name().Kind != shimast.KindIdentifier {
|
|
1517
2324
|
return ""
|
|
1518
2325
|
}
|
|
1519
|
-
return
|
|
2326
|
+
return node.Name().Text()
|
|
1520
2327
|
}
|
|
1521
2328
|
func nestiaSDKParameterTypeNode(node *shimast.Node) *shimast.Node {
|
|
1522
2329
|
if node != nil && node.AsParameterDeclaration() != nil {
|
|
@@ -1536,7 +2343,7 @@ func nestiaSDKMethodReturnTypeNode(prog *driver.Program, method *shimast.Node) *
|
|
|
1536
2343
|
func nestiaSDKReturnTypeNode(prog *driver.Program, node *shimast.Node) *shimast.Node {
|
|
1537
2344
|
if node != nil && node.Kind == shimast.KindTypeReference {
|
|
1538
2345
|
ref := node.AsTypeReferenceNode()
|
|
1539
|
-
if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 &&
|
|
2346
|
+
if ref != nil && ref.TypeArguments != nil && len(ref.TypeArguments.Nodes) == 1 && transform.NestiaCoreIsAsyncReturnWrapperReference(prog, ref.TypeName) {
|
|
1540
2347
|
return ref.TypeArguments.Nodes[0]
|
|
1541
2348
|
}
|
|
1542
2349
|
}
|
|
@@ -1546,84 +2353,18 @@ func nestiaSDKReturnTypeNode(prog *driver.Program, node *shimast.Node) *shimast.
|
|
|
1546
2353
|
func nestiaSDKEntityNameText(node *shimast.Node) string {
|
|
1547
2354
|
return nestiaSDKTypeNodeText(node)
|
|
1548
2355
|
}
|
|
1549
|
-
func nestiaSDKIsAsyncReturnWrapper(
|
|
1550
|
-
prog *driver.Program,
|
|
1551
|
-
node *shimast.Node,
|
|
1552
|
-
name string,
|
|
1553
|
-
) bool {
|
|
1554
|
-
if name == "Promise" {
|
|
1555
|
-
return true
|
|
1556
|
-
}
|
|
1557
|
-
if name != "Observable" || prog == nil || prog.Checker == nil {
|
|
1558
|
-
return false
|
|
1559
|
-
}
|
|
1560
|
-
symbol := prog.Checker.GetSymbolAtLocation(node)
|
|
1561
|
-
return nestiaSDKIsRxjsObservableImport(node) ||
|
|
1562
|
-
(symbol != nil && nestiaSDKIsRxjsDeclarations(symbol.Declarations))
|
|
1563
|
-
}
|
|
1564
|
-
|
|
1565
|
-
func nestiaSDKIsRxjsDeclarations(declarations []*shimast.Node) bool {
|
|
1566
|
-
for _, decl := range declarations {
|
|
1567
|
-
sourceFile := shimast.GetSourceFileOfNode(decl)
|
|
1568
|
-
if sourceFile == nil {
|
|
1569
|
-
continue
|
|
1570
|
-
}
|
|
1571
|
-
file := filepath.ToSlash(sourceFile.FileName())
|
|
1572
|
-
if strings.Contains(file, "/node_modules/rxjs/") {
|
|
1573
|
-
return true
|
|
1574
|
-
}
|
|
1575
|
-
}
|
|
1576
|
-
return false
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
func nestiaSDKIsRxjsObservableImport(node *shimast.Node) bool {
|
|
1580
|
-
source, ok := transform.SourceFileText(shimast.GetSourceFileOfNode(node))
|
|
1581
|
-
return ok && nestiaSDKHasNamedImport(source, "rxjs", "Observable", "Observable")
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
func nestiaSDKHasNamedImport(
|
|
1585
|
-
source string,
|
|
1586
|
-
module string,
|
|
1587
|
-
imported string,
|
|
1588
|
-
local string,
|
|
1589
|
-
) bool {
|
|
1590
|
-
for _, match := range nestiaSDKImportFromPattern.FindAllStringSubmatch(source, -1) {
|
|
1591
|
-
if len(match) < 3 || match[2] != module {
|
|
1592
|
-
continue
|
|
1593
|
-
}
|
|
1594
|
-
open := strings.Index(match[1], "{")
|
|
1595
|
-
close := strings.LastIndex(match[1], "}")
|
|
1596
|
-
if open < 0 || close <= open {
|
|
1597
|
-
continue
|
|
1598
|
-
}
|
|
1599
|
-
for _, part := range strings.Split(match[1][open+1:close], ",") {
|
|
1600
|
-
fields := strings.Fields(strings.TrimPrefix(strings.TrimSpace(part), "type "))
|
|
1601
|
-
if len(fields) == 1 && fields[0] == local && imported == local {
|
|
1602
|
-
return true
|
|
1603
|
-
}
|
|
1604
|
-
if len(fields) == 3 &&
|
|
1605
|
-
fields[0] == imported &&
|
|
1606
|
-
fields[1] == "as" &&
|
|
1607
|
-
fields[2] == local {
|
|
1608
|
-
return true
|
|
1609
|
-
}
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
return false
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
var nestiaSDKImportFromPattern = regexp.MustCompile(
|
|
1616
|
-
`(?s)import\s+(?:type\s+)?(.+?)\s+from\s+["']([^"']+)["']`,
|
|
1617
|
-
)
|
|
1618
|
-
|
|
1619
2356
|
func nestiaSDKTypeNodeText(node *shimast.Node) string {
|
|
1620
2357
|
if node == nil {
|
|
1621
2358
|
return ""
|
|
1622
2359
|
}
|
|
1623
2360
|
file := shimast.GetSourceFileOfNode(node)
|
|
1624
|
-
source, ok :=
|
|
2361
|
+
source, ok := "", false
|
|
2362
|
+
if file != nil {
|
|
2363
|
+
source, ok = transform.SourceFileText(file)
|
|
2364
|
+
}
|
|
1625
2365
|
if ok == false {
|
|
1626
|
-
|
|
2366
|
+
printer := shimprinter.NewPrinter(shimprinter.PrinterOptions{}, shimprinter.PrintHandlers{}, shimprinter.NewEmitContext())
|
|
2367
|
+
return strings.TrimSpace(printer.Emit(node, file))
|
|
1627
2368
|
}
|
|
1628
2369
|
start, end := node.Pos(), node.End()
|
|
1629
2370
|
if start < 0 || end > len(source) || start >= end {
|
|
@@ -1640,7 +2381,9 @@ type sdkOperationMetadataInsertResult struct {
|
|
|
1640
2381
|
func nestiaSDKDiagnostic(site nestiaSDKSite, message string) transform.Diagnostic {
|
|
1641
2382
|
line, column := 0, 0
|
|
1642
2383
|
if site.File != nil && site.Method != nil {
|
|
1643
|
-
|
|
2384
|
+
// the first token's position: Pos() starts at the leading trivia, the
|
|
2385
|
+
// previous line's end and any JSDoc
|
|
2386
|
+
if pos := shimscanner.GetTokenPosOfNode(site.Method, site.File, false); pos >= 0 {
|
|
1644
2387
|
l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(site.File, pos)
|
|
1645
2388
|
line, column = l+1, c+1
|
|
1646
2389
|
}
|