typia 5.1.3 → 5.1.4-dev.20230929

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/lib/executable/TypiaSetupWizard.js +1 -19
  2. package/lib/executable/TypiaSetupWizard.js.map +1 -1
  3. package/package.json +1 -1
  4. package/src/executable/TypiaSetupWizard.ts +1 -16
  5. package/src/factories/MetadataCollection.ts +277 -277
  6. package/src/factories/MetadataFactory.ts +238 -238
  7. package/src/factories/MetadataTypeTagFactory.ts +325 -325
  8. package/src/factories/internal/metadata/emend_metadata_atomics.ts +41 -41
  9. package/src/factories/internal/metadata/iterate_metadata_intersection.ts +259 -259
  10. package/src/functional/$HeadersReader.ts +28 -28
  11. package/src/functional/$ParameterReader.ts +31 -31
  12. package/src/functional/$QueryReader.ts +56 -56
  13. package/src/functional/Namespace.ts +142 -142
  14. package/src/http.ts +1149 -1149
  15. package/src/json.ts +648 -648
  16. package/src/misc.ts +651 -651
  17. package/src/module.ts +657 -657
  18. package/src/programmers/helpers/HttpMetadataUtil.ts +21 -21
  19. package/src/programmers/http/HttpAssertHeadersProgrammer.ts +77 -77
  20. package/src/programmers/http/HttpAssertQueryProgrammer.ts +77 -77
  21. package/src/programmers/http/HttpHeadersProgrammer.ts +339 -339
  22. package/src/programmers/http/HttpIsHeadersProgrammer.ts +87 -87
  23. package/src/programmers/http/HttpIsQueryProgrammer.ts +87 -87
  24. package/src/programmers/http/HttpParameterProgrammer.ts +104 -104
  25. package/src/programmers/http/HttpQueryProgrammer.ts +273 -273
  26. package/src/programmers/http/HttpValidateHeadersProgrammer.ts +77 -77
  27. package/src/programmers/http/HttpValidateQueryProgrammer.ts +77 -77
  28. package/src/programmers/internal/application_boolean.ts +30 -30
  29. package/src/programmers/internal/application_number.ts +90 -90
  30. package/src/programmers/internal/application_schema.ts +180 -180
  31. package/src/programmers/internal/application_string.ts +54 -54
  32. package/src/programmers/internal/check_array_length.ts +44 -44
  33. package/src/programmers/internal/check_bigint.ts +48 -48
  34. package/src/programmers/internal/check_number.ts +108 -108
  35. package/src/programmers/internal/check_string.ts +48 -48
  36. package/src/programmers/protobuf/ProtobufEncodeProgrammer.ts +882 -882
  37. package/src/protobuf.ts +887 -887
  38. package/src/schemas/json/IJsonComponents.ts +34 -34
  39. package/src/schemas/json/IJsonSchema.ts +112 -112
  40. package/src/schemas/metadata/IMetadataConstant.ts +25 -25
  41. package/src/schemas/metadata/IMetadataTypeTag.ts +8 -8
  42. package/src/schemas/metadata/Metadata.ts +686 -686
  43. package/src/tags/Default.ts +15 -15
  44. package/src/tags/Format.ts +30 -30
  45. package/src/tags/Pattern.ts +9 -9
  46. package/src/tags/TagBase.ts +68 -68
  47. package/src/tags/index.ts +14 -14
  48. package/src/transformers/CallExpressionTransformer.ts +289 -289
  49. package/src/transformers/features/http/CreateHttpAssertHeadersTransformer.ts +12 -12
  50. package/src/transformers/features/http/CreateHttpAssertQueryTransformer.ts +12 -12
  51. package/src/transformers/features/http/CreateHttpHeadersTransformer.ts +9 -9
  52. package/src/transformers/features/http/CreateHttpIsHeadersTransformer.ts +9 -9
  53. package/src/transformers/features/http/CreateHttpIsQueryTransformer.ts +9 -9
  54. package/src/transformers/features/http/CreateHttpParameterTransformer.ts +9 -9
  55. package/src/transformers/features/http/CreateHttpQueryTransformer.ts +9 -9
  56. package/src/transformers/features/http/CreateHttpValidateHeadersTransformer.ts +12 -12
  57. package/src/transformers/features/http/CreateHttpValidateQueryTransformer.ts +12 -12
  58. package/src/transformers/features/http/HttpAssertHeadersTransformer.ts +10 -10
  59. package/src/transformers/features/http/HttpAssertQueryTransformer.ts +10 -10
  60. package/src/transformers/features/http/HttpHeadersTransformer.ts +9 -9
  61. package/src/transformers/features/http/HttpIsHeadersTransformer.ts +9 -9
  62. package/src/transformers/features/http/HttpIsQueryTransformer.ts +9 -9
  63. package/src/transformers/features/http/HttpParameterTransformer.ts +9 -9
  64. package/src/transformers/features/http/HttpQueryTransformer.ts +9 -9
  65. package/src/transformers/features/http/HttpValidateHeadersTransformer.ts +10 -10
  66. package/src/transformers/features/http/HttpValidateQueryTransformer.ts +10 -10
