@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.
Files changed (391) hide show
  1. package/README.md +1 -1
  2. package/assets/bundle/distribute/build-sdk.cjs +28 -0
  3. package/assets/bundle/distribute/package.json +7 -3
  4. package/assets/bundle/distribute/tsconfig.json +15 -105
  5. package/lib/INestiaConfig.d.ts +74 -34
  6. package/lib/NestiaSdkApplication.d.ts +60 -0
  7. package/lib/NestiaSdkApplication.js +121 -32
  8. package/lib/NestiaSdkApplication.js.map +1 -1
  9. package/lib/NestiaSwaggerComposer.d.ts +21 -0
  10. package/lib/NestiaSwaggerComposer.js +32 -13
  11. package/lib/NestiaSwaggerComposer.js.map +1 -1
  12. package/lib/analyses/AccessorAnalyzer.d.ts +26 -0
  13. package/lib/analyses/AccessorAnalyzer.js +26 -0
  14. package/lib/analyses/AccessorAnalyzer.js.map +1 -1
  15. package/lib/analyses/ConfigAnalyzer.d.ts +41 -0
  16. package/lib/analyses/ConfigAnalyzer.js +52 -77
  17. package/lib/analyses/ConfigAnalyzer.js.map +1 -1
  18. package/lib/analyses/ImportAnalyzer.d.ts +25 -0
  19. package/lib/analyses/ImportAnalyzer.js +25 -0
  20. package/lib/analyses/ImportAnalyzer.js.map +1 -1
  21. package/lib/analyses/ParameterNameAnalyzer.d.ts +44 -0
  22. package/lib/analyses/ParameterNameAnalyzer.js +63 -0
  23. package/lib/analyses/ParameterNameAnalyzer.js.map +1 -0
  24. package/lib/analyses/PathAnalyzer.d.ts +93 -1
  25. package/lib/analyses/PathAnalyzer.js +160 -23
  26. package/lib/analyses/PathAnalyzer.js.map +1 -1
  27. package/lib/analyses/ReflectControllerAnalyzer.d.ts +32 -0
  28. package/lib/analyses/ReflectControllerAnalyzer.js +24 -1
  29. package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
  30. package/lib/analyses/ReflectHttpOperationAnalyzer.d.ts +29 -0
  31. package/lib/analyses/ReflectHttpOperationAnalyzer.js +35 -24
  32. package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
  33. package/lib/analyses/ReflectHttpOperationExceptionAnalyzer.d.ts +31 -0
  34. package/lib/analyses/ReflectHttpOperationExceptionAnalyzer.js +21 -0
  35. package/lib/analyses/ReflectHttpOperationExceptionAnalyzer.js.map +1 -1
  36. package/lib/analyses/ReflectHttpOperationParameterAnalyzer.d.ts +31 -0
  37. package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js +143 -23
  38. package/lib/analyses/ReflectHttpOperationParameterAnalyzer.js.map +1 -1
  39. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.d.ts +32 -0
  40. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +27 -2
  41. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
  42. package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +29 -0
  43. package/lib/analyses/ReflectMcpOperationAnalyzer.js +35 -11
  44. package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -1
  45. package/lib/analyses/ReflectMetadataAnalyzer.d.ts +47 -0
  46. package/lib/analyses/ReflectMetadataAnalyzer.js +47 -0
  47. package/lib/analyses/ReflectMetadataAnalyzer.js.map +1 -1
  48. package/lib/analyses/ReflectWebSocketOperationAnalyzer.d.ts +29 -0
  49. package/lib/analyses/ReflectWebSocketOperationAnalyzer.js +27 -5
  50. package/lib/analyses/ReflectWebSocketOperationAnalyzer.js.map +1 -1
  51. package/lib/analyses/SecurityAnalyzer.d.ts +25 -0
  52. package/lib/analyses/SecurityAnalyzer.js +39 -19
  53. package/lib/analyses/SecurityAnalyzer.js.map +1 -1
  54. package/lib/analyses/SwaggerExampleAnalyzer.d.ts +31 -0
  55. package/lib/analyses/SwaggerExampleAnalyzer.js +36 -0
  56. package/lib/analyses/SwaggerExampleAnalyzer.js.map +1 -0
  57. package/lib/analyses/TypedHttpRouteAnalyzer.d.ts +41 -3
  58. package/lib/analyses/TypedHttpRouteAnalyzer.js +196 -172
  59. package/lib/analyses/TypedHttpRouteAnalyzer.js.map +1 -1
  60. package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +29 -0
  61. package/lib/analyses/TypedMcpRouteAnalyzer.js +152 -20
  62. package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -1
  63. package/lib/analyses/TypedWebSocketRouteAnalyzer.d.ts +19 -0
  64. package/lib/analyses/TypedWebSocketRouteAnalyzer.js +20 -1
  65. package/lib/analyses/TypedWebSocketRouteAnalyzer.js.map +1 -1
  66. package/lib/decorators/OperationMetadata.d.ts +6 -0
  67. package/lib/decorators/OperationMetadata.js +6 -0
  68. package/lib/decorators/OperationMetadata.js.map +1 -1
  69. package/lib/executable/internal/CommandParser.d.ts +19 -0
  70. package/lib/executable/internal/CommandParser.js +19 -0
  71. package/lib/executable/internal/CommandParser.js.map +1 -1
  72. package/lib/executable/internal/NestiaConfigLoader.d.ts +56 -0
  73. package/lib/executable/internal/NestiaConfigLoader.js +48 -42
  74. package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
  75. package/lib/executable/internal/NestiaSdkCommand.d.ts +60 -0
  76. package/lib/executable/internal/NestiaSdkCommand.js +95 -3
  77. package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
  78. package/lib/executable/internal/NestiaSdkWatcher.d.ts +81 -0
  79. package/lib/executable/internal/NestiaSdkWatcher.js +125 -41
  80. package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -1
  81. package/lib/executable/internal/NestiaSwaggerWatch.d.ts +80 -0
  82. package/lib/executable/internal/NestiaSwaggerWatch.js +308 -0
  83. package/lib/executable/internal/NestiaSwaggerWatch.js.map +1 -0
  84. package/lib/executable/sdk.js +16 -1
  85. package/lib/executable/sdk.js.map +1 -1
  86. package/lib/factories/ExpressionFactory.d.ts +18 -0
  87. package/lib/factories/ExpressionFactory.js +21 -2
  88. package/lib/factories/ExpressionFactory.js.map +1 -1
  89. package/lib/factories/IdentifierFactory.d.ts +24 -0
  90. package/lib/factories/IdentifierFactory.js +24 -0
  91. package/lib/factories/IdentifierFactory.js.map +1 -1
  92. package/lib/factories/LiteralFactory.d.ts +22 -4
  93. package/lib/factories/LiteralFactory.js +33 -11
  94. package/lib/factories/LiteralFactory.js.map +1 -1
  95. package/lib/factories/StatementFactory.d.ts +28 -1
  96. package/lib/factories/StatementFactory.js +28 -1
  97. package/lib/factories/StatementFactory.js.map +1 -1
  98. package/lib/factories/TypeFactory.d.ts +24 -0
  99. package/lib/factories/TypeFactory.js +15 -0
  100. package/lib/factories/TypeFactory.js.map +1 -1
  101. package/lib/generates/CloneGenerator.d.ts +22 -0
  102. package/lib/generates/CloneGenerator.js +26 -5
  103. package/lib/generates/CloneGenerator.js.map +1 -1
  104. package/lib/generates/E2eGenerator.d.ts +19 -0
  105. package/lib/generates/E2eGenerator.js +19 -0
  106. package/lib/generates/E2eGenerator.js.map +1 -1
  107. package/lib/generates/SdkGenerator.d.ts +38 -0
  108. package/lib/generates/SdkGenerator.js +43 -5
  109. package/lib/generates/SdkGenerator.js.map +1 -1
  110. package/lib/generates/SwaggerGenerator.d.ts +36 -6
  111. package/lib/generates/SwaggerGenerator.js +190 -40
  112. package/lib/generates/SwaggerGenerator.js.map +1 -1
  113. package/lib/generates/internal/E2eFileProgrammer.d.ts +19 -0
  114. package/lib/generates/internal/E2eFileProgrammer.js +20 -1
  115. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  116. package/lib/generates/internal/FilePrinter.d.ts +57 -0
  117. package/lib/generates/internal/FilePrinter.js +74 -0
  118. package/lib/generates/internal/FilePrinter.js.map +1 -1
  119. package/lib/generates/internal/ImportDictionary.d.ts +93 -1
  120. package/lib/generates/internal/ImportDictionary.js +103 -2
  121. package/lib/generates/internal/ImportDictionary.js.map +1 -1
  122. package/lib/generates/internal/SdkAliasCollection.d.ts +121 -1
  123. package/lib/generates/internal/SdkAliasCollection.js +132 -11
  124. package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
  125. package/lib/generates/internal/SdkDistributionComposer.d.ts +19 -0
  126. package/lib/generates/internal/SdkDistributionComposer.js +149 -52
  127. package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
  128. package/lib/generates/internal/SdkFileProgrammer.d.ts +18 -0
  129. package/lib/generates/internal/SdkFileProgrammer.js +101 -12
  130. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  131. package/lib/generates/internal/SdkHttpCloneProgrammer.d.ts +29 -0
  132. package/lib/generates/internal/SdkHttpCloneProgrammer.js +33 -0
  133. package/lib/generates/internal/SdkHttpCloneProgrammer.js.map +1 -1
  134. package/lib/generates/internal/SdkHttpCloneReferencer.d.ts +19 -0
  135. package/lib/generates/internal/SdkHttpCloneReferencer.js +78 -25
  136. package/lib/generates/internal/SdkHttpCloneReferencer.js.map +1 -1
  137. package/lib/generates/internal/SdkHttpFunctionProgrammer.d.ts +19 -0
  138. package/lib/generates/internal/SdkHttpFunctionProgrammer.js +40 -23
  139. package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
  140. package/lib/generates/internal/SdkHttpNamespaceProgrammer.d.ts +19 -0
  141. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +46 -34
  142. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
  143. package/lib/generates/internal/SdkHttpParameterProgrammer.d.ts +142 -1
  144. package/lib/generates/internal/SdkHttpParameterProgrammer.js +175 -6
  145. package/lib/generates/internal/SdkHttpParameterProgrammer.js.map +1 -1
  146. package/lib/generates/internal/SdkHttpRouteProgrammer.d.ts +20 -0
  147. package/lib/generates/internal/SdkHttpRouteProgrammer.js +36 -32
  148. package/lib/generates/internal/SdkHttpRouteProgrammer.js.map +1 -1
  149. package/lib/generates/internal/SdkHttpSimulationProgrammer.d.ts +38 -0
  150. package/lib/generates/internal/SdkHttpSimulationProgrammer.js +66 -18
  151. package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
  152. package/lib/generates/internal/SdkImportWizard.d.ts +63 -0
  153. package/lib/generates/internal/SdkImportWizard.js +63 -0
  154. package/lib/generates/internal/SdkImportWizard.js.map +1 -1
  155. package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +17 -2
  156. package/lib/generates/internal/SdkMcpRouteProgrammer.js +72 -24
  157. package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -1
  158. package/lib/generates/internal/SdkPathTemplate.d.ts +36 -0
  159. package/lib/generates/internal/SdkPathTemplate.js +65 -0
  160. package/lib/generates/internal/SdkPathTemplate.js.map +1 -0
  161. package/lib/generates/internal/SdkRouteDirectory.d.ts +10 -0
  162. package/lib/generates/internal/SdkRouteDirectory.js +10 -0
  163. package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
  164. package/lib/generates/internal/SdkTypeProgrammer.d.ts +54 -1
  165. package/lib/generates/internal/SdkTypeProgrammer.js +111 -27
  166. package/lib/generates/internal/SdkTypeProgrammer.js.map +1 -1
  167. package/lib/generates/internal/SdkTypeTagProgrammer.d.ts +53 -1
  168. package/lib/generates/internal/SdkTypeTagProgrammer.js +407 -67
  169. package/lib/generates/internal/SdkTypeTagProgrammer.js.map +1 -1
  170. package/lib/generates/internal/SdkWebSocketCloneProgrammer.d.ts +40 -0
  171. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js +149 -142
  172. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js.map +1 -1
  173. package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.d.ts +19 -0
  174. package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js +46 -36
  175. package/lib/generates/internal/SdkWebSocketNamespaceProgrammer.js.map +1 -1
  176. package/lib/generates/internal/SdkWebSocketParameterProgrammer.d.ts +114 -1
  177. package/lib/generates/internal/SdkWebSocketParameterProgrammer.js +120 -5
  178. package/lib/generates/internal/SdkWebSocketParameterProgrammer.js.map +1 -1
  179. package/lib/generates/internal/SdkWebSocketRouteProgrammer.d.ts +18 -0
  180. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +75 -33
  181. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
  182. package/lib/generates/internal/SwaggerDescriptionComposer.d.ts +42 -0
  183. package/lib/generates/internal/SwaggerDescriptionComposer.js +44 -1
  184. package/lib/generates/internal/SwaggerDescriptionComposer.js.map +1 -1
  185. package/lib/generates/internal/SwaggerOperationComposer.d.ts +18 -0
  186. package/lib/generates/internal/SwaggerOperationComposer.js +37 -8
  187. package/lib/generates/internal/SwaggerOperationComposer.js.map +1 -1
  188. package/lib/generates/internal/SwaggerOperationParameterComposer.d.ts +40 -1
  189. package/lib/generates/internal/SwaggerOperationParameterComposer.js +230 -25
  190. package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
  191. package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +21 -0
  192. package/lib/generates/internal/SwaggerOperationResponseComposer.js +58 -7
  193. package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
  194. package/lib/internal/legacy.d.ts +342 -31
  195. package/lib/internal/legacy.js +207 -31
  196. package/lib/internal/legacy.js.map +1 -1
  197. package/lib/structures/INestiaProject.d.ts +10 -0
  198. package/lib/structures/INestiaSdkInput.d.ts +30 -0
  199. package/lib/structures/IOperationMetadata.d.ts +77 -0
  200. package/lib/structures/IReflectController.d.ts +10 -0
  201. package/lib/structures/IReflectHttpOperation.d.ts +10 -0
  202. package/lib/structures/IReflectHttpOperationException.d.ts +13 -2
  203. package/lib/structures/IReflectHttpOperationParameter.d.ts +55 -3
  204. package/lib/structures/IReflectHttpOperationSuccess.d.ts +15 -3
  205. package/lib/structures/IReflectImport.d.ts +10 -0
  206. package/lib/structures/IReflectMcpOperation.d.ts +19 -0
  207. package/lib/structures/IReflectMcpOperationParameter.d.ts +9 -0
  208. package/lib/structures/IReflectOperationError.d.ts +28 -0
  209. package/lib/structures/IReflectOperationError.js +22 -2
  210. package/lib/structures/IReflectOperationError.js.map +1 -1
  211. package/lib/structures/IReflectType.d.ts +9 -0
  212. package/lib/structures/IReflectWebSocketOperation.d.ts +10 -0
  213. package/lib/structures/IReflectWebSocketOperationParameter.d.ts +55 -0
  214. package/lib/structures/ITypedApplication.d.ts +10 -0
  215. package/lib/structures/ITypedHttpRoute.d.ts +18 -0
  216. package/lib/structures/ITypedHttpRouteException.d.ts +13 -1
  217. package/lib/structures/ITypedHttpRouteParameter.d.ts +48 -1
  218. package/lib/structures/ITypedHttpRouteSuccess.d.ts +13 -1
  219. package/lib/structures/ITypedMcpRoute.d.ts +11 -0
  220. package/lib/structures/ITypedWebSocketRoute.d.ts +18 -0
  221. package/lib/transform.d.ts +6 -0
  222. package/lib/transform.js +6 -0
  223. package/lib/transform.js.map +1 -1
  224. package/lib/utils/ArrayUtil.d.ts +31 -1
  225. package/lib/utils/ArrayUtil.js +32 -11
  226. package/lib/utils/ArrayUtil.js.map +1 -1
  227. package/lib/utils/EmittedJavaScriptPatcher.d.ts +20 -0
  228. package/lib/utils/EmittedJavaScriptPatcher.js +65 -46
  229. package/lib/utils/EmittedJavaScriptPatcher.js.map +1 -1
  230. package/lib/utils/FileRetriever.d.ts +29 -0
  231. package/lib/utils/FileRetriever.js +29 -0
  232. package/lib/utils/FileRetriever.js.map +1 -1
  233. package/lib/utils/HttpResponseContentTypeUtil.d.ts +41 -0
  234. package/lib/utils/HttpResponseContentTypeUtil.js +31 -0
  235. package/lib/utils/HttpResponseContentTypeUtil.js.map +1 -1
  236. package/lib/utils/MapUtil.d.ts +64 -1
  237. package/lib/utils/MapUtil.js +24 -3
  238. package/lib/utils/MapUtil.js.map +1 -1
  239. package/lib/utils/PathUtil.d.ts +31 -0
  240. package/lib/utils/PathUtil.js +38 -0
  241. package/lib/utils/PathUtil.js.map +1 -1
  242. package/lib/utils/SdkBundlePath.d.ts +2 -0
  243. package/lib/utils/SdkBundlePath.js +10 -0
  244. package/lib/utils/SdkBundlePath.js.map +1 -0
  245. package/lib/utils/SdkInputFilter.d.ts +31 -0
  246. package/lib/utils/SdkInputFilter.js +115 -0
  247. package/lib/utils/SdkInputFilter.js.map +1 -0
  248. package/lib/utils/SourceFinder.d.ts +45 -0
  249. package/lib/utils/SourceFinder.js +74 -16
  250. package/lib/utils/SourceFinder.js.map +1 -1
  251. package/lib/utils/StringUtil.d.ts +77 -2
  252. package/lib/utils/StringUtil.js +166 -4
  253. package/lib/utils/StringUtil.js.map +1 -1
  254. package/lib/utils/TemporaryDirectory.d.ts +60 -0
  255. package/lib/utils/TemporaryDirectory.js +112 -0
  256. package/lib/utils/TemporaryDirectory.js.map +1 -0
  257. package/lib/utils/TsConfigReader.d.ts +42 -0
  258. package/lib/utils/TsConfigReader.js +98 -14
  259. package/lib/utils/TsConfigReader.js.map +1 -1
  260. package/lib/utils/TtscExecutor.d.ts +23 -1
  261. package/lib/utils/TtscExecutor.js +22 -0
  262. package/lib/utils/TtscExecutor.js.map +1 -1
  263. package/lib/utils/VersioningStrategy.d.ts +39 -2
  264. package/lib/utils/VersioningStrategy.js +46 -13
  265. package/lib/utils/VersioningStrategy.js.map +1 -1
  266. package/lib/validators/TextPlainValidator.d.ts +19 -0
  267. package/lib/validators/TextPlainValidator.js +19 -0
  268. package/lib/validators/TextPlainValidator.js.map +1 -1
  269. package/native/go.mod +1 -1
  270. package/native/go.sum +2 -2
  271. package/native/sdk/register.go +12 -2
  272. package/native/sdk/sdk_http_rules.go +210 -0
  273. package/native/sdk/sdk_metadata_json.go +79 -4
  274. package/native/sdk/sdk_transform.go +1157 -414
  275. package/package.json +8 -6
  276. package/src/INestiaConfig.ts +74 -34
  277. package/src/NestiaSdkApplication.ts +129 -41
  278. package/src/NestiaSwaggerComposer.ts +32 -14
  279. package/src/analyses/AccessorAnalyzer.ts +26 -0
  280. package/src/analyses/ConfigAnalyzer.ts +52 -90
  281. package/src/analyses/ImportAnalyzer.ts +25 -0
  282. package/src/analyses/ParameterNameAnalyzer.ts +75 -0
  283. package/src/analyses/PathAnalyzer.ts +167 -23
  284. package/src/analyses/ReflectControllerAnalyzer.ts +34 -1
  285. package/src/analyses/ReflectHttpOperationAnalyzer.ts +36 -14
  286. package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +31 -0
  287. package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +153 -39
  288. package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +38 -7
  289. package/src/analyses/ReflectMcpOperationAnalyzer.ts +38 -3
  290. package/src/analyses/ReflectMetadataAnalyzer.ts +47 -0
  291. package/src/analyses/ReflectWebSocketOperationAnalyzer.ts +40 -7
  292. package/src/analyses/SecurityAnalyzer.ts +45 -20
  293. package/src/analyses/SwaggerExampleAnalyzer.ts +39 -0
  294. package/src/analyses/TypedHttpRouteAnalyzer.ts +263 -198
  295. package/src/analyses/TypedMcpRouteAnalyzer.ts +180 -20
  296. package/src/analyses/TypedWebSocketRouteAnalyzer.ts +20 -0
  297. package/src/decorators/OperationMetadata.ts +6 -0
  298. package/src/executable/internal/CommandParser.ts +19 -0
  299. package/src/executable/internal/NestiaConfigLoader.ts +66 -50
  300. package/src/executable/internal/NestiaSdkCommand.ts +96 -3
  301. package/src/executable/internal/NestiaSdkWatcher.ts +172 -44
  302. package/src/executable/internal/NestiaSwaggerWatch.ts +359 -0
  303. package/src/executable/sdk.ts +24 -1
  304. package/src/factories/ExpressionFactory.ts +23 -2
  305. package/src/factories/IdentifierFactory.ts +24 -0
  306. package/src/factories/LiteralFactory.ts +51 -13
  307. package/src/factories/StatementFactory.ts +28 -1
  308. package/src/factories/TypeFactory.ts +24 -0
  309. package/src/generates/CloneGenerator.ts +26 -5
  310. package/src/generates/E2eGenerator.ts +19 -0
  311. package/src/generates/SdkGenerator.ts +43 -12
  312. package/src/generates/SwaggerGenerator.ts +229 -46
  313. package/src/generates/internal/E2eFileProgrammer.ts +20 -0
  314. package/src/generates/internal/FilePrinter.ts +71 -0
  315. package/src/generates/internal/ImportDictionary.ts +120 -2
  316. package/src/generates/internal/SdkAliasCollection.ts +144 -15
  317. package/src/generates/internal/SdkDistributionComposer.ts +181 -56
  318. package/src/generates/internal/SdkFileProgrammer.ts +126 -16
  319. package/src/generates/internal/SdkHttpCloneProgrammer.ts +55 -0
  320. package/src/generates/internal/SdkHttpCloneReferencer.ts +97 -23
  321. package/src/generates/internal/SdkHttpFunctionProgrammer.ts +47 -30
  322. package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +53 -64
  323. package/src/generates/internal/SdkHttpParameterProgrammer.ts +295 -13
  324. package/src/generates/internal/SdkHttpRouteProgrammer.ts +37 -31
  325. package/src/generates/internal/SdkHttpSimulationProgrammer.ts +122 -18
  326. package/src/generates/internal/SdkImportWizard.ts +63 -0
  327. package/src/generates/internal/SdkMcpRouteProgrammer.ts +150 -33
  328. package/src/generates/internal/SdkPathTemplate.ts +88 -0
  329. package/src/generates/internal/SdkRouteDirectory.ts +10 -0
  330. package/src/generates/internal/SdkTypeProgrammer.ts +137 -36
  331. package/src/generates/internal/SdkTypeTagProgrammer.ts +536 -78
  332. package/src/generates/internal/SdkWebSocketCloneProgrammer.ts +185 -142
  333. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +62 -68
  334. package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +205 -5
  335. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +89 -48
  336. package/src/generates/internal/SwaggerDescriptionComposer.ts +47 -1
  337. package/src/generates/internal/SwaggerOperationComposer.ts +38 -6
  338. package/src/generates/internal/SwaggerOperationParameterComposer.ts +318 -36
  339. package/src/generates/internal/SwaggerOperationResponseComposer.ts +74 -11
  340. package/src/internal/legacy-augmentation.d.ts +16 -0
  341. package/src/internal/legacy.ts +407 -49
  342. package/src/structures/INestiaProject.ts +10 -0
  343. package/src/structures/INestiaSdkInput.ts +30 -0
  344. package/src/structures/IOperationMetadata.ts +83 -0
  345. package/src/structures/IReflectController.ts +10 -0
  346. package/src/structures/IReflectHttpOperation.ts +10 -0
  347. package/src/structures/IReflectHttpOperationException.ts +17 -2
  348. package/src/structures/IReflectHttpOperationParameter.ts +56 -2
  349. package/src/structures/IReflectHttpOperationSuccess.ts +19 -3
  350. package/src/structures/IReflectImport.ts +10 -0
  351. package/src/structures/IReflectMcpOperation.ts +19 -0
  352. package/src/structures/IReflectMcpOperationParameter.ts +9 -0
  353. package/src/structures/IReflectOperationError.ts +31 -2
  354. package/src/structures/IReflectType.ts +9 -0
  355. package/src/structures/IReflectWebSocketOperation.ts +10 -0
  356. package/src/structures/IReflectWebSocketOperationParameter.ts +55 -0
  357. package/src/structures/ITypedApplication.ts +10 -0
  358. package/src/structures/ITypedHttpRoute.ts +18 -0
  359. package/src/structures/ITypedHttpRouteException.ts +14 -1
  360. package/src/structures/ITypedHttpRouteParameter.ts +48 -1
  361. package/src/structures/ITypedHttpRouteSuccess.ts +14 -1
  362. package/src/structures/ITypedMcpRoute.ts +12 -0
  363. package/src/structures/ITypedWebSocketRoute.ts +18 -0
  364. package/src/transform.ts +6 -0
  365. package/src/utils/ArrayUtil.ts +32 -13
  366. package/src/utils/EmittedJavaScriptPatcher.ts +70 -47
  367. package/src/utils/FileRetriever.ts +29 -0
  368. package/src/utils/HttpResponseContentTypeUtil.ts +41 -0
  369. package/src/utils/MapUtil.ts +68 -3
  370. package/src/utils/PathUtil.ts +38 -0
  371. package/src/utils/SdkBundlePath.ts +11 -0
  372. package/src/utils/SdkInputFilter.ts +108 -0
  373. package/src/utils/SourceFinder.ts +76 -18
  374. package/src/utils/StringUtil.ts +182 -5
  375. package/src/utils/TemporaryDirectory.ts +108 -0
  376. package/src/utils/TsConfigReader.ts +124 -26
  377. package/src/utils/TtscExecutor.ts +23 -1
  378. package/src/utils/VersioningStrategy.ts +51 -16
  379. package/src/validators/TextPlainValidator.ts +19 -0
  380. package/lib/generates/internal/SwaggerReadonlyArrayEmender.d.ts +0 -9
  381. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js +0 -178
  382. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js.map +0 -1
  383. package/lib/validators/HttpHeadersValidator.d.ts +0 -11
  384. package/lib/validators/HttpHeadersValidator.js +0 -15
  385. package/lib/validators/HttpHeadersValidator.js.map +0 -1
  386. package/lib/validators/HttpQueryValidator.d.ts +0 -10
  387. package/lib/validators/HttpQueryValidator.js +0 -14
  388. package/lib/validators/HttpQueryValidator.js.map +0 -1
  389. package/src/generates/internal/SwaggerReadonlyArrayEmender.ts +0 -264
  390. package/src/validators/HttpHeadersValidator.ts +0 -12
  391. 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
