typia 6.9.0-dev.20240821 → 6.10.0-dev.20240823

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.
@@ -1,534 +1,534 @@
1
- import ts from "typescript";
2
-
3
- import { IMetadataTypeTag } from "../schemas/metadata/IMetadataTypeTag";
4
- import { Metadata } from "../schemas/metadata/Metadata";
5
-
6
- import { Writable } from "../typings/Writable";
7
-
8
- import { FormatCheatSheet } from "../tags/internal/FormatCheatSheet";
9
- import { MetadataFactory } from "./MetadataFactory";
10
- import { MetadataTypeTagFactory } from "./MetadataTypeTagFactory";
11
-
12
- /**
13
- * Extremely hard coded, but no reason to maintain.
14
- *
15
- * @internal
16
- */
17
- export namespace MetadataCommentTagFactory {
18
- export const analyze =
19
- (errors: MetadataFactory.IError[]) =>
20
- (metadata: Metadata) =>
21
- (
22
- commentList: ts.JSDocTagInfo[],
23
- explore: MetadataFactory.IExplore,
24
- ): void => {
25
- // PREPARE MESSAGE CONTAINER
26
- const messages: string[] = [];
27
- const report = (msg: string) => {
28
- messages.push(msg);
29
- return null;
30
- };
31
- const validateReport =
32
- (property: string | null) =>
33
- (msg: string): false => {
34
- messages.push(
35
- `the property ${
36
- property === null ? `["typia.tag"]` : `["typia.tag.${property}"]`
37
- } ${msg}.`,
38
- );
39
- return false;
40
- };
41
-
42
- // VALIDATE AND CONSTRUCT COMMENT TAGS
43
- for (const comment of commentList) {
44
- const tagger: TagRecord | null = parse(report)(comment);
45
- if (tagger === null) continue;
46
- for (const [key, value] of Object.entries(tagger)) {
47
- const filtered: IMetadataTypeTag[] = value.filter(
48
- (v) => v.validate !== null,
49
- ) as IMetadataTypeTag[];
50
- if (key === "array") {
51
- if (metadata.arrays.length === 0) {
52
- report(`requires array type`);
53
- continue;
54
- }
55
- for (const a of metadata.arrays) {
56
- Writable(a).tags = a.tags.filter((x) =>
57
- MetadataTypeTagFactory.validate(validateReport)("array")([
58
- ...x,
59
- ...filtered,
60
- ]),
61
- );
62
- if (a.tags.length === 0) a.tags.push(filtered);
63
- else for (const tags of a.tags) tags.push(...filtered);
64
- }
65
- } else {
66
- const atomic = metadata.atomics.find((a) => a.type == key);
67
- if (atomic === undefined)
68
- if (key === "bigint" || key === "number") {
69
- const opposite = key === "bigint" ? "number" : "bigint";
70
- if (
71
- tagger[opposite] !== undefined &&
72
- metadata.atomics.some((a) => a.type === opposite)
73
- )
74
- continue;
75
- } else if (
76
- key === "string" &&
77
- value[0]?.kind === "format" &&
78
- value[0]?.value === "date-time"
79
- )
80
- continue;
81
- else report(`requires ${key} type`);
82
- else {
83
- Writable(atomic).tags = atomic.tags.filter((x) =>
84
- MetadataTypeTagFactory.validate(validateReport)(
85
- key as "string",
86
- )([...x, ...filtered]),
87
- );
88
- if (atomic.tags.length === 0) atomic.tags.push(filtered);
89
- else for (const tags of atomic.tags) tags.push(...filtered);
90
- }
91
- }
92
- }
93
- }
94
-
95
- // DO REPORT
96
- if (messages.length !== 0)
97
- errors.push({
98
- name: "comment tag(s)",
99
- explore,
100
- messages,
101
- });
102
- };
103
-
104
- const parse =
105
- (report: (msg: string) => null) =>
106
- (comment: ts.JSDocTagInfo): TagRecord | null => {
107
- const parser = PARSER[comment.name];
108
- if (parser === undefined) return {};
109
-
110
- const text = (comment.text || [])[0]?.text;
111
- if (text === undefined && comment.name !== "uniqueItems")
112
- return report(`no comment tag value`);
113
- return parser(report)(text!);
114
- };
115
- }
116
-
117
- type TagRecord = {
118
- [P in Target]?: NotDeterminedTypeTag[];
119
- };
120
- type Target = "bigint" | "number" | "string" | "array";
121
- type NotDeterminedTypeTag = Omit<IMetadataTypeTag, "validate" | "schema"> & {
122
- validate: string | null;
123
- schema: object | undefined;
124
- };
125
-
126
- const PARSER: Record<
127
- string,
128
- (report: (msg: string) => null) => (text: string) => {
129
- [P in Target]?: NotDeterminedTypeTag[];
130
- }
131
- > = {
132
- /* -----------------------------------------------------------
133
- ARRAY
134
- ----------------------------------------------------------- */
135
- items: (report) => (Value) => ({
136
- array: [
137
- {
138
- name: `MinItems<${Value}>`,
139
- target: "array",
140
- kind: "minItems",
141
- value: parse_integer(report)(true)(Value),
142
- validate: `${Value} <= $input.length`,
143
- exclusive: true,
144
- schema: {
145
- minItems: parse_integer(report)(true)(Value),
146
- },
147
- },
148
- {
149
- name: `MaxItems<${Value}>`,
150
- target: "array",
151
- kind: "maxItems",
152
- value: parse_integer(report)(true)(Value),
153
- validate: `$input.length <= ${Value}`,
154
- exclusive: true,
155
- schema: {
156
- maxItems: parse_integer(report)(true)(Value),
157
- },
158
- },
159
- ],
160
- }),
161
- minItems: (report) => (Value) => ({
162
- array: [
163
- {
164
- name: `MinItems<${Value}>`,
165
- target: "array",
166
- kind: "minItems",
167
- value: parse_integer(report)(true)(Value),
168
- validate: `${Value} <= $input.length`,
169
- exclusive: true,
170
- schema: {
171
- minItems: parse_integer(report)(true)(Value),
172
- },
173
- },
174
- ],
175
- }),
176
- maxItems: (report) => (Value) => ({
177
- array: [
178
- {
179
- name: `MaxItems<${Value}>`,
180
- target: "array",
181
- kind: "maxItems",
182
- value: parse_integer(report)(true)(Value),
183
- validate: `$input.length <= ${Value}`,
184
- exclusive: true,
185
- schema: {
186
- maxItems: parse_integer(report)(true)(Value),
187
- },
188
- },
189
- ],
190
- }),
191
- uniqueItems: () => () => ({
192
- array: [
193
- {
194
- name: `UniqueItems`,
195
- target: "array",
196
- kind: "uniqueItems",
197
- value: true,
198
- validate: `$input.length <= 1 || (new Set($input).size === $input.length)`,
199
- exclusive: true,
200
- schema: {
201
- uniqueItems: true,
202
- },
203
- },
204
- ],
205
- }),
206
-
207
- /* -----------------------------------------------------------
208
- NUMBER
209
- ----------------------------------------------------------- */
210
- type: () => (Value) => {
211
- // EMENDATIONS
212
- if (Value.startsWith("{") && Value.endsWith("}"))
213
- Value = Value.substring(1, Value.length - 1);
214
- if (Value === "int") Value = "int32";
215
- else if (Value === "uint") Value = "uint32";
216
-
217
- // MUST BE ONE OF THEM
218
- if (
219
- ["int32", "uint32", "int64", "uint64", "float", "double"].includes(
220
- Value,
221
- ) === false
222
- )
223
- return {};
224
- return {
225
- number: [
226
- {
227
- name: `Type<${JSON.stringify(Value)}>`,
228
- target: "number",
229
- kind: "type",
230
- value: Value,
231
- validate:
232
- Value === "int32"
233
- ? `Math.floor($input) === $input && -2147483648 <= $input && $input <= 2147483647`
234
- : Value === "uint32"
235
- ? `Math.floor($input) === $input && 0 <= $input && $input <= 4294967295`
236
- : Value === "int64"
237
- ? `Math.floor($input) === $input && -9223372036854775808 <= $input && $input <= 9223372036854775807`
238
- : Value === "uint64"
239
- ? `Math.floor($input) === $input && 0 <= $input && $input <= 18446744073709551615`
240
- : Value === "float"
241
- ? `-1.175494351e38 <= $input && $input <= 3.4028235e38`
242
- : `true`,
243
- exclusive: true,
244
- schema: ["int32", "uint32", "int64", "uint64"].includes(Value)
245
- ? { type: "integer" }
246
- : undefined,
247
- },
248
- ],
249
- bigint:
250
- Value === "int64" || "uint64"
251
- ? [
252
- {
253
- name: `Type<${JSON.stringify(Value)}>`,
254
- target: "bigint",
255
- kind: "type",
256
- value: Value,
257
- validate: Value === "int64" ? "true" : "BigInt(0) <= $input",
258
- exclusive: true,
259
- schema: undefined,
260
- },
261
- ]
262
- : [],
263
- };
264
- },
265
- minimum: (report) => (Value) => ({
266
- number: [
267
- {
268
- name: `Minimum<${Value}>`,
269
- target: "number",
270
- kind: "minimum",
271
- value: parse_number(report)(Value),
272
- validate: `${Value} <= $input`,
273
- exclusive: ["minimum", "exclusiveMinimum"],
274
- schema: {
275
- minimum: parse_number(report)(Value),
276
- },
277
- },
278
- ],
279
- bigint: [
280
- {
281
- name: `Minimum<${Value}n>`,
282
- target: "bigint",
283
- kind: "minimum",
284
- value: (() => {
285
- const value = parse_integer(report)(false)(Value);
286
- return value === null ? null : BigInt(value);
287
- })(),
288
- validate: `${Value} <= $input`,
289
- exclusive: ["minimum", "exclusiveMinimum"],
290
- schema: undefined,
291
- },
292
- ],
293
- }),
294
- maximum: (report) => (Value) => ({
295
- number: [
296
- {
297
- name: `Maximum<${Value}>`,
298
- target: "number",
299
- kind: "maximum",
300
- value: parse_number(report)(Value),
301
- validate: `$input <= ${Value}`,
302
- exclusive: ["maximum", "exclusiveMaximum"],
303
- schema: {
304
- maximum: parse_number(report)(Value),
305
- },
306
- },
307
- ],
308
- bigint: [
309
- {
310
- name: `Maximum<${Value}n>`,
311
- target: "bigint",
312
- kind: "maximum",
313
- value: (() => {
314
- const value = parse_integer(report)(false)(Value);
315
- return value === null ? null : BigInt(value);
316
- })(),
317
- validate: `$input <= ${Value}`,
318
- exclusive: ["maximum", "exclusiveMaximum"],
319
- schema: undefined,
320
- },
321
- ],
322
- }),
323
- exclusiveMinimum: (report) => (Value) => ({
324
- number: [
325
- {
326
- name: `ExclusiveMinimum<${Value}>`,
327
- target: "number",
328
- kind: "exclusiveMinimum",
329
- value: parse_number(report)(Value),
330
- validate: `${Value} < $input`,
331
- exclusive: ["minimum", "exclusiveMinimum"],
332
- schema: {
333
- exclusiveMinimum: true,
334
- minimum: parse_number(report)(Value),
335
- },
336
- },
337
- ],
338
- bigint: [
339
- {
340
- name: `ExclusiveMinimum<${Value}n>`,
341
- target: "bigint",
342
- kind: "exclusiveMinimum",
343
- value: (() => {
344
- const value = parse_integer(report)(false)(Value);
345
- return value === null ? null : BigInt(value);
346
- })(),
347
- validate: `${Value} < $input`,
348
- exclusive: ["minimum", "exclusiveMinimum"],
349
- schema: undefined,
350
- },
351
- ],
352
- }),
353
- exclusiveMaximum: (report) => (Value) => ({
354
- number: [
355
- {
356
- name: `ExclusiveMaximum<${Value}>`,
357
- target: "number",
358
- kind: "exclusiveMaximum",
359
- value: parse_number(report)(Value),
360
- validate: `$input < ${Value}`,
361
- exclusive: ["maximum", "exclusiveMaximum"],
362
- schema: {
363
- exclusiveMaximum: true,
364
- maximum: parse_number(report)(Value),
365
- },
366
- },
367
- ],
368
- bigint: [
369
- {
370
- name: `ExclusiveMaximum<${Value}n>`,
371
- target: "bigint",
372
- kind: "exclusiveMaximum",
373
- value: (() => {
374
- const value = parse_integer(report)(false)(Value);
375
- return value === null ? null : BigInt(value);
376
- })(),
377
- validate: `$input < ${Value}`,
378
- exclusive: ["maximum", "exclusiveMaximum"],
379
- schema: undefined,
380
- },
381
- ],
382
- }),
383
- multipleOf: (report) => (Value) => ({
384
- number: [
385
- {
386
- name: `MultipleOf<${Value}>`,
387
- target: "number",
388
- kind: "multipleOf",
389
- value: parse_number(report)(Value),
390
- validate: `$input % ${Value} === 0`,
391
- exclusive: true,
392
- schema: {
393
- multipleOf: parse_number(report)(Value),
394
- },
395
- },
396
- ],
397
- bigint: [
398
- {
399
- name: `MultipleOf<${Value}n>`,
400
- target: "bigint",
401
- kind: "multipleOf",
402
- value: (() => {
403
- const value = parse_integer(report)(false)(Value);
404
- return value === null ? null : BigInt(value);
405
- })(),
406
- validate: `$input % ${Value}n === 0n`,
407
- exclusive: true,
408
- schema: undefined,
409
- },
410
- ],
411
- }),
412
-
413
- /* -----------------------------------------------------------
414
- STRING
415
- ----------------------------------------------------------- */
416
- format: () => (Value) => {
417
- const matched = FORMATS.get(Value);
418
- if (matched === undefined) return {};
419
- return {
420
- string: [
421
- {
422
- name: `Format<${JSON.stringify(matched[0])}>`,
423
- target: "string",
424
- kind: "format",
425
- value: matched[0],
426
- validate: matched[1],
427
- exclusive: true,
428
- schema: {
429
- format: matched[0],
430
- },
431
- },
432
- ],
433
- };
434
- },
435
- pattern: () => (Value) => ({
436
- string: [
437
- {
438
- name: `Pattern<${JSON.stringify(Value)}>`,
439
- target: "string",
440
- kind: "pattern",
441
- value: Value,
442
- validate: `RegExp(${JSON.stringify(Value)}).test($input)`,
443
- exclusive: ["format"],
444
- schema: {
445
- pattern: Value,
446
- },
447
- },
448
- ],
449
- }),
450
- length: (report) => (Value) => ({
451
- string: [
452
- {
453
- name: `MinLength<${Value}>`,
454
- target: "string",
455
- kind: "minLength",
456
- value: parse_number(report)(Value),
457
- validate: `${Value} <= $input.length`,
458
- exclusive: true,
459
- schema: {
460
- minLength: parse_number(report)(Value),
461
- },
462
- },
463
- {
464
- name: `MaxLength<${Value}>`,
465
- target: "string",
466
- kind: "maxLength",
467
- value: parse_number(report)(Value),
468
- validate: `$input.length <= ${Value}`,
469
- exclusive: true,
470
- schema: {
471
- maxLength: parse_number(report)(Value),
472
- },
473
- },
474
- ],
475
- }),
476
- minLength: (report) => (Value) => ({
477
- string: [
478
- {
479
- name: `MinLength<${Value}>`,
480
- target: "string",
481
- kind: "minLength",
482
- value: parse_number(report)(Value),
483
- validate: `${Value} <= $input.length`,
484
- exclusive: true,
485
- schema: {
486
- minLength: parse_number(report)(Value),
487
- },
488
- },
489
- ],
490
- }),
491
- maxLength: (report) => (Value) => ({
492
- string: [
493
- {
494
- name: `MaxLength<${Value}>`,
495
- target: "string",
496
- kind: "maxLength",
497
- value: parse_number(report)(Value),
498
- validate: `$input.length <= ${Value}`,
499
- exclusive: true,
500
- schema: {
501
- maxLength: parse_number(report)(Value),
502
- },
503
- },
504
- ],
505
- }),
506
- };
507
-
508
- const parse_number =
509
- (report: (msg: string) => null) =>
510
- (str: string): number | null => {
511
- const value: number = Number(str);
512
- if (isNaN(value) === true) return report(`invalid number`);
513
- return value;
514
- };
515
-
516
- const parse_integer =
517
- (report: (msg: string) => null) =>
518
- (unsigned: boolean) =>
519
- (str: string): number | null => {
520
- const value: number | null = parse_number(report)(str);
521
- if (value === null) return null;
522
- else if (Math.floor(value) !== value) return report(`invalid integer`);
523
- else if (unsigned === true && value < 0)
524
- return report(`invalid unsigned integer`);
525
- return value;
526
- };
527
-
528
- const FORMATS: Map<string, [string, string]> = new Map([
529
- ...Object.entries(FormatCheatSheet).map(
530
- ([key, value]) => [key, [key, value]] as any,
531
- ),
532
- ["datetime", ["date-time", `!isNaN(new Date($input).getTime())`]],
533
- ["dateTime", ["date-time", `!isNaN(new Date($input).getTime())`]],
534
- ]);
1
+ import ts from "typescript";
2
+
3
+ import { IMetadataTypeTag } from "../schemas/metadata/IMetadataTypeTag";
4
+ import { Metadata } from "../schemas/metadata/Metadata";
5
+
6
+ import { Writable } from "../typings/Writable";
7
+
8
+ import { FormatCheatSheet } from "../tags/internal/FormatCheatSheet";
9
+ import { MetadataFactory } from "./MetadataFactory";
10
+ import { MetadataTypeTagFactory } from "./MetadataTypeTagFactory";
11
+
12
+ /**
13
+ * Extremely hard coded, but no reason to maintain.
14
+ *
15
+ * @internal
16
+ */
17
+ export namespace MetadataCommentTagFactory {
18
+ export const analyze =
19
+ (errors: MetadataFactory.IError[]) =>
20
+ (metadata: Metadata) =>
21
+ (
22
+ commentList: ts.JSDocTagInfo[],
23
+ explore: MetadataFactory.IExplore,
24
+ ): void => {
25
+ // PREPARE MESSAGE CONTAINER
26
+ const messages: string[] = [];
27
+ const report = (msg: string) => {
28
+ messages.push(msg);
29
+ return null;
30
+ };
31
+ const validateReport =
32
+ (property: string | null) =>
33
+ (msg: string): false => {
34
+ messages.push(
35
+ `the property ${
36
+ property === null ? `["typia.tag"]` : `["typia.tag.${property}"]`
37
+ } ${msg}.`,
38
+ );
39
+ return false;
40
+ };
41
+
42
+ // VALIDATE AND CONSTRUCT COMMENT TAGS
43
+ for (const comment of commentList) {
44
+ const tagger: TagRecord | null = parse(report)(comment);
45
+ if (tagger === null) continue;
46
+ for (const [key, value] of Object.entries(tagger)) {
47
+ const filtered: IMetadataTypeTag[] = value.filter(
48
+ (v) => v.validate !== null,
49
+ ) as IMetadataTypeTag[];
50
+ if (key === "array") {
51
+ if (metadata.arrays.length === 0) {
52
+ report(`requires array type`);
53
+ continue;
54
+ }
55
+ for (const a of metadata.arrays) {
56
+ Writable(a).tags = a.tags.filter((x) =>
57
+ MetadataTypeTagFactory.validate(validateReport)("array")([
58
+ ...x,
59
+ ...filtered,
60
+ ]),
61
+ );
62
+ if (a.tags.length === 0) a.tags.push(filtered);
63
+ else for (const tags of a.tags) tags.push(...filtered);
64
+ }
65
+ } else {
66
+ const atomic = metadata.atomics.find((a) => a.type == key);
67
+ if (atomic === undefined)
68
+ if (key === "bigint" || key === "number") {
69
+ const opposite = key === "bigint" ? "number" : "bigint";
70
+ if (
71
+ tagger[opposite] !== undefined &&
72
+ metadata.atomics.some((a) => a.type === opposite)
73
+ )
74
+ continue;
75
+ } else if (
76
+ key === "string" &&
77
+ value[0]?.kind === "format" &&
78
+ value[0]?.value === "date-time"
79
+ )
80
+ continue;
81
+ else report(`requires ${key} type`);
82
+ else {
83
+ Writable(atomic).tags = atomic.tags.filter((x) =>
84
+ MetadataTypeTagFactory.validate(validateReport)(
85
+ key as "string",
86
+ )([...x, ...filtered]),
87
+ );
88
+ if (atomic.tags.length === 0) atomic.tags.push(filtered);
89
+ else for (const tags of atomic.tags) tags.push(...filtered);
90
+ }
91
+ }
92
+ }
93
+ }
94
+
95
+ // DO REPORT
96
+ if (messages.length !== 0)
97
+ errors.push({
98
+ name: "comment tag(s)",
99
+ explore,
100
+ messages,
101
+ });
102
+ };
103
+
104
+ const parse =
105
+ (report: (msg: string) => null) =>
106
+ (comment: ts.JSDocTagInfo): TagRecord | null => {
107
+ const parser = PARSER[comment.name];
108
+ if (parser === undefined) return {};
109
+
110
+ const text = (comment.text || [])[0]?.text;
111
+ if (text === undefined && comment.name !== "uniqueItems")
112
+ return report(`no comment tag value`);
113
+ return parser(report)(text!);
114
+ };
115
+ }
116
+
117
+ type TagRecord = {
118
+ [P in Target]?: NotDeterminedTypeTag[];
119
+ };
120
+ type Target = "bigint" | "number" | "string" | "array";
121
+ type NotDeterminedTypeTag = Omit<IMetadataTypeTag, "validate" | "schema"> & {
122
+ validate: string | null;
123
+ schema: object | undefined;
124
+ };
125
+
126
+ const PARSER: Record<
127
+ string,
128
+ (report: (msg: string) => null) => (text: string) => {
129
+ [P in Target]?: NotDeterminedTypeTag[];
130
+ }
131
+ > = {
132
+ /* -----------------------------------------------------------
133
+ ARRAY
134
+ ----------------------------------------------------------- */
135
+ items: (report) => (Value) => ({
136
+ array: [
137
+ {
138
+ name: `MinItems<${Value}>`,
139
+ target: "array",
140
+ kind: "minItems",
141
+ value: parse_integer(report)(true)(Value),
142
+ validate: `${Value} <= $input.length`,
143
+ exclusive: true,
144
+ schema: {
145
+ minItems: parse_integer(report)(true)(Value),
146
+ },
147
+ },
148
+ {
149
+ name: `MaxItems<${Value}>`,
150
+ target: "array",
151
+ kind: "maxItems",
152
+ value: parse_integer(report)(true)(Value),
153
+ validate: `$input.length <= ${Value}`,
154
+ exclusive: true,
155
+ schema: {
156
+ maxItems: parse_integer(report)(true)(Value),
157
+ },
158
+ },
159
+ ],
160
+ }),
161
+ minItems: (report) => (Value) => ({
162
+ array: [
163
+ {
164
+ name: `MinItems<${Value}>`,
165
+ target: "array",
166
+ kind: "minItems",
167
+ value: parse_integer(report)(true)(Value),
168
+ validate: `${Value} <= $input.length`,
169
+ exclusive: true,
170
+ schema: {
171
+ minItems: parse_integer(report)(true)(Value),
172
+ },
173
+ },
174
+ ],
175
+ }),
176
+ maxItems: (report) => (Value) => ({
177
+ array: [
178
+ {
179
+ name: `MaxItems<${Value}>`,
180
+ target: "array",
181
+ kind: "maxItems",
182
+ value: parse_integer(report)(true)(Value),
183
+ validate: `$input.length <= ${Value}`,
184
+ exclusive: true,
185
+ schema: {
186
+ maxItems: parse_integer(report)(true)(Value),
187
+ },
188
+ },
189
+ ],
190
+ }),
191
+ uniqueItems: () => () => ({
192
+ array: [
193
+ {
194
+ name: `UniqueItems`,
195
+ target: "array",
196
+ kind: "uniqueItems",
197
+ value: true,
198
+ validate: `$input.length <= 1 || (new Set($input).size === $input.length)`,
199
+ exclusive: true,
200
+ schema: {
201
+ uniqueItems: true,
202
+ },
203
+ },
204
+ ],
205
+ }),
206
+
207
+ /* -----------------------------------------------------------
208
+ NUMBER
209
+ ----------------------------------------------------------- */
210
+ type: () => (Value) => {
211
+ // EMENDATIONS
212
+ if (Value.startsWith("{") && Value.endsWith("}"))
213
+ Value = Value.substring(1, Value.length - 1);
214
+ if (Value === "int") Value = "int32";
215
+ else if (Value === "uint") Value = "uint32";
216
+
217
+ // MUST BE ONE OF THEM
218
+ if (
219
+ ["int32", "uint32", "int64", "uint64", "float", "double"].includes(
220
+ Value,
221
+ ) === false
222
+ )
223
+ return {};
224
+ return {
225
+ number: [
226
+ {
227
+ name: `Type<${JSON.stringify(Value)}>`,
228
+ target: "number",
229
+ kind: "type",
230
+ value: Value,
231
+ validate:
232
+ Value === "int32"
233
+ ? `Math.floor($input) === $input && -2147483648 <= $input && $input <= 2147483647`
234
+ : Value === "uint32"
235
+ ? `Math.floor($input) === $input && 0 <= $input && $input <= 4294967295`
236
+ : Value === "int64"
237
+ ? `Math.floor($input) === $input && -9223372036854775808 <= $input && $input <= 9223372036854775807`
238
+ : Value === "uint64"
239
+ ? `Math.floor($input) === $input && 0 <= $input && $input <= 18446744073709551615`
240
+ : Value === "float"
241
+ ? `-1.175494351e38 <= $input && $input <= 3.4028235e38`
242
+ : `true`,
243
+ exclusive: true,
244
+ schema: ["int32", "uint32", "int64", "uint64"].includes(Value)
245
+ ? { type: "integer" }
246
+ : undefined,
247
+ },
248
+ ],
249
+ bigint:
250
+ Value === "int64" || "uint64"
251
+ ? [
252
+ {
253
+ name: `Type<${JSON.stringify(Value)}>`,
254
+ target: "bigint",
255
+ kind: "type",
256
+ value: Value,
257
+ validate: Value === "int64" ? "true" : "BigInt(0) <= $input",
258
+ exclusive: true,
259
+ schema: undefined,
260
+ },
261
+ ]
262
+ : [],
263
+ };
264
+ },
265
+ minimum: (report) => (Value) => ({
266
+ number: [
267
+ {
268
+ name: `Minimum<${Value}>`,
269
+ target: "number",
270
+ kind: "minimum",
271
+ value: parse_number(report)(Value),
272
+ validate: `${Value} <= $input`,
273
+ exclusive: ["minimum", "exclusiveMinimum"],
274
+ schema: {
275
+ minimum: parse_number(report)(Value),
276
+ },
277
+ },
278
+ ],
279
+ bigint: [
280
+ {
281
+ name: `Minimum<${Value}n>`,
282
+ target: "bigint",
283
+ kind: "minimum",
284
+ value: (() => {
285
+ const value = parse_integer(report)(false)(Value);
286
+ return value === null ? null : BigInt(value);
287
+ })(),
288
+ validate: `${Value} <= $input`,
289
+ exclusive: ["minimum", "exclusiveMinimum"],
290
+ schema: undefined,
291
+ },
292
+ ],
293
+ }),
294
+ maximum: (report) => (Value) => ({
295
+ number: [
296
+ {
297
+ name: `Maximum<${Value}>`,
298
+ target: "number",
299
+ kind: "maximum",
300
+ value: parse_number(report)(Value),
301
+ validate: `$input <= ${Value}`,
302
+ exclusive: ["maximum", "exclusiveMaximum"],
303
+ schema: {
304
+ maximum: parse_number(report)(Value),
305
+ },
306
+ },
307
+ ],
308
+ bigint: [
309
+ {
310
+ name: `Maximum<${Value}n>`,
311
+ target: "bigint",
312
+ kind: "maximum",
313
+ value: (() => {
314
+ const value = parse_integer(report)(false)(Value);
315
+ return value === null ? null : BigInt(value);
316
+ })(),
317
+ validate: `$input <= ${Value}`,
318
+ exclusive: ["maximum", "exclusiveMaximum"],
319
+ schema: undefined,
320
+ },
321
+ ],
322
+ }),
323
+ exclusiveMinimum: (report) => (Value) => ({
324
+ number: [
325
+ {
326
+ name: `ExclusiveMinimum<${Value}>`,
327
+ target: "number",
328
+ kind: "exclusiveMinimum",
329
+ value: parse_number(report)(Value),
330
+ validate: `${Value} < $input`,
331
+ exclusive: ["minimum", "exclusiveMinimum"],
332
+ schema: {
333
+ exclusiveMinimum: true,
334
+ minimum: parse_number(report)(Value),
335
+ },
336
+ },
337
+ ],
338
+ bigint: [
339
+ {
340
+ name: `ExclusiveMinimum<${Value}n>`,
341
+ target: "bigint",
342
+ kind: "exclusiveMinimum",
343
+ value: (() => {
344
+ const value = parse_integer(report)(false)(Value);
345
+ return value === null ? null : BigInt(value);
346
+ })(),
347
+ validate: `${Value} < $input`,
348
+ exclusive: ["minimum", "exclusiveMinimum"],
349
+ schema: undefined,
350
+ },
351
+ ],
352
+ }),
353
+ exclusiveMaximum: (report) => (Value) => ({
354
+ number: [
355
+ {
356
+ name: `ExclusiveMaximum<${Value}>`,
357
+ target: "number",
358
+ kind: "exclusiveMaximum",
359
+ value: parse_number(report)(Value),
360
+ validate: `$input < ${Value}`,
361
+ exclusive: ["maximum", "exclusiveMaximum"],
362
+ schema: {
363
+ exclusiveMaximum: true,
364
+ maximum: parse_number(report)(Value),
365
+ },
366
+ },
367
+ ],
368
+ bigint: [
369
+ {
370
+ name: `ExclusiveMaximum<${Value}n>`,
371
+ target: "bigint",
372
+ kind: "exclusiveMaximum",
373
+ value: (() => {
374
+ const value = parse_integer(report)(false)(Value);
375
+ return value === null ? null : BigInt(value);
376
+ })(),
377
+ validate: `$input < ${Value}`,
378
+ exclusive: ["maximum", "exclusiveMaximum"],
379
+ schema: undefined,
380
+ },
381
+ ],
382
+ }),
383
+ multipleOf: (report) => (Value) => ({
384
+ number: [
385
+ {
386
+ name: `MultipleOf<${Value}>`,
387
+ target: "number",
388
+ kind: "multipleOf",
389
+ value: parse_number(report)(Value),
390
+ validate: `$input % ${Value} === 0`,
391
+ exclusive: true,
392
+ schema: {
393
+ multipleOf: parse_number(report)(Value),
394
+ },
395
+ },
396
+ ],
397
+ bigint: [
398
+ {
399
+ name: `MultipleOf<${Value}n>`,
400
+ target: "bigint",
401
+ kind: "multipleOf",
402
+ value: (() => {
403
+ const value = parse_integer(report)(false)(Value);
404
+ return value === null ? null : BigInt(value);
405
+ })(),
406
+ validate: `$input % ${Value}n === 0n`,
407
+ exclusive: true,
408
+ schema: undefined,
409
+ },
410
+ ],
411
+ }),
412
+
413
+ /* -----------------------------------------------------------
414
+ STRING
415
+ ----------------------------------------------------------- */
416
+ format: () => (Value) => {
417
+ const matched = FORMATS.get(Value);
418
+ if (matched === undefined) return {};
419
+ return {
420
+ string: [
421
+ {
422
+ name: `Format<${JSON.stringify(matched[0])}>`,
423
+ target: "string",
424
+ kind: "format",
425
+ value: matched[0],
426
+ validate: matched[1],
427
+ exclusive: true,
428
+ schema: {
429
+ format: matched[0],
430
+ },
431
+ },
432
+ ],
433
+ };
434
+ },
435
+ pattern: () => (Value) => ({
436
+ string: [
437
+ {
438
+ name: `Pattern<${JSON.stringify(Value)}>`,
439
+ target: "string",
440
+ kind: "pattern",
441
+ value: Value,
442
+ validate: `RegExp(${JSON.stringify(Value)}).test($input)`,
443
+ exclusive: ["format"],
444
+ schema: {
445
+ pattern: Value,
446
+ },
447
+ },
448
+ ],
449
+ }),
450
+ length: (report) => (Value) => ({
451
+ string: [
452
+ {
453
+ name: `MinLength<${Value}>`,
454
+ target: "string",
455
+ kind: "minLength",
456
+ value: parse_number(report)(Value),
457
+ validate: `${Value} <= $input.length`,
458
+ exclusive: true,
459
+ schema: {
460
+ minLength: parse_number(report)(Value),
461
+ },
462
+ },
463
+ {
464
+ name: `MaxLength<${Value}>`,
465
+ target: "string",
466
+ kind: "maxLength",
467
+ value: parse_number(report)(Value),
468
+ validate: `$input.length <= ${Value}`,
469
+ exclusive: true,
470
+ schema: {
471
+ maxLength: parse_number(report)(Value),
472
+ },
473
+ },
474
+ ],
475
+ }),
476
+ minLength: (report) => (Value) => ({
477
+ string: [
478
+ {
479
+ name: `MinLength<${Value}>`,
480
+ target: "string",
481
+ kind: "minLength",
482
+ value: parse_number(report)(Value),
483
+ validate: `${Value} <= $input.length`,
484
+ exclusive: true,
485
+ schema: {
486
+ minLength: parse_number(report)(Value),
487
+ },
488
+ },
489
+ ],
490
+ }),
491
+ maxLength: (report) => (Value) => ({
492
+ string: [
493
+ {
494
+ name: `MaxLength<${Value}>`,
495
+ target: "string",
496
+ kind: "maxLength",
497
+ value: parse_number(report)(Value),
498
+ validate: `$input.length <= ${Value}`,
499
+ exclusive: true,
500
+ schema: {
501
+ maxLength: parse_number(report)(Value),
502
+ },
503
+ },
504
+ ],
505
+ }),
506
+ };
507
+
508
+ const parse_number =
509
+ (report: (msg: string) => null) =>
510
+ (str: string): number | null => {
511
+ const value: number = Number(str);
512
+ if (isNaN(value) === true) return report(`invalid number`);
513
+ return value;
514
+ };
515
+
516
+ const parse_integer =
517
+ (report: (msg: string) => null) =>
518
+ (unsigned: boolean) =>
519
+ (str: string): number | null => {
520
+ const value: number | null = parse_number(report)(str);
521
+ if (value === null) return null;
522
+ else if (Math.floor(value) !== value) return report(`invalid integer`);
523
+ else if (unsigned === true && value < 0)
524
+ return report(`invalid unsigned integer`);
525
+ return value;
526
+ };
527
+
528
+ const FORMATS: Map<string, [string, string]> = new Map([
529
+ ...Object.entries(FormatCheatSheet).map(
530
+ ([key, value]) => [key, [key, value]] as any,
531
+ ),
532
+ ["datetime", ["date-time", `!isNaN(new Date($input).getTime())`]],
533
+ ["dateTime", ["date-time", `!isNaN(new Date($input).getTime())`]],
534
+ ]);