@dzeio/schema 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,769 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __decorateClass = (decorators, target, key, kind) => {
4
+ var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
5
+ for (var i = decorators.length - 1, decorator; i >= 0; i--)
6
+ if (decorator = decorators[i])
7
+ result = (kind ? decorator(target, key, result) : decorator(result)) || result;
8
+ if (kind && result) __defProp(target, key, result);
9
+ return result;
10
+ };
11
+
12
+ // src/helpers.ts
13
+ import { objectGet, objectLoop as objectLoop2, objectSet } from "@dzeio/object-util";
14
+
15
+ // src/SchemaItem.ts
16
+ import { objectClean } from "@dzeio/object-util";
17
+ var SchemaItem = class _SchemaItem {
18
+ constructor(items) {
19
+ // standard Schema V1 spec
20
+ this["~standard"] = {
21
+ vendor: "aptatio",
22
+ version: 1,
23
+ validate: (value) => {
24
+ const res = this.parse(value);
25
+ if (!res.valid) {
26
+ return {
27
+ issues: res.errors
28
+ };
29
+ }
30
+ return {
31
+ value: res.object,
32
+ issues: res.errors
33
+ };
34
+ }
35
+ };
36
+ /**
37
+ * keep public ?
38
+ */
39
+ this.validations = [];
40
+ /**
41
+ * Function calls saved for serialization
42
+ */
43
+ this.savedCalls = [];
44
+ /**
45
+ * Pre process the variable for various reasons
46
+ *
47
+ * It will execute every pre-process sequentially in order of being added
48
+ *
49
+ * note: The type of the final Pre-Process MUST be valid
50
+ */
51
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
52
+ this.preProcess = [];
53
+ /**
54
+ * post process the variable after it being validated
55
+ *
56
+ * it will execute each post-processes sequentially
57
+ *
58
+ * note: the type of the final post-process MUST be valid
59
+ */
60
+ this.postProcess = [];
61
+ /**
62
+ * list of attributes for custom works
63
+ */
64
+ this.attributes = [];
65
+ this.invalidError = "the field is invalid";
66
+ if (items && items.length > 0) {
67
+ this.items = Array.isArray(items) ? items : Array.from(items);
68
+ }
69
+ }
70
+ attrs(...attributes) {
71
+ this.attributes.concat(attributes);
72
+ return this;
73
+ }
74
+ attr(...attributes) {
75
+ return this.attrs(...attributes);
76
+ }
77
+ setInvalidError(err) {
78
+ this.invalidError = err;
79
+ return this;
80
+ }
81
+ clone() {
82
+ return Schema.fromJSON(this.toJSON());
83
+ }
84
+ parse(input, options) {
85
+ var _a;
86
+ for (const preProcess of this.preProcess) {
87
+ input = preProcess(input);
88
+ }
89
+ if (!this.isOfType(input)) {
90
+ return {
91
+ valid: false,
92
+ errors: [{ message: "invalid type" }]
93
+ };
94
+ }
95
+ const errors = [];
96
+ for (const validation of this.validations) {
97
+ if (!validation.fn(input)) {
98
+ errors.push({
99
+ message: (_a = validation.error) != null ? _a : this.invalidError
100
+ });
101
+ if (options == null ? void 0 : options.fast) {
102
+ return {
103
+ valid: false,
104
+ object: input,
105
+ errors
106
+ };
107
+ }
108
+ }
109
+ }
110
+ if (errors.length > 0) {
111
+ return {
112
+ valid: false,
113
+ object: input,
114
+ errors
115
+ };
116
+ }
117
+ for (const postProcess of this.postProcess) {
118
+ input = postProcess(input);
119
+ }
120
+ return {
121
+ valid: true,
122
+ object: input
123
+ };
124
+ }
125
+ toJSON() {
126
+ var _a;
127
+ const res = {
128
+ i: this.constructor.name,
129
+ a: this.attributes.length > 0 ? this.attributes : void 0,
130
+ c: (_a = this.items) == null ? void 0 : _a.map((it) => it instanceof _SchemaItem ? it.toJSON() : it),
131
+ f: this.savedCalls.map((it) => it.args ? { n: it.name, a: Array.from(it.args) } : { n: it.name })
132
+ };
133
+ objectClean(res, { deep: false });
134
+ return res;
135
+ }
136
+ addValidation(fn, error) {
137
+ this.validations.push(typeof fn === "function" ? { fn, error } : fn);
138
+ return this;
139
+ }
140
+ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
141
+ addPreProcess(fn) {
142
+ this.preProcess.push(fn);
143
+ return this;
144
+ }
145
+ addPostProcess(fn) {
146
+ this.postProcess.push(fn);
147
+ return this;
148
+ }
149
+ };
150
+
151
+ // src/items/array.ts
152
+ var SchemaArray = class extends SchemaItem {
153
+ constructor(values) {
154
+ super([values]);
155
+ this.values = values;
156
+ }
157
+ unique() {
158
+ this.postProcess.push((input) => input.filter((it, idx) => input.indexOf(it) === idx));
159
+ return this;
160
+ }
161
+ parse(input, options) {
162
+ const { valid, object, errors = [] } = super.parse(input, options);
163
+ if (!valid) {
164
+ return {
165
+ valid,
166
+ object,
167
+ errors
168
+ };
169
+ }
170
+ const clone = [];
171
+ const errs = [];
172
+ for (let idx = 0; idx < object.length; idx++) {
173
+ const item = object[idx];
174
+ const res = this.values.parse(item);
175
+ if (res.valid) {
176
+ clone.push(res.object);
177
+ continue;
178
+ }
179
+ const errss = res.errors.map((it) => ({
180
+ ...it,
181
+ field: it.field ? `${idx}.${it.field}` : idx.toString()
182
+ }));
183
+ if (options == null ? void 0 : options.fast) {
184
+ return {
185
+ valid: false,
186
+ object: clone,
187
+ errors: errss
188
+ };
189
+ }
190
+ errs.push(...errss);
191
+ }
192
+ if (errs.length > 0) {
193
+ return {
194
+ valid: false,
195
+ object: clone,
196
+ errors: errs
197
+ };
198
+ }
199
+ return {
200
+ valid: true,
201
+ object: clone
202
+ };
203
+ }
204
+ unwrap() {
205
+ return this.values;
206
+ }
207
+ isOfType(input) {
208
+ return Array.isArray(input);
209
+ }
210
+ };
211
+ __decorateClass([
212
+ parceable()
213
+ ], SchemaArray.prototype, "unique", 1);
214
+
215
+ // src/items/boolean.ts
216
+ var SchemaBoolean = class extends SchemaItem {
217
+ parseString(trueValue = "true", falseValue = "false") {
218
+ this.addPreProcess((it) => {
219
+ if (typeof it !== "string") {
220
+ return it;
221
+ }
222
+ if (it === trueValue) {
223
+ return true;
224
+ }
225
+ if (it === falseValue) {
226
+ return false;
227
+ }
228
+ return it;
229
+ });
230
+ return this;
231
+ }
232
+ isOfType(input) {
233
+ return typeof input === "boolean";
234
+ }
235
+ };
236
+ __decorateClass([
237
+ parceable()
238
+ ], SchemaBoolean.prototype, "parseString", 1);
239
+
240
+ // src/items/nullable.ts
241
+ var SchemaNullable = class extends SchemaItem {
242
+ constructor(child) {
243
+ super([child]);
244
+ this.child = child;
245
+ }
246
+ unwrap() {
247
+ return this.child;
248
+ }
249
+ parse(input, options) {
250
+ if (this.isNull(input)) {
251
+ return {
252
+ valid: true,
253
+ object: void 0
254
+ };
255
+ }
256
+ return this.child.parse(input, options);
257
+ }
258
+ isOfType(input) {
259
+ return Array.isArray(input);
260
+ }
261
+ isNull(value) {
262
+ return typeof value === "undefined" || value === null;
263
+ }
264
+ };
265
+
266
+ // src/items/object.ts
267
+ import { isObject, objectClone, objectLoop } from "@dzeio/object-util";
268
+ var SchemaObject = class extends SchemaItem {
269
+ constructor(model) {
270
+ super([model]);
271
+ this.model = model;
272
+ this.id = "object";
273
+ }
274
+ parse(input, options) {
275
+ const { valid, object, errors = [] } = super.parse(input, options);
276
+ if (!valid) {
277
+ return {
278
+ valid,
279
+ object,
280
+ errors
281
+ };
282
+ }
283
+ const clone = objectClone(object);
284
+ objectLoop(this.model, (childSchema, key) => {
285
+ const childValue = clone[key];
286
+ const child = childSchema.parse(childValue);
287
+ if (!child.valid) {
288
+ errors.push(...child.errors.map((it) => ({
289
+ ...it,
290
+ field: it.field ? `${key}.${it.field}` : key
291
+ })));
292
+ }
293
+ clone[key] = child.object;
294
+ return child.valid || !(options == null ? void 0 : options.fast);
295
+ });
296
+ return {
297
+ valid: errors.length === 0,
298
+ errors,
299
+ object: clone
300
+ };
301
+ }
302
+ isOfType(input) {
303
+ return isObject(input);
304
+ }
305
+ };
306
+
307
+ // src/helpers.ts
308
+ function parseQuery(model, query, opts) {
309
+ const record = {};
310
+ for (const [key, value] of query) {
311
+ record[key] = value;
312
+ }
313
+ return model.parse(record, opts);
314
+ }
315
+ function parseFormData(model, data, opts) {
316
+ var _a;
317
+ const record = {};
318
+ for (const [key, value] of data) {
319
+ const isArray = (_a = model.model[key].isOfType([])) != null ? _a : false;
320
+ objectSet(record, key.split(".").map((it) => /^\d+$/g.test(it) ? parseInt(it, 10) : it), isArray ? data.getAll(key) : value);
321
+ }
322
+ const handleBoolean = (value, keys) => {
323
+ var _a2;
324
+ if (value instanceof SchemaNullable) {
325
+ handleBoolean(value.unwrap(), keys);
326
+ }
327
+ if (value instanceof SchemaArray) {
328
+ const elements = objectGet(record, keys);
329
+ for (let it = 0; it < ((_a2 = elements == null ? void 0 : elements.length) != null ? _a2 : 0); it++) {
330
+ handleBoolean(value.unwrap(), [...keys, it]);
331
+ }
332
+ }
333
+ if (value instanceof SchemaObject) {
334
+ handleSchemaForBoolean(value.model, keys);
335
+ }
336
+ if (value instanceof SchemaBoolean) {
337
+ objectSet(record, keys, !!data.get(keys.join(".")));
338
+ }
339
+ };
340
+ const handleSchemaForBoolean = (modl, keys = []) => {
341
+ objectLoop2(modl, (value, key) => {
342
+ handleBoolean(value, [...keys, key]);
343
+ });
344
+ };
345
+ handleSchemaForBoolean(model.model);
346
+ return model.parse(record, opts);
347
+ }
348
+ function parseForm(model, form, opts) {
349
+ return parseFormData(model, new FormData(form), opts);
350
+ }
351
+
352
+ // src/items/date.ts
353
+ var SchemaDate = class extends SchemaItem {
354
+ isOfType(input) {
355
+ return input instanceof Date;
356
+ }
357
+ };
358
+
359
+ // src/items/record.ts
360
+ import { isObject as isObject2, objectLoop as objectLoop3 } from "@dzeio/object-util";
361
+ var SchemaRecord = class extends SchemaItem {
362
+ constructor(keys, values) {
363
+ super([keys, values]);
364
+ this.keys = keys;
365
+ this.values = values;
366
+ }
367
+ parse(input, options) {
368
+ const { valid, object, errors = [] } = super.parse(input, options);
369
+ if (!valid) {
370
+ return {
371
+ valid,
372
+ object,
373
+ errors
374
+ };
375
+ }
376
+ const clone = {};
377
+ objectLoop3(object, (value, key) => {
378
+ var _a, _b;
379
+ const res1 = this.keys.parse(key);
380
+ const res2 = this.values.parse(value);
381
+ if (!res1.valid || !res2.valid) {
382
+ errors.push(...((_a = res1.errors) != null ? _a : []).concat(...(_b = res2.errors) != null ? _b : []).map((it) => ({
383
+ message: it.message,
384
+ field: it.field ? `${key}.${it.field}` : key.toString()
385
+ })));
386
+ } else {
387
+ clone[res1.object] = res2.object;
388
+ }
389
+ return errors.length === 0 || !(options == null ? void 0 : options.fast);
390
+ });
391
+ return {
392
+ valid: errors.length === 0,
393
+ errors,
394
+ object: clone
395
+ };
396
+ }
397
+ isOfType(input) {
398
+ return isObject2(input) && Object.prototype.toString.call(input) === "[object Object]";
399
+ }
400
+ };
401
+
402
+ // src/items/enum.ts
403
+ var SchemaEnum = class extends SchemaItem {
404
+ constructor(templateEnum) {
405
+ super();
406
+ this.templateEnum = templateEnum;
407
+ this.type = "string";
408
+ const firstValue = Object.values(templateEnum)[0];
409
+ if (typeof firstValue === "number" || typeof templateEnum[firstValue] === "number") {
410
+ this.type = "number";
411
+ }
412
+ this.validations.push({
413
+ fn: (input) => Object.values(this.templateEnum).includes(input),
414
+ error: `Input is not part of ${templateEnum.constructor.name}`
415
+ });
416
+ }
417
+ isOfType(input) {
418
+ return typeof input === this.type;
419
+ }
420
+ };
421
+
422
+ // src/items/literal.ts
423
+ var SchemaLiteral = class extends SchemaItem {
424
+ constructor(value) {
425
+ super([value]);
426
+ this.value = value;
427
+ this.validations.push({ fn: (it) => it === this.value });
428
+ }
429
+ isOfType(input) {
430
+ return typeof input === typeof this.value;
431
+ }
432
+ };
433
+
434
+ // src/items/number.ts
435
+ var SchemaNumber = class extends SchemaItem {
436
+ lte(value, message) {
437
+ this.addValidation({
438
+ fn(input) {
439
+ return input <= value;
440
+ },
441
+ error: message
442
+ });
443
+ return this;
444
+ }
445
+ gte(value, message) {
446
+ this.addValidation({
447
+ fn(input) {
448
+ return input >= value;
449
+ },
450
+ error: message
451
+ });
452
+ return this;
453
+ }
454
+ lt(value, message) {
455
+ this.addValidation({
456
+ fn(input) {
457
+ return input < value;
458
+ },
459
+ error: message
460
+ });
461
+ return this;
462
+ }
463
+ gt(value, message) {
464
+ this.addValidation({
465
+ fn(input) {
466
+ return input > value;
467
+ },
468
+ error: message
469
+ });
470
+ return this;
471
+ }
472
+ parseString() {
473
+ this.addPreProcess(
474
+ (input) => typeof input === "string" ? Number.parseFloat(input) : input
475
+ );
476
+ return this;
477
+ }
478
+ isOfType(input) {
479
+ return typeof input === "number" && !Number.isNaN(input);
480
+ }
481
+ min(...params) {
482
+ return this.gte(...params);
483
+ }
484
+ max(...params) {
485
+ return this.lte(...params);
486
+ }
487
+ };
488
+ __decorateClass([
489
+ parceable()
490
+ ], SchemaNumber.prototype, "lte", 1);
491
+ __decorateClass([
492
+ parceable()
493
+ ], SchemaNumber.prototype, "gte", 1);
494
+ __decorateClass([
495
+ parceable()
496
+ ], SchemaNumber.prototype, "lt", 1);
497
+ __decorateClass([
498
+ parceable()
499
+ ], SchemaNumber.prototype, "gt", 1);
500
+ __decorateClass([
501
+ parceable()
502
+ ], SchemaNumber.prototype, "parseString", 1);
503
+
504
+ // src/items/string.ts
505
+ var SchemaString = class extends SchemaItem {
506
+ min(value, message) {
507
+ this.addValidation({
508
+ fn(input) {
509
+ return input.length >= value;
510
+ },
511
+ error: message
512
+ });
513
+ return this;
514
+ }
515
+ toCasing(casing) {
516
+ const fn = casing === "lower" ? "toLowerCase" : "toUpperCase";
517
+ this.addPostProcess((it) => it[fn]());
518
+ return this;
519
+ }
520
+ max(value, message) {
521
+ this.addValidation({
522
+ fn(input) {
523
+ return input.length <= value;
524
+ },
525
+ error: message
526
+ });
527
+ return this;
528
+ }
529
+ notEmpty(message) {
530
+ this.addValidation({
531
+ fn(input) {
532
+ return input !== "";
533
+ },
534
+ error: message
535
+ });
536
+ return this;
537
+ }
538
+ regex(regex, message) {
539
+ this.addValidation({
540
+ fn(input) {
541
+ return regex.test(input);
542
+ },
543
+ error: message
544
+ });
545
+ return this;
546
+ }
547
+ minLength(value, message) {
548
+ return this.min(value, message);
549
+ }
550
+ maxLength(value, message) {
551
+ return this.max(value, message);
552
+ }
553
+ isOfType(input) {
554
+ return typeof input === "string";
555
+ }
556
+ };
557
+ __decorateClass([
558
+ parceable()
559
+ ], SchemaString.prototype, "min", 1);
560
+ __decorateClass([
561
+ parceable()
562
+ ], SchemaString.prototype, "toCasing", 1);
563
+ __decorateClass([
564
+ parceable()
565
+ ], SchemaString.prototype, "max", 1);
566
+ __decorateClass([
567
+ parceable()
568
+ ], SchemaString.prototype, "notEmpty", 1);
569
+ __decorateClass([
570
+ parceable()
571
+ ], SchemaString.prototype, "regex", 1);
572
+
573
+ // src/items/union.ts
574
+ var SchemaUnion = class extends SchemaItem {
575
+ constructor(...schemas) {
576
+ super();
577
+ this.schemas = schemas;
578
+ }
579
+ parse(input, options) {
580
+ const { valid, object, errors = [] } = super.parse(input, options);
581
+ if (!valid) {
582
+ return {
583
+ valid,
584
+ object,
585
+ errors
586
+ };
587
+ }
588
+ for (const schema of this.schemas) {
589
+ const validation = schema.parse(input, options);
590
+ if (validation.valid) {
591
+ return validation;
592
+ } else {
593
+ errors.push(...validation.errors);
594
+ }
595
+ }
596
+ return {
597
+ valid: false,
598
+ errors,
599
+ object
600
+ };
601
+ }
602
+ isOfType(input) {
603
+ return this.schemas.some((it) => it.isOfType(input));
604
+ }
605
+ };
606
+
607
+ // src/Schema.ts
608
+ function parceable() {
609
+ return (target, propertyKey, descriptor) => {
610
+ if (!(target instanceof SchemaItem)) {
611
+ throw new Error("the decorator is only usable on Schema");
612
+ }
613
+ if (!(propertyKey in target)) {
614
+ throw new Error("property not set in object");
615
+ }
616
+ const original = target[propertyKey];
617
+ descriptor.value = function(...args) {
618
+ this.savedCalls.push({ name: propertyKey, args });
619
+ const res = original.call(this, ...args);
620
+ return res;
621
+ };
622
+ return descriptor;
623
+ };
624
+ }
625
+ var Types = {
626
+ Array: SchemaArray,
627
+ Boolean: SchemaBoolean,
628
+ Date: SchemaDate,
629
+ Enum: SchemaEnum,
630
+ Literal: SchemaLiteral,
631
+ Nullable: SchemaNullable,
632
+ Object: SchemaObject,
633
+ Record: SchemaRecord,
634
+ String: SchemaString,
635
+ Union: SchemaUnion
636
+ };
637
+ var _Schema = class _Schema extends SchemaObject {
638
+ static register(module) {
639
+ this.registeredModules.push(module);
640
+ }
641
+ static getModule(name) {
642
+ return this.registeredModules.find((it) => it.name === name);
643
+ }
644
+ static array(...inputs) {
645
+ return new SchemaArray(...inputs);
646
+ }
647
+ static boolean(...inputs) {
648
+ return new SchemaBoolean(...inputs);
649
+ }
650
+ static date(...inputs) {
651
+ return new SchemaDate(...inputs);
652
+ }
653
+ static enum(...inputs) {
654
+ return new SchemaEnum(...inputs);
655
+ }
656
+ /**
657
+ *
658
+ * @param input the literal value (note: append `as const` else the typing won't work correctly)
659
+ * @returns
660
+ */
661
+ static literal(input) {
662
+ return new SchemaLiteral(input);
663
+ }
664
+ static nullable(...inputs) {
665
+ return new SchemaNullable(...inputs);
666
+ }
667
+ static number(...inputs) {
668
+ return new SchemaNumber(...inputs);
669
+ }
670
+ static object(...inputs) {
671
+ return new SchemaObject(...inputs);
672
+ }
673
+ static record(...inputs) {
674
+ return new SchemaRecord(...inputs);
675
+ }
676
+ /**
677
+ * See {@link SchemaString}
678
+ */
679
+ static string(...inputs) {
680
+ return new SchemaString(...inputs);
681
+ }
682
+ static union(...inputs) {
683
+ return new SchemaUnion(...inputs);
684
+ }
685
+ static fromJSON(json) {
686
+ var _a, _b, _c, _d, _e, _f;
687
+ const fn = this.getModule(json.i);
688
+ if (!fn) {
689
+ throw new Error(`Schema cannot parse ${json.i}`);
690
+ }
691
+ const item = new fn(...(_b = (_a = json.c) == null ? void 0 : _a.map((it) => this.isSchemaJSON(it) ? _Schema.fromJSON(it) : it)) != null ? _b : []);
692
+ for (const validation of (_c = json.f) != null ? _c : []) {
693
+ if (!(validation.n in item)) {
694
+ throw new Error("validation not available in Schema Item");
695
+ }
696
+ item[validation.n](...(_e = (_d = validation.a) == null ? void 0 : _d.map((it) => _Schema.isSchemaJSON(it) ? _Schema.fromJSON(it) : it)) != null ? _e : []);
697
+ }
698
+ item.attrs(...(_f = json.a) != null ? _f : []);
699
+ return item;
700
+ }
701
+ static isSchemaJSON(data) {
702
+ if (typeof data !== "object" || data === null) {
703
+ return false;
704
+ }
705
+ if (!("i" in data)) {
706
+ return false;
707
+ }
708
+ return true;
709
+ }
710
+ /**
711
+ * @deprecated use helper `parseQuery`
712
+ */
713
+ validateQuery(query, fast = false) {
714
+ return parseQuery(this, query, { fast });
715
+ }
716
+ /**
717
+ * @deprecated use `parse`
718
+ */
719
+ validate(input, fast = false) {
720
+ return this.parse(input, { fast });
721
+ }
722
+ /**
723
+ * @deprecated use helper `parseForm`
724
+ */
725
+ validateForm(form, fast = false) {
726
+ return parseForm(this, form, { fast });
727
+ }
728
+ /**
729
+ * @deprecated use helper `parseFormData`
730
+ */
731
+ validateFormData(data, fast = false) {
732
+ return parseFormData(this, data, { fast });
733
+ }
734
+ };
735
+ _Schema.registeredModules = [
736
+ SchemaArray,
737
+ SchemaBoolean,
738
+ SchemaDate,
739
+ SchemaEnum,
740
+ SchemaLiteral,
741
+ SchemaNullable,
742
+ SchemaObject,
743
+ SchemaRecord,
744
+ SchemaString,
745
+ SchemaUnion
746
+ ];
747
+ var Schema = _Schema;
748
+ var s = Schema;
749
+ export {
750
+ SchemaArray,
751
+ SchemaBoolean,
752
+ SchemaDate,
753
+ SchemaEnum,
754
+ SchemaItem,
755
+ SchemaLiteral,
756
+ SchemaNullable,
757
+ SchemaNumber,
758
+ SchemaObject,
759
+ SchemaRecord,
760
+ SchemaString,
761
+ SchemaUnion,
762
+ Types,
763
+ Schema as default,
764
+ parceable,
765
+ parseForm,
766
+ parseFormData,
767
+ parseQuery,
768
+ s
769
+ };