- "regexp"
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 *shimchecker.Type
44
- Text string
45
- Escape bool
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
- const nestiaSDKLiteralNull = "__NESTIA_LITERAL_NULL__"
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
- text := string(data)
185
- text = strings.ReplaceAll(text, `"`+nestiaSDKLiteralNull+`"`, "null")
186
- return text, nil
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
- inTags := false
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
- text := strings.TrimSpace(line)
212
- text = strings.TrimPrefix(text, "*")
213
- text = strings.TrimSpace(text)
214
- if text == "" {
215
- if inTags == false && len(description) != 0 {
216
- description = append(description, "")
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 strings.HasPrefix(text, "@") {
221
- inTags = true
222
- name, body := nestiaSDKParseJSDocTag(text)
223
- doc.Tags = append(doc.Tags, nestiaSDKJSDocTag(name, body))
224
- if name == "param" {
225
- param, desc := nestiaSDKParseParamTag(body)
226
- if param != "" {
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 inTags == false {
233
- description = append(description, text)
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
- parts := strings.Fields(body)
293
- if len(parts) == 0 {
294
- return "", ""
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
- param := parts[0]
297
- desc := strings.TrimSpace(strings.TrimPrefix(body, param))
298
- return param, desc
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
- segments := transform.NestiaCoreExpressionSegments(call.Expression)
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
- func nestiaSDKReflectImports(name string, imports []nestiaSDKImportInfo) []any {
453
- prefixes := nestiaSDKTypePrefixes(name)
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
- seen := map[string]bool{}
456
- for _, imp := range imports {
457
- if nestiaSDKImportMatches(prefixes, imp) == false {
458
- continue
459
- }
460
- item := nestiaSDKImportLiteral(imp, prefixes)
461
- key := fmt.Sprintf("%v", item)
462
- if seen[key] {
463
- continue
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
- seen[key] = true
466
- output = append(output, item)
753
+ output = append(output, nestiaSDKReflectTypeReferenceSymbolImport(prog, entity, nestiaSDKEntityNameText(entity))...)
467
754
  }
468
- return output
469
- }
470
-
471
- func nestiaSDKTypePrefixes(name string) map[string]bool {
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
- return output
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
- func nestiaSDKIsGlobalTypePrefix(prefix string) bool {
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: typ,
564
- Text: nestiaSDKTypeNodeText(typeNode),
565
- Escape: 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 — the sdk generator falls back to its own derived
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, typeNode, result.Data); baked != nil {
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": map[string]any{
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) — the JS-side reader handles missing
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
- typeNode *shimast.Node,
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] = map[string]any(val)
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": map[string]any(collection.Schemas[0]),
995
+ "schema": nestiaSDKJsonSchemaLiteral(collection.Schemas[0]),
678
996
  }
679
- nestiaSDKMarkReadonlyArrayJsonSchema(prog, typeNode, baked)
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
- func nestiaSDKMarkReadonlyArrayJsonSchema(prog *driver.Program, typeNode *shimast.Node, baked map[string]any) {
684
- components, _ := baked["components"].(map[string]any)
685
- schemas, _ := components["schemas"].(map[string]any)
686
- schema := nestiaSDKSchemaMap(baked["schema"])
687
- nestiaSDKMarkReadonlyArraySchemaNode(
688
- prog,
689
- typeNode,
690
- schema,
691
- schemas,
692
- map[*shimast.Node]bool{},
693
- )
694
- }
695
-
696
- func nestiaSDKMarkReadonlyArraySchemaNode(
697
- prog *driver.Program,
698
- typeNode *shimast.Node,
699
- schema map[string]any,
700
- components map[string]any,
701
- visiting map[*shimast.Node]bool,
702
- ) {
703
- if typeNode == nil || schema == nil {
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
- if visiting[typeNode] {
707
- return
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
- visiting[typeNode] = true
710
- defer delete(visiting, typeNode)
1048
+ return output
1049
+ }
711
1050
 
712
- switch typeNode.Kind {
713
- case shimast.KindArrayType:
714
- child := nestiaSDKSchemaMap(schema["items"])
715
- nestiaSDKMarkReadonlyArraySchemaNode(
716
- prog,
717
- typeNode.AsArrayTypeNode().ElementType,
718
- child,
719
- components,
720
- visiting,
721
- )
722
- case shimast.KindTupleType:
723
- tuple := typeNode.AsTupleTypeNode()
724
- items := nestiaSDKSchemaList(schema["prefixItems"])
725
- if tuple.Elements != nil {
726
- for i, elem := range tuple.Elements.Nodes {
727
- if i >= len(items) {
728
- break
729
- }
730
- child := nestiaSDKSchemaMap(items[i])
731
- nestiaSDKMarkReadonlyArraySchemaNode(prog, elem, child, components, visiting)
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 nestiaSDKMarkReadonlyArrayTypeReference(
775
- prog *driver.Program,
776
- typeNode *shimast.Node,
777
- schema map[string]any,
778
- components map[string]any,
779
- visiting map[*shimast.Node]bool,
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
- func nestiaSDKTypeReferenceDeclarations(prog *driver.Program, node *shimast.Node) []*shimast.Node {
853
- if prog == nil || prog.Checker == nil || node == nil {
854
- return nil
855
- }
856
- symbol := prog.Checker.GetSymbolAtLocation(node)
857
- if symbol == nil {
858
- typ := prog.Checker.GetTypeFromTypeNode(node)
859
- if typ != nil {
860
- symbol = typ.Symbol()
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
- if symbol == nil {
864
- return nil
865
- }
866
- return symbol.Declarations
1124
+ return output
867
1125
  }
868
1126
 
869
- func nestiaSDKReferencedSchema(schema map[string]any, components map[string]any, name string) map[string]any {
870
- ref, _ := schema["$ref"].(string)
871
- if ref == "" {
872
- return schema
873
- }
874
- const prefix = "#/components/schemas/"
875
- if strings.HasPrefix(ref, prefix) {
876
- name = strings.TrimPrefix(ref, prefix)
877
- }
878
- target := nestiaSDKSchemaMap(components[name])
879
- if target != nil {
880
- return target
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 schema
1148
+ return member
883
1149
  }
884
1150
 
885
- func nestiaSDKSchemaMap(input any) map[string]any {
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 map[string]any:
888
- return value
889
- case nativeiterate.JsonSchema:
890
- return map[string]any(value)
891
- default:
892
- return nil
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 nestiaSDKSchemaList(input any) []any {
897
- switch value := input.(type) {
898
- case []any:
899
- return value
900
- case []nativeiterate.JsonSchema:
901
- output := make([]any, len(value))
902
- for i, elem := range value {
903
- output[i] = elem
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
- return output
906
- default:
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
- func nestiaSDKReadonlyArrayOperand(node *shimast.Node) bool {
912
- if node == nil {
913
- return false
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
- switch node.Kind {
916
- case shimast.KindArrayType, shimast.KindTupleType:
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
- case shimast.KindTypeReference:
919
- return nestiaSDKEntityNameText(node.AsTypeReferenceNode().TypeName) == "Array"
920
- case shimast.KindParenthesizedType:
921
- return nestiaSDKReadonlyArrayOperand(node.AsParenthesizedTypeNode().Type)
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
- func nestiaSDKSchemaPropertyName(node *shimast.Node) string {
928
- text := nestiaSDKTypeNodeText(node)
929
- return strings.Trim(text, "\"'")
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
- if prog == nil || prog.FS == nil || source == nil {
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 — does not
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(name, imports)
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(element["name"].(string), imports)
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(child["name"].(string), imports)
1899
+ refs = nestiaSDKReflectImports(prog, node.AsParenthesizedTypeNode().Type, imports)
1336
1900
  }
1337
- name, _ := child["name"].(string)
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
- child, refs, ok := nestiaSDKReflectTypeNode(prog, imports, operator.Type)
1346
- if ok == false {
1347
- child = map[string]any{"name": nestiaSDKTypeNodeText(operator.Type)}
1348
- refs = nestiaSDKReflectImports(child["name"].(string), imports)
1349
- }
1350
- name, _ := child["name"].(string)
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(name, imports)
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 nestiaSDKIsAsyncReturnWrapper(prog, ref.TypeName, name) && len(ref.TypeArguments.Nodes) == 1 {
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(text, imports)
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(name, imports), true
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 nestiaSDKIsGlobalTypePrefix(prefix) {
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
- file := filepath.ToSlash(sourceFile.FileName())
1415
- if strings.Contains(file, "/typescript/lib/") {
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(text, imports))
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
- return strings.Trim(node.Name().Text(), "\"'")
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 strings.Trim(node.Name().Text(), "\"'")
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 && nestiaSDKIsAsyncReturnWrapper(prog, ref.TypeName, nestiaSDKEntityNameText(ref.TypeName)) {
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 := transform.SourceFileText(file)
2361
+ source, ok := "", false
2362
+ if file != nil {
2363
+ source, ok = transform.SourceFileText(file)
2364
+ }
1625
2365
  if ok == false {
1626
- return ""
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
- if pos := site.Method.Pos(); pos >= 0 {
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
  }