@@ -1,686 +1,686 @@
1
- import { ClassProperties } from "../../typings/ClassProperties";
2
- import { Writable } from "../../typings/Writable";
3
-
4
- import { ArrayUtil } from "../../utils/ArrayUtil";
5
-
6
- import { IMetadata } from "./IMetadata";
7
- import { IMetadataCollection } from "./IMetadataCollection";
8
- import { IMetadataDictionary } from "./IMetadataDictionary";
9
- import { IMetadataTypeTag } from "./IMetadataTypeTag";
10
- import { MetadataAlias } from "./MetadataAlias";
11
- import { MetadataArray } from "./MetadataArray";
12
- import { MetadataArrayType } from "./MetadataArrayType";
13
- import { MetadataAtomic } from "./MetadataAtomic";
14
- import { MetadataConstant } from "./MetadataConstant";
15
- import { MetadataEscaped } from "./MetadataEscaped";
16
- import { MetadataObject } from "./MetadataObject";
17
- import { MetadataProperty } from "./MetadataProperty";
18
- import { MetadataTuple } from "./MetadataTuple";
19
- import { MetadataTupleType } from "./MetadataTupleType";
20
-
21
- export class Metadata {
22
- public any: boolean;
23
- public required: boolean;
24
- public optional: boolean;
25
- public nullable: boolean;
26
- public functional: boolean;
27
-
28
- public escaped: MetadataEscaped | null;
29
- public atomics: MetadataAtomic[];
30
- public constants: MetadataConstant[];
31
- public templates: Metadata[][];
32
-
33
- public rest: Metadata | null;
34
- public aliases: MetadataAlias[];
35
- public arrays: MetadataArray[];
36
- public tuples: MetadataTuple[];
37
- public objects: MetadataObject[];
38
-
39
- public natives: string[];
40
- public sets: Metadata[];
41
- public maps: Metadata.Entry[];
42
-
43
- /** @internal */ private name_?: string;
44
- /** @internal */ private parent_resolved_: boolean = false;
45
- /** @internal */ public union_index?: number;
46
- /** @internal */ public fixed_?: number | null;
47
- /** @internal */ public boolean_literal_intersected_?: boolean;
48
-
49
- /* -----------------------------------------------------------
50
- CONSTRUCTORS
51
- ----------------------------------------------------------- */
52
- /**
53
- * @hidden
54
- */
55
- private constructor(props: ClassProperties<Metadata>) {
56
- this.any = props.any;
57
- this.required = props.required;
58
- this.optional = props.optional;
59
- this.nullable = props.nullable;
60
- this.functional = props.functional;
61
-
62
- this.escaped = props.escaped;
63
- this.atomics = props.atomics;
64
- this.constants = props.constants;
65
- this.templates = props.templates;
66
-
67
- this.rest = props.rest;
68
- this.arrays = props.arrays;
69
- this.tuples = props.tuples;
70
- this.objects = props.objects;
71
- this.aliases = props.aliases;
72
-
73
- this.natives = props.natives;
74
- this.sets = props.sets;
75
- this.maps = props.maps;
76
- }
77
-
78
- /**
79
- * @internal
80
- */
81
- public static create(props: ClassProperties<Metadata>): Metadata {
82
- return new Metadata(props);
83
- }
84
-
85
- /**
86
- * @internal
87
- */
88
- public static initialize(parentResolved: boolean = false): Metadata {
89
- const meta: Metadata = this.create({
90
- any: false,
91
- nullable: false,
92
- required: true,
93
- optional: false,
94
- functional: false,
95
-
96
- escaped: null,
97
- constants: [],
98
- atomics: [],
99
- templates: [],
100
- arrays: [],
101
- tuples: [],
102
- objects: [],
103
- aliases: [],
104
-
105
- rest: null,
106
- natives: [],
107
- sets: [],
108
- maps: [],
109
- });
110
- meta.parent_resolved_ = parentResolved;
111
- return meta;
112
- }
113
-
114
- public toJSON(): IMetadata {
115
- return {
116
- any: this.any,
117
- required: this.required,
118
- optional: this.optional,
119
- nullable: this.nullable,
120
- functional: this.functional,
121
-
122
- atomics: this.atomics.map((a) =>
123
- MetadataAtomic.create({
124
- type: a.type,
125
- tags: a.tags.map((r) => r.slice()),
126
- }),
127
- ),
128
- constants: this.constants.map((c) => ({
129
- type: c.type,
130
- values: c.values.slice() as any,
131
- })),
132
- templates: this.templates.map((tpl) =>
133
- tpl.map((meta) => meta.toJSON()),
134
- ),
135
- escaped: this.escaped ? this.escaped.toJSON() : null,
136
-
137
- rest: this.rest ? this.rest.toJSON() : null,
138
- arrays: this.arrays.map((array) => ({
139
- name: array.type.name,
140
- tags: array.tags.map((r) => r.slice()),
141
- })),
142
- tuples: this.tuples.map((tuple) => ({
143
- name: tuple.type.name,
144
- tags: tuple.tags.map((r) => r.slice()),
145
- })),
146
- objects: this.objects.map((obj) => obj.name),
147
- aliases: this.aliases.map((alias) => alias.name),
148
-
149
- natives: this.natives.slice(),
150
- sets: this.sets.map((meta) => meta.toJSON()),
151
- maps: this.maps.map((entry) => ({
152
- key: entry.key.toJSON(),
153
- value: entry.value.toJSON(),
154
- })),
155
- };
156
- }
157
-
158
- public static from(
159
- meta: IMetadata,
160
- collection: IMetadataCollection,
161
- ): Metadata {
162
- const dict: IMetadataDictionary = {
163
- objects: new Map(
164
- collection.objects.map((obj) => [
165
- obj.name,
166
- MetadataObject._From_without_properties(obj),
167
- ]),
168
- ),
169
- aliases: new Map(
170
- collection.aliases.map((alias) => [
171
- alias.name,
172
- MetadataAlias._From_without_value(alias),
173
- ]),
174
- ),
175
- arrays: new Map(
176
- collection.arrays.map((arr) => [
177
- arr.name,
178
- MetadataArrayType._From_without_value(arr),
179
- ]),
180
- ),
181
- tuples: new Map(
182
- collection.tuples.map((tpl) => [
183
- tpl.name,
184
- MetadataTupleType._From_without_elements(tpl),
185
- ]),
186
- ),
187
- };
188
-
189
- for (const obj of collection.objects) {
190
- const initialized = dict.objects.get(obj.name)!;
191
- initialized.properties.push(
192
- ...obj.properties.map((prop) =>
193
- MetadataProperty._From(prop, dict),
194
- ),
195
- );
196
- }
197
- for (const alias of collection.aliases)
198
- Writable(dict.aliases.get(alias.name)!).value = this._From(
199
- alias.value,
200
- dict,
201
- );
202
- for (const array of collection.arrays)
203
- Writable(dict.arrays.get(array.name)!).value = this._From(
204
- array.value,
205
- dict,
206
- );
207
- for (const tuple of collection.tuples)
208
- Writable(dict.tuples.get(tuple.name)!).elements =
209
- tuple.elements.map((elem) => this._From(elem, dict));
210
-
211
- return this._From(meta, dict);
212
- }
213
-
214
- /**
215
- * @internal
216
- */
217
- public static _From(meta: IMetadata, dict: IMetadataDictionary): Metadata {
218
- return this.create({
219
- any: meta.any,
220
- required: meta.required,
221
- optional: meta.optional,
222
- nullable: meta.nullable,
223
- functional: meta.functional,
224
-
225
- constants: meta.constants.slice(),
226
- atomics: meta.atomics.map((a) =>
227
- MetadataAtomic.create({ type: a.type, tags: a.tags }),
228
- ),
229
- templates: meta.templates.map((tpl) =>
230
- tpl.map((meta) => this._From(meta, dict)),
231
- ),
232
- escaped: meta.escaped
233
- ? MetadataEscaped._From(meta.escaped, dict)
234
- : null,
235
-
236
- rest: meta.rest ? this._From(meta.rest, dict) : null,
237
- arrays: meta.arrays.map((ref) => {
238
- const type = dict.arrays.get(ref.name);
239
- if (type === undefined)
240
- throw new RangeError(
241
- `Error on Metadata.from(): failed to find array "${ref.name}".`,
242
- );
243
- return MetadataArray.create({
244
- type,
245
- tags: ref.tags.map((row) => row.slice()),
246
- });
247
- }),
248
- tuples: meta.tuples.map((t) => {
249
- const type = dict.tuples.get(t.name);
250
- if (type === undefined)
251
- throw new RangeError(
252
- `Error on Metadata.from(): failed to find tuple "${t.name}".`,
253
- );
254
- return MetadataTuple.create({
255
- type,
256
- tags: t.tags.map((r) => r.slice()),
257
- });
258
- }),
259
- objects: meta.objects.map((name) => {
260
- const found = dict.objects.get(name);
261
- if (found === undefined)
262
- throw new RangeError(
263
- `Error on Metadata.from(): failed to find object "${name}".`,
264
- );
265
- return found;
266
- }),
267
- aliases: meta.aliases.map((alias) => {
268
- const found = dict.aliases.get(alias);
269
- if (found === undefined)
270
- throw new RangeError(
271
- `Error on Metadata.from(): failed to find alias "${alias}".`,
272
- );
273
- return found;
274
- }),
275
-
276
- natives: meta.natives.slice(),
277
- sets: meta.sets.map((meta) => this._From(meta, dict)),
278
- maps: meta.maps.map((entry) => ({
279
- key: this._From(entry.key, dict),
280
- value: this._From(entry.value, dict),
281
- })),
282
- });
283
- }
284
-
285
- /* -----------------------------------------------------------
286
- ACCESSORS
287
- ----------------------------------------------------------- */
288
- public getName(): string {
289
- return (this.name_ ??= getName(this));
290
- }
291
-
292
- public empty(): boolean {
293
- return this.bucket() === 0 || this.size() === 0;
294
- }
295
-
296
- public size(): number {
297
- return (
298
- (this.any ? 1 : 0) +
299
- (this.escaped ? 1 : 0) +
300
- (this.functional ? 1 : 0) +
301
- (this.rest ? this.rest.size() : 0) +
302
- this.templates.length +
303
- this.atomics.length +
304
- this.constants
305
- .map((c) => c.values.length)
306
- .reduce((x, y) => x + y, 0) +
307
- this.arrays.length +
308
- this.tuples.length +
309
- this.natives.length +
310
- this.maps.length +
311
- this.sets.length +
312
- this.objects.length +
313
- this.aliases.length
314
- );
315
- }
316
-
317
- public bucket(): number {
318
- return (
319
- (this.any ? 1 : 0) +
320
- (this.escaped ? 1 : 0) +
321
- (this.functional ? 1 : 0) +
322
- (this.templates.length ? 1 : 0) +
323
- (this.atomics.length ? 1 : 0) +
324
- (this.constants.length ? 1 : 0) +
325
- (this.rest ? this.rest.size() : 0) +
326
- (this.arrays.length ? 1 : 0) +
327
- (this.tuples.length ? 1 : 0) +
328
- (this.natives.length ? 1 : 0) +
329
- (this.sets.length ? 1 : 0) +
330
- (this.maps.length ? 1 : 0) +
331
- (this.objects.length ? 1 : 0) +
332
- (this.aliases.length ? 1 : 0)
333
- );
334
- }
335
-
336
- public isConstant(): boolean {
337
- return this.bucket() === (this.constants.length ? 1 : 0);
338
- }
339
-
340
- public isRequired(): boolean {
341
- return this.required === true && this.optional === false;
342
- }
343
-
344
- /**
345
- * @internal
346
- */
347
- public isUnionBucket(): boolean {
348
- const size: number = this.bucket();
349
- const emended: number =
350
- !!this.atomics.length && !!this.constants.length ? size - 1 : size;
351
- return emended > 1;
352
- }
353
-
354
- /**
355
- * @internal
356
- */
357
- public getSoleLiteral(): string | null {
358
- if (
359
- this.size() === 1 &&
360
- this.constants.length === 1 &&
361
- this.constants[0]!.type === "string" &&
362
- this.constants[0]!.values.length === 1
363
- )
364
- return this.constants[0]!.values[0] as string;
365
- else return null;
366
- }
367
-
368
- public isSoleLiteral(): boolean {
369
- return this.getSoleLiteral() !== null;
370
- }
371
-
372
- /**
373
- * @internal
374
- */
375
- public isParentResolved(): boolean {
376
- return this.parent_resolved_;
377
- }
378
- }
379
- export namespace Metadata {
380
- export const intersects = (x: Metadata, y: Metadata): boolean => {
381
- // CHECK ANY & OPTIONAL
382
- if (x.any || y.any) return true;
383
- if (x.isRequired() === false && false === y.isRequired()) return true;
384
- if (x.nullable === true && true === y.nullable) return true;
385
- if (x.functional === true && y.functional === true) return true;
386
-
387
- //----
388
- // INSTANCES
389
- //----
390
- // ARRAYS
391
- if (x.arrays.length && y.arrays.length) return true;
392
- if (x.tuples.length && y.tuples.length) return true;
393
- if (x.objects.length && y.objects.length) return true;
394
- if (x.aliases.length && y.aliases.length) return true;
395
-
396
- // NATIVES
397
- if (x.natives.length && y.natives.length)
398
- if (x.natives.some((xn) => y.natives.some((yn) => xn === yn)))
399
- return true;
400
-
401
- //----
402
- // VALUES
403
- //----
404
- // ATOMICS
405
- for (const atomic of x.atomics)
406
- if (y.atomics.some((ya) => atomic.type === ya.type)) return true;
407
-
408
- // CONSTANTS
409
- for (const constant of x.constants) {
410
- const opposite: MetadataConstant | undefined = y.constants.find(
411
- (elem) => elem.type === constant.type,
412
- );
413
- if (opposite === undefined) continue;
414
-
415
- const values: Set<any> = new Set([
416
- ...constant.values,
417
- ...opposite.values,
418
- ]);
419
- if (values.size !== constant.values.length + opposite.values.length)
420
- return true;
421
- }
422
- return false;
423
- };
424
-
425
- export const covers = (
426
- x: Metadata,
427
- y: Metadata,
428
- level: number = 0,
429
- ): boolean => {
430
- // CHECK ANY
431
- if (x === y) return false;
432
- else if (x.any) return true;
433
- else if (y.any) return false;
434
-
435
- //----
436
- // INSTANCES
437
- //----
438
- if (level === 0) {
439
- // ARRAYS
440
- for (const ya of y.arrays)
441
- if (
442
- !x.arrays.some((xa) =>
443
- covers(xa.type.value, ya.type.value, level + 1),
444
- )
445
- ) {
446
- return false;
447
- }
448
-
449
- // TUPLES
450
- for (const yt of y.tuples)
451
- if (
452
- yt.type.elements.length !== 0 &&
453
- x.tuples.some(
454
- (xt) =>
455
- xt.type.elements.length >=
456
- yt.type.elements.length &&
457
- xt.type.elements
458
- .slice(yt.type.elements.length)
459
- .every((xv, i) =>
460
- covers(xv, yt.type.elements[i]!, level + 1),
461
- ),
462
- ) === false
463
- )
464
- return false;
465
- }
466
-
467
- // OBJECTS
468
- for (const yo of y.objects)
469
- if (x.objects.some((xo) => MetadataObject.covers(xo, yo)) === false)
470
- return false;
471
-
472
- // ALIASES
473
- for (const yd of y.aliases)
474
- if (x.aliases.some((xd) => xd.name === yd.name) === false)
475
- return false;
476
-
477
- // NATIVES
478
- for (const yn of y.natives)
479
- if (x.natives.some((xn) => xn === yn) === false) return false;
480
-
481
- // SETS
482
- for (const ys of y.sets)
483
- if (x.sets.some((xs) => covers(xs, ys)) === false) return false;
484
-
485
- //----
486
- // VALUES
487
- //----
488
- // ATOMICS
489
- if (
490
- y.atomics.some(
491
- (ya) => x.atomics.some((xa) => xa.type === ya.type) === false,
492
- )
493
- )
494
- return false;
495
-
496
- // CONSTANTS
497
- for (const yc of y.constants) {
498
- if (x.atomics.some((atom) => yc.type === atom.type)) continue;
499
- const xc: MetadataConstant | undefined = x.constants.find(
500
- (elem) => elem.type === yc.type,
501
- );
502
- if (xc === undefined) return false;
503
- else if (
504
- (yc.values as number[]).some(
505
- (yv) => xc.values.includes(yv as never) === false,
506
- )
507
- )
508
- return false;
509
- }
510
-
511
- // FUNCTIONAL
512
- if (x.functional === false && y.functional) return false;
513
-
514
- // SUCCESS
515
- return true;
516
- };
517
-
518
- /**
519
- * @internal
520
- */
521
- export const merge = (x: Metadata, y: Metadata): Metadata => {
522
- const output: Metadata = Metadata.create({
523
- any: x.any || y.any,
524
- nullable: x.nullable || y.nullable,
525
- required: x.required && y.required,
526
- optional: x.optional || y.optional,
527
- functional: x.functional || y.functional,
528
-
529
- escaped:
530
- x.escaped !== null && y.escaped !== null
531
- ? MetadataEscaped.create({
532
- original: merge(
533
- x.escaped.original,
534
- y.escaped.original,
535
- ),
536
- returns: merge(x.escaped.returns, y.escaped.returns),
537
- })
538
- : x.escaped ?? y.escaped,
539
- atomics: mergeTaggedTypes({
540
- container: x.atomics,
541
- equals: (x, y) => x.type === y.type,
542
- getter: (x) => x.tags,
543
- })(y.atomics),
544
- constants: [...x.constants],
545
- templates: x.templates.slice(),
546
-
547
- rest:
548
- x.rest !== null && y.rest !== null
549
- ? merge(x.rest, y.rest)
550
- : x.rest ?? y.rest,
551
- // arrays: x.arrays.slice(),
552
- arrays: mergeTaggedTypes({
553
- container: x.arrays,
554
- equals: (x, y) => x.type.name === y.type.name,
555
- getter: (x) => x.tags,
556
- })(y.arrays),
557
- tuples: mergeTaggedTypes({
558
- container: x.tuples,
559
- equals: (x, y) => x.type.name === y.type.name,
560
- getter: (x) => x.tags,
561
- })(y.tuples),
562
- objects: x.objects.slice(),
563
- aliases: x.aliases.slice(),
564
-
565
- natives: [...new Set([...x.natives, ...y.natives])],
566
- sets: x.sets.slice(),
567
- maps: x.maps.slice(),
568
- });
569
- for (const constant of y.constants) {
570
- const target: MetadataConstant = ArrayUtil.take(
571
- output.constants,
572
- (elem) => elem.type === constant.type,
573
- () => ({
574
- type: constant.type,
575
- values: [],
576
- }),
577
- );
578
- for (const value of constant.values)
579
- ArrayUtil.add(target.values, value);
580
- }
581
- for (const obj of y.objects)
582
- ArrayUtil.set(output.objects, obj, (elem) => elem.name);
583
- for (const alias of y.aliases)
584
- ArrayUtil.set(output.aliases, alias, (elem) => elem.name);
585
-
586
- return output;
587
- };
588
- }
589
-
590
- const getName = (metadata: Metadata): string => {
591
- if (metadata.any === true) return "any";
592
-
593
- const elements: string[] = [];
594
-
595
- // OPTIONAL
596
- if (metadata.nullable === true) elements.push("null");
597
- if (metadata.isRequired() === false) elements.push("undefined");
598
-
599
- // ATOMIC
600
- for (const atom of metadata.atomics) {
601
- elements.push(atom.getName());
602
- }
603
- for (const constant of metadata.constants)
604
- for (const value of constant.values)
605
- elements.push(
606
- constant.type === "string"
607
- ? JSON.stringify(value)
608
- : value.toString(),
609
- );
610
- for (const template of metadata.templates)
611
- elements.push(
612
- "`" +
613
- template
614
- .map((child) =>
615
- child.isConstant() && child.size() === 1
616
- ? child.constants[0]!.values[0]!
617
- : `$\{${child.getName()}\}`,
618
- )
619
- .join("")
620
- .split("`")
621
- .join("\\`") +
622
- "`",
623
- );
624
-
625
- // NATIVES
626
- for (const native of metadata.natives) elements.push(native);
627
- for (const set of metadata.sets) elements.push(`Set<${set.getName()}>`);
628
- for (const map of metadata.maps)
629
- elements.push(`Map<${map.key.getName()}, ${map.value.getName()}>`);
630
-
631
- // INSTANCES
632
- if (metadata.rest !== null) elements.push(`...${metadata.rest.getName()}`);
633
- for (const tuple of metadata.tuples) elements.push(tuple.type.name);
634
- for (const array of metadata.arrays) elements.push(array.getName());
635
- for (const object of metadata.objects) elements.push(object.name);
636
- for (const alias of metadata.aliases) elements.push(alias.name);
637
- if (metadata.escaped !== null) elements.push(metadata.escaped.getName());
638
-
639
- // RETURNS
640
- if (elements.length === 0) return "unknown";
641
- else if (elements.length === 1) return elements[0]!;
642
-
643
- elements.sort();
644
- return `(${elements.join(" | ")})`;
645
- };
646
- export namespace Metadata {
647
- export interface Entry {
648
- key: Metadata;
649
- value: Metadata;
650
- }
651
- }
652
-
653
- const mergeTaggedTypes =
654
- <T>(props: {
655
- container: T[];
656
- equals: (x: T, y: T) => boolean;
657
- getter: (x: T) => IMetadataTypeTag[][];
658
- }) =>
659
- (opposite: T[]) => {
660
- const output: T[] = [...props.container];
661
- for (const elem of opposite) {
662
- const equal = props.container.find((x) => props.equals(x, elem));
663
- if (equal === undefined) {
664
- output.push(elem);
665
- continue;
666
- }
667
-
668
- const matrix: string[][] = props
669
- .getter(equal)
670
- .map((tags) => tags.map((t) => t.name))
671
- .sort();
672
- for (const tags of props.getter(elem)) {
673
- const names: string[] = tags.map((t) => t.name).sort();
674
- if (
675
- matrix.some(
676
- (m) =>
677
- m.length === names.length &&
678
- m.every((s, i) => s === names[i]),
679
- )
680
- )
681
- continue;
682
- props.getter(equal).push(tags);
683
- }
684
- }
685
- return output;
686
- };
1
+ import { ClassProperties } from "../../typings/ClassProperties";
2
+ import { Writable } from "../../typings/Writable";
3
+
4
+ import { ArrayUtil } from "../../utils/ArrayUtil";
5
+
6
+ import { IMetadata } from "./IMetadata";
7
+ import { IMetadataCollection } from "./IMetadataCollection";
8
+ import { IMetadataDictionary } from "./IMetadataDictionary";
9
+ import { IMetadataTypeTag } from "./IMetadataTypeTag";
10
+ import { MetadataAlias } from "./MetadataAlias";
11
+ import { MetadataArray } from "./MetadataArray";
12
+ import { MetadataArrayType } from "./MetadataArrayType";
13
+ import { MetadataAtomic } from "./MetadataAtomic";
14
+ import { MetadataConstant } from "./MetadataConstant";
15
+ import { MetadataEscaped } from "./MetadataEscaped";
16
+ import { MetadataObject } from "./MetadataObject";
17
+ import { MetadataProperty } from "./MetadataProperty";
18
+ import { MetadataTuple } from "./MetadataTuple";
19
+ import { MetadataTupleType } from "./MetadataTupleType";
20
+
21
+ export class Metadata {
22
+ public any: boolean;
23
+ public required: boolean;
24
+ public optional: boolean;
25
+ public nullable: boolean;
26
+ public functional: boolean;
27
+
28
+ public escaped: MetadataEscaped | null;
29
+ public atomics: MetadataAtomic[];
30
+ public constants: MetadataConstant[];
31
+ public templates: Metadata[][];
32
+
33
+ public rest: Metadata | null;
34
+ public aliases: MetadataAlias[];
35
+ public arrays: MetadataArray[];
36
+ public tuples: MetadataTuple[];
37
+ public objects: MetadataObject[];
38
+
39
+ public natives: string[];
40
+ public sets: Metadata[];
41
+ public maps: Metadata.Entry[];
42
+
43
+ /** @internal */ private name_?: string;
44
+ /** @internal */ private parent_resolved_: boolean = false;
45
+ /** @internal */ public union_index?: number;
46
+ /** @internal */ public fixed_?: number | null;
47
+ /** @internal */ public boolean_literal_intersected_?: boolean;
48
+
49
+ /* -----------------------------------------------------------
50
+ CONSTRUCTORS
51
+ ----------------------------------------------------------- */
52
+ /**
53
+ * @hidden
54
+ */
55
+ private constructor(props: ClassProperties<Metadata>) {
56
+ this.any = props.any;
57
+ this.required = props.required;
58
+ this.optional = props.optional;
59
+ this.nullable = props.nullable;
60
+ this.functional = props.functional;
61
+
62
+ this.escaped = props.escaped;
63
+ this.atomics = props.atomics;
64
+ this.constants = props.constants;
65
+ this.templates = props.templates;
66
+
67
+ this.rest = props.rest;
68
+ this.arrays = props.arrays;
69
+ this.tuples = props.tuples;
70
+ this.objects = props.objects;
71
+ this.aliases = props.aliases;
72
+
73
+ this.natives = props.natives;
74
+ this.sets = props.sets;
75
+ this.maps = props.maps;
76
+ }
77
+
78
+ /**
79
+ * @internal
80
+ */
81
+ public static create(props: ClassProperties<Metadata>): Metadata {
82
+ return new Metadata(props);
83
+ }
84
+
85
+ /**
86
+ * @internal
87
+ */
88
+ public static initialize(parentResolved: boolean = false): Metadata {
89
+ const meta: Metadata = this.create({
90
+ any: false,
91
+ nullable: false,
92
+ required: true,
93
+ optional: false,
94
+ functional: false,
95
+
96
+ escaped: null,
97
+ constants: [],
98
+ atomics: [],
99
+ templates: [],
100
+ arrays: [],
101
+ tuples: [],
102
+ objects: [],
103
+ aliases: [],
104
+
105
+ rest: null,
106
+ natives: [],
107
+ sets: [],
108
+ maps: [],
109
+ });
110
+ meta.parent_resolved_ = parentResolved;
111
+ return meta;
112
+ }
113
+
114
+ public toJSON(): IMetadata {
115
+ return {
116
+ any: this.any,
117
+ required: this.required,
118
+ optional: this.optional,
119
+ nullable: this.nullable,
120
+ functional: this.functional,
121
+
122
+ atomics: this.atomics.map((a) =>
123
+ MetadataAtomic.create({
124
+ type: a.type,
125
+ tags: a.tags.map((r) => r.slice()),
126
+ }),
127
+ ),
128
+ constants: this.constants.map((c) => ({
129
+ type: c.type,
130
+ values: c.values.slice() as any,
131
+ })),
132
+ templates: this.templates.map((tpl) =>
133
+ tpl.map((meta) => meta.toJSON()),
134
+ ),
135
+ escaped: this.escaped ? this.escaped.toJSON() : null,
136
+
137
+ rest: this.rest ? this.rest.toJSON() : null,
138
+ arrays: this.arrays.map((array) => ({
139
+ name: array.type.name,
140
+ tags: array.tags.map((r) => r.slice()),
141
+ })),
142
+ tuples: this.tuples.map((tuple) => ({
143
+ name: tuple.type.name,
144
+ tags: tuple.tags.map((r) => r.slice()),
145
+ })),
146
+ objects: this.objects.map((obj) => obj.name),
147
+ aliases: this.aliases.map((alias) => alias.name),
148
+
149
+ natives: this.natives.slice(),
150
+ sets: this.sets.map((meta) => meta.toJSON()),
151
+ maps: this.maps.map((entry) => ({
152
+ key: entry.key.toJSON(),
153
+ value: entry.value.toJSON(),
154
+ })),
155
+ };
156
+ }
157
+
158
+ public static from(
159
+ meta: IMetadata,
160
+ collection: IMetadataCollection,
161
+ ): Metadata {
162
+ const dict: IMetadataDictionary = {
163
+ objects: new Map(
164
+ collection.objects.map((obj) => [
165
+ obj.name,
166
+ MetadataObject._From_without_properties(obj),
167
+ ]),
168
+ ),
169
+ aliases: new Map(
170
+ collection.aliases.map((alias) => [
171
+ alias.name,
172
+ MetadataAlias._From_without_value(alias),
173
+ ]),
174
+ ),
175
+ arrays: new Map(
176
+ collection.arrays.map((arr) => [
177
+ arr.name,
178
+ MetadataArrayType._From_without_value(arr),
179
+ ]),
180
+ ),
181
+ tuples: new Map(
182
+ collection.tuples.map((tpl) => [
183
+ tpl.name,
184
+ MetadataTupleType._From_without_elements(tpl),
185
+ ]),
186
+ ),
187
+ };
188
+
189
+ for (const obj of collection.objects) {
190
+ const initialized = dict.objects.get(obj.name)!;
191
+ initialized.properties.push(
192
+ ...obj.properties.map((prop) =>
193
+ MetadataProperty._From(prop, dict),
194
+ ),
195
+ );
196
+ }
197
+ for (const alias of collection.aliases)
198
+ Writable(dict.aliases.get(alias.name)!).value = this._From(
199
+ alias.value,
200
+ dict,
201
+ );
202
+ for (const array of collection.arrays)
203
+ Writable(dict.arrays.get(array.name)!).value = this._From(
204
+ array.value,
205
+ dict,
206
+ );
207
+ for (const tuple of collection.tuples)
208
+ Writable(dict.tuples.get(tuple.name)!).elements =
209
+ tuple.elements.map((elem) => this._From(elem, dict));
210
+
211
+ return this._From(meta, dict);
212
+ }
213
+
214
+ /**
215
+ * @internal
216
+ */
217
+ public static _From(meta: IMetadata, dict: IMetadataDictionary): Metadata {
218
+ return this.create({
219
+ any: meta.any,
220
+ required: meta.required,
221
+ optional: meta.optional,
222
+ nullable: meta.nullable,
223
+ functional: meta.functional,
224
+
225
+ constants: meta.constants.slice(),
226
+ atomics: meta.atomics.map((a) =>
227
+ MetadataAtomic.create({ type: a.type, tags: a.tags }),
228
+ ),
229
+ templates: meta.templates.map((tpl) =>
230
+ tpl.map((meta) => this._From(meta, dict)),
231
+ ),
232
+ escaped: meta.escaped
233
+ ? MetadataEscaped._From(meta.escaped, dict)
234
+ : null,
235
+
236
+ rest: meta.rest ? this._From(meta.rest, dict) : null,
237
+ arrays: meta.arrays.map((ref) => {
238
+ const type = dict.arrays.get(ref.name);
239
+ if (type === undefined)
240
+ throw new RangeError(
241
+ `Error on Metadata.from(): failed to find array "${ref.name}".`,
242
+ );
243
+ return MetadataArray.create({
244
+ type,
245
+ tags: ref.tags.map((row) => row.slice()),
246
+ });
247
+ }),
248
+ tuples: meta.tuples.map((t) => {
249
+ const type = dict.tuples.get(t.name);
250
+ if (type === undefined)
251
+ throw new RangeError(
252
+ `Error on Metadata.from(): failed to find tuple "${t.name}".`,
253
+ );
254
+ return MetadataTuple.create({
255
+ type,
256
+ tags: t.tags.map((r) => r.slice()),
257
+ });
258
+ }),
259
+ objects: meta.objects.map((name) => {
260
+ const found = dict.objects.get(name);
261
+ if (found === undefined)
262
+ throw new RangeError(
263
+ `Error on Metadata.from(): failed to find object "${name}".`,
264
+ );
265
+ return found;
266
+ }),
267
+ aliases: meta.aliases.map((alias) => {
268
+ const found = dict.aliases.get(alias);
269
+ if (found === undefined)
270
+ throw new RangeError(
271
+ `Error on Metadata.from(): failed to find alias "${alias}".`,
272
+ );
273
+ return found;
274
+ }),
275
+
276
+ natives: meta.natives.slice(),
277
+ sets: meta.sets.map((meta) => this._From(meta, dict)),
278
+ maps: meta.maps.map((entry) => ({
279
+ key: this._From(entry.key, dict),
280
+ value: this._From(entry.value, dict),
281
+ })),
282
+ });
283
+ }
284
+
285
+ /* -----------------------------------------------------------
286
+ ACCESSORS
287
+ ----------------------------------------------------------- */
288
+ public getName(): string {
289
+ return (this.name_ ??= getName(this));
290
+ }
291
+
292
+ public empty(): boolean {
293
+ return this.bucket() === 0 || this.size() === 0;
294
+ }
295
+
296
+ public size(): number {
297
+ return (
298
+ (this.any ? 1 : 0) +
299
+ (this.escaped ? 1 : 0) +
300
+ (this.functional ? 1 : 0) +
301
+ (this.rest ? this.rest.size() : 0) +
302
+ this.templates.length +
303
+ this.atomics.length +
304
+ this.constants
305
+ .map((c) => c.values.length)
306
+ .reduce((x, y) => x + y, 0) +
307
+ this.arrays.length +
308
+ this.tuples.length +
309
+ this.natives.length +
310
+ this.maps.length +
311
+ this.sets.length +
312
+ this.objects.length +
313
+ this.aliases.length
314
+ );
315
+ }
316
+
317
+ public bucket(): number {
318
+ return (
319
+ (this.any ? 1 : 0) +
320
+ (this.escaped ? 1 : 0) +
321
+ (this.functional ? 1 : 0) +
322
+ (this.templates.length ? 1 : 0) +
323
+ (this.atomics.length ? 1 : 0) +
324
+ (this.constants.length ? 1 : 0) +
325
+ (this.rest ? this.rest.size() : 0) +
326
+ (this.arrays.length ? 1 : 0) +
327
+ (this.tuples.length ? 1 : 0) +
328
+ (this.natives.length ? 1 : 0) +
329
+ (this.sets.length ? 1 : 0) +
330
+ (this.maps.length ? 1 : 0) +
331
+ (this.objects.length ? 1 : 0) +
332
+ (this.aliases.length ? 1 : 0)
333
+ );
334
+ }
335
+
336
+ public isConstant(): boolean {
337
+ return this.bucket() === (this.constants.length ? 1 : 0);
338
+ }
339
+
340
+ public isRequired(): boolean {
341
+ return this.required === true && this.optional === false;
342
+ }
343
+
344
+ /**
345
+ * @internal
346
+ */
347
+ public isUnionBucket(): boolean {
348
+ const size: number = this.bucket();
349
+ const emended: number =
350
+ !!this.atomics.length && !!this.constants.length ? size - 1 : size;
351
+ return emended > 1;
352
+ }
353
+
354
+ /**
355
+ * @internal
356
+ */
357
+ public getSoleLiteral(): string | null {
358
+ if (
359
+ this.size() === 1 &&
360
+ this.constants.length === 1 &&
361
+ this.constants[0]!.type === "string" &&
362
+ this.constants[0]!.values.length === 1
363
+ )
364
+ return this.constants[0]!.values[0] as string;
365
+ else return null;
366
+ }
367
+
368
+ public isSoleLiteral(): boolean {
369
+ return this.getSoleLiteral() !== null;
370
+ }
371
+
372
+ /**
373
+ * @internal
374
+ */
375
+ public isParentResolved(): boolean {
376
+ return this.parent_resolved_;
377
+ }
378
+ }
379
+ export namespace Metadata {
380
+ export const intersects = (x: Metadata, y: Metadata): boolean => {
381
+ // CHECK ANY & OPTIONAL
382
+ if (x.any || y.any) return true;
383
+ if (x.isRequired() === false && false === y.isRequired()) return true;
384
+ if (x.nullable === true && true === y.nullable) return true;
385
+ if (x.functional === true && y.functional === true) return true;
386
+
387
+ //----
388
+ // INSTANCES
389
+ //----
390
+ // ARRAYS
391
+ if (x.arrays.length && y.arrays.length) return true;
392
+ if (x.tuples.length && y.tuples.length) return true;
393
+ if (x.objects.length && y.objects.length) return true;
394
+ if (x.aliases.length && y.aliases.length) return true;
395
+
396
+ // NATIVES
397
+ if (x.natives.length && y.natives.length)
398
+ if (x.natives.some((xn) => y.natives.some((yn) => xn === yn)))
399
+ return true;
400
+
401
+ //----
402
+ // VALUES
403
+ //----
404
+ // ATOMICS
405
+ for (const atomic of x.atomics)
406
+ if (y.atomics.some((ya) => atomic.type === ya.type)) return true;
407
+
408
+ // CONSTANTS
409
+ for (const constant of x.constants) {
410
+ const opposite: MetadataConstant | undefined = y.constants.find(
411
+ (elem) => elem.type === constant.type,
412
+ );
413
+ if (opposite === undefined) continue;
414
+
415
+ const values: Set<any> = new Set([
416
+ ...constant.values,
417
+ ...opposite.values,
418
+ ]);
419
+ if (values.size !== constant.values.length + opposite.values.length)
420
+ return true;
421
+ }
422
+ return false;
423
+ };
424
+
425
+ export const covers = (
426
+ x: Metadata,
427
+ y: Metadata,
428
+ level: number = 0,
429
+ ): boolean => {
430
+ // CHECK ANY
431
+ if (x === y) return false;
432
+ else if (x.any) return true;
433
+ else if (y.any) return false;
434
+
435
+ //----
436
+ // INSTANCES
437
+ //----
438
+ if (level === 0) {
439
+ // ARRAYS
440
+ for (const ya of y.arrays)
441
+ if (
442
+ !x.arrays.some((xa) =>
443
+ covers(xa.type.value, ya.type.value, level + 1),
444
+ )
445
+ ) {
446
+ return false;
447
+ }
448
+
449
+ // TUPLES
450
+ for (const yt of y.tuples)
451
+ if (
452
+ yt.type.elements.length !== 0 &&
453
+ x.tuples.some(
454
+ (xt) =>
455
+ xt.type.elements.length >=
456
+ yt.type.elements.length &&
457
+ xt.type.elements
458
+ .slice(yt.type.elements.length)
459
+ .every((xv, i) =>
460
+ covers(xv, yt.type.elements[i]!, level + 1),
461
+ ),
462
+ ) === false
463
+ )
464
+ return false;
465
+ }
466
+
467
+ // OBJECTS
468
+ for (const yo of y.objects)
469
+ if (x.objects.some((xo) => MetadataObject.covers(xo, yo)) === false)
470
+ return false;
471
+
472
+ // ALIASES
473
+ for (const yd of y.aliases)
474
+ if (x.aliases.some((xd) => xd.name === yd.name) === false)
475
+ return false;
476
+
477
+ // NATIVES
478
+ for (const yn of y.natives)
479
+ if (x.natives.some((xn) => xn === yn) === false) return false;
480
+
481
+ // SETS
482
+ for (const ys of y.sets)
483
+ if (x.sets.some((xs) => covers(xs, ys)) === false) return false;
484
+
485
+ //----
486
+ // VALUES
487
+ //----
488
+ // ATOMICS
489
+ if (
490
+ y.atomics.some(
491
+ (ya) => x.atomics.some((xa) => xa.type === ya.type) === false,
492
+ )
493
+ )
494
+ return false;
495
+
496
+ // CONSTANTS
497
+ for (const yc of y.constants) {
498
+ if (x.atomics.some((atom) => yc.type === atom.type)) continue;
499
+ const xc: MetadataConstant | undefined = x.constants.find(
500
+ (elem) => elem.type === yc.type,
501
+ );
502
+ if (xc === undefined) return false;
503
+ else if (
504
+ (yc.values as number[]).some(
505
+ (yv) => xc.values.includes(yv as never) === false,
506
+ )
507
+ )
508
+ return false;
509
+ }
510
+
511
+ // FUNCTIONAL
512
+ if (x.functional === false && y.functional) return false;
513
+
514
+ // SUCCESS
515
+ return true;
516
+ };
517
+
518
+ /**
519
+ * @internal
520
+ */
521
+ export const merge = (x: Metadata, y: Metadata): Metadata => {
522
+ const output: Metadata = Metadata.create({
523
+ any: x.any || y.any,
524
+ nullable: x.nullable || y.nullable,
525
+ required: x.required && y.required,
526
+ optional: x.optional || y.optional,
527
+ functional: x.functional || y.functional,
528
+
529
+ escaped:
530
+ x.escaped !== null && y.escaped !== null
531
+ ? MetadataEscaped.create({
532
+ original: merge(
533
+ x.escaped.original,
534
+ y.escaped.original,
535
+ ),
536
+ returns: merge(x.escaped.returns, y.escaped.returns),
537
+ })
538
+ : x.escaped ?? y.escaped,
539
+ atomics: mergeTaggedTypes({
540
+ container: x.atomics,
541
+ equals: (x, y) => x.type === y.type,
542
+ getter: (x) => x.tags,
543
+ })(y.atomics),
544
+ constants: [...x.constants],
545
+ templates: x.templates.slice(),
546
+
547
+ rest:
548
+ x.rest !== null && y.rest !== null
549
+ ? merge(x.rest, y.rest)
550
+ : x.rest ?? y.rest,
551
+ // arrays: x.arrays.slice(),
552
+ arrays: mergeTaggedTypes({
553
+ container: x.arrays,
554
+ equals: (x, y) => x.type.name === y.type.name,
555
+ getter: (x) => x.tags,
556
+ })(y.arrays),
557
+ tuples: mergeTaggedTypes({
558
+ container: x.tuples,
559
+ equals: (x, y) => x.type.name === y.type.name,
560
+ getter: (x) => x.tags,
561
+ })(y.tuples),
562
+ objects: x.objects.slice(),
563
+ aliases: x.aliases.slice(),
564
+
565
+ natives: [...new Set([...x.natives, ...y.natives])],
566
+ sets: x.sets.slice(),
567
+ maps: x.maps.slice(),
568
+ });
569
+ for (const constant of y.constants) {
570
+ const target: MetadataConstant = ArrayUtil.take(
571
+ output.constants,
572
+ (elem) => elem.type === constant.type,
573
+ () => ({
574
+ type: constant.type,
575
+ values: [],
576
+ }),
577
+ );
578
+ for (const value of constant.values)
579
+ ArrayUtil.add(target.values, value);
580
+ }
581
+ for (const obj of y.objects)
582
+ ArrayUtil.set(output.objects, obj, (elem) => elem.name);
583
+ for (const alias of y.aliases)
584
+ ArrayUtil.set(output.aliases, alias, (elem) => elem.name);
585
+
586
+ return output;
587
+ };
588
+ }
589
+
590
+ const getName = (metadata: Metadata): string => {
591
+ if (metadata.any === true) return "any";
592
+
593
+ const elements: string[] = [];
594
+
595
+ // OPTIONAL
596
+ if (metadata.nullable === true) elements.push("null");
597
+ if (metadata.isRequired() === false) elements.push("undefined");
598
+
599
+ // ATOMIC
600
+ for (const atom of metadata.atomics) {
601
+ elements.push(atom.getName());
602
+ }
603
+ for (const constant of metadata.constants)
604
+ for (const value of constant.values)
605
+ elements.push(
606
+ constant.type === "string"
607
+ ? JSON.stringify(value)
608
+ : value.toString(),
609
+ );
610
+ for (const template of metadata.templates)
611
+ elements.push(
612
+ "`" +
613
+ template
614
+ .map((child) =>
615
+ child.isConstant() && child.size() === 1
616
+ ? child.constants[0]!.values[0]!
617
+ : `$\{${child.getName()}\}`,
618
+ )
619
+ .join("")
620
+ .split("`")
621
+ .join("\\`") +
622
+ "`",
623
+ );
624
+
625
+ // NATIVES
626
+ for (const native of metadata.natives) elements.push(native);
627
+ for (const set of metadata.sets) elements.push(`Set<${set.getName()}>`);
628
+ for (const map of metadata.maps)
629
+ elements.push(`Map<${map.key.getName()}, ${map.value.getName()}>`);
630
+
631
+ // INSTANCES
632
+ if (metadata.rest !== null) elements.push(`...${metadata.rest.getName()}`);
633
+ for (const tuple of metadata.tuples) elements.push(tuple.type.name);
634
+ for (const array of metadata.arrays) elements.push(array.getName());
635
+ for (const object of metadata.objects) elements.push(object.name);
636
+ for (const alias of metadata.aliases) elements.push(alias.name);
637
+ if (metadata.escaped !== null) elements.push(metadata.escaped.getName());
638
+
639
+ // RETURNS
640
+ if (elements.length === 0) return "unknown";
641
+ else if (elements.length === 1) return elements[0]!;
642
+
643
+ elements.sort();
644
+ return `(${elements.join(" | ")})`;
645
+ };
646
+ export namespace Metadata {
647
+ export interface Entry {
648
+ key: Metadata;
649
+ value: Metadata;
650
+ }
651
+ }
652
+
653
+ const mergeTaggedTypes =
654
+ <T>(props: {
655
+ container: T[];
656
+ equals: (x: T, y: T) => boolean;
657
+ getter: (x: T) => IMetadataTypeTag[][];
658
+ }) =>
659
+ (opposite: T[]) => {
660
+ const output: T[] = [...props.container];
661
+ for (const elem of opposite) {
662
+ const equal = props.container.find((x) => props.equals(x, elem));
663
+ if (equal === undefined) {
664
+ output.push(elem);
665
+ continue;
666
+ }
667
+
668
+ const matrix: string[][] = props
669
+ .getter(equal)
670
+ .map((tags) => tags.map((t) => t.name))
671
+ .sort();
672
+ for (const tags of props.getter(elem)) {
673
+ const names: string[] = tags.map((t) => t.name).sort();
674
+ if (
675
+ matrix.some(
676
+ (m) =>
677
+ m.length === names.length &&
678
+ m.every((s, i) => s === names[i]),
679
+ )
680
+ )
681
+ continue;
682
+ props.getter(equal).push(tags);
683
+ }
684
+ }
685
+ return output;
686
+ };