@nestia/core 12.0.0-dev.20260521.6 → 12.0.0-dev.20260612.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/MIGRATION.md +169 -169
- package/README.md +93 -93
- package/lib/adaptors/McpAdaptor.d.ts +75 -0
- package/lib/adaptors/McpAdaptor.js +257 -0
- package/lib/adaptors/McpAdaptor.js.map +1 -0
- package/lib/adaptors/WebSocketAdaptor.js +4 -4
- package/lib/adaptors/WebSocketAdaptor.js.map +1 -1
- package/lib/decorators/McpRoute.d.ts +69 -0
- package/lib/decorators/McpRoute.js +58 -0
- package/lib/decorators/McpRoute.js.map +1 -0
- package/lib/decorators/TypedParam.js +4 -4
- package/lib/decorators/TypedParam.js.map +1 -1
- package/lib/decorators/TypedRoute.js +1 -1
- package/lib/decorators/TypedRoute.js.map +1 -1
- package/lib/decorators/internal/IMcpRouteReflect.d.ts +2 -0
- package/lib/decorators/internal/IMcpRouteReflect.js +3 -0
- package/lib/decorators/internal/IMcpRouteReflect.js.map +1 -0
- package/lib/decorators/internal/get_path_and_querify.js +4 -4
- package/lib/decorators/internal/get_path_and_querify.js.map +1 -1
- package/lib/decorators/internal/get_path_and_stringify.js +4 -4
- package/lib/decorators/internal/get_path_and_stringify.js.map +1 -1
- package/lib/decorators/internal/load_controller.js +34 -65
- package/lib/decorators/internal/load_controller.js.map +1 -1
- package/lib/decorators/internal/validate_request_body.js +4 -4
- package/lib/decorators/internal/validate_request_body.js.map +1 -1
- package/lib/decorators/internal/validate_request_form_data.js +4 -4
- package/lib/decorators/internal/validate_request_form_data.js.map +1 -1
- package/lib/decorators/internal/validate_request_headers.js +4 -4
- package/lib/decorators/internal/validate_request_headers.js.map +1 -1
- package/lib/decorators/internal/validate_request_query.js +4 -4
- package/lib/decorators/internal/validate_request_query.js.map +1 -1
- package/lib/module.d.ts +2 -0
- package/lib/module.js +2 -0
- package/lib/module.js.map +1 -1
- package/native/cmd/ttsc-nestia/main.go +11 -11
- package/native/go.mod +32 -32
- package/native/go.sum +54 -54
- package/native/plugin/plan.go +102 -102
- package/native/transform/ast.go +32 -32
- package/native/transform/build.go +380 -437
- package/native/transform/cleanup.go +408 -408
- package/native/transform/contributor.go +97 -68
- package/native/transform/core_querify.go +231 -227
- package/native/transform/core_transform.go +1996 -1713
- package/native/transform/core_websocket.go +115 -115
- package/native/transform/exports.go +13 -13
- package/native/transform/mcp_transform.go +414 -0
- package/native/transform/node_transform.go +357 -0
- package/native/transform/path_rewrite.go +285 -285
- package/native/transform/printer.go +244 -244
- package/native/transform/rewrite.go +668 -662
- package/native/transform/run.go +73 -73
- package/native/transform/transform.go +336 -387
- package/native/transform/typia_fast.go +352 -326
- package/native/transform/typia_replacement.go +24 -24
- package/native/transform.cjs +43 -43
- package/package.json +15 -8
- package/src/adaptors/McpAdaptor.ts +276 -0
- package/src/adaptors/WebSocketAdaptor.ts +429 -429
- package/src/decorators/DynamicModule.ts +44 -44
- package/src/decorators/EncryptedBody.ts +97 -97
- package/src/decorators/EncryptedController.ts +40 -40
- package/src/decorators/EncryptedModule.ts +98 -98
- package/src/decorators/EncryptedRoute.ts +213 -213
- package/src/decorators/HumanRoute.ts +21 -21
- package/src/decorators/McpRoute.ts +154 -0
- package/src/decorators/NoTransformConfigurationError.ts +40 -40
- package/src/decorators/PlainBody.ts +76 -76
- package/src/decorators/SwaggerCustomizer.ts +97 -97
- package/src/decorators/SwaggerExample.ts +180 -180
- package/src/decorators/TypedBody.ts +57 -57
- package/src/decorators/TypedException.ts +147 -147
- package/src/decorators/TypedFormData.ts +187 -187
- package/src/decorators/TypedHeaders.ts +66 -66
- package/src/decorators/TypedParam.ts +77 -77
- package/src/decorators/TypedQuery.ts +234 -234
- package/src/decorators/TypedRoute.ts +198 -196
- package/src/decorators/WebSocketRoute.ts +242 -242
- package/src/decorators/doNotThrowTransformError.ts +5 -5
- package/src/decorators/internal/EncryptedConstant.ts +2 -2
- package/src/decorators/internal/IMcpRouteReflect.ts +40 -0
- package/src/decorators/internal/IWebSocketRouteReflect.ts +23 -23
- package/src/decorators/internal/get_path_and_querify.ts +94 -94
- package/src/decorators/internal/get_path_and_stringify.ts +110 -110
- package/src/decorators/internal/get_text_body.ts +16 -16
- package/src/decorators/internal/headers_to_object.ts +11 -11
- package/src/decorators/internal/is_request_body_undefined.ts +12 -12
- package/src/decorators/internal/load_controller.ts +91 -76
- package/src/decorators/internal/route_error.ts +43 -43
- package/src/decorators/internal/validate_request_body.ts +64 -64
- package/src/decorators/internal/validate_request_form_data.ts +67 -67
- package/src/decorators/internal/validate_request_headers.ts +76 -76
- package/src/decorators/internal/validate_request_query.ts +83 -83
- package/src/index.ts +5 -5
- package/src/module.ts +25 -23
- package/src/options/IRequestBodyValidator.ts +20 -20
- package/src/options/IRequestFormDataProps.ts +27 -27
- package/src/options/IRequestHeadersValidator.ts +22 -22
- package/src/options/IRequestQueryValidator.ts +20 -20
- package/src/options/IResponseBodyQuerifier.ts +25 -25
- package/src/options/IResponseBodyStringifier.ts +30 -30
- package/src/transform.ts +101 -101
- package/src/typings/Creator.ts +3 -3
- package/src/typings/get-function-location.d.ts +7 -7
- package/src/utils/ArrayUtil.ts +7 -7
- package/src/utils/ExceptionManager.ts +115 -115
- package/src/utils/Singleton.ts +16 -16
- package/src/utils/SourceFinder.ts +54 -54
- package/src/utils/VersioningStrategy.ts +27 -27
- package/native/transform/cleanup_test.go +0 -76
- package/native/transform/commonjs_import_alias_test.go +0 -49
- package/native/transform/core_dispatch_test.go +0 -127
- package/native/transform/path_rewrite_test.go +0 -243
- package/native/transform/rewrite_test.go +0 -118
- package/native/transform/rewrite_unique_base_test.go +0 -48
|
@@ -1,1713 +1,1996 @@
|
|
|
1
|
-
package transform
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"fmt"
|
|
5
|
-
"os"
|
|
6
|
-
"path/filepath"
|
|
7
|
-
"regexp"
|
|
8
|
-
"runtime/debug"
|
|
9
|
-
"sort"
|
|
10
|
-
"strconv"
|
|
11
|
-
"strings"
|
|
12
|
-
"sync"
|
|
13
|
-
"unicode"
|
|
14
|
-
|
|
15
|
-
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
16
|
-
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
17
|
-
|
|
18
|
-
"github.com/
|
|
19
|
-
"github.com/samchon/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
)
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
"
|
|
690
|
-
"WebSocketRoute
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
return
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
return
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
if
|
|
731
|
-
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
case "
|
|
798
|
-
return
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
case
|
|
830
|
-
return
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
//
|
|
842
|
-
//
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
if
|
|
984
|
-
return "",
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
Context:
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
return
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
Checker:
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
return
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
func
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
if
|
|
1608
|
-
return nil
|
|
1609
|
-
}
|
|
1610
|
-
if
|
|
1611
|
-
return
|
|
1612
|
-
}
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1
|
+
package transform
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"os"
|
|
6
|
+
"path/filepath"
|
|
7
|
+
"regexp"
|
|
8
|
+
"runtime/debug"
|
|
9
|
+
"sort"
|
|
10
|
+
"strconv"
|
|
11
|
+
"strings"
|
|
12
|
+
"sync"
|
|
13
|
+
"unicode"
|
|
14
|
+
|
|
15
|
+
shimast "github.com/microsoft/typescript-go/shim/ast"
|
|
16
|
+
shimchecker "github.com/microsoft/typescript-go/shim/checker"
|
|
17
|
+
shimprinter "github.com/microsoft/typescript-go/shim/printer"
|
|
18
|
+
shimscanner "github.com/microsoft/typescript-go/shim/scanner"
|
|
19
|
+
"github.com/samchon/nestia/packages/core/native/plugin"
|
|
20
|
+
"github.com/samchon/ttsc/packages/ttsc/driver"
|
|
21
|
+
nativecontext "github.com/samchon/typia/packages/typia/native/core/context"
|
|
22
|
+
nativefactories "github.com/samchon/typia/packages/typia/native/core/factories"
|
|
23
|
+
nativeprogrammers "github.com/samchon/typia/packages/typia/native/core/programmers"
|
|
24
|
+
nativehttp "github.com/samchon/typia/packages/typia/native/core/programmers/http"
|
|
25
|
+
nativejson "github.com/samchon/typia/packages/typia/native/core/programmers/json"
|
|
26
|
+
nativellm "github.com/samchon/typia/packages/typia/native/core/programmers/llm"
|
|
27
|
+
nativemisc "github.com/samchon/typia/packages/typia/native/core/programmers/misc"
|
|
28
|
+
schemametadata "github.com/samchon/typia/packages/typia/native/core/schemas/metadata"
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
type nestiaCoreOptions struct {
|
|
32
|
+
Validate string
|
|
33
|
+
Stringify string
|
|
34
|
+
StringifyNull bool
|
|
35
|
+
Llm bool
|
|
36
|
+
LlmStrict bool
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type nestiaCoreSite struct {
|
|
40
|
+
File *shimast.SourceFile
|
|
41
|
+
FilePath string
|
|
42
|
+
Call *shimast.CallExpression
|
|
43
|
+
Modulo *shimast.Node
|
|
44
|
+
Kind string
|
|
45
|
+
Type *shimchecker.Type
|
|
46
|
+
ArgCount int
|
|
47
|
+
Segments []string
|
|
48
|
+
Arguments []string
|
|
49
|
+
ReplaceArguments bool
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
type nestiaCoreTransformState struct {
|
|
53
|
+
prog *driver.Program
|
|
54
|
+
options nestiaCoreOptions
|
|
55
|
+
// importer is the file-scoped ImportProgrammer shared by every validator
|
|
56
|
+
// generated for the current file on the AST-integration emit path; nil on
|
|
57
|
+
// the legacy text-splice paths, where each generation gets a throwaway
|
|
58
|
+
// importer. When set, generation result caching is disabled: the cache keys
|
|
59
|
+
// on text, but ec-mode nodes embed per-file NewGeneratedNameForNode imports
|
|
60
|
+
// that cannot be reused verbatim across files.
|
|
61
|
+
importer *nativecontext.ImportProgrammer
|
|
62
|
+
// ec is the emit EmitContext on the AST-integration path (nil on the legacy
|
|
63
|
+
// text path). Threaded into ITypiaContext.Emit so typia's per-programmer
|
|
64
|
+
// factories build emit-tracked nodes.
|
|
65
|
+
ec *shimprinter.EmitContext
|
|
66
|
+
cache map[nestiaCoreCacheKey][]string
|
|
67
|
+
cacheHits int
|
|
68
|
+
cacheMisses int
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
type nestiaCoreCacheKey struct {
|
|
72
|
+
Kind string
|
|
73
|
+
Type *shimchecker.Type
|
|
74
|
+
TypeName string
|
|
75
|
+
Modulo string
|
|
76
|
+
Validate string
|
|
77
|
+
Stringify string
|
|
78
|
+
StringifyNull bool
|
|
79
|
+
Llm bool
|
|
80
|
+
LlmStrict bool
|
|
81
|
+
ArgCount int
|
|
82
|
+
AllowOptional bool
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
func newNestiaCoreTransformState(prog *driver.Program, options nestiaCoreOptions) *nestiaCoreTransformState {
|
|
86
|
+
return &nestiaCoreTransformState{
|
|
87
|
+
prog: prog,
|
|
88
|
+
options: options,
|
|
89
|
+
cache: map[nestiaCoreCacheKey][]string{},
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
var nestiaCoreFactory = shimast.NewNodeFactory(shimast.NodeFactoryHooks{})
|
|
94
|
+
|
|
95
|
+
const NestiaCoreKindDecorator = shimast.KindDecorator
|
|
96
|
+
|
|
97
|
+
type nestiaCoreFileContext struct {
|
|
98
|
+
file *shimast.SourceFile
|
|
99
|
+
coreImports map[string]string
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func readNestiaCoreOptions(plan plugin.Plan) nestiaCoreOptions {
|
|
103
|
+
options := nestiaCoreOptions{}
|
|
104
|
+
for _, entry := range plan.Entries {
|
|
105
|
+
if entry.Name != "@nestia/core" && !strings.Contains(entry.Transform, "@nestia/core") {
|
|
106
|
+
continue
|
|
107
|
+
}
|
|
108
|
+
if value, ok := entry.Config["validate"].(string); ok {
|
|
109
|
+
options.Validate = value
|
|
110
|
+
}
|
|
111
|
+
if value, ok := entry.Config["stringify"]; ok {
|
|
112
|
+
if value == nil {
|
|
113
|
+
options.StringifyNull = true
|
|
114
|
+
} else if text, ok := value.(string); ok {
|
|
115
|
+
options.Stringify = text
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if value, ok := entry.Config["llm"]; ok {
|
|
119
|
+
switch v := value.(type) {
|
|
120
|
+
case bool:
|
|
121
|
+
options.Llm = v
|
|
122
|
+
case map[string]any:
|
|
123
|
+
options.Llm = true
|
|
124
|
+
if strict, ok := v["strict"].(bool); ok {
|
|
125
|
+
options.LlmStrict = strict
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return options
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
func collectNestiaCoreSourceRewriteMap(
|
|
134
|
+
prog *driver.Program,
|
|
135
|
+
plan plugin.Plan,
|
|
136
|
+
onlyFile string,
|
|
137
|
+
) (map[string][]SourceRewrite, []Diagnostic) {
|
|
138
|
+
if plan.Core == false {
|
|
139
|
+
return map[string][]SourceRewrite{}, nil
|
|
140
|
+
}
|
|
141
|
+
options := readNestiaCoreOptions(plan)
|
|
142
|
+
sites, diagnostics := collectNestiaCoreSites(newNestiaCoreTransformState(prog, options))
|
|
143
|
+
rewrites := map[string][]SourceRewrite{}
|
|
144
|
+
for _, site := range sites {
|
|
145
|
+
if onlyFile != "" && filepath.ToSlash(site.FilePath) != filepath.ToSlash(onlyFile) {
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
source, ok := SourceFileText(site.File)
|
|
149
|
+
if !ok {
|
|
150
|
+
diagnostics = append(diagnostics, nestiaCoreDiagnostic(site, "source text is unavailable"))
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
open, close, ok := callArgumentBounds(source, site.Call)
|
|
154
|
+
if !ok {
|
|
155
|
+
diagnostics = append(diagnostics, nestiaCoreDiagnostic(site, "failed to locate decorator arguments"))
|
|
156
|
+
continue
|
|
157
|
+
}
|
|
158
|
+
replacement := strings.Join(site.Arguments, ", ")
|
|
159
|
+
if site.ReplaceArguments == false {
|
|
160
|
+
replacement = appendArgumentsText(source[open+1:close], site.Arguments)
|
|
161
|
+
}
|
|
162
|
+
rewrites[filepath.ToSlash(site.FilePath)] = append(rewrites[filepath.ToSlash(site.FilePath)], SourceRewrite{
|
|
163
|
+
start: open + 1,
|
|
164
|
+
end: close,
|
|
165
|
+
replacement: replacement,
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
return rewrites, diagnostics
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
func collectNestiaCoreBuildRewrites(
|
|
172
|
+
prog *driver.Program,
|
|
173
|
+
plan plugin.Plan,
|
|
174
|
+
rewrites *nativeRewriteSet,
|
|
175
|
+
) []Diagnostic {
|
|
176
|
+
if plan.Core == false {
|
|
177
|
+
return nil
|
|
178
|
+
}
|
|
179
|
+
options := readNestiaCoreOptions(plan)
|
|
180
|
+
sites, diagnostics := collectNestiaCoreSites(newNestiaCoreTransformState(prog, options))
|
|
181
|
+
for _, site := range sites {
|
|
182
|
+
expectedArgumentCount := site.ArgCount
|
|
183
|
+
expectedArgumentsText := nestiaCoreStableOriginalArgumentText(site)
|
|
184
|
+
rewrites.Add(nativeRewrite{
|
|
185
|
+
FilePath: site.FilePath,
|
|
186
|
+
RootName: site.Segments[0],
|
|
187
|
+
Namespaces: site.Segments[1:],
|
|
188
|
+
AppendArguments: site.Arguments,
|
|
189
|
+
ReplaceArguments: site.ReplaceArguments,
|
|
190
|
+
TargetExpressionCandidates: nestiaCoreTargetCandidates(prog, site),
|
|
191
|
+
SourceStart: site.Call.AsNode().Pos(),
|
|
192
|
+
ExpectedArgumentCount: &expectedArgumentCount,
|
|
193
|
+
ExpectedArgumentsText: expectedArgumentsText,
|
|
194
|
+
})
|
|
195
|
+
}
|
|
196
|
+
return diagnostics
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
func nestiaCoreOriginalArgumentText(site nestiaCoreSite) string {
|
|
200
|
+
source, ok := SourceFileText(site.File)
|
|
201
|
+
if !ok {
|
|
202
|
+
return ""
|
|
203
|
+
}
|
|
204
|
+
open, close, ok := callArgumentBounds(source, site.Call)
|
|
205
|
+
if !ok {
|
|
206
|
+
return ""
|
|
207
|
+
}
|
|
208
|
+
return strings.TrimSpace(source[open+1 : close])
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
func nestiaCoreStableOriginalArgumentText(site nestiaCoreSite) string {
|
|
212
|
+
text := nestiaCoreOriginalArgumentText(site)
|
|
213
|
+
if strings.Contains(text, "=>") || strings.Contains(text, "function") {
|
|
214
|
+
return ""
|
|
215
|
+
}
|
|
216
|
+
return text
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
func collectNestiaCoreSites(state *nestiaCoreTransformState) ([]nestiaCoreSite, []Diagnostic) {
|
|
220
|
+
sites := []nestiaCoreSite{}
|
|
221
|
+
diagnostics := []Diagnostic{}
|
|
222
|
+
prog := state.prog
|
|
223
|
+
if nestiaCoreStrictMode(prog) == false {
|
|
224
|
+
diagnostics = append(diagnostics, nestiaCoreGlobalDiagnostic("@nestia/core", "strict mode is required."))
|
|
225
|
+
}
|
|
226
|
+
for _, file := range prog.SourceFiles() {
|
|
227
|
+
if file == nil || file.IsDeclarationFile {
|
|
228
|
+
continue
|
|
229
|
+
}
|
|
230
|
+
context := newNestiaCoreFileContext(file)
|
|
231
|
+
file.ForEachChild(func(node *shimast.Node) bool {
|
|
232
|
+
visitNestiaCoreNode(state, context, node, &sites, &diagnostics)
|
|
233
|
+
return false
|
|
234
|
+
})
|
|
235
|
+
}
|
|
236
|
+
if os.Getenv("TTSC_NESTIA_PROFILE") != "" {
|
|
237
|
+
fmt.Fprintf(stderr, "ttsc-nestia profile: core-cache hits=%d misses=%d\n", state.cacheHits, state.cacheMisses)
|
|
238
|
+
}
|
|
239
|
+
return sites, diagnostics
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
func visitNestiaCoreNode(
|
|
243
|
+
state *nestiaCoreTransformState,
|
|
244
|
+
context nestiaCoreFileContext,
|
|
245
|
+
node *shimast.Node,
|
|
246
|
+
sites *[]nestiaCoreSite,
|
|
247
|
+
diagnostics *[]Diagnostic,
|
|
248
|
+
) {
|
|
249
|
+
if node == nil {
|
|
250
|
+
return
|
|
251
|
+
}
|
|
252
|
+
switch node.Kind {
|
|
253
|
+
case shimast.KindParameter:
|
|
254
|
+
decorators := node.Decorators()
|
|
255
|
+
if len(decorators) == 0 {
|
|
256
|
+
break
|
|
257
|
+
}
|
|
258
|
+
type candidate struct {
|
|
259
|
+
decorator *shimast.Node
|
|
260
|
+
call *shimast.CallExpression
|
|
261
|
+
segments []string
|
|
262
|
+
kind string
|
|
263
|
+
}
|
|
264
|
+
candidates := []candidate{}
|
|
265
|
+
for _, decorator := range decorators {
|
|
266
|
+
call, segments, ok := nestiaCoreRawDecoratorCall(decorator)
|
|
267
|
+
if !ok {
|
|
268
|
+
continue
|
|
269
|
+
}
|
|
270
|
+
canonical := nestiaCoreCanonicalSegments(context, segments)
|
|
271
|
+
kind := nestiaCoreParameterKind(canonical)
|
|
272
|
+
if kind == "" || nestiaCoreDecoratorReference(state.prog, context, decorator, segments, canonical) == false {
|
|
273
|
+
continue
|
|
274
|
+
}
|
|
275
|
+
candidates = append(candidates, candidate{
|
|
276
|
+
decorator: decorator,
|
|
277
|
+
call: call,
|
|
278
|
+
segments: segments,
|
|
279
|
+
kind: kind,
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
if len(candidates) == 0 {
|
|
283
|
+
break
|
|
284
|
+
}
|
|
285
|
+
typ := state.prog.Checker.GetTypeAtLocation(node)
|
|
286
|
+
for _, candidate := range candidates {
|
|
287
|
+
site, ok, err := transformNestiaCoreParameterDecorator(
|
|
288
|
+
state,
|
|
289
|
+
context.file,
|
|
290
|
+
candidate.call,
|
|
291
|
+
candidate.segments,
|
|
292
|
+
candidate.kind,
|
|
293
|
+
typ,
|
|
294
|
+
)
|
|
295
|
+
if err != nil {
|
|
296
|
+
*diagnostics = append(*diagnostics, nestiaCoreDiagnostic(site, err.Error()))
|
|
297
|
+
} else if ok {
|
|
298
|
+
*sites = append(*sites, site)
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
case shimast.KindMethodDeclaration:
|
|
302
|
+
decorators := node.Decorators()
|
|
303
|
+
if len(decorators) == 0 {
|
|
304
|
+
break
|
|
305
|
+
}
|
|
306
|
+
type candidate struct {
|
|
307
|
+
call *shimast.CallExpression
|
|
308
|
+
segments []string
|
|
309
|
+
kind string
|
|
310
|
+
}
|
|
311
|
+
candidates := []candidate{}
|
|
312
|
+
for _, decorator := range decorators {
|
|
313
|
+
call, segments, ok := nestiaCoreRawDecoratorCall(decorator)
|
|
314
|
+
if !ok {
|
|
315
|
+
continue
|
|
316
|
+
}
|
|
317
|
+
canonical := nestiaCoreCanonicalSegments(context, segments)
|
|
318
|
+
if nestiaCoreDecoratorReference(state.prog, context, decorator, segments, canonical) == false {
|
|
319
|
+
continue
|
|
320
|
+
}
|
|
321
|
+
if len(canonical) != 0 && canonical[len(canonical)-1] == "WebSocketRoute" {
|
|
322
|
+
*diagnostics = append(*diagnostics, validateNestiaCoreWebSocketRoute(state.prog, context, node, call, canonical)...)
|
|
323
|
+
}
|
|
324
|
+
kind := nestiaCoreMethodKind(canonical)
|
|
325
|
+
if kind == "" {
|
|
326
|
+
continue
|
|
327
|
+
}
|
|
328
|
+
if kind == "McpRoute" {
|
|
329
|
+
if nestiaCoreMcpRouteAlreadyTransformed(call) {
|
|
330
|
+
continue
|
|
331
|
+
}
|
|
332
|
+
} else if nestiaCoreShouldSkipMethodDecorator(state.prog, call) {
|
|
333
|
+
continue
|
|
334
|
+
}
|
|
335
|
+
candidates = append(candidates, candidate{
|
|
336
|
+
call: call,
|
|
337
|
+
segments: segments,
|
|
338
|
+
kind: kind,
|
|
339
|
+
})
|
|
340
|
+
}
|
|
341
|
+
if len(candidates) == 0 {
|
|
342
|
+
break
|
|
343
|
+
}
|
|
344
|
+
typ := NestiaCoreMethodReturnType(state.prog, node)
|
|
345
|
+
if typ != nil {
|
|
346
|
+
for _, candidate := range candidates {
|
|
347
|
+
site, ok, err := transformNestiaCoreMethodDecorator(
|
|
348
|
+
state,
|
|
349
|
+
context.file,
|
|
350
|
+
node,
|
|
351
|
+
candidate.call,
|
|
352
|
+
candidate.segments,
|
|
353
|
+
candidate.kind,
|
|
354
|
+
typ,
|
|
355
|
+
)
|
|
356
|
+
if err != nil {
|
|
357
|
+
*diagnostics = append(*diagnostics, nestiaCoreDiagnostic(site, err.Error()))
|
|
358
|
+
} else if ok {
|
|
359
|
+
*sites = append(*sites, site)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
node.ForEachChild(func(child *shimast.Node) bool {
|
|
365
|
+
visitNestiaCoreNode(state, context, child, sites, diagnostics)
|
|
366
|
+
return false
|
|
367
|
+
})
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
func transformNestiaCoreParameterDecorator(
|
|
371
|
+
state *nestiaCoreTransformState,
|
|
372
|
+
file *shimast.SourceFile,
|
|
373
|
+
call *shimast.CallExpression,
|
|
374
|
+
segments []string,
|
|
375
|
+
kind string,
|
|
376
|
+
typ *shimchecker.Type,
|
|
377
|
+
) (nestiaCoreSite, bool, error) {
|
|
378
|
+
modulo := nestiaCoreModuloNode(call.Expression)
|
|
379
|
+
arguments, ok, err := state.parameterArguments(call, segments, modulo, kind, typ)
|
|
380
|
+
if err != nil || !ok {
|
|
381
|
+
return nestiaCoreSite{
|
|
382
|
+
File: file,
|
|
383
|
+
FilePath: file.FileName(),
|
|
384
|
+
Call: call,
|
|
385
|
+
Modulo: modulo,
|
|
386
|
+
Kind: kind,
|
|
387
|
+
Type: typ,
|
|
388
|
+
Segments: segments,
|
|
389
|
+
}, ok, err
|
|
390
|
+
}
|
|
391
|
+
return nestiaCoreSite{
|
|
392
|
+
File: file,
|
|
393
|
+
FilePath: file.FileName(),
|
|
394
|
+
Call: call,
|
|
395
|
+
Modulo: modulo,
|
|
396
|
+
Kind: kind,
|
|
397
|
+
Type: typ,
|
|
398
|
+
ArgCount: nestiaCoreArgumentCount(call),
|
|
399
|
+
Segments: segments,
|
|
400
|
+
Arguments: arguments,
|
|
401
|
+
}, true, nil
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
func transformNestiaCoreMethodDecorator(
|
|
405
|
+
state *nestiaCoreTransformState,
|
|
406
|
+
file *shimast.SourceFile,
|
|
407
|
+
method *shimast.Node,
|
|
408
|
+
call *shimast.CallExpression,
|
|
409
|
+
segments []string,
|
|
410
|
+
kind string,
|
|
411
|
+
typ *shimchecker.Type,
|
|
412
|
+
) (nestiaCoreSite, bool, error) {
|
|
413
|
+
modulo := nestiaCoreModuloNode(call.Expression)
|
|
414
|
+
if kind == "McpRoute" {
|
|
415
|
+
arguments, err := state.mcpRouteArguments(file, method, call, typ)
|
|
416
|
+
if err != nil {
|
|
417
|
+
return nestiaCoreSite{
|
|
418
|
+
File: file,
|
|
419
|
+
FilePath: file.FileName(),
|
|
420
|
+
Call: call,
|
|
421
|
+
Modulo: modulo,
|
|
422
|
+
Kind: kind,
|
|
423
|
+
Type: typ,
|
|
424
|
+
Segments: segments,
|
|
425
|
+
Arguments: arguments,
|
|
426
|
+
}, false, err
|
|
427
|
+
}
|
|
428
|
+
return nestiaCoreSite{
|
|
429
|
+
File: file,
|
|
430
|
+
FilePath: file.FileName(),
|
|
431
|
+
Call: call,
|
|
432
|
+
Modulo: modulo,
|
|
433
|
+
Kind: kind,
|
|
434
|
+
Type: typ,
|
|
435
|
+
ArgCount: nestiaCoreArgumentCount(call),
|
|
436
|
+
Segments: segments,
|
|
437
|
+
Arguments: arguments,
|
|
438
|
+
ReplaceArguments: true,
|
|
439
|
+
}, true, nil
|
|
440
|
+
}
|
|
441
|
+
arguments, err := state.methodArguments(file, segments, modulo, kind, typ, nestiaCoreArgumentCount(call))
|
|
442
|
+
if err != nil {
|
|
443
|
+
return nestiaCoreSite{
|
|
444
|
+
File: file,
|
|
445
|
+
FilePath: file.FileName(),
|
|
446
|
+
Call: call,
|
|
447
|
+
Modulo: modulo,
|
|
448
|
+
Kind: kind,
|
|
449
|
+
Type: typ,
|
|
450
|
+
Segments: segments,
|
|
451
|
+
}, false, err
|
|
452
|
+
}
|
|
453
|
+
return nestiaCoreSite{
|
|
454
|
+
File: file,
|
|
455
|
+
FilePath: file.FileName(),
|
|
456
|
+
Call: call,
|
|
457
|
+
Modulo: modulo,
|
|
458
|
+
Kind: kind,
|
|
459
|
+
Type: typ,
|
|
460
|
+
ArgCount: nestiaCoreArgumentCount(call),
|
|
461
|
+
Segments: segments,
|
|
462
|
+
Arguments: arguments,
|
|
463
|
+
}, true, nil
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
func (state *nestiaCoreTransformState) parameterArguments(
|
|
467
|
+
call *shimast.CallExpression,
|
|
468
|
+
segments []string,
|
|
469
|
+
modulo *shimast.Node,
|
|
470
|
+
kind string,
|
|
471
|
+
typ *shimchecker.Type,
|
|
472
|
+
) ([]string, bool, error) {
|
|
473
|
+
key := state.cacheKey(segments, kind, typ, nestiaCoreArgumentCount(call), kind == "TypedQuery")
|
|
474
|
+
return state.cachedArguments(key, func() ([]string, bool, error) {
|
|
475
|
+
return nestiaCoreParameterArguments(state.prog, state.options, call, modulo, kind, typ)
|
|
476
|
+
})
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
func (state *nestiaCoreTransformState) methodArguments(
|
|
480
|
+
file *shimast.SourceFile,
|
|
481
|
+
segments []string,
|
|
482
|
+
modulo *shimast.Node,
|
|
483
|
+
kind string,
|
|
484
|
+
typ *shimchecker.Type,
|
|
485
|
+
argCount int,
|
|
486
|
+
) ([]string, error) {
|
|
487
|
+
key := state.cacheKey(segments, kind, typ, argCount, kind == "TypedQueryRoute")
|
|
488
|
+
arguments, _, err := state.cachedArguments(key, func() ([]string, bool, error) {
|
|
489
|
+
node, err := nestiaCoreMethodArgumentNode(state.prog, state.importer, state.ec, state.options, modulo, kind, typ)
|
|
490
|
+
if err != nil {
|
|
491
|
+
return nil, false, err
|
|
492
|
+
}
|
|
493
|
+
return []string{emitNestiaCoreExpression(state.prog, file, node, false)}, true, nil
|
|
494
|
+
})
|
|
495
|
+
return arguments, err
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
func (state *nestiaCoreTransformState) mcpRouteArguments(
|
|
499
|
+
file *shimast.SourceFile,
|
|
500
|
+
method *shimast.Node,
|
|
501
|
+
call *shimast.CallExpression,
|
|
502
|
+
typ *shimchecker.Type,
|
|
503
|
+
) ([]string, error) {
|
|
504
|
+
node, err := nestiaCoreMcpRouteArgumentNode(state.prog, nil, nil, state.options, file, method, call, typ)
|
|
505
|
+
if err != nil {
|
|
506
|
+
return nil, err
|
|
507
|
+
}
|
|
508
|
+
return []string{emitNestiaCoreExpression(state.prog, file, node, false)}, nil
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// nestiaCoreMethodArgumentNode builds the single appended decorator-argument
|
|
512
|
+
// node for a method decorator (TypedRoute / TypedQueryRoute). The importer is
|
|
513
|
+
// the shared ec-mode ImportProgrammer on the node-emit path; nil on the legacy
|
|
514
|
+
// text path.
|
|
515
|
+
func nestiaCoreMethodArgumentNode(
|
|
516
|
+
prog *driver.Program,
|
|
517
|
+
importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext,
|
|
518
|
+
options nestiaCoreOptions,
|
|
519
|
+
modulo *shimast.Node,
|
|
520
|
+
kind string,
|
|
521
|
+
typ *shimchecker.Type,
|
|
522
|
+
) (*shimast.Node, error) {
|
|
523
|
+
return safeNestiaCoreGenerateNode(func() (*shimast.Node, error) {
|
|
524
|
+
switch kind {
|
|
525
|
+
case "TypedQueryRoute":
|
|
526
|
+
return nestiaCoreGenerateTypedQueryRoute(prog, importer, ec, options, modulo, typ), nil
|
|
527
|
+
default:
|
|
528
|
+
return nestiaCoreGenerateTypedRoute(prog, importer, ec, options, modulo, typ), nil
|
|
529
|
+
}
|
|
530
|
+
})
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
func (state *nestiaCoreTransformState) cachedArguments(
|
|
534
|
+
key nestiaCoreCacheKey,
|
|
535
|
+
generate func() ([]string, bool, error),
|
|
536
|
+
) ([]string, bool, error) {
|
|
537
|
+
if cached, ok := state.cache[key]; ok {
|
|
538
|
+
state.cacheHits++
|
|
539
|
+
return append([]string(nil), cached...), true, nil
|
|
540
|
+
}
|
|
541
|
+
state.cacheMisses++
|
|
542
|
+
arguments, ok, err := generate()
|
|
543
|
+
if err != nil || ok == false {
|
|
544
|
+
return arguments, ok, err
|
|
545
|
+
}
|
|
546
|
+
state.cache[key] = append([]string(nil), arguments...)
|
|
547
|
+
return append([]string(nil), arguments...), true, nil
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
func (state *nestiaCoreTransformState) cacheKey(
|
|
551
|
+
segments []string,
|
|
552
|
+
kind string,
|
|
553
|
+
typ *shimchecker.Type,
|
|
554
|
+
argCount int,
|
|
555
|
+
allowOptional bool,
|
|
556
|
+
) nestiaCoreCacheKey {
|
|
557
|
+
return nestiaCoreCacheKey{
|
|
558
|
+
Kind: kind,
|
|
559
|
+
Type: typ,
|
|
560
|
+
TypeName: nestiaCoreTypeNameText(state.prog, typ),
|
|
561
|
+
Modulo: strings.Join(segments, "."),
|
|
562
|
+
Validate: state.options.Validate,
|
|
563
|
+
Stringify: state.options.Stringify,
|
|
564
|
+
StringifyNull: state.options.StringifyNull,
|
|
565
|
+
Llm: state.options.Llm,
|
|
566
|
+
LlmStrict: state.options.LlmStrict,
|
|
567
|
+
ArgCount: argCount,
|
|
568
|
+
AllowOptional: allowOptional,
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
func nestiaCoreRawDecoratorCall(decorator *shimast.Node) (*shimast.CallExpression, []string, bool) {
|
|
573
|
+
if decorator == nil || decorator.Kind != NestiaCoreKindDecorator {
|
|
574
|
+
return nil, nil, false
|
|
575
|
+
}
|
|
576
|
+
expression := decorator.AsDecorator().Expression
|
|
577
|
+
if expression == nil || expression.Kind != shimast.KindCallExpression {
|
|
578
|
+
return nil, nil, false
|
|
579
|
+
}
|
|
580
|
+
call := expression.AsCallExpression()
|
|
581
|
+
segments := NestiaCoreExpressionSegments(call.Expression)
|
|
582
|
+
if len(segments) == 0 {
|
|
583
|
+
return nil, nil, false
|
|
584
|
+
}
|
|
585
|
+
return call, segments, true
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
func nestiaCoreDecoratorCall(prog *driver.Program, decorator *shimast.Node) (*shimast.CallExpression, []string, bool) {
|
|
589
|
+
call, segments, ok := nestiaCoreRawDecoratorCall(decorator)
|
|
590
|
+
if !ok {
|
|
591
|
+
return nil, nil, false
|
|
592
|
+
}
|
|
593
|
+
context := newNestiaCoreFileContext(shimast.GetSourceFileOfNode(decorator))
|
|
594
|
+
canonical := nestiaCoreCanonicalSegments(context, segments)
|
|
595
|
+
if nestiaCoreDecoratorReference(prog, context, decorator, segments, canonical) == false {
|
|
596
|
+
return nil, nil, false
|
|
597
|
+
}
|
|
598
|
+
return call, canonical, true
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
func newNestiaCoreFileContext(file *shimast.SourceFile) nestiaCoreFileContext {
|
|
602
|
+
context := nestiaCoreFileContext{
|
|
603
|
+
file: file,
|
|
604
|
+
coreImports: map[string]string{},
|
|
605
|
+
}
|
|
606
|
+
if file == nil || file.Statements == nil {
|
|
607
|
+
return context
|
|
608
|
+
}
|
|
609
|
+
for _, stmt := range file.Statements.Nodes {
|
|
610
|
+
if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
|
|
611
|
+
continue
|
|
612
|
+
}
|
|
613
|
+
decl := stmt.AsImportDeclaration()
|
|
614
|
+
if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
|
|
615
|
+
continue
|
|
616
|
+
}
|
|
617
|
+
if decl.ModuleSpecifier.Text() != "@nestia/core" {
|
|
618
|
+
continue
|
|
619
|
+
}
|
|
620
|
+
clause := decl.ImportClause.AsImportClause()
|
|
621
|
+
if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
|
|
622
|
+
continue
|
|
623
|
+
}
|
|
624
|
+
if name := clause.Name(); name != nil {
|
|
625
|
+
context.coreImports[name.Text()] = name.Text()
|
|
626
|
+
}
|
|
627
|
+
if clause.NamedBindings == nil || clause.NamedBindings.Kind != shimast.KindNamedImports {
|
|
628
|
+
continue
|
|
629
|
+
}
|
|
630
|
+
named := clause.NamedBindings.AsNamedImports()
|
|
631
|
+
if named == nil || named.Elements == nil {
|
|
632
|
+
continue
|
|
633
|
+
}
|
|
634
|
+
for _, elem := range named.Elements.Nodes {
|
|
635
|
+
if elem == nil {
|
|
636
|
+
continue
|
|
637
|
+
}
|
|
638
|
+
spec := elem.AsImportSpecifier()
|
|
639
|
+
if spec == nil || spec.IsTypeOnly {
|
|
640
|
+
continue
|
|
641
|
+
}
|
|
642
|
+
name := spec.Name()
|
|
643
|
+
if name != nil {
|
|
644
|
+
imported := name.Text()
|
|
645
|
+
if spec.PropertyName != nil {
|
|
646
|
+
imported = spec.PropertyName.Text()
|
|
647
|
+
}
|
|
648
|
+
context.coreImports[name.Text()] = imported
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
return context
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
func nestiaCoreCanonicalSegments(context nestiaCoreFileContext, segments []string) []string {
|
|
656
|
+
if len(segments) == 0 {
|
|
657
|
+
return segments
|
|
658
|
+
}
|
|
659
|
+
imported, ok := context.coreImports[segments[0]]
|
|
660
|
+
if !ok || imported == "" || imported == segments[0] {
|
|
661
|
+
return segments
|
|
662
|
+
}
|
|
663
|
+
canonical := append([]string{}, segments...)
|
|
664
|
+
canonical[0] = imported
|
|
665
|
+
return canonical
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
func nestiaCoreDecoratorReference(
|
|
669
|
+
prog *driver.Program,
|
|
670
|
+
context nestiaCoreFileContext,
|
|
671
|
+
decorator *shimast.Node,
|
|
672
|
+
segments []string,
|
|
673
|
+
canonical []string,
|
|
674
|
+
) bool {
|
|
675
|
+
if len(segments) == 0 {
|
|
676
|
+
return false
|
|
677
|
+
}
|
|
678
|
+
if _, ok := context.coreImports[segments[0]]; ok {
|
|
679
|
+
return true
|
|
680
|
+
}
|
|
681
|
+
if nestiaCorePotentialDecoratorSegments(canonical) == false {
|
|
682
|
+
return false
|
|
683
|
+
}
|
|
684
|
+
return IsNestiaCoreCall(prog, decorator.AsDecorator().Expression)
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
func nestiaCorePotentialDecoratorSegments(segments []string) bool {
|
|
688
|
+
return nestiaCoreParameterKind(segments) != "" ||
|
|
689
|
+
nestiaCoreMethodKind(segments) != "" ||
|
|
690
|
+
(len(segments) != 0 && segments[len(segments)-1] == "WebSocketRoute")
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
func IsNestiaCoreCall(prog *driver.Program, node *shimast.Node) bool {
|
|
694
|
+
signature := prog.Checker.GetResolvedSignature(node)
|
|
695
|
+
if signature == nil || signature.Declaration() == nil {
|
|
696
|
+
return false
|
|
697
|
+
}
|
|
698
|
+
source := shimast.GetSourceFileOfNode(signature.Declaration())
|
|
699
|
+
if source == nil {
|
|
700
|
+
return false
|
|
701
|
+
}
|
|
702
|
+
location := filepath.ToSlash(source.FileName())
|
|
703
|
+
return strings.Contains(location, "@nestia/core/lib/") ||
|
|
704
|
+
strings.Contains(location, "packages/core/lib/") ||
|
|
705
|
+
strings.Contains(location, "@nestia/core/src/decorators/") ||
|
|
706
|
+
strings.Contains(location, "packages/core/src/decorators/")
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
func isNestiaCoreImportedExpression(node *shimast.Node, segments []string) bool {
|
|
710
|
+
if len(segments) == 0 {
|
|
711
|
+
return false
|
|
712
|
+
}
|
|
713
|
+
source := shimast.GetSourceFileOfNode(node)
|
|
714
|
+
if source == nil || source.Statements == nil {
|
|
715
|
+
return false
|
|
716
|
+
}
|
|
717
|
+
root := segments[0]
|
|
718
|
+
for _, stmt := range source.Statements.Nodes {
|
|
719
|
+
if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
|
|
720
|
+
continue
|
|
721
|
+
}
|
|
722
|
+
decl := stmt.AsImportDeclaration()
|
|
723
|
+
if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
|
|
724
|
+
continue
|
|
725
|
+
}
|
|
726
|
+
if decl.ModuleSpecifier.Text() != "@nestia/core" {
|
|
727
|
+
continue
|
|
728
|
+
}
|
|
729
|
+
clause := decl.ImportClause.AsImportClause()
|
|
730
|
+
if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
|
|
731
|
+
continue
|
|
732
|
+
}
|
|
733
|
+
if name := clause.Name(); name != nil && name.Text() == root {
|
|
734
|
+
return true
|
|
735
|
+
}
|
|
736
|
+
if clause.NamedBindings == nil || clause.NamedBindings.Kind != shimast.KindNamedImports {
|
|
737
|
+
continue
|
|
738
|
+
}
|
|
739
|
+
named := clause.NamedBindings.AsNamedImports()
|
|
740
|
+
if named == nil || named.Elements == nil {
|
|
741
|
+
continue
|
|
742
|
+
}
|
|
743
|
+
for _, elem := range named.Elements.Nodes {
|
|
744
|
+
if elem == nil {
|
|
745
|
+
continue
|
|
746
|
+
}
|
|
747
|
+
spec := elem.AsImportSpecifier()
|
|
748
|
+
if spec == nil || spec.IsTypeOnly {
|
|
749
|
+
continue
|
|
750
|
+
}
|
|
751
|
+
name := spec.Name()
|
|
752
|
+
if name != nil && name.Text() == root {
|
|
753
|
+
return true
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return false
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
func nestiaCoreParameterKind(segments []string) string {
|
|
761
|
+
suffixes := map[string]string{
|
|
762
|
+
"EncryptedBody": "TypedBody",
|
|
763
|
+
"TypedBody": "TypedBody",
|
|
764
|
+
"TypedHeaders": "TypedHeaders",
|
|
765
|
+
"TypedParam": "TypedParam",
|
|
766
|
+
"TypedQuery": "TypedQuery",
|
|
767
|
+
"TypedQuery.Body": "TypedQueryBody",
|
|
768
|
+
"TypedFormData.Body": "TypedFormDataBody",
|
|
769
|
+
"McpRoute.Params": "McpRouteParams",
|
|
770
|
+
"PlainBody": "PlainBody",
|
|
771
|
+
"WebSocketRoute.Header": "TypedBody",
|
|
772
|
+
"WebSocketRoute.Param": "TypedParam",
|
|
773
|
+
"WebSocketRoute.Query": "TypedQuery",
|
|
774
|
+
}
|
|
775
|
+
for suffix, kind := range suffixes {
|
|
776
|
+
if nestiaCoreSegmentsHaveSuffix(segments, strings.Split(suffix, ".")) {
|
|
777
|
+
return kind
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return ""
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
func nestiaCoreMethodKind(segments []string) string {
|
|
784
|
+
if len(segments) != 0 && segments[len(segments)-1] == "McpRoute" {
|
|
785
|
+
return "McpRoute"
|
|
786
|
+
}
|
|
787
|
+
if len(segments) < 2 {
|
|
788
|
+
return ""
|
|
789
|
+
}
|
|
790
|
+
methods := map[string]bool{"Get": true, "Post": true, "Patch": true, "Put": true, "Delete": true}
|
|
791
|
+
if methods[segments[len(segments)-1]] == false {
|
|
792
|
+
return ""
|
|
793
|
+
}
|
|
794
|
+
switch segments[len(segments)-2] {
|
|
795
|
+
case "EncryptedRoute", "TypedRoute":
|
|
796
|
+
return "TypedRoute"
|
|
797
|
+
case "TypedQuery":
|
|
798
|
+
return "TypedQueryRoute"
|
|
799
|
+
default:
|
|
800
|
+
return ""
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
func nestiaCoreParameterArguments(
|
|
805
|
+
prog *driver.Program,
|
|
806
|
+
options nestiaCoreOptions,
|
|
807
|
+
call *shimast.CallExpression,
|
|
808
|
+
modulo *shimast.Node,
|
|
809
|
+
kind string,
|
|
810
|
+
typ *shimchecker.Type,
|
|
811
|
+
) ([]string, bool, error) {
|
|
812
|
+
nodes, ok, err := nestiaCoreParameterArgumentNodes(prog, nil, nil, options, call, modulo, kind, typ)
|
|
813
|
+
if err != nil || !ok {
|
|
814
|
+
return nil, ok, err
|
|
815
|
+
}
|
|
816
|
+
file := shimast.GetSourceFileOfNode(call.AsNode())
|
|
817
|
+
output := make([]string, 0, len(nodes))
|
|
818
|
+
for _, node := range nodes {
|
|
819
|
+
output = append(output, nestiaCoreArgumentNodeToText(prog, file, node))
|
|
820
|
+
}
|
|
821
|
+
return output, true, nil
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
// nestiaCoreArgumentNodeToText prints a single appended decorator-argument node
|
|
825
|
+
// for the legacy text-splice path. Keyword nodes (undefined / true) print as
|
|
826
|
+
// their literal text; validator nodes go through the type-stripping printer.
|
|
827
|
+
func nestiaCoreArgumentNodeToText(prog *driver.Program, file *shimast.SourceFile, node *shimast.Node) string {
|
|
828
|
+
switch node.Kind {
|
|
829
|
+
case shimast.KindUndefinedKeyword:
|
|
830
|
+
return "undefined"
|
|
831
|
+
case shimast.KindTrueKeyword:
|
|
832
|
+
return "true"
|
|
833
|
+
}
|
|
834
|
+
return emitNestiaCoreExpression(prog, file, node, false)
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
// nestiaCoreParameterArgumentNodes builds the appended decorator-argument nodes
|
|
838
|
+
// for a parameter decorator. The importer is the file-scoped ImportProgrammer:
|
|
839
|
+
// on the node-emit path it is the shared ec-mode importer, so the validator's
|
|
840
|
+
// runtime references resolve to tsgo-aliased namespace imports; nil keeps the
|
|
841
|
+
// legacy text behavior. Returning nodes (not text) is the single source of truth
|
|
842
|
+
// shared by the text path (nestiaCoreParameterArguments) and the AST emit path.
|
|
843
|
+
func nestiaCoreParameterArgumentNodes(
|
|
844
|
+
prog *driver.Program,
|
|
845
|
+
importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext,
|
|
846
|
+
options nestiaCoreOptions,
|
|
847
|
+
call *shimast.CallExpression,
|
|
848
|
+
modulo *shimast.Node,
|
|
849
|
+
kind string,
|
|
850
|
+
typ *shimchecker.Type,
|
|
851
|
+
) ([]*shimast.Node, bool, error) {
|
|
852
|
+
argCount := nestiaCoreArgumentCount(call)
|
|
853
|
+
switch kind {
|
|
854
|
+
case "TypedBody", "TypedHeaders", "TypedQuery", "TypedQueryBody", "McpRouteParams", "PlainBody":
|
|
855
|
+
if argCount != 0 {
|
|
856
|
+
return nil, false, nil
|
|
857
|
+
}
|
|
858
|
+
case "TypedParam":
|
|
859
|
+
if argCount != 1 {
|
|
860
|
+
return nil, false, nil
|
|
861
|
+
}
|
|
862
|
+
case "TypedFormDataBody":
|
|
863
|
+
if argCount > 1 {
|
|
864
|
+
return nil, false, nil
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
node, err := safeNestiaCoreGenerateNode(func() (*shimast.Node, error) {
|
|
868
|
+
switch kind {
|
|
869
|
+
case "TypedBody":
|
|
870
|
+
return nestiaCoreGenerateTypedBody(prog, importer, ec, options, modulo, typ), nil
|
|
871
|
+
case "McpRouteParams":
|
|
872
|
+
return nestiaCoreGenerateMcpRouteParams(prog, importer, ec, options, modulo, typ), nil
|
|
873
|
+
case "TypedHeaders":
|
|
874
|
+
return nestiaCoreGenerateTypedHeaders(prog, importer, ec, options, modulo, typ), nil
|
|
875
|
+
case "TypedParam":
|
|
876
|
+
return nestiaCoreGenerateTypedParam(prog, importer, ec, modulo, typ), nil
|
|
877
|
+
case "TypedQuery":
|
|
878
|
+
return nestiaCoreGenerateTypedQuery(prog, importer, ec, options, modulo, typ, true), nil
|
|
879
|
+
case "TypedQueryBody":
|
|
880
|
+
return nestiaCoreGenerateTypedQuery(prog, importer, ec, options, modulo, typ, false), nil
|
|
881
|
+
case "TypedFormDataBody":
|
|
882
|
+
return nestiaCoreGenerateTypedFormDataBody(prog, importer, ec, options, modulo, typ), nil
|
|
883
|
+
case "PlainBody":
|
|
884
|
+
return nestiaCoreGeneratePlainBody(prog, importer, ec, modulo, typ), nil
|
|
885
|
+
default:
|
|
886
|
+
return nil, fmt.Errorf("unsupported parameter decorator %s", kind)
|
|
887
|
+
}
|
|
888
|
+
})
|
|
889
|
+
if err != nil {
|
|
890
|
+
return nil, false, err
|
|
891
|
+
}
|
|
892
|
+
output := []*shimast.Node{}
|
|
893
|
+
if kind == "TypedFormDataBody" && argCount == 0 {
|
|
894
|
+
output = append(output, nestiaCoreFactory.NewKeywordExpression(shimast.KindUndefinedKeyword))
|
|
895
|
+
}
|
|
896
|
+
output = append(output, node)
|
|
897
|
+
// TypedParam takes a third `validate?: boolean` argument (see
|
|
898
|
+
// packages/core/src/decorators/TypedParam.ts). When the configured
|
|
899
|
+
// validate mode starts with "validate", emit `true` so the runtime
|
|
900
|
+
// returns the detailed report shape instead of the single-error shape.
|
|
901
|
+
// The legacy TypedParamProgrammer applied the same conditional.
|
|
902
|
+
if kind == "TypedParam" && strings.HasPrefix(options.Validate, "validate") {
|
|
903
|
+
output = append(output, nestiaCoreFactory.NewKeywordExpression(shimast.KindTrueKeyword))
|
|
904
|
+
}
|
|
905
|
+
return output, true, nil
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
func nestiaCoreGenerateTypedBody(
|
|
909
|
+
prog *driver.Program,
|
|
910
|
+
importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext,
|
|
911
|
+
options nestiaCoreOptions,
|
|
912
|
+
modulo *shimast.Node,
|
|
913
|
+
typ *shimchecker.Type,
|
|
914
|
+
) *shimast.Node {
|
|
915
|
+
nestiaCoreValidateTypedBody(prog, options, typ)
|
|
916
|
+
context := nestiaCoreTypiaContext(prog, importer, ec, false, false, false)
|
|
917
|
+
name := nestiaCoreTypeName(prog, typ)
|
|
918
|
+
category := options.Validate
|
|
919
|
+
switch category {
|
|
920
|
+
case "assert":
|
|
921
|
+
return nestiaCoreValidatorObject("type", "assert", nativeprogrammers.AssertProgrammer.Write(nativeprogrammers.AssertProgrammer_IProps{
|
|
922
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
923
|
+
Config: nativeprogrammers.AssertProgrammer_IConfig{Equals: false, Guard: false},
|
|
924
|
+
}), ec)
|
|
925
|
+
case "is":
|
|
926
|
+
return nestiaCoreValidatorObject("type", "is", nativeprogrammers.IsProgrammer.Write(nativeprogrammers.IsProgrammer_IProps{
|
|
927
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
928
|
+
Config: nativeprogrammers.IsProgrammer_IConfig{Equals: false},
|
|
929
|
+
}), ec)
|
|
930
|
+
case "validateEquals":
|
|
931
|
+
return nestiaCoreValidatorObject("type", "validate", nativeprogrammers.ValidateProgrammer.Write(nativeprogrammers.ValidateProgrammer_IProps{
|
|
932
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
933
|
+
Config: nativeprogrammers.ValidateProgrammer_IConfig{Equals: true},
|
|
934
|
+
}), ec)
|
|
935
|
+
case "equals":
|
|
936
|
+
return nestiaCoreValidatorObject("type", "is", nativeprogrammers.IsProgrammer.Write(nativeprogrammers.IsProgrammer_IProps{
|
|
937
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
938
|
+
Config: nativeprogrammers.IsProgrammer_IConfig{Equals: true},
|
|
939
|
+
}), ec)
|
|
940
|
+
case "assertEquals":
|
|
941
|
+
return nestiaCoreValidatorObject("type", "assert", nativeprogrammers.AssertProgrammer.Write(nativeprogrammers.AssertProgrammer_IProps{
|
|
942
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
943
|
+
Config: nativeprogrammers.AssertProgrammer_IConfig{Equals: true, Guard: false},
|
|
944
|
+
}), ec)
|
|
945
|
+
case "assertClone":
|
|
946
|
+
return nestiaCoreValidatorObject("type", "assert", nativemisc.MiscAssertCloneProgrammer.Write(nativecontext.IProgrammerProps{
|
|
947
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
948
|
+
}), ec)
|
|
949
|
+
case "validateClone":
|
|
950
|
+
return nestiaCoreValidatorObject("type", "validate", nativemisc.MiscValidateCloneProgrammer.Write(nativecontext.IProgrammerProps{
|
|
951
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
952
|
+
}), ec)
|
|
953
|
+
case "assertPrune":
|
|
954
|
+
return nestiaCoreValidatorObject("type", "assert", nativemisc.MiscAssertPruneProgrammer.Write(nativecontext.IProgrammerProps{
|
|
955
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
956
|
+
}), ec)
|
|
957
|
+
case "validatePrune":
|
|
958
|
+
return nestiaCoreValidatorObject("type", "validate", nativemisc.MiscValidatePruneProgrammer.Write(nativecontext.IProgrammerProps{
|
|
959
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
960
|
+
}), ec)
|
|
961
|
+
default:
|
|
962
|
+
return nestiaCoreValidatorObject("type", "validate", nativeprogrammers.ValidateProgrammer.Write(nativeprogrammers.ValidateProgrammer_IProps{
|
|
963
|
+
Context: context, Modulo: modulo, Type: typ, Name: name,
|
|
964
|
+
Config: nativeprogrammers.ValidateProgrammer_IConfig{Equals: false},
|
|
965
|
+
}), ec)
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// nestiaCoreGenerateTypedHeaders intentionally collapses the 10-mode validate
|
|
970
|
+
// option down to {assert, is, validate}. Header values are strings keyed by
|
|
971
|
+
// name; deep-clone and prune semantics that @TypedBody honors (assertClone,
|
|
972
|
+
// assertPrune, validateClone, validatePrune, etc.) have no meaningful effect
|
|
973
|
+
// on a flat string→string map. Pass-through to the base programmer is the
|
|
974
|
+
// intended behavior, not a fallthrough — matches v6 parity. See also
|
|
975
|
+
// nestiaCoreGenerateTypedQuery and nestiaCoreGenerateTypedFormDataBody.
|
|
976
|
+
func nestiaCoreGenerateTypedHeaders(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, options nestiaCoreOptions, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
977
|
+
context := nestiaCoreTypiaContext(prog, importer, ec, false, false, false)
|
|
978
|
+
name := nestiaCoreTypeName(prog, typ)
|
|
979
|
+
category := options.Validate
|
|
980
|
+
if category == "is" || category == "equals" {
|
|
981
|
+
return nestiaCoreValidatorObject("type", "is", nativehttp.HttpIsHeadersProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
982
|
+
}
|
|
983
|
+
if strings.HasPrefix(category, "validate") {
|
|
984
|
+
return nestiaCoreValidatorObject("type", "validate", nativehttp.HttpValidateHeadersProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
985
|
+
}
|
|
986
|
+
return nestiaCoreValidatorObject("type", "assert", nativehttp.HttpAssertHeadersProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
func nestiaCoreGenerateTypedParam(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
990
|
+
return nativehttp.HttpParameterProgrammer.Write(nativecontext.IProgrammerProps{
|
|
991
|
+
Context: nestiaCoreTypiaContext(prog, importer, ec, true, false, false),
|
|
992
|
+
Modulo: modulo,
|
|
993
|
+
Type: typ,
|
|
994
|
+
Name: nestiaCoreTypeName(prog, typ),
|
|
995
|
+
})
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
func nestiaCoreGenerateTypedQuery(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, options nestiaCoreOptions, modulo *shimast.Node, typ *shimchecker.Type, allowOptional bool) *shimast.Node {
|
|
999
|
+
nestiaCoreValidateTypedQuery(prog, options, typ, allowOptional, "@nestia.core.TypedQuery")
|
|
1000
|
+
context := nestiaCoreTypiaContext(prog, importer, ec, false, false, false)
|
|
1001
|
+
name := nestiaCoreTypeName(prog, typ)
|
|
1002
|
+
category := options.Validate
|
|
1003
|
+
if category == "is" || category == "equals" {
|
|
1004
|
+
return nestiaCoreValidatorObject("type", "is", nativehttp.HttpIsQueryProgrammer.Write(nativehttp.HttpIsQueryProgrammer_IProps{Context: context, Modulo: modulo, Type: typ, Name: name, AllowOptional: allowOptional}), ec)
|
|
1005
|
+
}
|
|
1006
|
+
if category == "validate" || category == "validateEquals" || category == "validateClone" || category == "validatePrune" {
|
|
1007
|
+
return nestiaCoreValidatorObject("type", "validate", nativehttp.HttpValidateQueryProgrammer.Write(nativehttp.HttpValidateQueryProgrammer_IProps{Context: context, Modulo: modulo, Type: typ, Name: name, AllowOptional: allowOptional}), ec)
|
|
1008
|
+
}
|
|
1009
|
+
return nestiaCoreValidatorObject("type", "assert", nativehttp.HttpAssertQueryProgrammer.Write(nativehttp.HttpAssertQueryProgrammer_IProps{Context: context, Modulo: modulo, Type: typ, Name: name, AllowOptional: allowOptional}), ec)
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
func nestiaCoreGenerateTypedFormDataBody(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, options nestiaCoreOptions, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
1013
|
+
context := nestiaCoreTypiaContext(prog, importer, ec, false, false, false)
|
|
1014
|
+
name := nestiaCoreTypeName(prog, typ)
|
|
1015
|
+
category := options.Validate
|
|
1016
|
+
files := nestiaCoreFormDataFiles(prog, typ)
|
|
1017
|
+
validator := nativehttp.HttpAssertFormDataProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name})
|
|
1018
|
+
key := "assert"
|
|
1019
|
+
if category == "is" || category == "equals" {
|
|
1020
|
+
key = "is"
|
|
1021
|
+
validator = nativehttp.HttpIsFormDataProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name})
|
|
1022
|
+
} else if category == "validate" || category == "validateEquals" || category == "validateClone" || category == "validatePrune" {
|
|
1023
|
+
key = "validate"
|
|
1024
|
+
validator = nativehttp.HttpValidateFormDataProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name})
|
|
1025
|
+
}
|
|
1026
|
+
f := nativecontext.EmitFactoryOf(nestiaCoreFactory, ec)
|
|
1027
|
+
return f.NewObjectLiteralExpression(f.NewNodeList([]*shimast.Node{
|
|
1028
|
+
nestiaCoreProperty("files", nestiaCoreFormDataFilesExpression(files, ec), ec),
|
|
1029
|
+
nestiaCoreProperty("validator", nestiaCoreValidatorObject("type", key, validator, ec), ec),
|
|
1030
|
+
}), true)
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
type nestiaCoreFormDataFile struct {
|
|
1034
|
+
Name string
|
|
1035
|
+
Limit *int
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
func nestiaCoreFormDataFiles(prog *driver.Program, typ *shimchecker.Type) []nestiaCoreFormDataFile {
|
|
1039
|
+
collection := schemametadata.NewMetadataCollection()
|
|
1040
|
+
result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
|
|
1041
|
+
Checker: prog.Checker,
|
|
1042
|
+
Options: nativefactories.MetadataFactory_IOptions{
|
|
1043
|
+
Escape: false,
|
|
1044
|
+
Constant: true,
|
|
1045
|
+
Absorb: true,
|
|
1046
|
+
Validate: nativehttp.HttpFormDataProgrammer.Validate,
|
|
1047
|
+
},
|
|
1048
|
+
Components: collection,
|
|
1049
|
+
Type: typ,
|
|
1050
|
+
})
|
|
1051
|
+
if result.Success == false {
|
|
1052
|
+
panic(fmt.Errorf("failed to analyze form-data metadata: %d error(s)", len(result.Errors)))
|
|
1053
|
+
}
|
|
1054
|
+
files := []nestiaCoreFormDataFile{}
|
|
1055
|
+
if result.Data == nil || len(result.Data.Objects) == 0 || result.Data.Objects[0] == nil || result.Data.Objects[0].Type == nil {
|
|
1056
|
+
return files
|
|
1057
|
+
}
|
|
1058
|
+
for _, property := range result.Data.Objects[0].Type.Properties {
|
|
1059
|
+
if property == nil || property.Value == nil {
|
|
1060
|
+
continue
|
|
1061
|
+
}
|
|
1062
|
+
direct := nestiaCoreMetadataHasFile(property.Value)
|
|
1063
|
+
array := nestiaCoreMetadataArrayHasFile(property.Value)
|
|
1064
|
+
if direct == false && array == false {
|
|
1065
|
+
continue
|
|
1066
|
+
}
|
|
1067
|
+
name, ok := nestiaCorePropertyStringKey(property)
|
|
1068
|
+
if ok == false {
|
|
1069
|
+
continue
|
|
1070
|
+
}
|
|
1071
|
+
var limit *int
|
|
1072
|
+
if direct {
|
|
1073
|
+
one := 1
|
|
1074
|
+
limit = &one
|
|
1075
|
+
}
|
|
1076
|
+
files = append(files, nestiaCoreFormDataFile{
|
|
1077
|
+
Name: name,
|
|
1078
|
+
Limit: limit,
|
|
1079
|
+
})
|
|
1080
|
+
}
|
|
1081
|
+
return files
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
func nestiaCoreMetadataHasFile(metadata *schemametadata.MetadataSchema) bool {
|
|
1085
|
+
if metadata == nil {
|
|
1086
|
+
return false
|
|
1087
|
+
}
|
|
1088
|
+
for _, native := range metadata.Natives {
|
|
1089
|
+
if native != nil && (native.Name == "File" || native.Name == "Blob") {
|
|
1090
|
+
return true
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return false
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
func nestiaCoreMetadataArrayHasFile(metadata *schemametadata.MetadataSchema) bool {
|
|
1097
|
+
if metadata == nil {
|
|
1098
|
+
return false
|
|
1099
|
+
}
|
|
1100
|
+
for _, array := range metadata.Arrays {
|
|
1101
|
+
if array == nil || array.Type == nil || array.Type.Value == nil {
|
|
1102
|
+
continue
|
|
1103
|
+
}
|
|
1104
|
+
if nestiaCoreMetadataHasFile(array.Type.Value) {
|
|
1105
|
+
return true
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
return false
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
func nestiaCorePropertyStringKey(property *schemametadata.MetadataProperty) (string, bool) {
|
|
1112
|
+
if property == nil || property.Key == nil {
|
|
1113
|
+
return "", false
|
|
1114
|
+
}
|
|
1115
|
+
for _, constant := range property.Key.Constants {
|
|
1116
|
+
if constant == nil || constant.Type != "string" {
|
|
1117
|
+
continue
|
|
1118
|
+
}
|
|
1119
|
+
for _, value := range constant.Values {
|
|
1120
|
+
if value == nil {
|
|
1121
|
+
continue
|
|
1122
|
+
}
|
|
1123
|
+
name, ok := value.Value.(string)
|
|
1124
|
+
if ok {
|
|
1125
|
+
return name, true
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
return "", false
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
func nestiaCoreFormDataFilesExpression(files []nestiaCoreFormDataFile, ec *shimprinter.EmitContext) *shimast.Node {
|
|
1133
|
+
f := nativecontext.EmitFactoryOf(nestiaCoreFactory, ec)
|
|
1134
|
+
elements := make([]*shimast.Node, 0, len(files))
|
|
1135
|
+
for _, file := range files {
|
|
1136
|
+
limit := f.NewKeywordExpression(shimast.KindNullKeyword)
|
|
1137
|
+
if file.Limit != nil {
|
|
1138
|
+
limit = nativefactories.LiteralFactory.Write(*file.Limit)
|
|
1139
|
+
}
|
|
1140
|
+
elements = append(elements, f.NewObjectLiteralExpression(f.NewNodeList([]*shimast.Node{
|
|
1141
|
+
nestiaCoreProperty("name", nativefactories.LiteralFactory.Write(file.Name), ec),
|
|
1142
|
+
nestiaCoreProperty("limit", limit, ec),
|
|
1143
|
+
}), true))
|
|
1144
|
+
}
|
|
1145
|
+
return f.NewArrayLiteralExpression(f.NewNodeList(elements), true)
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
func nestiaCoreGeneratePlainBody(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
1149
|
+
nestiaCoreValidatePlainBody(prog, typ)
|
|
1150
|
+
return nativeprogrammers.AssertProgrammer.Write(nativeprogrammers.AssertProgrammer_IProps{
|
|
1151
|
+
Context: nestiaCoreTypiaContext(prog, importer, ec, false, false, false),
|
|
1152
|
+
Modulo: modulo,
|
|
1153
|
+
Type: typ,
|
|
1154
|
+
Name: nestiaCoreTypeName(prog, typ),
|
|
1155
|
+
Config: nativeprogrammers.AssertProgrammer_IConfig{Equals: false, Guard: false},
|
|
1156
|
+
})
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
func nestiaCoreGenerateTypedRoute(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, options nestiaCoreOptions, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
1160
|
+
nestiaCoreValidateTypedRoute(prog, options, typ)
|
|
1161
|
+
if options.StringifyNull {
|
|
1162
|
+
return nestiaCoreFactory.NewKeywordExpression(shimast.KindNullKeyword)
|
|
1163
|
+
}
|
|
1164
|
+
context := nestiaCoreTypiaContext(prog, importer, ec, false, false, false)
|
|
1165
|
+
name := nestiaCoreTypeName(prog, typ)
|
|
1166
|
+
switch options.Stringify {
|
|
1167
|
+
case "is":
|
|
1168
|
+
return nestiaCoreValidatorObject("type", "is", nativejson.JsonIsStringifyProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
1169
|
+
case "validate":
|
|
1170
|
+
return nestiaCoreValidatorObject("type", "validate", nativejson.JsonValidateStringifyProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
1171
|
+
case "stringify":
|
|
1172
|
+
return nestiaCoreValidatorObject("type", "stringify", nativejson.JsonStringifyProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
1173
|
+
case "validate.log":
|
|
1174
|
+
return nestiaCoreValidatorObjectWithKey("type", "validate.log", "validate", nativejson.JsonValidateStringifyProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
1175
|
+
default:
|
|
1176
|
+
return nestiaCoreValidatorObject("type", "assert", nativejson.JsonAssertStringifyProgrammer.Write(nativecontext.IProgrammerProps{Context: context, Modulo: modulo, Type: typ, Name: name}), ec)
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
func nestiaCoreGenerateTypedQueryRoute(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, options nestiaCoreOptions, modulo *shimast.Node, typ *shimchecker.Type) *shimast.Node {
|
|
1181
|
+
nestiaCoreValidateTypedQueryRoute(prog, options, typ)
|
|
1182
|
+
if options.StringifyNull {
|
|
1183
|
+
return nestiaCoreFactory.NewKeywordExpression(shimast.KindNullKeyword)
|
|
1184
|
+
}
|
|
1185
|
+
switch options.Stringify {
|
|
1186
|
+
case "is":
|
|
1187
|
+
return nestiaCoreValidatorObject("type", "is", nestiaCoreHttpIsQuerifyProgrammer(prog, importer, ec, modulo, typ), ec)
|
|
1188
|
+
case "validate":
|
|
1189
|
+
return nestiaCoreValidatorObject("type", "validate", nestiaCoreHttpValidateQuerifyProgrammer(prog, importer, ec, modulo, typ), ec)
|
|
1190
|
+
case "stringify":
|
|
1191
|
+
return nestiaCoreValidatorObject("type", "stringify", nestiaCoreHttpQuerifyProgrammer(prog, ec, typ), ec)
|
|
1192
|
+
default:
|
|
1193
|
+
return nestiaCoreValidatorObject("type", "assert", nestiaCoreHttpAssertQuerifyProgrammer(prog, importer, ec, modulo, typ), ec)
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// nestiaCoreTypiaContext builds the typia transform context for a single
|
|
1198
|
+
// validator generation. The importer argument is the file-scoped ImportProgrammer:
|
|
1199
|
+
// on the AST-integration emit path it is the shared, ec-mode importer (so every
|
|
1200
|
+
// generated validator references namespace imports tsgo's module-transform
|
|
1201
|
+
// aliases, and all injected imports collapse into one ToStatements() set). When
|
|
1202
|
+
// importer is nil a throwaway importer is allocated, preserving the legacy
|
|
1203
|
+
// text-splice behavior used by the `transform` / `check` source paths.
|
|
1204
|
+
func nestiaCoreTypiaContext(prog *driver.Program, importer *nativecontext.ImportProgrammer, ec *shimprinter.EmitContext, numeric bool, finite bool, functional bool) nativecontext.ITypiaContext {
|
|
1205
|
+
if importer == nil {
|
|
1206
|
+
importer = nativecontext.NewImportProgrammer(nativecontext.ImportProgrammer_IOptions{
|
|
1207
|
+
InternalPrefix: "typia_transform_",
|
|
1208
|
+
Runtime: "typia",
|
|
1209
|
+
})
|
|
1210
|
+
}
|
|
1211
|
+
return nativecontext.ITypiaContext{
|
|
1212
|
+
Program: prog,
|
|
1213
|
+
CompilerOptions: prog.ParsedConfig.ParsedConfig.CompilerOptions,
|
|
1214
|
+
Checker: prog.Checker,
|
|
1215
|
+
Options: nativecontext.ITransformOptions{
|
|
1216
|
+
Numeric: &numeric,
|
|
1217
|
+
Finite: &finite,
|
|
1218
|
+
Functional: &functional,
|
|
1219
|
+
Runtime: "typia",
|
|
1220
|
+
},
|
|
1221
|
+
Importer: importer,
|
|
1222
|
+
// Seed the emit context so typia's per-programmer factories
|
|
1223
|
+
// (EmitFactoryOf(..., Context.Emit)) build emit-tracked nodes; without it
|
|
1224
|
+
// the generated validator/stringifier nodes have no original link and
|
|
1225
|
+
// tsgo's MarkLinkedReferences pass nil-panics during emit. nil on the
|
|
1226
|
+
// legacy text path.
|
|
1227
|
+
Emit: ec,
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
func nestiaCoreStrictMode(prog *driver.Program) bool {
|
|
1232
|
+
if prog == nil || prog.ParsedConfig == nil || prog.ParsedConfig.ParsedConfig == nil || prog.ParsedConfig.ParsedConfig.CompilerOptions == nil {
|
|
1233
|
+
return true
|
|
1234
|
+
}
|
|
1235
|
+
options := prog.ParsedConfig.ParsedConfig.CompilerOptions
|
|
1236
|
+
return options.GetStrictOptionValue(options.StrictNullChecks)
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
func nestiaCoreLlmConfig(options nestiaCoreOptions) map[string]any {
|
|
1240
|
+
return map[string]any{"strict": options.LlmStrict}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
func nestiaCoreValidateTypedBody(prog *driver.Program, options nestiaCoreOptions, typ *shimchecker.Type) {
|
|
1244
|
+
var validate nativefactories.MetadataFactory_Validator
|
|
1245
|
+
if options.Llm {
|
|
1246
|
+
validate = func(next struct {
|
|
1247
|
+
Metadata *schemametadata.MetadataSchema
|
|
1248
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1249
|
+
Top *schemametadata.MetadataSchema
|
|
1250
|
+
}) []string {
|
|
1251
|
+
return nativellm.LlmSchemaProgrammer.Validate(struct {
|
|
1252
|
+
Config map[string]any
|
|
1253
|
+
Metadata *schemametadata.MetadataSchema
|
|
1254
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1255
|
+
}{
|
|
1256
|
+
Config: nestiaCoreLlmConfig(options),
|
|
1257
|
+
Metadata: next.Metadata,
|
|
1258
|
+
Explore: next.Explore,
|
|
1259
|
+
})
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
nativefactories.JsonMetadataFactory.Analyze(nativefactories.JsonMetadataFactory_IProps{
|
|
1263
|
+
Method: "@nestia.core.TypedBody",
|
|
1264
|
+
Checker: prog.Checker,
|
|
1265
|
+
Type: typ,
|
|
1266
|
+
Validate: validate,
|
|
1267
|
+
})
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
func nestiaCoreValidateTypedRoute(prog *driver.Program, options nestiaCoreOptions, typ *shimchecker.Type) {
|
|
1271
|
+
if options.Llm == false {
|
|
1272
|
+
return
|
|
1273
|
+
}
|
|
1274
|
+
nativefactories.JsonMetadataFactory.Analyze(nativefactories.JsonMetadataFactory_IProps{
|
|
1275
|
+
Method: "@nestia.core.TypedRoute",
|
|
1276
|
+
Checker: prog.Checker,
|
|
1277
|
+
Type: typ,
|
|
1278
|
+
Validate: func(next struct {
|
|
1279
|
+
Metadata *schemametadata.MetadataSchema
|
|
1280
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1281
|
+
Top *schemametadata.MetadataSchema
|
|
1282
|
+
}) []string {
|
|
1283
|
+
if next.Metadata == nil || next.Metadata.Size() == 0 {
|
|
1284
|
+
return nil
|
|
1285
|
+
}
|
|
1286
|
+
return nativellm.LlmParametersProgrammer.Validate(struct {
|
|
1287
|
+
Config map[string]any
|
|
1288
|
+
Metadata *schemametadata.MetadataSchema
|
|
1289
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1290
|
+
}{
|
|
1291
|
+
Config: nestiaCoreLlmConfig(options),
|
|
1292
|
+
Metadata: next.Metadata,
|
|
1293
|
+
Explore: next.Explore,
|
|
1294
|
+
})
|
|
1295
|
+
},
|
|
1296
|
+
})
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
func nestiaCoreValidateTypedQuery(prog *driver.Program, options nestiaCoreOptions, typ *shimchecker.Type, allowOptional bool, code string) {
|
|
1300
|
+
if options.Llm == false {
|
|
1301
|
+
return
|
|
1302
|
+
}
|
|
1303
|
+
collection := schemametadata.NewMetadataCollection()
|
|
1304
|
+
result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
|
|
1305
|
+
Checker: prog.Checker,
|
|
1306
|
+
Options: nativefactories.MetadataFactory_IOptions{
|
|
1307
|
+
Escape: false,
|
|
1308
|
+
Constant: true,
|
|
1309
|
+
Absorb: true,
|
|
1310
|
+
Validate: func(next struct {
|
|
1311
|
+
Metadata *schemametadata.MetadataSchema
|
|
1312
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1313
|
+
Top *schemametadata.MetadataSchema
|
|
1314
|
+
}) []string {
|
|
1315
|
+
errors := nativehttp.HttpQueryProgrammer.Validate(struct {
|
|
1316
|
+
Metadata *schemametadata.MetadataSchema
|
|
1317
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1318
|
+
Top *schemametadata.MetadataSchema
|
|
1319
|
+
AllowOptional bool
|
|
1320
|
+
}{
|
|
1321
|
+
Metadata: next.Metadata,
|
|
1322
|
+
Explore: next.Explore,
|
|
1323
|
+
Top: next.Top,
|
|
1324
|
+
AllowOptional: allowOptional,
|
|
1325
|
+
})
|
|
1326
|
+
errors = append(errors, nativellm.LlmSchemaProgrammer.Validate(struct {
|
|
1327
|
+
Config map[string]any
|
|
1328
|
+
Metadata *schemametadata.MetadataSchema
|
|
1329
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1330
|
+
}{
|
|
1331
|
+
Config: nestiaCoreLlmConfig(options),
|
|
1332
|
+
Metadata: next.Metadata,
|
|
1333
|
+
Explore: next.Explore,
|
|
1334
|
+
})...)
|
|
1335
|
+
return errors
|
|
1336
|
+
},
|
|
1337
|
+
},
|
|
1338
|
+
Components: collection,
|
|
1339
|
+
Type: typ,
|
|
1340
|
+
})
|
|
1341
|
+
if result.Success == false {
|
|
1342
|
+
panic(nativecontext.TransformerError_from(struct {
|
|
1343
|
+
Code string
|
|
1344
|
+
Errors []nativecontext.TransformerError_MetadataFactory_IError
|
|
1345
|
+
}{
|
|
1346
|
+
Code: code,
|
|
1347
|
+
Errors: nestiaCoreMetadataErrors(result.Errors),
|
|
1348
|
+
}))
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
func nestiaCoreValidateTypedQueryRoute(prog *driver.Program, options nestiaCoreOptions, typ *shimchecker.Type) {
|
|
1353
|
+
if options.Llm == false {
|
|
1354
|
+
return
|
|
1355
|
+
}
|
|
1356
|
+
collection := schemametadata.NewMetadataCollection()
|
|
1357
|
+
result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
|
|
1358
|
+
Checker: prog.Checker,
|
|
1359
|
+
Options: nativefactories.MetadataFactory_IOptions{
|
|
1360
|
+
Escape: false,
|
|
1361
|
+
Constant: true,
|
|
1362
|
+
Absorb: true,
|
|
1363
|
+
Validate: func(next struct {
|
|
1364
|
+
Metadata *schemametadata.MetadataSchema
|
|
1365
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1366
|
+
Top *schemametadata.MetadataSchema
|
|
1367
|
+
}) []string {
|
|
1368
|
+
errors := nativehttp.HttpQueryProgrammer.Validate(struct {
|
|
1369
|
+
Metadata *schemametadata.MetadataSchema
|
|
1370
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1371
|
+
Top *schemametadata.MetadataSchema
|
|
1372
|
+
AllowOptional bool
|
|
1373
|
+
}{
|
|
1374
|
+
Metadata: next.Metadata,
|
|
1375
|
+
Explore: next.Explore,
|
|
1376
|
+
Top: next.Top,
|
|
1377
|
+
AllowOptional: true,
|
|
1378
|
+
})
|
|
1379
|
+
if next.Metadata != nil && next.Metadata.Size() != 0 {
|
|
1380
|
+
errors = append(errors, nativellm.LlmParametersProgrammer.Validate(struct {
|
|
1381
|
+
Config map[string]any
|
|
1382
|
+
Metadata *schemametadata.MetadataSchema
|
|
1383
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1384
|
+
}{
|
|
1385
|
+
Config: nestiaCoreLlmConfig(options),
|
|
1386
|
+
Metadata: next.Metadata,
|
|
1387
|
+
Explore: next.Explore,
|
|
1388
|
+
})...)
|
|
1389
|
+
}
|
|
1390
|
+
return errors
|
|
1391
|
+
},
|
|
1392
|
+
},
|
|
1393
|
+
Components: collection,
|
|
1394
|
+
Type: typ,
|
|
1395
|
+
})
|
|
1396
|
+
if result.Success == false {
|
|
1397
|
+
panic(nativecontext.TransformerError_from(struct {
|
|
1398
|
+
Code string
|
|
1399
|
+
Errors []nativecontext.TransformerError_MetadataFactory_IError
|
|
1400
|
+
}{
|
|
1401
|
+
Code: "@nestia.core.TypedQueryRoute",
|
|
1402
|
+
Errors: nestiaCoreMetadataErrors(result.Errors),
|
|
1403
|
+
}))
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
func nestiaCoreValidatePlainBody(prog *driver.Program, typ *shimchecker.Type) {
|
|
1408
|
+
collection := schemametadata.NewMetadataCollection()
|
|
1409
|
+
result := nativefactories.MetadataFactory.Analyze(nativefactories.MetadataFactory_IProps{
|
|
1410
|
+
Checker: prog.Checker,
|
|
1411
|
+
Options: nativefactories.MetadataFactory_IOptions{
|
|
1412
|
+
Escape: false,
|
|
1413
|
+
Constant: true,
|
|
1414
|
+
Absorb: true,
|
|
1415
|
+
Validate: func(next struct {
|
|
1416
|
+
Metadata *schemametadata.MetadataSchema
|
|
1417
|
+
Explore nativefactories.MetadataFactory_IExplore
|
|
1418
|
+
Top *schemametadata.MetadataSchema
|
|
1419
|
+
}) []string {
|
|
1420
|
+
return nestiaCoreValidatePlainBodyMetadata(next.Metadata)
|
|
1421
|
+
},
|
|
1422
|
+
},
|
|
1423
|
+
Components: collection,
|
|
1424
|
+
Type: typ,
|
|
1425
|
+
})
|
|
1426
|
+
if result.Success == false {
|
|
1427
|
+
panic(nativecontext.TransformerError_from(struct {
|
|
1428
|
+
Code string
|
|
1429
|
+
Errors []nativecontext.TransformerError_MetadataFactory_IError
|
|
1430
|
+
}{
|
|
1431
|
+
Code: "nestia.core.PlainBody",
|
|
1432
|
+
Errors: nestiaCoreMetadataErrors(result.Errors),
|
|
1433
|
+
}))
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
func nestiaCoreValidatePlainBodyMetadata(metadata *schemametadata.MetadataSchema) []string {
|
|
1438
|
+
if metadata == nil {
|
|
1439
|
+
return nil
|
|
1440
|
+
}
|
|
1441
|
+
errors := []string{}
|
|
1442
|
+
expected := 0
|
|
1443
|
+
for _, atomic := range metadata.Atomics {
|
|
1444
|
+
if atomic != nil && atomic.Type == "string" {
|
|
1445
|
+
expected = 1
|
|
1446
|
+
break
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
expected += len(metadata.Templates)
|
|
1450
|
+
for _, constant := range metadata.Constants {
|
|
1451
|
+
if constant != nil && constant.Type == "string" {
|
|
1452
|
+
expected += len(constant.Values)
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
if expected == 0 || expected != metadata.Size() {
|
|
1456
|
+
errors = append(errors, "only string type is allowed")
|
|
1457
|
+
}
|
|
1458
|
+
if metadata.Nullable {
|
|
1459
|
+
errors = append(errors, "do not allow nullable type")
|
|
1460
|
+
} else if metadata.Any {
|
|
1461
|
+
errors = append(errors, "do not allow any type")
|
|
1462
|
+
}
|
|
1463
|
+
return errors
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
func nestiaCoreMetadataErrors(errors []nativefactories.MetadataFactory_IError) []nativecontext.TransformerError_MetadataFactory_IError {
|
|
1467
|
+
output := make([]nativecontext.TransformerError_MetadataFactory_IError, 0, len(errors))
|
|
1468
|
+
for _, err := range errors {
|
|
1469
|
+
output = append(output, nativecontext.TransformerError_MetadataFactory_IError{
|
|
1470
|
+
Name: err.Name,
|
|
1471
|
+
Explore: nativecontext.TransformerError_MetadataFactory_IExplore{
|
|
1472
|
+
Object: err.Explore.Object,
|
|
1473
|
+
Property: err.Explore.Property,
|
|
1474
|
+
Parameter: err.Explore.Parameter,
|
|
1475
|
+
Output: err.Explore.Output,
|
|
1476
|
+
},
|
|
1477
|
+
Messages: err.Messages,
|
|
1478
|
+
})
|
|
1479
|
+
}
|
|
1480
|
+
return output
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
func nestiaCoreValidatorObject(typeKey string, key string, validator *shimast.Node, ec *shimprinter.EmitContext) *shimast.Node {
|
|
1484
|
+
return nestiaCoreValidatorObjectWithKey(typeKey, key, key, validator, ec)
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
func nestiaCoreValidatorObjectWithKey(typeKey string, typeValue string, validatorKey string, validator *shimast.Node, ec *shimprinter.EmitContext) *shimast.Node {
|
|
1488
|
+
f := nativecontext.EmitFactoryOf(nestiaCoreFactory, ec)
|
|
1489
|
+
return f.NewObjectLiteralExpression(f.NewNodeList([]*shimast.Node{
|
|
1490
|
+
nestiaCoreProperty(typeKey, f.NewStringLiteral(typeValue, shimast.TokenFlagsNone), ec),
|
|
1491
|
+
nestiaCoreProperty(validatorKey, validator, ec),
|
|
1492
|
+
}), true)
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
func nestiaCoreProperty(name string, initializer *shimast.Node, ec *shimprinter.EmitContext) *shimast.Node {
|
|
1496
|
+
f := nativecontext.EmitFactoryOf(nestiaCoreFactory, ec)
|
|
1497
|
+
return f.NewPropertyAssignment(
|
|
1498
|
+
nil,
|
|
1499
|
+
nativefactories.IdentifierFactory.Identifier(name),
|
|
1500
|
+
nil,
|
|
1501
|
+
nil,
|
|
1502
|
+
initializer,
|
|
1503
|
+
)
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
// safeNestiaCoreGenerateNode runs a validator generator, recovering any panic
|
|
1507
|
+
// (a typia programmer raises one for user-facing transform errors) into an
|
|
1508
|
+
// error so the caller can surface a diagnostic instead of crashing the emit.
|
|
1509
|
+
// It is the node-emit twin of safeNestiaCoreGenerate, which additionally prints
|
|
1510
|
+
// the node to text for the legacy splice path.
|
|
1511
|
+
func safeNestiaCoreGenerateNode(generator func() (*shimast.Node, error)) (node *shimast.Node, err error) {
|
|
1512
|
+
defer func() {
|
|
1513
|
+
if exp := recover(); exp != nil {
|
|
1514
|
+
if os.Getenv("NESTIA_NATIVE_DEBUG_STACK") != "" {
|
|
1515
|
+
err = fmt.Errorf("%v\n%s", exp, debug.Stack())
|
|
1516
|
+
} else {
|
|
1517
|
+
err = fmt.Errorf("%v", exp)
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}()
|
|
1521
|
+
return generator()
|
|
1522
|
+
}
|
|
1523
|
+
|
|
1524
|
+
func safeNestiaCoreGenerate(
|
|
1525
|
+
generator func() (*shimast.Node, error),
|
|
1526
|
+
prog *driver.Program,
|
|
1527
|
+
file *shimast.SourceFile,
|
|
1528
|
+
preserveTypes bool,
|
|
1529
|
+
) (text string, err error) {
|
|
1530
|
+
defer func() {
|
|
1531
|
+
if exp := recover(); exp != nil {
|
|
1532
|
+
if os.Getenv("NESTIA_NATIVE_DEBUG_STACK") != "" {
|
|
1533
|
+
err = fmt.Errorf("%v\n%s", exp, debug.Stack())
|
|
1534
|
+
} else {
|
|
1535
|
+
err = fmt.Errorf("%v", exp)
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
}()
|
|
1539
|
+
node, err := generator()
|
|
1540
|
+
if err != nil {
|
|
1541
|
+
return "", err
|
|
1542
|
+
}
|
|
1543
|
+
return emitNestiaCoreExpression(prog, file, node, preserveTypes), nil
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
func emitNestiaCoreExpression(prog *driver.Program, file *shimast.SourceFile, node *shimast.Node, preserveTypes bool) string {
|
|
1547
|
+
var text string
|
|
1548
|
+
if preserveTypes {
|
|
1549
|
+
text = emitNestiaPreservingTypesWithIdentifierSubstitutions(node, file, nil)
|
|
1550
|
+
} else {
|
|
1551
|
+
text = emitNestiaWithIdentifierSubstitutions(
|
|
1552
|
+
node,
|
|
1553
|
+
file,
|
|
1554
|
+
identifierSubstitutionsForEmit(prog, file),
|
|
1555
|
+
)
|
|
1556
|
+
}
|
|
1557
|
+
return cleanupNestiaCorePrintedExpression(text)
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
func cleanupNestiaCorePrintedExpression(text string) string {
|
|
1561
|
+
text = strings.TrimSpace(text)
|
|
1562
|
+
text = strings.TrimSuffix(text, ";")
|
|
1563
|
+
text = nestiaCoreSingleParameterArrowPattern.ReplaceAllString(text, `${1}(${2}) =>`)
|
|
1564
|
+
if strings.HasPrefix(text, "(") && strings.HasSuffix(text, ")") {
|
|
1565
|
+
return text
|
|
1566
|
+
}
|
|
1567
|
+
if strings.Contains(text, "=>") || strings.Contains(text, "function") {
|
|
1568
|
+
return "(" + text + ")"
|
|
1569
|
+
}
|
|
1570
|
+
return text
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
var nestiaCoreSingleParameterArrowPattern = regexp.MustCompile(`(^|[\s(=,:?])([A-Za-z_$][A-Za-z0-9_$]*) =>`)
|
|
1574
|
+
|
|
1575
|
+
func NestiaCoreMethodReturnType(prog *driver.Program, node *shimast.Node) *shimchecker.Type {
|
|
1576
|
+
if typ := nestiaCoreExplicitAsyncReturnType(prog, node); typ != nil {
|
|
1577
|
+
return typ
|
|
1578
|
+
}
|
|
1579
|
+
signature := prog.Checker.GetSignatureFromDeclaration(node)
|
|
1580
|
+
if signature == nil {
|
|
1581
|
+
return nil
|
|
1582
|
+
}
|
|
1583
|
+
typ := prog.Checker.GetReturnTypeOfSignature(signature)
|
|
1584
|
+
if typ == nil {
|
|
1585
|
+
return nil
|
|
1586
|
+
}
|
|
1587
|
+
symbol := typ.Symbol()
|
|
1588
|
+
if symbol != nil &&
|
|
1589
|
+
nestiaCoreIsAsyncReturnWrapperSymbol(symbol.Name, symbol.Declarations) {
|
|
1590
|
+
args := prog.Checker.GetTypeArguments(typ)
|
|
1591
|
+
if len(args) == 1 {
|
|
1592
|
+
return args[0]
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
return typ
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
func nestiaCoreExplicitAsyncReturnType(prog *driver.Program, node *shimast.Node) *shimchecker.Type {
|
|
1599
|
+
if prog == nil || prog.Checker == nil || node == nil || node.FunctionLikeData() == nil {
|
|
1600
|
+
return nil
|
|
1601
|
+
}
|
|
1602
|
+
typeNode := node.FunctionLikeData().Type
|
|
1603
|
+
if typeNode == nil || typeNode.Kind != shimast.KindTypeReference {
|
|
1604
|
+
return nil
|
|
1605
|
+
}
|
|
1606
|
+
ref := typeNode.AsTypeReferenceNode()
|
|
1607
|
+
if ref == nil || ref.TypeArguments == nil || len(ref.TypeArguments.Nodes) != 1 {
|
|
1608
|
+
return nil
|
|
1609
|
+
}
|
|
1610
|
+
if nestiaCoreIsAsyncReturnWrapperReference(prog, ref.TypeName) == false {
|
|
1611
|
+
return nil
|
|
1612
|
+
}
|
|
1613
|
+
return prog.Checker.GetTypeFromTypeNode(ref.TypeArguments.Nodes[0])
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
func nestiaCoreIsAsyncReturnWrapperReference(
|
|
1617
|
+
prog *driver.Program,
|
|
1618
|
+
node *shimast.Node,
|
|
1619
|
+
) bool {
|
|
1620
|
+
name := nestiaCoreTypeNodeText(node)
|
|
1621
|
+
if name == "Promise" {
|
|
1622
|
+
return true
|
|
1623
|
+
}
|
|
1624
|
+
if name != "Observable" || prog == nil || prog.Checker == nil {
|
|
1625
|
+
return false
|
|
1626
|
+
}
|
|
1627
|
+
symbol := prog.Checker.GetSymbolAtLocation(node)
|
|
1628
|
+
return nestiaCoreIsRxjsObservableImport(node) ||
|
|
1629
|
+
(symbol != nil && nestiaCoreIsRxjsDeclarations(symbol.Declarations))
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
func nestiaCoreIsAsyncReturnWrapperSymbol(
|
|
1633
|
+
name string,
|
|
1634
|
+
declarations []*shimast.Node,
|
|
1635
|
+
) bool {
|
|
1636
|
+
if name == "Promise" {
|
|
1637
|
+
return true
|
|
1638
|
+
}
|
|
1639
|
+
return name == "Observable" && nestiaCoreIsRxjsDeclarations(declarations)
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
func nestiaCoreIsRxjsDeclarations(declarations []*shimast.Node) bool {
|
|
1643
|
+
for _, decl := range declarations {
|
|
1644
|
+
sourceFile := shimast.GetSourceFileOfNode(decl)
|
|
1645
|
+
if sourceFile == nil {
|
|
1646
|
+
continue
|
|
1647
|
+
}
|
|
1648
|
+
file := filepath.ToSlash(sourceFile.FileName())
|
|
1649
|
+
if strings.Contains(file, "/node_modules/rxjs/") {
|
|
1650
|
+
return true
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
return false
|
|
1654
|
+
}
|
|
1655
|
+
|
|
1656
|
+
func nestiaCoreIsRxjsObservableImport(node *shimast.Node) bool {
|
|
1657
|
+
source, ok := SourceFileText(shimast.GetSourceFileOfNode(node))
|
|
1658
|
+
return ok && nestiaCoreHasNamedImport(source, "rxjs", "Observable", "Observable")
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
func nestiaCoreHasNamedImport(
|
|
1662
|
+
source string,
|
|
1663
|
+
module string,
|
|
1664
|
+
imported string,
|
|
1665
|
+
local string,
|
|
1666
|
+
) bool {
|
|
1667
|
+
for _, match := range nestiaCoreImportFromPattern.FindAllStringSubmatch(source, -1) {
|
|
1668
|
+
if len(match) < 3 || match[2] != module {
|
|
1669
|
+
continue
|
|
1670
|
+
}
|
|
1671
|
+
open := strings.Index(match[1], "{")
|
|
1672
|
+
close := strings.LastIndex(match[1], "}")
|
|
1673
|
+
if open < 0 || close <= open {
|
|
1674
|
+
continue
|
|
1675
|
+
}
|
|
1676
|
+
for _, part := range strings.Split(match[1][open+1:close], ",") {
|
|
1677
|
+
fields := strings.Fields(strings.TrimPrefix(strings.TrimSpace(part), "type "))
|
|
1678
|
+
if len(fields) == 1 && fields[0] == local && imported == local {
|
|
1679
|
+
return true
|
|
1680
|
+
}
|
|
1681
|
+
if len(fields) == 3 &&
|
|
1682
|
+
fields[0] == imported &&
|
|
1683
|
+
fields[1] == "as" &&
|
|
1684
|
+
fields[2] == local {
|
|
1685
|
+
return true
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
return false
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
var nestiaCoreImportFromPattern = regexp.MustCompile(
|
|
1693
|
+
`(?s)import\s+(?:type\s+)?(.+?)\s+from\s+["']([^"']+)["']`,
|
|
1694
|
+
)
|
|
1695
|
+
|
|
1696
|
+
func nestiaCoreTypeNodeText(node *shimast.Node) string {
|
|
1697
|
+
if node == nil {
|
|
1698
|
+
return ""
|
|
1699
|
+
}
|
|
1700
|
+
source, ok := SourceFileText(shimast.GetSourceFileOfNode(node))
|
|
1701
|
+
if ok == false {
|
|
1702
|
+
return ""
|
|
1703
|
+
}
|
|
1704
|
+
start, end := node.Pos(), node.End()
|
|
1705
|
+
if start < 0 || end > len(source) || start >= end {
|
|
1706
|
+
return ""
|
|
1707
|
+
}
|
|
1708
|
+
return strings.TrimSpace(source[start:end])
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
func nestiaCoreShouldSkipMethodDecorator(prog *driver.Program, call *shimast.CallExpression) bool {
|
|
1712
|
+
count := nestiaCoreArgumentCount(call)
|
|
1713
|
+
if count >= 2 {
|
|
1714
|
+
return true
|
|
1715
|
+
}
|
|
1716
|
+
if count == 1 {
|
|
1717
|
+
last := call.Arguments.Nodes[0]
|
|
1718
|
+
if last.Kind == shimast.KindObjectLiteralExpression {
|
|
1719
|
+
return true
|
|
1720
|
+
}
|
|
1721
|
+
if nestiaCoreHasPathLiteralArgument(call) {
|
|
1722
|
+
return false
|
|
1723
|
+
}
|
|
1724
|
+
typ := prog.Checker.GetTypeAtLocation(last)
|
|
1725
|
+
if typ != nil && typ.Flags()&shimchecker.TypeFlagsObject != 0 &&
|
|
1726
|
+
shimchecker.IsTupleType(typ) == false &&
|
|
1727
|
+
shimchecker.Checker_isArrayType(prog.Checker, typ) == false {
|
|
1728
|
+
return true
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
return false
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
func nestiaCoreHasPathLiteralArgument(call *shimast.CallExpression) bool {
|
|
1735
|
+
source, ok := SourceFileText(shimast.GetSourceFileOfNode(call.AsNode()))
|
|
1736
|
+
if !ok {
|
|
1737
|
+
return false
|
|
1738
|
+
}
|
|
1739
|
+
open, close, ok := callArgumentBounds(source, call)
|
|
1740
|
+
if !ok {
|
|
1741
|
+
return false
|
|
1742
|
+
}
|
|
1743
|
+
text := strings.TrimSpace(source[open+1 : close])
|
|
1744
|
+
return strings.HasPrefix(text, `"`) ||
|
|
1745
|
+
strings.HasPrefix(text, `'`) ||
|
|
1746
|
+
strings.HasPrefix(text, "`") ||
|
|
1747
|
+
strings.HasPrefix(text, "[")
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
func nestiaCoreArgumentCount(call *shimast.CallExpression) int {
|
|
1751
|
+
if call == nil || call.Arguments == nil {
|
|
1752
|
+
return 0
|
|
1753
|
+
}
|
|
1754
|
+
return len(call.Arguments.Nodes)
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
func callArgumentBounds(source string, call *shimast.CallExpression) (int, int, bool) {
|
|
1758
|
+
if call == nil || call.AsNode() == nil || call.Expression == nil {
|
|
1759
|
+
return 0, 0, false
|
|
1760
|
+
}
|
|
1761
|
+
start := call.Expression.End()
|
|
1762
|
+
end := call.AsNode().End()
|
|
1763
|
+
if start < 0 || end > len(source) || start >= end {
|
|
1764
|
+
start = call.AsNode().Pos()
|
|
1765
|
+
}
|
|
1766
|
+
open := strings.IndexByte(source[start:end], '(')
|
|
1767
|
+
if open < 0 {
|
|
1768
|
+
return 0, 0, false
|
|
1769
|
+
}
|
|
1770
|
+
open += start
|
|
1771
|
+
close, ok := matchNativeParen(source, open)
|
|
1772
|
+
return open, close, ok
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
func appendArgumentsText(current string, arguments []string) string {
|
|
1776
|
+
current = strings.TrimSpace(current)
|
|
1777
|
+
next := strings.Join(arguments, ", ")
|
|
1778
|
+
if current == "" {
|
|
1779
|
+
return next
|
|
1780
|
+
}
|
|
1781
|
+
return current + ", " + next
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
func NestiaCoreExpressionSegments(node *shimast.Node) []string {
|
|
1785
|
+
if node == nil {
|
|
1786
|
+
return nil
|
|
1787
|
+
}
|
|
1788
|
+
if node.Kind == shimast.KindIdentifier {
|
|
1789
|
+
if id := node.AsIdentifier(); id != nil {
|
|
1790
|
+
return []string{id.Text}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
if node.Kind == shimast.KindPropertyAccessExpression {
|
|
1794
|
+
access := node.AsPropertyAccessExpression()
|
|
1795
|
+
if access == nil {
|
|
1796
|
+
return nil
|
|
1797
|
+
}
|
|
1798
|
+
left := NestiaCoreExpressionSegments(access.Expression)
|
|
1799
|
+
name := access.Name()
|
|
1800
|
+
if len(left) == 0 || name == nil || name.Kind != shimast.KindIdentifier {
|
|
1801
|
+
return nil
|
|
1802
|
+
}
|
|
1803
|
+
return append(left, name.AsIdentifier().Text)
|
|
1804
|
+
}
|
|
1805
|
+
return nil
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
func nestiaCoreModuloNode(node *shimast.Node) *shimast.Node {
|
|
1809
|
+
segments := NestiaCoreExpressionSegments(node)
|
|
1810
|
+
if len(segments) == 0 {
|
|
1811
|
+
return nestiaCoreFactory.NewIdentifier("nestia_core_transform")
|
|
1812
|
+
}
|
|
1813
|
+
return nestiaCoreFactory.NewIdentifier(strings.Join(segments, "_"))
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
func nestiaCoreTypeName(prog *driver.Program, typ *shimchecker.Type) *string {
|
|
1817
|
+
name := nestiaCoreTypeNameText(prog, typ)
|
|
1818
|
+
return &name
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
type nestiaCoreTypeNameCacheKey struct {
|
|
1822
|
+
checker *shimchecker.Checker
|
|
1823
|
+
typ *shimchecker.Type
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
var nestiaCoreTypeNameCache sync.Map
|
|
1827
|
+
|
|
1828
|
+
func nestiaCoreTypeNameText(prog *driver.Program, typ *shimchecker.Type) string {
|
|
1829
|
+
if prog != nil && prog.Checker != nil && typ != nil {
|
|
1830
|
+
key := nestiaCoreTypeNameCacheKey{checker: prog.Checker, typ: typ}
|
|
1831
|
+
if cached, ok := nestiaCoreTypeNameCache.Load(key); ok {
|
|
1832
|
+
return cached.(string)
|
|
1833
|
+
}
|
|
1834
|
+
name := prog.Checker.TypeToString(typ)
|
|
1835
|
+
nestiaCoreTypeNameCache.Store(key, name)
|
|
1836
|
+
return name
|
|
1837
|
+
}
|
|
1838
|
+
return "any"
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
func nestiaCoreSegmentsHaveSuffix(segments []string, suffix []string) bool {
|
|
1842
|
+
if len(suffix) > len(segments) {
|
|
1843
|
+
return false
|
|
1844
|
+
}
|
|
1845
|
+
offset := len(segments) - len(suffix)
|
|
1846
|
+
for i, part := range suffix {
|
|
1847
|
+
if segments[offset+i] != part {
|
|
1848
|
+
return false
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
return true
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
func nestiaCoreTargetCandidates(prog *driver.Program, site nestiaCoreSite) []string {
|
|
1855
|
+
if len(site.Segments) == 0 {
|
|
1856
|
+
return nil
|
|
1857
|
+
}
|
|
1858
|
+
candidates := []string{strings.Join(site.Segments, ".")}
|
|
1859
|
+
substitutions := identifierSubstitutionsForEmit(prog, site.File)
|
|
1860
|
+
if substitutions != nil {
|
|
1861
|
+
if mapped, ok := substitutions[site.Segments[0]]; ok {
|
|
1862
|
+
parts := append([]string{mapped}, site.Segments[1:]...)
|
|
1863
|
+
candidates = append(candidates, strings.Join(parts, "."))
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
sort.SliceStable(candidates, func(i, j int) bool {
|
|
1867
|
+
return len(candidates[i]) > len(candidates[j])
|
|
1868
|
+
})
|
|
1869
|
+
return candidates
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
func identifierSubstitutionsForEmit(program *driver.Program, file any) map[string]string {
|
|
1873
|
+
if program == nil {
|
|
1874
|
+
return nil
|
|
1875
|
+
}
|
|
1876
|
+
sourceFile, ok := file.(*shimast.SourceFile)
|
|
1877
|
+
if ok == false {
|
|
1878
|
+
return nil
|
|
1879
|
+
}
|
|
1880
|
+
return commonJSImportIdentifierSubstitutions(sourceFile)
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
type commonJSImportIdentifierSubstitutionsCacheEntry struct {
|
|
1884
|
+
value map[string]string
|
|
1885
|
+
}
|
|
1886
|
+
|
|
1887
|
+
var commonJSImportIdentifierSubstitutionsCache sync.Map
|
|
1888
|
+
|
|
1889
|
+
func commonJSImportIdentifierSubstitutions(file *shimast.SourceFile) map[string]string {
|
|
1890
|
+
if file == nil || file.Statements == nil {
|
|
1891
|
+
return nil
|
|
1892
|
+
}
|
|
1893
|
+
if cached, ok := commonJSImportIdentifierSubstitutionsCache.Load(file); ok {
|
|
1894
|
+
return cached.(commonJSImportIdentifierSubstitutionsCacheEntry).value
|
|
1895
|
+
}
|
|
1896
|
+
output := map[string]string{}
|
|
1897
|
+
counts := map[string]int{}
|
|
1898
|
+
for _, stmt := range file.Statements.Nodes {
|
|
1899
|
+
if stmt == nil || stmt.Kind != shimast.KindImportDeclaration {
|
|
1900
|
+
continue
|
|
1901
|
+
}
|
|
1902
|
+
decl := stmt.AsImportDeclaration()
|
|
1903
|
+
if decl == nil || decl.ImportClause == nil || decl.ModuleSpecifier == nil || decl.ModuleSpecifier.Kind != shimast.KindStringLiteral {
|
|
1904
|
+
continue
|
|
1905
|
+
}
|
|
1906
|
+
clause := decl.ImportClause.AsImportClause()
|
|
1907
|
+
if clause == nil || clause.PhaseModifier == shimast.KindTypeKeyword {
|
|
1908
|
+
continue
|
|
1909
|
+
}
|
|
1910
|
+
base := commonJSImportAliasBase(decl.ModuleSpecifier.Text())
|
|
1911
|
+
counts[base]++
|
|
1912
|
+
moduleAlias := base + "_" + strconv.Itoa(counts[base])
|
|
1913
|
+
if name := clause.Name(); name != nil {
|
|
1914
|
+
output[name.Text()] = moduleAlias + ".default"
|
|
1915
|
+
}
|
|
1916
|
+
if clause.NamedBindings == nil || clause.NamedBindings.Kind != shimast.KindNamedImports {
|
|
1917
|
+
continue
|
|
1918
|
+
}
|
|
1919
|
+
named := clause.NamedBindings.AsNamedImports()
|
|
1920
|
+
if named == nil || named.Elements == nil {
|
|
1921
|
+
continue
|
|
1922
|
+
}
|
|
1923
|
+
for _, elem := range named.Elements.Nodes {
|
|
1924
|
+
if elem == nil {
|
|
1925
|
+
continue
|
|
1926
|
+
}
|
|
1927
|
+
spec := elem.AsImportSpecifier()
|
|
1928
|
+
if spec == nil || spec.IsTypeOnly {
|
|
1929
|
+
continue
|
|
1930
|
+
}
|
|
1931
|
+
name := spec.Name()
|
|
1932
|
+
if name == nil {
|
|
1933
|
+
continue
|
|
1934
|
+
}
|
|
1935
|
+
local := name.Text()
|
|
1936
|
+
imported := local
|
|
1937
|
+
if spec.PropertyName != nil {
|
|
1938
|
+
imported = spec.PropertyName.Text()
|
|
1939
|
+
}
|
|
1940
|
+
output[local] = moduleAlias + "." + imported
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
if len(output) == 0 {
|
|
1944
|
+
output = nil
|
|
1945
|
+
}
|
|
1946
|
+
commonJSImportIdentifierSubstitutionsCache.Store(file, commonJSImportIdentifierSubstitutionsCacheEntry{value: output})
|
|
1947
|
+
return output
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
func commonJSImportAliasBase(module string) string {
|
|
1951
|
+
base := strings.TrimSuffix(filepath.Base(module), filepath.Ext(module))
|
|
1952
|
+
if base == "" || base == "." || base == string(filepath.Separator) {
|
|
1953
|
+
base = "mod"
|
|
1954
|
+
}
|
|
1955
|
+
var builder strings.Builder
|
|
1956
|
+
for _, r := range base {
|
|
1957
|
+
if r == '_' || r == '$' || unicode.IsLetter(r) || unicode.IsDigit(r) {
|
|
1958
|
+
builder.WriteRune(r)
|
|
1959
|
+
} else {
|
|
1960
|
+
builder.WriteByte('_')
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
text := builder.String()
|
|
1964
|
+
if text == "" {
|
|
1965
|
+
text = "mod"
|
|
1966
|
+
}
|
|
1967
|
+
first := []rune(text)[0]
|
|
1968
|
+
if first != '_' && first != '$' && !unicode.IsLetter(first) {
|
|
1969
|
+
text = "_" + text
|
|
1970
|
+
}
|
|
1971
|
+
return text
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
func nestiaCoreDiagnostic(site nestiaCoreSite, message string) Diagnostic {
|
|
1975
|
+
line, column := 0, 0
|
|
1976
|
+
if site.File != nil && site.Call != nil {
|
|
1977
|
+
if pos := site.Call.AsNode().Pos(); pos >= 0 {
|
|
1978
|
+
l, c := shimscanner.GetECMALineAndByteOffsetOfPosition(site.File, pos)
|
|
1979
|
+
line, column = l+1, c+1
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return Diagnostic{
|
|
1983
|
+
File: site.FilePath,
|
|
1984
|
+
Line: line,
|
|
1985
|
+
Column: column,
|
|
1986
|
+
Code: "nestia.core." + site.Kind,
|
|
1987
|
+
Message: message,
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
func nestiaCoreGlobalDiagnostic(code string, message string) Diagnostic {
|
|
1992
|
+
return Diagnostic{
|
|
1993
|
+
Code: code,
|
|
1994
|
+
Message: message,
|
|
1995
|
+
}
|
|
1996
|
+
}
|