@nestia/sdk 12.0.0-dev.20260601.1 → 12.0.0-dev.20260612.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +93 -93
  3. package/assets/bundle/api/HttpError.ts +1 -1
  4. package/assets/bundle/api/IConnection.ts +1 -1
  5. package/assets/bundle/api/Primitive.ts +1 -1
  6. package/assets/bundle/api/Resolved.ts +1 -1
  7. package/assets/bundle/api/index.ts +4 -4
  8. package/assets/bundle/api/module.ts +6 -6
  9. package/assets/bundle/distribute/README.md +37 -37
  10. package/assets/bundle/distribute/package.json +28 -28
  11. package/assets/bundle/distribute/tsconfig.json +109 -109
  12. package/assets/bundle/e2e/index.ts +42 -42
  13. package/assets/config/nestia.config.ts +97 -97
  14. package/lib/NestiaSdkApplication.js +29 -7
  15. package/lib/NestiaSdkApplication.js.map +1 -1
  16. package/lib/NestiaSwaggerComposer.js +21 -13
  17. package/lib/NestiaSwaggerComposer.js.map +1 -1
  18. package/lib/analyses/AccessorAnalyzer.d.ts +4 -1
  19. package/lib/analyses/AccessorAnalyzer.js.map +1 -1
  20. package/lib/analyses/ConfigAnalyzer.js +1 -1
  21. package/lib/analyses/PathAnalyzer.d.ts +18 -3
  22. package/lib/analyses/PathAnalyzer.js +32 -0
  23. package/lib/analyses/PathAnalyzer.js.map +1 -1
  24. package/lib/analyses/ReflectControllerAnalyzer.js +3 -2
  25. package/lib/analyses/ReflectControllerAnalyzer.js.map +1 -1
  26. package/lib/analyses/ReflectHttpOperationAnalyzer.d.ts +1 -1
  27. package/lib/analyses/ReflectHttpOperationAnalyzer.js +1 -1
  28. package/lib/analyses/ReflectHttpOperationAnalyzer.js.map +1 -1
  29. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.d.ts +1 -1
  30. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js +53 -20
  31. package/lib/analyses/ReflectHttpOperationResponseAnalyzer.js.map +1 -1
  32. package/lib/analyses/ReflectMcpOperationAnalyzer.d.ts +14 -0
  33. package/lib/analyses/ReflectMcpOperationAnalyzer.js +79 -0
  34. package/lib/analyses/ReflectMcpOperationAnalyzer.js.map +1 -0
  35. package/lib/analyses/TypedMcpRouteAnalyzer.d.ts +9 -0
  36. package/lib/analyses/TypedMcpRouteAnalyzer.js +31 -0
  37. package/lib/analyses/TypedMcpRouteAnalyzer.js.map +1 -0
  38. package/lib/executable/internal/NestiaConfigLoader.js +5 -1
  39. package/lib/executable/internal/NestiaConfigLoader.js.map +1 -1
  40. package/lib/executable/internal/NestiaSdkCommand.js +30 -14
  41. package/lib/executable/internal/NestiaSdkCommand.js.map +1 -1
  42. package/lib/executable/internal/NestiaSdkWatcher.d.ts +10 -0
  43. package/lib/executable/internal/NestiaSdkWatcher.js +322 -0
  44. package/lib/executable/internal/NestiaSdkWatcher.js.map +1 -0
  45. package/lib/executable/sdk.js +12 -12
  46. package/lib/executable/sdk.js.map +1 -1
  47. package/lib/generates/CloneGenerator.js +4 -2
  48. package/lib/generates/CloneGenerator.js.map +1 -1
  49. package/lib/generates/SdkGenerator.js +50 -1
  50. package/lib/generates/SdkGenerator.js.map +1 -1
  51. package/lib/generates/SwaggerGenerator.js +18 -2
  52. package/lib/generates/SwaggerGenerator.js.map +1 -1
  53. package/lib/generates/internal/E2eFileProgrammer.js +3 -1
  54. package/lib/generates/internal/E2eFileProgrammer.js.map +1 -1
  55. package/lib/generates/internal/ImportDictionary.d.ts +1 -0
  56. package/lib/generates/internal/ImportDictionary.js +9 -4
  57. package/lib/generates/internal/ImportDictionary.js.map +1 -1
  58. package/lib/generates/internal/SdkAliasCollection.d.ts +2 -0
  59. package/lib/generates/internal/SdkAliasCollection.js +11 -2
  60. package/lib/generates/internal/SdkAliasCollection.js.map +1 -1
  61. package/lib/generates/internal/SdkDistributionComposer.d.ts +1 -0
  62. package/lib/generates/internal/SdkDistributionComposer.js +3 -0
  63. package/lib/generates/internal/SdkDistributionComposer.js.map +1 -1
  64. package/lib/generates/internal/SdkFileProgrammer.js +4 -1
  65. package/lib/generates/internal/SdkFileProgrammer.js.map +1 -1
  66. package/lib/generates/internal/SdkHttpCloneReferencer.d.ts +1 -1
  67. package/lib/generates/internal/SdkHttpCloneReferencer.js +42 -9
  68. package/lib/generates/internal/SdkHttpCloneReferencer.js.map +1 -1
  69. package/lib/generates/internal/SdkHttpFunctionProgrammer.js +3 -4
  70. package/lib/generates/internal/SdkHttpFunctionProgrammer.js.map +1 -1
  71. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js +2 -1
  72. package/lib/generates/internal/SdkHttpNamespaceProgrammer.js.map +1 -1
  73. package/lib/generates/internal/SdkHttpSimulationProgrammer.js +6 -3
  74. package/lib/generates/internal/SdkHttpSimulationProgrammer.js.map +1 -1
  75. package/lib/generates/internal/SdkMcpRouteProgrammer.d.ts +15 -0
  76. package/lib/generates/internal/SdkMcpRouteProgrammer.js +148 -0
  77. package/lib/generates/internal/SdkMcpRouteProgrammer.js.map +1 -0
  78. package/lib/generates/internal/SdkRouteDirectory.d.ts +2 -1
  79. package/lib/generates/internal/SdkRouteDirectory.js.map +1 -1
  80. package/lib/generates/internal/SdkWebSocketCloneProgrammer.d.ts +6 -0
  81. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js +283 -0
  82. package/lib/generates/internal/SdkWebSocketCloneProgrammer.js.map +1 -0
  83. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js +11 -9
  84. package/lib/generates/internal/SdkWebSocketRouteProgrammer.js.map +1 -1
  85. package/lib/generates/internal/SwaggerOperationParameterComposer.js +10 -2
  86. package/lib/generates/internal/SwaggerOperationParameterComposer.js.map +1 -1
  87. package/lib/generates/internal/SwaggerOperationResponseComposer.d.ts +1 -1
  88. package/lib/generates/internal/SwaggerOperationResponseComposer.js +6 -1
  89. package/lib/generates/internal/SwaggerOperationResponseComposer.js.map +1 -1
  90. package/lib/generates/internal/SwaggerReadonlyArrayEmender.d.ts +9 -0
  91. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js +174 -0
  92. package/lib/generates/internal/SwaggerReadonlyArrayEmender.js.map +1 -0
  93. package/lib/structures/INestiaSdkInput.d.ts +9 -2
  94. package/lib/structures/IReflectController.d.ts +2 -1
  95. package/lib/structures/IReflectHttpOperationSuccess.d.ts +4 -2
  96. package/lib/structures/IReflectMcpOperation.d.ts +35 -0
  97. package/lib/structures/IReflectMcpOperation.js +3 -0
  98. package/lib/structures/IReflectMcpOperation.js.map +1 -0
  99. package/lib/structures/IReflectMcpOperationParameter.d.ts +19 -0
  100. package/lib/structures/IReflectMcpOperationParameter.js +3 -0
  101. package/lib/structures/IReflectMcpOperationParameter.js.map +1 -0
  102. package/lib/structures/ITypedApplication.d.ts +2 -1
  103. package/lib/structures/ITypedHttpRouteSuccess.d.ts +3 -1
  104. package/lib/structures/ITypedMcpRoute.d.ts +31 -0
  105. package/lib/structures/ITypedMcpRoute.js +3 -0
  106. package/lib/structures/ITypedMcpRoute.js.map +1 -0
  107. package/lib/utils/HttpResponseContentTypeUtil.d.ts +5 -0
  108. package/lib/utils/HttpResponseContentTypeUtil.js +22 -0
  109. package/lib/utils/HttpResponseContentTypeUtil.js.map +1 -0
  110. package/native/go.mod +52 -52
  111. package/native/go.sum +84 -54
  112. package/native/sdk/register.go +322 -165
  113. package/native/sdk/sdk.go +17 -17
  114. package/native/sdk/sdk_metadata_json.go +327 -327
  115. package/native/sdk/sdk_transform.go +1879 -1549
  116. package/package.json +11 -9
  117. package/src/INestiaConfig.ts +267 -267
  118. package/src/NestiaSdkApplication.ts +39 -8
  119. package/src/NestiaSwaggerComposer.ts +153 -142
  120. package/src/analyses/AccessorAnalyzer.ts +64 -67
  121. package/src/analyses/ConfigAnalyzer.ts +330 -330
  122. package/src/analyses/ImportAnalyzer.ts +92 -92
  123. package/src/analyses/PathAnalyzer.ts +130 -69
  124. package/src/analyses/ReflectControllerAnalyzer.ts +112 -105
  125. package/src/analyses/ReflectHttpOperationAnalyzer.ts +183 -183
  126. package/src/analyses/ReflectHttpOperationExceptionAnalyzer.ts +90 -90
  127. package/src/analyses/ReflectHttpOperationParameterAnalyzer.ts +350 -350
  128. package/src/analyses/ReflectHttpOperationResponseAnalyzer.ts +163 -130
  129. package/src/analyses/ReflectMcpOperationAnalyzer.ts +124 -0
  130. package/src/analyses/ReflectMetadataAnalyzer.ts +44 -44
  131. package/src/analyses/SecurityAnalyzer.ts +25 -25
  132. package/src/analyses/TypedMcpRouteAnalyzer.ts +34 -0
  133. package/src/decorators/OperationMetadata.ts +29 -29
  134. package/src/executable/internal/CommandParser.ts +15 -15
  135. package/src/executable/internal/NestiaConfigLoader.ts +451 -446
  136. package/src/executable/internal/NestiaSdkCommand.ts +124 -106
  137. package/src/executable/internal/NestiaSdkWatcher.ts +342 -0
  138. package/src/executable/sdk.ts +90 -88
  139. package/src/generates/CloneGenerator.ts +73 -66
  140. package/src/generates/E2eGenerator.ts +32 -32
  141. package/src/generates/SdkGenerator.ts +176 -118
  142. package/src/generates/SwaggerGenerator.ts +342 -310
  143. package/src/generates/internal/E2eFileProgrammer.ts +240 -233
  144. package/src/generates/internal/FilePrinter.ts +65 -65
  145. package/src/generates/internal/ImportDictionary.ts +209 -204
  146. package/src/generates/internal/SdkAliasCollection.ts +274 -261
  147. package/src/generates/internal/SdkDistributionComposer.ts +123 -116
  148. package/src/generates/internal/SdkFileProgrammer.ts +116 -112
  149. package/src/generates/internal/SdkHttpCloneProgrammer.ts +126 -126
  150. package/src/generates/internal/SdkHttpCloneReferencer.ts +131 -77
  151. package/src/generates/internal/SdkHttpFunctionProgrammer.ts +301 -301
  152. package/src/generates/internal/SdkHttpNamespaceProgrammer.ts +520 -510
  153. package/src/generates/internal/SdkHttpParameterProgrammer.ts +165 -165
  154. package/src/generates/internal/SdkHttpRouteProgrammer.ts +109 -109
  155. package/src/generates/internal/SdkHttpSimulationProgrammer.ts +331 -314
  156. package/src/generates/internal/SdkImportWizard.ts +62 -62
  157. package/src/generates/internal/SdkMcpRouteProgrammer.ts +452 -0
  158. package/src/generates/internal/SdkRouteDirectory.ts +21 -18
  159. package/src/generates/internal/SdkTypeTagProgrammer.ts +114 -114
  160. package/src/generates/internal/SdkWebSocketCloneProgrammer.ts +319 -0
  161. package/src/generates/internal/SdkWebSocketNamespaceProgrammer.ts +389 -389
  162. package/src/generates/internal/SdkWebSocketParameterProgrammer.ts +89 -89
  163. package/src/generates/internal/SdkWebSocketRouteProgrammer.ts +331 -323
  164. package/src/generates/internal/SwaggerDescriptionComposer.ts +64 -64
  165. package/src/generates/internal/SwaggerOperationComposer.ts +119 -119
  166. package/src/generates/internal/SwaggerOperationParameterComposer.ts +175 -162
  167. package/src/generates/internal/SwaggerOperationResponseComposer.ts +115 -110
  168. package/src/generates/internal/SwaggerReadonlyArrayEmender.ts +262 -0
  169. package/src/index.ts +4 -4
  170. package/src/internal/legacy.ts +492 -492
  171. package/src/module.ts +4 -4
  172. package/src/structures/INestiaProject.ts +10 -10
  173. package/src/structures/INestiaSdkInput.ts +27 -20
  174. package/src/structures/IOperationMetadata.ts +41 -41
  175. package/src/structures/IReflectController.ts +18 -15
  176. package/src/structures/IReflectHttpOperation.ts +26 -26
  177. package/src/structures/IReflectHttpOperationException.ts +18 -18
  178. package/src/structures/IReflectHttpOperationParameter.ts +79 -79
  179. package/src/structures/IReflectHttpOperationSuccess.ts +18 -21
  180. package/src/structures/IReflectImport.ts +6 -6
  181. package/src/structures/IReflectMcpOperation.ts +38 -0
  182. package/src/structures/IReflectMcpOperationParameter.ts +27 -0
  183. package/src/structures/IReflectOperationError.ts +26 -26
  184. package/src/structures/IReflectType.ts +4 -4
  185. package/src/structures/IReflectWebSocketOperation.ts +17 -17
  186. package/src/structures/ITypedApplication.ts +12 -11
  187. package/src/structures/ITypedHttpRoute.ts +41 -41
  188. package/src/structures/ITypedHttpRouteException.ts +15 -15
  189. package/src/structures/ITypedHttpRouteParameter.ts +41 -41
  190. package/src/structures/ITypedHttpRouteSuccess.ts +18 -22
  191. package/src/structures/ITypedMcpRoute.ts +33 -0
  192. package/src/structures/ITypedWebSocketRoute.ts +24 -24
  193. package/src/structures/ITypedWebSocketRouteParameter.ts +3 -3
  194. package/src/transform.ts +59 -59
  195. package/src/typings/get-function-location.d.ts +7 -7
  196. package/src/utils/ArrayUtil.ts +26 -26
  197. package/src/utils/EmittedJavaScriptPatcher.ts +88 -88
  198. package/src/utils/FileRetriever.ts +22 -22
  199. package/src/utils/HttpResponseContentTypeUtil.ts +30 -0
  200. package/src/utils/MapUtil.ts +14 -14
  201. package/src/utils/PathUtil.ts +10 -10
  202. package/src/utils/SourceFinder.ts +63 -63
  203. package/src/utils/StringUtil.ts +17 -17
  204. package/src/utils/TsConfigReader.ts +108 -108
  205. package/src/utils/TtscExecutor.ts +68 -68
  206. package/src/utils/VersioningStrategy.ts +28 -28
  207. package/src/validators/HttpHeadersValidator.ts +11 -11
  208. package/src/validators/HttpQueryValidator.ts +11 -11
  209. package/src/validators/TextPlainValidator.ts +17 -17
