ajsc 5.0.3 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,924 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { TypescriptConverter } from "./TypescriptConverter.js";
3
+ describe("TypescriptConverter - unions and intersections with shared ancestor type merging", () => {
4
+ it("should merge enum unions with same name under shared ancestor", () => {
5
+ expect(new TypescriptConverter({
6
+ type: "object",
7
+ properties: {
8
+ timeLog: {
9
+ type: "array",
10
+ items: {
11
+ anyOf: [
12
+ {
13
+ type: "object",
14
+ properties: {
15
+ action: {
16
+ type: "string",
17
+ enum: ["startshift", "endshift"],
18
+ },
19
+ },
20
+ },
21
+ {
22
+ type: "object",
23
+ properties: {
24
+ action: {
25
+ type: "string",
26
+ enum: ["startbreak", "endbreak"],
27
+ },
28
+ break: {
29
+ type: "object",
30
+ properties: {
31
+ durationMin: {
32
+ type: "number",
33
+ },
34
+ mandatory: {
35
+ type: "boolean",
36
+ },
37
+ paid: {
38
+ type: "boolean",
39
+ },
40
+ startsAfterShiftHrs: {
41
+ type: "number",
42
+ },
43
+ type: {
44
+ type: "string",
45
+ },
46
+ },
47
+ },
48
+ },
49
+ },
50
+ ],
51
+ },
52
+ },
53
+ },
54
+ }, { enumStyle: "enum" }).code).toMatch(`export enum Action { Startshift = "startshift", Endshift = "endshift", Startbreak = "startbreak", Endbreak = "endbreak" }`);
55
+ });
56
+ it("should merge type unions with shared ancestor", () => {
57
+ expect(new TypescriptConverter({
58
+ type: "object",
59
+ properties: {
60
+ status: {
61
+ anyOf: [
62
+ {
63
+ type: "object",
64
+ properties: {
65
+ color: {
66
+ type: "string",
67
+ },
68
+ timeframe: {
69
+ type: "string",
70
+ enum: ["past"],
71
+ },
72
+ title: {
73
+ type: "string",
74
+ enum: ["completed", "missed"],
75
+ },
76
+ },
77
+ required: ["timeframe", "title"],
78
+ },
79
+ {
80
+ type: "object",
81
+ properties: {
82
+ color: {
83
+ type: "string",
84
+ },
85
+ timeframe: {
86
+ type: "string",
87
+ enum: ["current"],
88
+ },
89
+ title: {
90
+ type: "string",
91
+ enum: [
92
+ "ended",
93
+ "inprogress",
94
+ "notended",
95
+ "notstarted",
96
+ "onbreak",
97
+ ],
98
+ },
99
+ },
100
+ required: ["timeframe", "title"],
101
+ },
102
+ {
103
+ type: "object",
104
+ properties: {
105
+ color: {
106
+ type: "string",
107
+ },
108
+ timeframe: {
109
+ type: "string",
110
+ enum: ["future"],
111
+ },
112
+ },
113
+ required: ["timeframe"],
114
+ },
115
+ ],
116
+ },
117
+ },
118
+ }, { enumStyle: "enum" }).code).toContain(`export enum Timeframe { Past = "past", Current = "current", Future = "future" }\nexport enum Title { Completed = "completed", Missed = "missed", Ended = "ended", Inprogress = "inprogress", Notended = "notended", Notstarted = "notstarted", Onbreak = "onbreak" }\nexport type Status = { color?: string; timeframe: Timeframe; title?: Title; };\n\nexport type Root = { status?: Status; };\n`);
119
+ });
120
+ it("should merge enum + literal properties across union options", () => {
121
+ expect(new TypescriptConverter({
122
+ type: "object",
123
+ properties: {
124
+ item: {
125
+ anyOf: [
126
+ {
127
+ type: "object",
128
+ properties: {
129
+ kind: { const: "book" },
130
+ shared: { type: "string" },
131
+ },
132
+ },
133
+ {
134
+ type: "object",
135
+ properties: {
136
+ kind: {
137
+ type: "string",
138
+ enum: ["dvd", "cd"],
139
+ },
140
+ shared: { type: "string" },
141
+ },
142
+ },
143
+ ],
144
+ },
145
+ },
146
+ }, { enumStyle: "enum" }).code).toMatch(`export enum Kind { Book = "book", Dvd = "dvd", Cd = "cd" }`);
147
+ });
148
+ it("should preserve union when options have incompatible property types", () => {
149
+ const code = new TypescriptConverter({
150
+ type: "object",
151
+ properties: {
152
+ field: {
153
+ anyOf: [
154
+ {
155
+ type: "object",
156
+ properties: {
157
+ value: { type: "string" },
158
+ name: { type: "string" },
159
+ },
160
+ },
161
+ {
162
+ type: "object",
163
+ properties: {
164
+ value: { type: "number" },
165
+ name: { type: "string" },
166
+ },
167
+ },
168
+ ],
169
+ },
170
+ },
171
+ }, { enumStyle: "enum" }).code;
172
+ // Should remain as a union because "value" has incompatible types
173
+ expect(code).toMatch(/Field \| FieldType/);
174
+ });
175
+ it("should preserve discriminated unions as separate types", () => {
176
+ const code = new TypescriptConverter({
177
+ type: "object",
178
+ properties: {
179
+ shape: {
180
+ anyOf: [
181
+ {
182
+ type: "object",
183
+ properties: {
184
+ kind: { const: "circle" },
185
+ radius: { type: "number" },
186
+ },
187
+ },
188
+ {
189
+ type: "object",
190
+ properties: {
191
+ kind: { const: "square" },
192
+ side: { type: "number" },
193
+ },
194
+ },
195
+ ],
196
+ },
197
+ },
198
+ }, { enumStyle: "enum" }).code;
199
+ // Discriminated union: "kind" has distinct const per option with
200
+ // option-specific properties — should NOT be merged, but enhanced with
201
+ // discriminator-derived names and consolidated enum
202
+ expect(code).toContain('export enum Kind { Circle = "circle", Square = "square" }');
203
+ expect(code).toMatch(/CircleShape \| SquareShape/);
204
+ expect(code).toContain('kind?: Kind.Circle');
205
+ expect(code).toContain('kind?: Kind.Square');
206
+ });
207
+ it("should merge with union-style enums (default enumStyle)", () => {
208
+ const code = new TypescriptConverter({
209
+ type: "object",
210
+ properties: {
211
+ event: {
212
+ anyOf: [
213
+ {
214
+ type: "object",
215
+ properties: {
216
+ type: {
217
+ type: "string",
218
+ enum: ["click", "hover"],
219
+ },
220
+ x: { type: "number" },
221
+ },
222
+ },
223
+ {
224
+ type: "object",
225
+ properties: {
226
+ type: {
227
+ type: "string",
228
+ enum: ["scroll", "resize"],
229
+ },
230
+ x: { type: "number" },
231
+ },
232
+ },
233
+ ],
234
+ },
235
+ },
236
+ }).code;
237
+ // With default enumStyle ("union"), merged enums appear as string literal unions
238
+ expect(code).toMatch(`"click" | "hover" | "scroll" | "resize"`);
239
+ });
240
+ it("should handle nested anyOf merging (bottom-up)", () => {
241
+ const code = new TypescriptConverter({
242
+ type: "object",
243
+ properties: {
244
+ outer: {
245
+ type: "array",
246
+ items: {
247
+ anyOf: [
248
+ {
249
+ type: "object",
250
+ properties: {
251
+ tag: {
252
+ type: "string",
253
+ enum: ["a"],
254
+ },
255
+ inner: {
256
+ anyOf: [
257
+ {
258
+ type: "object",
259
+ properties: {
260
+ level: {
261
+ type: "string",
262
+ enum: ["low"],
263
+ },
264
+ },
265
+ },
266
+ {
267
+ type: "object",
268
+ properties: {
269
+ level: {
270
+ type: "string",
271
+ enum: ["high"],
272
+ },
273
+ },
274
+ },
275
+ ],
276
+ },
277
+ },
278
+ },
279
+ {
280
+ type: "object",
281
+ properties: {
282
+ tag: {
283
+ type: "string",
284
+ enum: ["b"],
285
+ },
286
+ },
287
+ },
288
+ ],
289
+ },
290
+ },
291
+ },
292
+ }, { enumStyle: "enum" }).code;
293
+ // Inner anyOf should be merged first (bottom-up), producing a merged Level enum
294
+ expect(code).toMatch(`export enum Level { Low = "low", High = "high" }`);
295
+ // Outer anyOf should also be merged (tag is shared)
296
+ expect(code).toMatch(`export enum Tag { A = "a", B = "b" }`);
297
+ });
298
+ it('should handle unions of an advanced type', () => {
299
+ const code = new TypescriptConverter({
300
+ type: "object",
301
+ properties: {
302
+ activity: {
303
+ type: "array",
304
+ items: {
305
+ type: "object",
306
+ properties: {
307
+ activityTypeTags: {
308
+ type: "array",
309
+ items: {
310
+ type: "object",
311
+ properties: {
312
+ color: {
313
+ type: "string",
314
+ },
315
+ id: {
316
+ type: "string",
317
+ },
318
+ level: {
319
+ type: "number",
320
+ description: "The level is an indicator for visual UI and also a sudo rating of the importance of a Tag.\n This value is also used in business logic that correlates a PrioritySchema value.\n ",
321
+ maximum: 3,
322
+ },
323
+ organizationId: {
324
+ type: "string",
325
+ },
326
+ relations: {
327
+ type: "array",
328
+ items: {
329
+ type: "string",
330
+ },
331
+ },
332
+ regionIds: {
333
+ type: "array",
334
+ items: {
335
+ type: "string",
336
+ },
337
+ },
338
+ title: {
339
+ type: "string",
340
+ },
341
+ },
342
+ required: ["title"],
343
+ additionalProperties: false,
344
+ },
345
+ },
346
+ detail: {
347
+ anyOf: [
348
+ {
349
+ type: "object",
350
+ properties: {
351
+ scope: {
352
+ type: "string",
353
+ const: "bolo",
354
+ },
355
+ boloId: {
356
+ type: "string",
357
+ },
358
+ },
359
+ required: ["scope", "boloId"],
360
+ additionalProperties: false,
361
+ },
362
+ {
363
+ type: "object",
364
+ properties: {
365
+ scope: {
366
+ type: "string",
367
+ const: "user-geo-fence",
368
+ },
369
+ userId: {
370
+ type: "string",
371
+ },
372
+ type: {
373
+ type: "string",
374
+ enum: ["arrive", "depart"],
375
+ },
376
+ },
377
+ required: ["scope", "userId", "type"],
378
+ additionalProperties: false,
379
+ },
380
+ {
381
+ type: "object",
382
+ properties: {
383
+ scope: {
384
+ type: "string",
385
+ const: "user-status",
386
+ },
387
+ userId: {
388
+ type: "string",
389
+ },
390
+ fromStatus: {
391
+ type: "object",
392
+ properties: {
393
+ autoClearStaleUserMin: {
394
+ type: "number",
395
+ },
396
+ color: {
397
+ type: "string",
398
+ },
399
+ expireDate: {
400
+ type: "string",
401
+ },
402
+ endDate: {
403
+ type: "string",
404
+ },
405
+ geoTracking: {
406
+ type: "boolean",
407
+ },
408
+ id: {
409
+ type: "string",
410
+ },
411
+ priority: {
412
+ type: "number",
413
+ },
414
+ setAtDate: {
415
+ type: "string",
416
+ },
417
+ shiftId: {
418
+ type: "string",
419
+ },
420
+ statusReminderInterval: {
421
+ type: "number",
422
+ },
423
+ title: {
424
+ type: "string",
425
+ },
426
+ },
427
+ required: ["setAtDate", "title"],
428
+ additionalProperties: false,
429
+ },
430
+ toStatus: {
431
+ type: "object",
432
+ properties: {
433
+ autoClearStaleUserMin: {
434
+ type: "number",
435
+ },
436
+ color: {
437
+ type: "string",
438
+ },
439
+ expireDate: {
440
+ type: "string",
441
+ },
442
+ endDate: {
443
+ type: "string",
444
+ },
445
+ geoTracking: {
446
+ type: "boolean",
447
+ },
448
+ id: {
449
+ type: "string",
450
+ },
451
+ priority: {
452
+ type: "number",
453
+ },
454
+ setAtDate: {
455
+ type: "string",
456
+ },
457
+ shiftId: {
458
+ type: "string",
459
+ },
460
+ statusReminderInterval: {
461
+ type: "number",
462
+ },
463
+ title: {
464
+ type: "string",
465
+ },
466
+ },
467
+ required: ["setAtDate", "title"],
468
+ additionalProperties: false,
469
+ },
470
+ },
471
+ required: ["scope", "userId"],
472
+ additionalProperties: false,
473
+ },
474
+ {
475
+ type: "object",
476
+ properties: {
477
+ scope: {
478
+ type: "string",
479
+ const: "passdown",
480
+ },
481
+ passDownId: {
482
+ type: "string",
483
+ },
484
+ },
485
+ required: ["scope", "passDownId"],
486
+ additionalProperties: false,
487
+ },
488
+ {
489
+ type: "object",
490
+ properties: {
491
+ scope: {
492
+ type: "string",
493
+ const: "event",
494
+ },
495
+ eventId: {
496
+ type: "string",
497
+ },
498
+ },
499
+ required: ["scope", "eventId"],
500
+ additionalProperties: false,
501
+ },
502
+ {
503
+ type: "object",
504
+ properties: {
505
+ scope: {
506
+ type: "string",
507
+ const: "event-assignment-user-status",
508
+ },
509
+ eventId: {
510
+ type: "string",
511
+ },
512
+ fromStatus: {
513
+ type: "object",
514
+ properties: {
515
+ abbreviation: {
516
+ type: "string",
517
+ },
518
+ color: {
519
+ type: "string",
520
+ },
521
+ setAtDate: {
522
+ type: "string",
523
+ },
524
+ title: {
525
+ type: "string",
526
+ },
527
+ },
528
+ required: ["setAtDate", "title"],
529
+ additionalProperties: false,
530
+ },
531
+ toStatus: {
532
+ type: "object",
533
+ properties: {
534
+ abbreviation: {
535
+ type: "string",
536
+ },
537
+ color: {
538
+ type: "string",
539
+ },
540
+ setAtDate: {
541
+ type: "string",
542
+ },
543
+ title: {
544
+ type: "string",
545
+ },
546
+ },
547
+ required: ["setAtDate", "title"],
548
+ additionalProperties: false,
549
+ },
550
+ },
551
+ required: ["scope", "eventId"],
552
+ additionalProperties: false,
553
+ },
554
+ {
555
+ type: "object",
556
+ properties: {
557
+ scope: {
558
+ type: "string",
559
+ const: "location-checkpoint-scan",
560
+ },
561
+ checkpointScanId: {
562
+ type: "string",
563
+ },
564
+ checkpointTourRunId: {
565
+ type: "string",
566
+ },
567
+ },
568
+ required: ["scope", "checkpointScanId"],
569
+ additionalProperties: false,
570
+ },
571
+ {
572
+ type: "object",
573
+ properties: {
574
+ scope: {
575
+ type: "string",
576
+ const: "location-checkpoint-tour",
577
+ },
578
+ checkpointTourId: {
579
+ type: "string",
580
+ },
581
+ checkpointTourRunId: {
582
+ type: "string",
583
+ },
584
+ },
585
+ required: [
586
+ "scope",
587
+ "checkpointTourId",
588
+ "checkpointTourRunId",
589
+ ],
590
+ additionalProperties: false,
591
+ },
592
+ {
593
+ type: "object",
594
+ properties: {
595
+ scope: {
596
+ type: "string",
597
+ const: "location-log-entry",
598
+ },
599
+ entryId: {
600
+ type: "string",
601
+ },
602
+ },
603
+ required: ["scope", "entryId"],
604
+ additionalProperties: false,
605
+ },
606
+ {
607
+ type: "object",
608
+ properties: {
609
+ scope: {
610
+ type: "string",
611
+ const: "patrol-route",
612
+ },
613
+ patrolRouteId: {
614
+ type: "string",
615
+ },
616
+ patrolRouteRunId: {
617
+ type: "string",
618
+ },
619
+ },
620
+ required: ["scope", "patrolRouteId"],
621
+ additionalProperties: false,
622
+ },
623
+ {
624
+ type: "object",
625
+ properties: {
626
+ scope: {
627
+ type: "string",
628
+ const: "patrol-route-visit",
629
+ },
630
+ patrolRouteRunId: {
631
+ type: "string",
632
+ },
633
+ patrolRouteId: {
634
+ type: "string",
635
+ },
636
+ patrolRouteVisitId: {
637
+ type: "string",
638
+ },
639
+ },
640
+ required: [
641
+ "scope",
642
+ "patrolRouteRunId",
643
+ "patrolRouteId",
644
+ "patrolRouteVisitId",
645
+ ],
646
+ additionalProperties: false,
647
+ },
648
+ {
649
+ type: "object",
650
+ properties: {
651
+ scope: {
652
+ type: "string",
653
+ const: "report",
654
+ },
655
+ reportId: {
656
+ type: "string",
657
+ },
658
+ },
659
+ required: ["scope", "reportId"],
660
+ additionalProperties: false,
661
+ },
662
+ {
663
+ type: "object",
664
+ properties: {
665
+ scope: {
666
+ type: "string",
667
+ const: "report-status",
668
+ },
669
+ reportId: {
670
+ type: "string",
671
+ },
672
+ },
673
+ required: ["scope", "reportId"],
674
+ additionalProperties: false,
675
+ },
676
+ {
677
+ type: "object",
678
+ properties: {
679
+ scope: {
680
+ type: "string",
681
+ const: "shift",
682
+ },
683
+ shiftId: {
684
+ type: "string",
685
+ },
686
+ },
687
+ required: ["scope", "shiftId"],
688
+ additionalProperties: false,
689
+ },
690
+ {
691
+ type: "object",
692
+ properties: {
693
+ scope: {
694
+ type: "string",
695
+ const: "user-sos",
696
+ },
697
+ userId: {
698
+ type: "string",
699
+ },
700
+ },
701
+ required: ["scope", "userId"],
702
+ additionalProperties: false,
703
+ },
704
+ ],
705
+ },
706
+ description: {
707
+ type: "string",
708
+ },
709
+ locationId: {
710
+ anyOf: [
711
+ {
712
+ type: "string",
713
+ },
714
+ {
715
+ type: "array",
716
+ items: {
717
+ type: "string",
718
+ },
719
+ },
720
+ ],
721
+ },
722
+ organizationId: {
723
+ type: "string",
724
+ },
725
+ regionIds: {
726
+ type: "array",
727
+ items: {
728
+ type: "string",
729
+ },
730
+ },
731
+ subTitle: {
732
+ type: "string",
733
+ },
734
+ timestamp: {
735
+ type: "string",
736
+ },
737
+ title: {
738
+ type: "string",
739
+ },
740
+ user: {
741
+ type: "object",
742
+ properties: {
743
+ avatarUrl: {
744
+ type: "string",
745
+ },
746
+ displayName: {
747
+ type: "string",
748
+ },
749
+ id: {
750
+ type: "string",
751
+ },
752
+ idNumber: {
753
+ type: "string",
754
+ },
755
+ lastGeoLocation: {
756
+ type: "object",
757
+ properties: {
758
+ accuracy: {
759
+ type: "number",
760
+ },
761
+ coords: {
762
+ type: "object",
763
+ properties: {
764
+ lat: {
765
+ type: "number",
766
+ },
767
+ lng: {
768
+ type: "number",
769
+ },
770
+ },
771
+ required: ["lat", "lng"],
772
+ additionalProperties: false,
773
+ },
774
+ device: {
775
+ type: "object",
776
+ properties: {
777
+ name: {
778
+ type: "string",
779
+ },
780
+ os: {
781
+ type: "string",
782
+ },
783
+ permissions: {
784
+ type: "object",
785
+ properties: {
786
+ backgroundSync: {
787
+ type: "boolean",
788
+ },
789
+ bluetooth: {
790
+ type: "boolean",
791
+ },
792
+ camera: {
793
+ type: "boolean",
794
+ },
795
+ geolocation: {
796
+ type: "boolean",
797
+ },
798
+ backgroundGeolocation: {
799
+ type: "boolean",
800
+ },
801
+ microphone: {
802
+ type: "boolean",
803
+ },
804
+ nfc: {
805
+ type: "boolean",
806
+ },
807
+ notifications: {
808
+ type: "boolean",
809
+ },
810
+ physicalActivity: {
811
+ type: "boolean",
812
+ },
813
+ },
814
+ additionalProperties: false,
815
+ },
816
+ type: {
817
+ description: "'browser', 'mobile', 'tablet', 'other', 'unknown'",
818
+ type: "string",
819
+ },
820
+ uuid: {
821
+ type: "string",
822
+ },
823
+ },
824
+ required: ["name", "type", "uuid"],
825
+ additionalProperties: false,
826
+ },
827
+ relativeLocation: {
828
+ type: "object",
829
+ properties: {
830
+ locationId: {
831
+ type: "string",
832
+ },
833
+ status: {
834
+ type: "string",
835
+ enum: ["inside", "near"],
836
+ },
837
+ },
838
+ required: ["locationId", "status"],
839
+ additionalProperties: false,
840
+ },
841
+ timestamp: {
842
+ type: "string",
843
+ },
844
+ },
845
+ required: ["accuracy", "coords", "device", "timestamp"],
846
+ additionalProperties: false,
847
+ },
848
+ status: {
849
+ type: "object",
850
+ properties: {
851
+ autoClearStaleUserMin: {
852
+ type: "number",
853
+ },
854
+ color: {
855
+ type: "string",
856
+ },
857
+ expireDate: {
858
+ type: "string",
859
+ },
860
+ endDate: {
861
+ type: "string",
862
+ },
863
+ geoTracking: {
864
+ type: "boolean",
865
+ },
866
+ id: {
867
+ type: "string",
868
+ },
869
+ priority: {
870
+ type: "number",
871
+ },
872
+ setAtDate: {
873
+ type: "string",
874
+ },
875
+ shiftId: {
876
+ type: "string",
877
+ },
878
+ statusReminderInterval: {
879
+ type: "number",
880
+ },
881
+ title: {
882
+ type: "string",
883
+ },
884
+ },
885
+ required: ["setAtDate", "title"],
886
+ additionalProperties: false,
887
+ },
888
+ },
889
+ required: ["id"],
890
+ additionalProperties: false,
891
+ },
892
+ },
893
+ required: ["detail", "regionIds", "timestamp", "title"],
894
+ additionalProperties: false,
895
+ },
896
+ },
897
+ },
898
+ required: ["activity"],
899
+ additionalProperties: false,
900
+ }, { enumStyle: 'enum' }).code;
901
+ // Single consolidated Scope enum with all 15 values
902
+ expect(code).toContain('export enum Scope { Bolo = "bolo", UserGeoFence = "user-geo-fence", UserStatus = "user-status", Passdown = "passdown", Event = "event", EventAssignmentUserStatus = "event-assignment-user-status", LocationCheckpointScan = "location-checkpoint-scan", LocationCheckpointTour = "location-checkpoint-tour", LocationLogEntry = "location-log-entry", PatrolRoute = "patrol-route", PatrolRouteVisit = "patrol-route-visit", Report = "report", ReportStatus = "report-status", Shift = "shift", UserSos = "user-sos" }');
903
+ // Discriminator-derived variant names
904
+ expect(code).toContain('BoloDetail');
905
+ expect(code).toContain('UserGeoFenceDetail');
906
+ expect(code).toContain('UserStatusDetail');
907
+ expect(code).toContain('EventAssignmentUserStatusDetail');
908
+ expect(code).toContain('PatrolRouteVisitDetail');
909
+ // Enum member references (not standalone single-value enums)
910
+ expect(code).toContain('Scope.Bolo');
911
+ expect(code).toContain('Scope.UserGeoFence');
912
+ expect(code).toContain('Scope.UserSos');
913
+ // No duplicate types: identical 11-property status structures share one type
914
+ // (fromStatus, toStatus, and user.status all reference the same type)
915
+ const fromStatusMatches = code.match(/export type FromStatus = /g);
916
+ expect(fromStatusMatches).toHaveLength(1);
917
+ // The 4-property status structure (event-assignment variants) also deduped
918
+ const detailFromStatusMatches = code.match(/export type DetailFromStatus = /g);
919
+ expect(detailFromStatusMatches).toHaveLength(1);
920
+ // No single-value scope enums (should be consolidated)
921
+ expect(code).not.toMatch(/export enum \w*Scope\d/);
922
+ });
923
+ });
924
+ //# sourceMappingURL=TypescriptConverter.composites.advanced.test.js.map