@nestia/sdk 12.0.0-dev.20260520.1 → 12.0.0-dev.20260521.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.
@@ -0,0 +1,130 @@
1
+ package sdk
2
+
3
+ import (
4
+ "fmt"
5
+ "strings"
6
+
7
+ shimast "github.com/microsoft/typescript-go/shim/ast"
8
+ "github.com/samchon/ttsc/packages/ttsc/driver"
9
+ )
10
+
11
+ // sdkMetadataNamespace is the import alias the injected decorator references.
12
+ const sdkMetadataNamespace = "__OperationMetadata"
13
+
14
+ // init registers the SDK metadata transform with the ttsc driver.
15
+ //
16
+ // ttsc classifies this package (name != "main") as a linked transform and
17
+ // statically links it into the `@nestia/core` host binary — but only for
18
+ // projects that depend on `@nestia/sdk`. A project depending on `@nestia/core`
19
+ // alone never links, compiles, or ships any of this code. When linked, this
20
+ // `init()` runs before the host's `main`; the driver then invokes
21
+ // `ApplyProgram` once per build, after the host's own typia + core passes.
22
+ func init() {
23
+ driver.RegisterPlugin(linkedPlugin{})
24
+ }
25
+
26
+ type linkedPlugin struct{}
27
+
28
+ // ApplyProgram injects the `@OperationMetadata("<json>")` decorator that the
29
+ // SDK / Swagger / e2e generators read at runtime, plus the namespace import it
30
+ // references.
31
+ //
32
+ // Insertion is done with synthesized AST nodes (NodeFlagsSynthesized): ttsc's
33
+ // emit prints those structurally, without slicing the original source text, so
34
+ // no source-text rewrite is needed. The metadata is carried as a single JSON
35
+ // string literal — the `OperationMetadata` decorator `JSON.parse`s it — which
36
+ // keeps the constructed AST to one literal node instead of a deep object tree.
37
+ func (linkedPlugin) ApplyProgram(prog *driver.Program, _ driver.PluginContext) error {
38
+ sites, diags := collectNestiaSDKSites(prog)
39
+ if len(diags) > 0 {
40
+ messages := make([]string, 0, len(diags))
41
+ for _, diag := range diags {
42
+ messages = append(messages, diag.String(""))
43
+ }
44
+ return fmt.Errorf("%s", strings.Join(messages, "\n"))
45
+ }
46
+ if len(sites) == 0 {
47
+ return nil
48
+ }
49
+ factory := shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
50
+ touched := map[*shimast.SourceFile]bool{}
51
+ for _, site := range sites {
52
+ if site.Method == nil || site.File == nil {
53
+ continue
54
+ }
55
+ injectOperationMetadataDecorator(factory, site.Method, site.Metadata)
56
+ touched[site.File] = true
57
+ }
58
+ for file := range touched {
59
+ injectOperationMetadataImport(factory, file)
60
+ }
61
+ return nil
62
+ }
63
+
64
+ func synthesized(node *shimast.Node) *shimast.Node {
65
+ if node != nil {
66
+ node.Flags |= shimast.NodeFlagsSynthesized
67
+ }
68
+ return node
69
+ }
70
+
71
+ // injectOperationMetadataDecorator prepends a synthesized
72
+ // `@__OperationMetadata.OperationMetadata("<json>")` decorator to a controller
73
+ // method. Every site is a method that already carries `@nestia/core`
74
+ // decorators, so its modifier list is non-nil and can be mutated in place.
75
+ func injectOperationMetadataDecorator(
76
+ factory *shimast.NodeFactory,
77
+ method *shimast.Node,
78
+ metadataJSON string,
79
+ ) {
80
+ modifiers := method.Modifiers()
81
+ if modifiers == nil {
82
+ return
83
+ }
84
+ namespaceID := synthesized(factory.NewIdentifier(sdkMetadataNamespace))
85
+ memberID := synthesized(factory.NewIdentifier("OperationMetadata"))
86
+ access := synthesized(factory.NewPropertyAccessExpression(
87
+ namespaceID,
88
+ nil,
89
+ memberID,
90
+ shimast.NodeFlagsNone,
91
+ ))
92
+ namespaceID.Parent = access
93
+ memberID.Parent = access
94
+ argument := synthesized(factory.NewStringLiteral(metadataJSON, shimast.TokenFlagsNone))
95
+ call := synthesized(factory.NewCallExpression(
96
+ access,
97
+ nil,
98
+ nil,
99
+ factory.NewNodeList([]*shimast.Node{argument}),
100
+ shimast.NodeFlagsNone,
101
+ ))
102
+ access.Parent = call
103
+ argument.Parent = call
104
+ decorator := synthesized(factory.NewDecorator(call))
105
+ call.Parent = decorator
106
+ decorator.Parent = method
107
+ modifiers.Nodes = append([]*shimast.Node{decorator}, modifiers.Nodes...)
108
+ }
109
+
110
+ // injectOperationMetadataImport prepends a synthesized
111
+ // `import * as __OperationMetadata from "@nestia/sdk"` to a touched file.
112
+ func injectOperationMetadataImport(
113
+ factory *shimast.NodeFactory,
114
+ file *shimast.SourceFile,
115
+ ) {
116
+ if file == nil || file.Statements == nil {
117
+ return
118
+ }
119
+ namespaceID := synthesized(factory.NewIdentifier(sdkMetadataNamespace))
120
+ namespace := synthesized(factory.NewNamespaceImport(namespaceID))
121
+ namespaceID.Parent = namespace
122
+ clause := synthesized(factory.NewImportClause(shimast.KindUnknown, nil, namespace))
123
+ namespace.Parent = clause
124
+ specifier := synthesized(factory.NewStringLiteral("@nestia/sdk", shimast.TokenFlagsNone))
125
+ declaration := synthesized(factory.NewImportDeclaration(nil, clause, specifier, nil))
126
+ clause.Parent = declaration
127
+ specifier.Parent = declaration
128
+ declaration.Parent = file.AsNode()
129
+ file.Statements.Nodes = append([]*shimast.Node{declaration}, file.Statements.Nodes...)
130
+ }
@@ -0,0 +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
+ )
@@ -0,0 +1,327 @@
1
+ package sdk
2
+
3
+ import schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
4
+
5
+ func nestiaSDKMetadataComponentsLiteral(
6
+ components schemametadata.IMetadataComponents,
7
+ ) map[string]any {
8
+ return map[string]any{
9
+ "objects": nestiaSDKMetadataObjectTypes(components.Objects),
10
+ "aliases": nestiaSDKMetadataAliasTypes(components.Aliases),
11
+ "arrays": nestiaSDKMetadataArrayTypes(components.Arrays),
12
+ "tuples": nestiaSDKMetadataTupleTypes(components.Tuples),
13
+ }
14
+ }
15
+
16
+ func nestiaSDKMetadataSchemaLiteral(meta *schemametadata.IMetadataSchema) any {
17
+ if meta == nil {
18
+ return nestiaSDKLiteralNull
19
+ }
20
+ return map[string]any{
21
+ "any": meta.Any,
22
+ "required": meta.Required,
23
+ "optional": meta.Optional,
24
+ "nullable": meta.Nullable,
25
+ "functions": nestiaSDKMetadataFunctions(meta.Functions),
26
+ "atomics": nestiaSDKMetadataAtomics(meta.Atomics),
27
+ "constants": nestiaSDKMetadataConstants(meta.Constants),
28
+ "templates": nestiaSDKMetadataTemplates(meta.Templates),
29
+ "escaped": nestiaSDKMetadataEscaped(meta.Escaped),
30
+ "rest": nestiaSDKMetadataSchemaLiteral(meta.Rest),
31
+ "arrays": nestiaSDKMetadataReferences(meta.Arrays),
32
+ "tuples": nestiaSDKMetadataReferences(meta.Tuples),
33
+ "objects": nestiaSDKMetadataReferences(meta.Objects),
34
+ "aliases": nestiaSDKMetadataReferences(meta.Aliases),
35
+ "natives": nestiaSDKMetadataReferences(meta.Natives),
36
+ "sets": nestiaSDKMetadataSets(meta.Sets),
37
+ "maps": nestiaSDKMetadataMaps(meta.Maps),
38
+ }
39
+ }
40
+
41
+ func nestiaSDKMetadataFunctions(
42
+ input []*schemametadata.IMetadataSchema_IFunction,
43
+ ) []any {
44
+ output := []any{}
45
+ for _, fn := range input {
46
+ if fn == nil {
47
+ continue
48
+ }
49
+ output = append(output, map[string]any{
50
+ "parameters": nestiaSDKMetadataParameters(fn.Parameters),
51
+ "output": nestiaSDKMetadataSchemaLiteral(fn.Output),
52
+ "async": fn.Async,
53
+ })
54
+ }
55
+ return output
56
+ }
57
+
58
+ func nestiaSDKMetadataParameters(
59
+ input []*schemametadata.IMetadataSchema_IParameter,
60
+ ) []any {
61
+ output := []any{}
62
+ for _, param := range input {
63
+ if param == nil {
64
+ continue
65
+ }
66
+ output = append(output, map[string]any{
67
+ "name": param.Name,
68
+ "type": nestiaSDKMetadataSchemaLiteral(param.Type),
69
+ "description": nestiaSDKOptionalString(param.Description),
70
+ "jsDocTags": nestiaSDKJSDocTags(param.JsDocTags),
71
+ })
72
+ }
73
+ return output
74
+ }
75
+
76
+ func nestiaSDKMetadataAtomics(
77
+ input []schemametadata.IMetadataSchema_IAtomic,
78
+ ) []any {
79
+ output := []any{}
80
+ for _, atomic := range input {
81
+ output = append(output, map[string]any{
82
+ "type": atomic.Type,
83
+ "tags": nestiaSDKMetadataTagMatrix(atomic.Tags),
84
+ })
85
+ }
86
+ return output
87
+ }
88
+
89
+ func nestiaSDKMetadataConstants(
90
+ input []schemametadata.IMetadataSchema_IConstant,
91
+ ) []any {
92
+ output := []any{}
93
+ for _, constant := range input {
94
+ values := []any{}
95
+ for _, value := range constant.Values {
96
+ values = append(values, map[string]any{
97
+ "value": value.Value,
98
+ "tags": nestiaSDKMetadataTagMatrix(value.Tags),
99
+ "description": nestiaSDKOptionalString(value.Description),
100
+ "jsDocTags": nestiaSDKJSDocTags(value.JsDocTags),
101
+ })
102
+ }
103
+ output = append(output, map[string]any{
104
+ "type": constant.Type,
105
+ "values": values,
106
+ })
107
+ }
108
+ return output
109
+ }
110
+
111
+ func nestiaSDKMetadataTemplates(
112
+ input []schemametadata.IMetadataSchema_ITemplate,
113
+ ) []any {
114
+ output := []any{}
115
+ for _, template := range input {
116
+ row := []any{}
117
+ for _, child := range template.Row {
118
+ row = append(row, nestiaSDKMetadataSchemaLiteral(child))
119
+ }
120
+ output = append(output, map[string]any{
121
+ "row": row,
122
+ "tags": nestiaSDKMetadataTagMatrix(template.Tags),
123
+ })
124
+ }
125
+ return output
126
+ }
127
+
128
+ func nestiaSDKMetadataEscaped(
129
+ input *schemametadata.IMetadataSchema_IEscaped,
130
+ ) any {
131
+ if input == nil {
132
+ return nestiaSDKLiteralNull
133
+ }
134
+ return map[string]any{
135
+ "original": nestiaSDKMetadataSchemaLiteral(input.Original),
136
+ "returns": nestiaSDKMetadataSchemaLiteral(input.Returns),
137
+ }
138
+ }
139
+
140
+ func nestiaSDKMetadataReferences(
141
+ input []schemametadata.IMetadataSchema_IReference,
142
+ ) []any {
143
+ output := []any{}
144
+ for _, ref := range input {
145
+ output = append(output, map[string]any{
146
+ "name": ref.Name,
147
+ "tags": nestiaSDKMetadataTagMatrix(ref.Tags),
148
+ })
149
+ }
150
+ return output
151
+ }
152
+
153
+ func nestiaSDKMetadataSets(input []schemametadata.IMetadataSchema_ISet) []any {
154
+ output := []any{}
155
+ for _, set := range input {
156
+ output = append(output, map[string]any{
157
+ "value": nestiaSDKMetadataSchemaLiteral(set.Value),
158
+ "tags": nestiaSDKMetadataTagMatrix(set.Tags),
159
+ })
160
+ }
161
+ return output
162
+ }
163
+
164
+ func nestiaSDKMetadataMaps(input []schemametadata.IMetadataSchema_IMap) []any {
165
+ output := []any{}
166
+ for _, entry := range input {
167
+ output = append(output, map[string]any{
168
+ "key": nestiaSDKMetadataSchemaLiteral(entry.Key),
169
+ "value": nestiaSDKMetadataSchemaLiteral(entry.Value),
170
+ "tags": nestiaSDKMetadataTagMatrix(entry.Tags),
171
+ })
172
+ }
173
+ return output
174
+ }
175
+
176
+ func nestiaSDKMetadataObjectTypes(
177
+ input []schemametadata.IMetadataSchema_IObjectType,
178
+ ) []any {
179
+ output := []any{}
180
+ for _, object := range input {
181
+ properties := []any{}
182
+ for _, property := range object.Properties {
183
+ if property == nil {
184
+ continue
185
+ }
186
+ properties = append(properties, map[string]any{
187
+ "key": nestiaSDKMetadataSchemaLiteral(property.Key),
188
+ "value": nestiaSDKMetadataSchemaLiteral(property.Value),
189
+ "description": nestiaSDKOptionalString(property.Description),
190
+ "jsDocTags": nestiaSDKJSDocTags(property.JsDocTags),
191
+ "mutability": nestiaSDKOptionalString(property.Mutability),
192
+ })
193
+ }
194
+ output = append(output, map[string]any{
195
+ "name": object.Name,
196
+ "properties": properties,
197
+ "description": nestiaSDKOptionalString(object.Description),
198
+ "jsDocTags": nestiaSDKJSDocTags(object.JsDocTags),
199
+ "index": object.Index,
200
+ "recursive": object.Recursive,
201
+ "nullables": nestiaSDKBoolArray(object.Nullables),
202
+ })
203
+ }
204
+ return output
205
+ }
206
+
207
+ func nestiaSDKMetadataAliasTypes(
208
+ input []schemametadata.IMetadataSchema_IAliasType,
209
+ ) []any {
210
+ output := []any{}
211
+ for _, alias := range input {
212
+ output = append(output, map[string]any{
213
+ "name": alias.Name,
214
+ "value": nestiaSDKMetadataSchemaLiteral(alias.Value),
215
+ "description": nestiaSDKOptionalString(alias.Description),
216
+ "jsDocTags": nestiaSDKJSDocTags(alias.JsDocTags),
217
+ "recursive": alias.Recursive,
218
+ "nullables": nestiaSDKBoolArray(alias.Nullables),
219
+ })
220
+ }
221
+ return output
222
+ }
223
+
224
+ func nestiaSDKMetadataArrayTypes(
225
+ input []schemametadata.IMetadataSchema_IArrayType,
226
+ ) []any {
227
+ output := []any{}
228
+ for _, array := range input {
229
+ output = append(output, map[string]any{
230
+ "name": array.Name,
231
+ "value": nestiaSDKMetadataSchemaLiteral(array.Value),
232
+ "nullables": nestiaSDKBoolArray(array.Nullables),
233
+ "recursive": array.Recursive,
234
+ "index": nestiaSDKOptionalInt(array.Index),
235
+ })
236
+ }
237
+ return output
238
+ }
239
+
240
+ func nestiaSDKMetadataTupleTypes(
241
+ input []schemametadata.IMetadataSchema_ITupleType,
242
+ ) []any {
243
+ output := []any{}
244
+ for _, tuple := range input {
245
+ elements := []any{}
246
+ for _, elem := range tuple.Elements {
247
+ elements = append(elements, nestiaSDKMetadataSchemaLiteral(elem))
248
+ }
249
+ output = append(output, map[string]any{
250
+ "name": tuple.Name,
251
+ "elements": elements,
252
+ "index": nestiaSDKOptionalInt(tuple.Index),
253
+ "recursive": tuple.Recursive,
254
+ "nullables": nestiaSDKBoolArray(tuple.Nullables),
255
+ })
256
+ }
257
+ return output
258
+ }
259
+
260
+ func nestiaSDKMetadataTagMatrix(
261
+ input [][]schemametadata.IMetadataTypeTag,
262
+ ) []any {
263
+ output := []any{}
264
+ for _, row := range input {
265
+ items := []any{}
266
+ for _, tag := range row {
267
+ items = append(items, nestiaSDKMetadataTypeTag(tag))
268
+ }
269
+ output = append(output, items)
270
+ }
271
+ return output
272
+ }
273
+
274
+ func nestiaSDKMetadataTypeTag(tag schemametadata.IMetadataTypeTag) map[string]any {
275
+ output := map[string]any{
276
+ "target": tag.Target,
277
+ "name": tag.Name,
278
+ "kind": tag.Kind,
279
+ "exclusive": tag.Exclusive,
280
+ "value": tag.Value,
281
+ "schema": tag.Schema,
282
+ }
283
+ if tag.Validate != "" {
284
+ output["validate"] = tag.Validate
285
+ }
286
+ return output
287
+ }
288
+
289
+ func nestiaSDKJSDocTags(input []schemametadata.IJsDocTagInfo) []any {
290
+ output := []any{}
291
+ for _, tag := range input {
292
+ text := []any{}
293
+ for _, item := range tag.Text {
294
+ text = append(text, map[string]any{
295
+ "text": item.Text,
296
+ "kind": item.Kind,
297
+ })
298
+ }
299
+ output = append(output, map[string]any{
300
+ "name": tag.Name,
301
+ "text": text,
302
+ })
303
+ }
304
+ return output
305
+ }
306
+
307
+ func nestiaSDKBoolArray(input []bool) []any {
308
+ output := []any{}
309
+ for _, value := range input {
310
+ output = append(output, value)
311
+ }
312
+ return output
313
+ }
314
+
315
+ func nestiaSDKOptionalString(input *string) any {
316
+ if input == nil {
317
+ return nestiaSDKLiteralNull
318
+ }
319
+ return *input
320
+ }
321
+
322
+ func nestiaSDKOptionalInt(input *int) any {
323
+ if input == nil {
324
+ return nestiaSDKLiteralNull
325
+ }
326
+ return *input
327
+ }