@@ -1,165 +1,322 @@
1
- package sdk
2
-
3
- import (
4
- "fmt"
5
- "os"
6
- "strings"
7
-
8
- shimast "github.com/microsoft/typescript-go/shim/ast"
9
- "github.com/samchon/nestia/packages/core/native/plugin"
10
- "github.com/samchon/nestia/packages/core/native/transform"
11
- "github.com/samchon/ttsc/packages/ttsc/driver"
12
- )
13
-
14
- // sdkMetadataNamespace is the import alias the injected decorator references.
15
- const sdkMetadataNamespace = "__OperationMetadata"
16
-
17
- // init registers the SDK metadata transform with both native entry paths.
18
- //
19
- // ttsc classifies this package (name != "main") as a linked transform and
20
- // statically links it into the `@nestia/core` host binary — but only for
21
- // projects that depend on `@nestia/sdk`. The direct linked-plugin path keeps
22
- // old plugin plans working, while the contributor collectors let the aggregate
23
- // core host run SDK metadata rewrites without starting a second native backend.
24
- func init() {
25
- driver.RegisterPlugin(linkedPlugin{})
26
- transform.RegisterBuildOutputRewriteCollector(collectSDKBuildOutputRewriter)
27
- transform.RegisterSourceRewriteCollector(collectSDKSourceRewriteMap)
28
- }
29
-
30
- type linkedPlugin struct{}
31
-
32
- // ApplyProgram injects the `@OperationMetadata("<json>")` decorator that the
33
- // SDK / Swagger / e2e generators read at runtime, plus the namespace import it
34
- // references.
35
- //
36
- // Insertion is done with synthesized AST nodes (NodeFlagsSynthesized): ttsc's
37
- // emit prints those structurally, without slicing the original source text, so
38
- // no source-text rewrite is needed. The metadata is carried as a single JSON
39
- // string literal — the `OperationMetadata` decorator `JSON.parse`s it — which
40
- // keeps the constructed AST to one literal node instead of a deep object tree.
41
- func (linkedPlugin) ApplyProgram(prog *driver.Program, _ driver.PluginContext) error {
42
- sites, diags := collectNestiaSDKSites(prog)
43
- if len(diags) > 0 {
44
- messages := make([]string, 0, len(diags))
45
- for _, diag := range diags {
46
- messages = append(messages, diag.String(""))
47
- }
48
- return fmt.Errorf("%s", strings.Join(messages, "\n"))
49
- }
50
- if len(sites) == 0 {
51
- return nil
52
- }
53
- factory := shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
54
- touched := map[*shimast.SourceFile]bool{}
55
- for _, site := range sites {
56
- if site.Method == nil || site.File == nil {
57
- continue
58
- }
59
- injectOperationMetadataDecorator(factory, site.Method, site.Metadata)
60
- touched[site.File] = true
61
- }
62
- for file := range touched {
63
- injectOperationMetadataImport(factory, file)
64
- }
65
- return nil
66
- }
67
-
68
- func collectSDKBuildOutputRewriter(
69
- prog *driver.Program,
70
- plan plugin.Plan,
71
- ) (*transform.BuildOutputRewriter, []transform.Diagnostic) {
72
- if shouldRunSDKContributorTransform() == false {
73
- return nil, nil
74
- }
75
- plan.SDK = true
76
- set, diagnostics := collectNestiaSDKBuildRewrites(prog, plan)
77
- return &transform.BuildOutputRewriter{
78
- Len: set.Len,
79
- Apply: set.Apply,
80
- }, diagnostics
81
- }
82
-
83
- func collectSDKSourceRewriteMap(
84
- prog *driver.Program,
85
- plan plugin.Plan,
86
- onlyFile string,
87
- ) (map[string][]transform.SourceRewrite, []transform.Diagnostic) {
88
- if shouldRunSDKContributorTransform() == false {
89
- return map[string][]transform.SourceRewrite{}, nil
90
- }
91
- plan.SDK = true
92
- return collectNestiaSDKSourceRewriteMap(prog, plan, onlyFile)
93
- }
94
-
95
- func shouldRunSDKContributorTransform() bool {
96
- return os.Getenv("NESTIA_SDK_TRANSFORM") == "1"
97
- }
98
-
99
- func synthesized(node *shimast.Node) *shimast.Node {
100
- if node != nil {
101
- node.Flags |= shimast.NodeFlagsSynthesized
102
- }
103
- return node
104
- }
105
-
106
- // injectOperationMetadataDecorator prepends a synthesized
107
- // `@__OperationMetadata.OperationMetadata("<json>")` decorator to a controller
108
- // method. Every site is a method that already carries `@nestia/core`
109
- // decorators, so its modifier list is non-nil and can be mutated in place.
110
- func injectOperationMetadataDecorator(
111
- factory *shimast.NodeFactory,
112
- method *shimast.Node,
113
- metadataJSON string,
114
- ) {
115
- modifiers := method.Modifiers()
116
- if modifiers == nil {
117
- return
118
- }
119
- namespaceID := synthesized(factory.NewIdentifier(sdkMetadataNamespace))
120
- memberID := synthesized(factory.NewIdentifier("OperationMetadata"))
121
- access := synthesized(factory.NewPropertyAccessExpression(
122
- namespaceID,
123
- nil,
124
- memberID,
125
- shimast.NodeFlagsNone,
126
- ))
127
- namespaceID.Parent = access
128
- memberID.Parent = access
129
- argument := synthesized(factory.NewStringLiteral(metadataJSON, shimast.TokenFlagsNone))
130
- call := synthesized(factory.NewCallExpression(
131
- access,
132
- nil,
133
- nil,
134
- factory.NewNodeList([]*shimast.Node{argument}),
135
- shimast.NodeFlagsNone,
136
- ))
137
- access.Parent = call
138
- argument.Parent = call
139
- decorator := synthesized(factory.NewDecorator(call))
140
- call.Parent = decorator
141
- decorator.Parent = method
142
- modifiers.Nodes = append([]*shimast.Node{decorator}, modifiers.Nodes...)
143
- }
144
-
145
- // injectOperationMetadataImport prepends a synthesized
146
- // `import * as __OperationMetadata from "@nestia/sdk"` to a touched file.
147
- func injectOperationMetadataImport(
148
- factory *shimast.NodeFactory,
149
- file *shimast.SourceFile,
150
- ) {
151
- if file == nil || file.Statements == nil {
152
- return
153
- }
154
- namespaceID := synthesized(factory.NewIdentifier(sdkMetadataNamespace))
155
- namespace := synthesized(factory.NewNamespaceImport(namespaceID))
156
- namespaceID.Parent = namespace
157
- clause := synthesized(factory.NewImportClause(shimast.KindUnknown, nil, namespace))
158
- namespace.Parent = clause
159
- specifier := synthesized(factory.NewStringLiteral("@nestia/sdk", shimast.TokenFlagsNone))
160
- declaration := synthesized(factory.NewImportDeclaration(nil, clause, specifier, nil))
161
- clause.Parent = declaration
162
- specifier.Parent = declaration
163
- declaration.Parent = file.AsNode()
164
- file.Statements.Nodes = append([]*shimast.Node{declaration}, file.Statements.Nodes...)
165
- }
1
+ package sdk
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "path/filepath"
7
+ "strings"
8
+
9
+ shimast "github.com/microsoft/typescript-go/shim/ast"
10
+ shimprinter "github.com/microsoft/typescript-go/shim/printer"
11
+ "github.com/samchon/nestia/packages/core/native/plugin"
12
+ "github.com/samchon/nestia/packages/core/native/transform"
13
+ "github.com/samchon/ttsc/packages/ttsc/driver"
14
+ )
15
+
16
+ // sdkMetadataNamespace is the import alias the legacy (text/program-mutation)
17
+ // path references. The emit-context path lets tsgo's module-transform pick the
18
+ // generated alias itself, so it does not use this constant.
19
+ const sdkMetadataNamespace = "__OperationMetadata"
20
+
21
+ // sdkMetadataModule is the runtime package the injected decorator is imported
22
+ // from.
23
+ const sdkMetadataModule = "@nestia/sdk"
24
+
25
+ // sdkMetadataMember is the decorator factory exported by sdkMetadataModule.
26
+ const sdkMetadataMember = "OperationMetadata"
27
+
28
+ // init registers the SDK metadata transform with both native entry paths.
29
+ //
30
+ // ttsc classifies this package (name != "main") as a linked transform and
31
+ // statically links it into the `@nestia/core` host binary — but only for
32
+ // projects that depend on `@nestia/sdk`. The direct linked-plugin path keeps
33
+ // old plugin plans working, while the contributor collectors let the aggregate
34
+ // core host run SDK metadata rewrites without starting a second native backend.
35
+ func init() {
36
+ driver.RegisterPlugin(linkedPlugin{})
37
+ transform.RegisterBuildOutputRewriteCollector(collectSDKBuildOutputRewriter)
38
+ transform.RegisterSourceRewriteCollector(collectSDKSourceRewriteMap)
39
+ transform.RegisterEmitTransformCollector(collectSDKEmitTransform)
40
+ }
41
+
42
+ type linkedPlugin struct{}
43
+
44
+ // ApplyProgram injects the `@OperationMetadata("<json>")` decorator that the
45
+ // SDK / Swagger / e2e generators read at runtime, plus the namespace import it
46
+ // references.
47
+ //
48
+ // Insertion is done with synthesized AST nodes (NodeFlagsSynthesized): ttsc's
49
+ // emit prints those structurally, without slicing the original source text, so
50
+ // no source-text rewrite is needed. The metadata is carried as a single JSON
51
+ // string literal — the `OperationMetadata` decorator `JSON.parse`s it — which
52
+ // keeps the constructed AST to one literal node instead of a deep object tree.
53
+ func (linkedPlugin) ApplyProgram(prog *driver.Program, _ driver.PluginContext) error {
54
+ sites, diags := collectNestiaSDKSites(prog)
55
+ if len(diags) > 0 {
56
+ messages := make([]string, 0, len(diags))
57
+ for _, diag := range diags {
58
+ messages = append(messages, diag.String(""))
59
+ }
60
+ return fmt.Errorf("%s", strings.Join(messages, "\n"))
61
+ }
62
+ if len(sites) == 0 {
63
+ return nil
64
+ }
65
+ factory := shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
66
+ touched := map[*shimast.SourceFile]bool{}
67
+ for _, site := range sites {
68
+ if site.Method == nil || site.File == nil {
69
+ continue
70
+ }
71
+ injectOperationMetadataDecorator(factory, site.Method, site.Metadata)
72
+ touched[site.File] = true
73
+ }
74
+ for file := range touched {
75
+ injectOperationMetadataImport(factory, file)
76
+ }
77
+ return nil
78
+ }
79
+
80
+ // EmitTransform builds the SDK metadata pass as an emit-phase AST transformer
81
+ // (the AST-integration path that mirrors typia's `transform.Transform`). It
82
+ // collects every controller-method site once for the program, then returns a
83
+ // per-file `driver.PluginTransform` that injects the
84
+ // `@<ns>.OperationMetadata("<json>")` decorator plus a namespace import of
85
+ // `@nestia/sdk`, both built with ec.Factory and referenced through
86
+ // NewGeneratedNameForNode(modSpec), so tsgo's builtin module-transform emits the
87
+ // `require("@nestia/sdk")` and aliases the reference itself — no hand-rolled
88
+ // `__OperationMetadata` namespace and no text-splice.
89
+ //
90
+ // Core's build command wires this into `prog.EmitWithPluginTransformers`
91
+ // alongside the typia and core transforms, exactly as the typia build command
92
+ // wires `nativetransform.Transform`. A site-collection failure is returned as
93
+ // diagnostics; the returned transform is nil when there are no sites.
94
+ func EmitTransform(prog *driver.Program) (driver.PluginTransform, []transform.Diagnostic) {
95
+ sites, diagnostics := collectNestiaSDKSites(prog)
96
+ if len(diagnostics) > 0 {
97
+ return nil, diagnostics
98
+ }
99
+ if len(sites) == 0 {
100
+ return nil, nil
101
+ }
102
+ byFile := map[string][]nestiaSDKSite{}
103
+ for _, site := range sites {
104
+ byFile[filepath.ToSlash(site.FilePath)] = append(byFile[filepath.ToSlash(site.FilePath)], site)
105
+ }
106
+ return func(ec *shimprinter.EmitContext, sf *shimast.SourceFile) *shimast.SourceFile {
107
+ if sf == nil {
108
+ return sf
109
+ }
110
+ fileSites := byFile[filepath.ToSlash(sf.FileName())]
111
+ if len(fileSites) == 0 {
112
+ return sf
113
+ }
114
+ // One module-specifier literal per file, shared between the injected
115
+ // import declaration and every decorator reference, so
116
+ // NewGeneratedNameForNode binds them to the same alias tsgo's
117
+ // module-transform emits for the require.
118
+ modSpec := ec.Factory.NewStringLiteral(sdkMetadataModule, shimast.TokenFlagsNone)
119
+ // Sites were collected from the parse-tree program, so site.Method is an
120
+ // original method node. By the time this contributor runs, typia and core
121
+ // have already rebuilt every decorated method into a synthetic copy with a
122
+ // fresh modifier list, and the original node is no longer in the emitted
123
+ // tree. Map each original method to its synthetic counterpart (via the
124
+ // emit context's original link) so the injected decorator lands on the
125
+ // node that is actually printed; fall back to the original when the method
126
+ // was untouched.
127
+ synthByOriginal := map[*shimast.Node]*shimast.Node{}
128
+ var collect func(node *shimast.Node)
129
+ collect = func(node *shimast.Node) {
130
+ if node == nil {
131
+ return
132
+ }
133
+ if node.Kind == shimast.KindMethodDeclaration {
134
+ if original := ec.MostOriginal(node); original != nil {
135
+ synthByOriginal[original] = node
136
+ }
137
+ }
138
+ node.ForEachChild(func(child *shimast.Node) bool {
139
+ collect(child)
140
+ return false
141
+ })
142
+ }
143
+ collect(sf.AsNode())
144
+ for _, site := range fileSites {
145
+ if site.Method == nil {
146
+ continue
147
+ }
148
+ method := site.Method
149
+ if synth, ok := synthByOriginal[site.Method]; ok {
150
+ method = synth
151
+ }
152
+ injectOperationMetadataDecoratorEC(ec, modSpec, method, site.Metadata)
153
+ }
154
+ injectOperationMetadataImportEC(ec, modSpec, sf)
155
+ return sf
156
+ }, nil
157
+ }
158
+
159
+ // collectSDKEmitTransform exposes EmitTransform to the core `transform`
160
+ // subcommand's contributor registry, the node-path twin of
161
+ // collectSDKSourceRewriteMap. It is gated by the same NESTIA_SDK_TRANSFORM flag
162
+ // so the SDK metadata pass only participates when the caller opts in.
163
+ func collectSDKEmitTransform(
164
+ prog *driver.Program,
165
+ _ plugin.Plan,
166
+ ) (driver.PluginTransform, []transform.Diagnostic) {
167
+ if shouldRunSDKContributorTransform() == false {
168
+ return nil, nil
169
+ }
170
+ return EmitTransform(prog)
171
+ }
172
+
173
+ func collectSDKBuildOutputRewriter(
174
+ prog *driver.Program,
175
+ plan plugin.Plan,
176
+ ) (*transform.BuildOutputRewriter, []transform.Diagnostic) {
177
+ if shouldRunSDKContributorTransform() == false {
178
+ return nil, nil
179
+ }
180
+ plan.SDK = true
181
+ set, diagnostics := collectNestiaSDKBuildRewrites(prog, plan)
182
+ return &transform.BuildOutputRewriter{
183
+ Len: set.Len,
184
+ Apply: set.Apply,
185
+ }, diagnostics
186
+ }
187
+
188
+ func collectSDKSourceRewriteMap(
189
+ prog *driver.Program,
190
+ plan plugin.Plan,
191
+ onlyFile string,
192
+ ) (map[string][]transform.SourceRewrite, []transform.Diagnostic) {
193
+ if shouldRunSDKContributorTransform() == false {
194
+ return map[string][]transform.SourceRewrite{}, nil
195
+ }
196
+ plan.SDK = true
197
+ return collectNestiaSDKSourceRewriteMap(prog, plan, onlyFile)
198
+ }
199
+
200
+ func shouldRunSDKContributorTransform() bool {
201
+ return os.Getenv("NESTIA_SDK_TRANSFORM") == "1"
202
+ }
203
+
204
+ func synthesized(node *shimast.Node) *shimast.Node {
205
+ if node != nil {
206
+ node.Flags |= shimast.NodeFlagsSynthesized
207
+ }
208
+ return node
209
+ }
210
+
211
+ // injectOperationMetadataDecorator prepends a synthesized
212
+ // `@__OperationMetadata.OperationMetadata("<json>")` decorator to a controller
213
+ // method. Every site is a method that already carries `@nestia/core`
214
+ // decorators, so its modifier list is non-nil and can be mutated in place.
215
+ func injectOperationMetadataDecorator(
216
+ factory *shimast.NodeFactory,
217
+ method *shimast.Node,
218
+ metadataJSON string,
219
+ ) {
220
+ modifiers := method.Modifiers()
221
+ if modifiers == nil {
222
+ return
223
+ }
224
+ namespaceID := synthesized(factory.NewIdentifier(sdkMetadataNamespace))
225
+ memberID := synthesized(factory.NewIdentifier("OperationMetadata"))
226
+ access := synthesized(factory.NewPropertyAccessExpression(
227
+ namespaceID,
228
+ nil,
229
+ memberID,
230
+ shimast.NodeFlagsNone,
231
+ ))
232
+ namespaceID.Parent = access
233
+ memberID.Parent = access
234
+ argument := synthesized(factory.NewStringLiteral(metadataJSON, shimast.TokenFlagsNone))
235
+ call := synthesized(factory.NewCallExpression(
236
+ access,
237
+ nil,
238
+ nil,
239
+ factory.NewNodeList([]*shimast.Node{argument}),
240
+ shimast.NodeFlagsNone,
241
+ ))
242
+ access.Parent = call
243
+ argument.Parent = call
244
+ decorator := synthesized(factory.NewDecorator(call))
245
+ call.Parent = decorator
246
+ decorator.Parent = method
247
+ modifiers.Nodes = append([]*shimast.Node{decorator}, modifiers.Nodes...)
248
+ }
249
+
250
+ // injectOperationMetadataImport prepends a synthesized
251
+ // `import * as __OperationMetadata from "@nestia/sdk"` to a touched file.
252
+ func injectOperationMetadataImport(
253
+ factory *shimast.NodeFactory,
254
+ file *shimast.SourceFile,
255
+ ) {
256
+ if file == nil || file.Statements == nil {
257
+ return
258
+ }
259
+ namespaceID := synthesized(factory.NewIdentifier(sdkMetadataNamespace))
260
+ namespace := synthesized(factory.NewNamespaceImport(namespaceID))
261
+ namespaceID.Parent = namespace
262
+ clause := synthesized(factory.NewImportClause(shimast.KindUnknown, nil, namespace))
263
+ namespace.Parent = clause
264
+ specifier := synthesized(factory.NewStringLiteral("@nestia/sdk", shimast.TokenFlagsNone))
265
+ declaration := synthesized(factory.NewImportDeclaration(nil, clause, specifier, nil))
266
+ clause.Parent = declaration
267
+ specifier.Parent = declaration
268
+ declaration.Parent = file.AsNode()
269
+ file.Statements.Nodes = append([]*shimast.Node{declaration}, file.Statements.Nodes...)
270
+ }
271
+
272
+ // injectOperationMetadataDecoratorEC is the emit-context (AST-integration)
273
+ // twin of injectOperationMetadataDecorator. The decorator's namespace reference
274
+ // is `ec.Factory.NewGeneratedNameForNode(modSpec)`, the same generated name
275
+ // tsgo's module-transform binds to `require("@nestia/sdk")`, so no hand-rolled
276
+ // `__OperationMetadata` alias is needed.
277
+ func injectOperationMetadataDecoratorEC(
278
+ ec *shimprinter.EmitContext,
279
+ modSpec *shimast.Node,
280
+ method *shimast.Node,
281
+ metadataJSON string,
282
+ ) {
283
+ modifiers := method.Modifiers()
284
+ if modifiers == nil {
285
+ return
286
+ }
287
+ access := ec.Factory.NewPropertyAccessExpression(
288
+ ec.Factory.NewGeneratedNameForNode(modSpec),
289
+ nil,
290
+ ec.Factory.NewIdentifier(sdkMetadataMember),
291
+ shimast.NodeFlagsNone,
292
+ )
293
+ argument := ec.Factory.NewStringLiteral(metadataJSON, shimast.TokenFlagsNone)
294
+ call := ec.Factory.NewCallExpression(
295
+ access,
296
+ nil,
297
+ nil,
298
+ ec.Factory.NewNodeList([]*shimast.Node{argument}),
299
+ shimast.NodeFlagsNone,
300
+ )
301
+ decorator := ec.Factory.NewDecorator(call)
302
+ modifiers.Nodes = append([]*shimast.Node{decorator}, modifiers.Nodes...)
303
+ }
304
+
305
+ // injectOperationMetadataImportEC is the emit-context twin of
306
+ // injectOperationMetadataImport. It prepends `import * as <gen> from
307
+ // "@nestia/sdk"` built with ec.Factory, reusing modSpec so the namespace alias
308
+ // matches every decorator reference; tsgo's module-transform turns it into the
309
+ // `const <gen> = require("@nestia/sdk")` binding.
310
+ func injectOperationMetadataImportEC(
311
+ ec *shimprinter.EmitContext,
312
+ modSpec *shimast.Node,
313
+ file *shimast.SourceFile,
314
+ ) {
315
+ if file == nil || file.Statements == nil {
316
+ return
317
+ }
318
+ namespace := ec.Factory.NewNamespaceImport(ec.Factory.NewGeneratedNameForNode(modSpec))
319
+ clause := ec.Factory.NewImportClause(shimast.KindUnknown, nil, namespace)
320
+ declaration := ec.Factory.NewImportDeclaration(nil, clause, modSpec, nil)
321
+ file.Statements.Nodes = append([]*shimast.Node{declaration}, file.Statements.Nodes...)
322
+ }
package/native/sdk/sdk.go CHANGED
@@ -1,17 +1,17 @@
1
- // Package sdk implements the `@nestia/sdk` ttsc transform plugin: it injects
2
- // the `@OperationMetadata({...})` decorator the SDK / Swagger / e2e generators
3
- // read at runtime. It is a separate plugin from `@nestia/core` so that a
4
- // project depending only on `@nestia/core` never compiles this code — see
5
- // `packages/core/native/transform` for the shared transform infrastructure
6
- // this package imports.
7
- package sdk
8
-
9
- import (
10
- "io"
11
- "os"
12
- )
13
-
14
- var (
15
- stdout io.Writer = os.Stdout
16
- stderr io.Writer = os.Stderr
17
- )
1
+ // Package sdk implements the `@nestia/sdk` ttsc transform plugin: it injects
2
+ // the `@OperationMetadata({...})` decorator the SDK / Swagger / e2e generators
3
+ // read at runtime. It is a separate plugin from `@nestia/core` so that a
4
+ // project depending only on `@nestia/core` never compiles this code — see
5
+ // `packages/core/native/transform` for the shared transform infrastructure
6
+ // this package imports.
7
+ package sdk
8
+
9
+ import (
10
+ "io"
11
+ "os"
12
+ )
13
+
14
+ var (
15
+ stdout io.Writer = os.Stdout
16
+ stderr io.Writer = os.Stderr
17
+ )