@lucania/schema 3.0.1 → 3.0.3

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,47 +1,158 @@
1
- import { BaseSchemaAny } from "./typing/extended";
2
- import { DefaultValue, ModelValue } from "./typing/toolbox";
1
+ import { BaseSchema, SourceValue } from ".";
3
2
  import { AnySchema } from "./schema/AnySchema";
4
3
  import { ArraySchema, ArraySource } from "./schema/ArraySchema";
5
4
  import { BooleanSchema, BooleanSource } from "./schema/BooleanSchema";
5
+ import { ConstantSchema, ConstantSource } from "./schema/ConstantSchema";
6
6
  import { DateSchema, DateSource } from "./schema/DateSchema";
7
7
  import { DynamicObjectSchema, DynamicObjectSource } from "./schema/DynamicObjectSchema";
8
8
  import { EnumerationSchema } from "./schema/EnumerationSchema";
9
+ import { LenientObjectModel, LenientObjectSchema, LenientObjectSource, LenientObjectSubschema } from "./schema/LenientObject";
9
10
  import { NumberSchema, NumberSource } from "./schema/NumberSchema";
10
11
  import { ObjectSchema, ObjectSource, ObjectSubschema } from "./schema/ObjectSchema";
11
12
  import { OrSetSchema, OrSetSchemaSource } from "./schema/OrSetSchema";
12
13
  import { StringSchema, StringSource } from "./schema/StringSchema";
13
- import { BaseSchema, SourceValue } from ".";
14
+ import { BaseSchemaAny } from "./typing/extended";
15
+ import { DefaultValue, ModelValue } from "./typing/toolbox";
16
+ /**
17
+ * A collection of helper functions used to create the standard set of schemas.
18
+ */
14
19
  export declare namespace Schema {
20
+ /**
21
+ * Creates a schema used to validate a string.
22
+ *
23
+ * @param required Flag representing whether the schema should enforce a value's presence.
24
+ * @param defaultValue A default the schema will use during validation if a value is not present.
25
+ * @returns A schema used to validate a string.
26
+ */
15
27
  function String(): StringSchema<true, undefined>;
16
28
  function String<Required extends boolean>(required: Required): StringSchema<Required, undefined>;
17
29
  function String<Required extends boolean, Default extends DefaultValue<StringSource>>(required: Required, defaultValue: Default): StringSchema<Required, Default>;
30
+ /**
31
+ * Creates a schema used to validate a number.
32
+ *
33
+ * @param required Flag representing whether the schema should enforce a value's presence.
34
+ * @param defaultValue A default the schema will use during validation if a value is not present.
35
+ * @returns A schema used to validate a number.
36
+ */
18
37
  function Number(): NumberSchema<true, undefined>;
19
38
  function Number<Required extends boolean>(required: Required): NumberSchema<Required, undefined>;
20
39
  function Number<Required extends boolean, Default extends DefaultValue<NumberSource>>(required: Required, defaultValue: Default): NumberSchema<Required, Default>;
40
+ /**
41
+ * Creates a schema used to validate a boolean.
42
+ *
43
+ * @param required Flag representing whether the schema should enforce a value's presence.
44
+ * @param defaultValue A default the schema will use during validation if a value is not present.
45
+ * @returns A schema used to validate a boolean.
46
+ */
21
47
  function Boolean(): BooleanSchema<true, undefined>;
22
48
  function Boolean<Required extends boolean>(required: Required): BooleanSchema<Required, undefined>;
23
49
  function Boolean<Required extends boolean, Default extends DefaultValue<BooleanSource>>(required: Required, defaultValue: Default): BooleanSchema<Required, Default>;
50
+ /**
51
+ * Creates a schema used to validate a Date.
52
+ *
53
+ * @param required Flag representing whether the schema should enforce a value's presence.
54
+ * @param defaultValue A default the schema will use during validation if a value is not present.
55
+ * @returns A schema used to validate a Date.
56
+ */
24
57
  function Date(): DateSchema<true, undefined>;
25
58
  function Date<Required extends boolean>(required: Required): DateSchema<Required, undefined>;
26
59
  function Date<Required extends boolean, Default extends DefaultValue<DateSource>>(required: Required, defaultValue: Default): DateSchema<Required, Default>;
60
+ /**
61
+ * Creates a schema used to validate anything! Any value will pass the validation provided by schemas created by this function.
62
+ *
63
+ * @param required Flag representing whether the schema should enforce a value's presence.
64
+ * @param defaultValue A default the schema will use during validation if a value is not present.
65
+ * @returns A schema used to validate anything.
66
+ */
27
67
  function Any(): AnySchema<true, undefined>;
28
68
  function Any<Required extends boolean>(required: Required): AnySchema<Required, undefined>;
29
69
  function Any<Required extends boolean, Default extends DefaultValue<any>>(required: Required, defaultValue: Default): AnySchema<Required, Default>;
70
+ /**
71
+ * Creates a schema used to validate an object.
72
+ *
73
+ * This function can be used to create schemas that describe a value's object hierarchy.
74
+ *
75
+ * @note Values validated by schemas created by this function will only retain properties specified by the schema.
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * $.Object({ name: $.String() }).validate({ name: "Jeremy", age: 1 })
80
+ * ```
81
+ * ->
82
+ * ```ts
83
+ * { "name": "Jeremy" }
84
+ * ```
85
+ *
86
+ * @param subschema An object describing the sole keys and values that must be present within an object.
87
+ * @param required Flag representing whether the schema should enforce a value's presence.
88
+ * @param defaultValue A default the schema will use during validation if a value is not present.
89
+ * @returns A schema used to validate an object.
90
+ */
30
91
  function Object<Subschema extends ObjectSubschema>(subschema: Subschema): ObjectSchema<Subschema, true, undefined>;
31
92
  function Object<Subschema extends ObjectSubschema, Required extends boolean>(subschema: Subschema, required: Required): ObjectSchema<Subschema, Required, undefined>;
32
93
  function Object<Subschema extends ObjectSubschema, Required extends boolean, Default extends DefaultValue<ObjectSource<Subschema>>>(subschema: Subschema, required: Required, defaultValue: Default): ObjectSchema<Subschema, Required, Default>;
94
+ /**
95
+ * Creates a schema used to validate an object.
96
+ *
97
+ * This function can be used to create schemas that describe a value's object hierarchy.
98
+ *
99
+ * @note This function creates schemas that differ from that of `$.Object` in that the values validated by schemas created by this function
100
+ * will RETAIN the properties not specified by the schema.
101
+ *
102
+ * @example
103
+ * ```ts
104
+ * $.LenientObject({ name: $.String() }).validate({ name: "Jeremy", age: 1 })
105
+ * ```
106
+ * ->
107
+ * ```ts
108
+ * { "name": "Jeremy", "age": 1 }
109
+ * ```
110
+ *
111
+ * @param subschema An object describing keys and values that must be present amongst any other keys and values within an object.
112
+ * @param required Flag representing whether the schema should enforce a value's presence.
113
+ * @param defaultValue A default the schema will use during validation if a value is not present.
114
+ * @returns A schema used to validate an object.
115
+ */
116
+ function LenientObject<Subschema extends LenientObjectSubschema>(subschema: Subschema): LenientObjectSchema<Subschema, LenientObjectSource<Subschema>, LenientObjectModel<Subschema>, true, undefined>;
117
+ function LenientObject<Subschema extends LenientObjectSubschema, Required extends boolean>(subschema: Subschema, required: Required): LenientObjectSchema<Subschema, LenientObjectSource<Subschema>, LenientObjectModel<Subschema>, Required, undefined>;
118
+ function LenientObject<Subschema extends LenientObjectSubschema, Required extends boolean, Default extends DefaultValue<LenientObjectSource<Subschema>>>(subschema: Subschema, required: Required, defaultValue: Default): LenientObjectSchema<Subschema, LenientObjectSource<Subschema>, LenientObjectModel<Subschema>, Required, Default>;
119
+ /**
120
+ * Creates a schema used to validate an object.
121
+ *
122
+ * This function can be used to create schemas that describe a value's object hierarchy.
123
+ *
124
+ * @note This function creates schemas that differ from that of `$.Object` in that schemas created by this function can have ANY string keys
125
+ * and only their values will be validated against the supplied `subschema`.
126
+ *
127
+ * @example
128
+ * ```ts
129
+ * $.DynamicObject($.String()).validate({ name: "Jeremy", age: 1, randomKey: true })
130
+ * ```
131
+ * ->
132
+ * ```ts
133
+ * { "name": "Jeremy", "age": "1", "randomKey": "true" }
134
+ * ```
135
+ *
136
+ * @param subschema Schema used to validate values within an object with dynamic keys.
137
+ * @param required Flag representing whether the schema should enforce a value's presence.
138
+ * @param defaultValue A default the schema will use during validation if a value is not present.
139
+ * @returns A schema used to validate an object.
140
+ */
141
+ function DynamicObject<Subschema extends BaseSchemaAny>(subschema: Subschema): DynamicObjectSchema<Subschema, true, undefined>;
142
+ function DynamicObject<Subschema extends BaseSchemaAny, Required extends boolean>(subschema: Subschema, required: Required): DynamicObjectSchema<Subschema, Required, undefined>;
143
+ function DynamicObject<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<DynamicObjectSource<Subschema>>>(subschema: Subschema, required: Required, defaultValue: Default): DynamicObjectSchema<Subschema, Required, Default>;
33
144
  function Array<Subschema extends BaseSchemaAny>(subschema: Subschema): ArraySchema<Subschema, true, undefined>;
34
145
  function Array<Subschema extends BaseSchemaAny, Required extends boolean>(subschema: Subschema, required: Required): ArraySchema<Subschema, Required, undefined>;
35
146
  function Array<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<ArraySource<Subschema>>>(subschema: Subschema, required: Required, defaultValue: Default): ArraySchema<Subschema, Required, Default>;
36
147
  function Enumeration<Member extends string>(subschema: TypedMembers<Member>): EnumerationSchema<Member, true, undefined>;
37
148
  function Enumeration<Member extends string, Required extends boolean>(subschema: TypedMembers<Member>, required: Required): EnumerationSchema<Member, Required, undefined>;
38
149
  function Enumeration<Member extends string, Required extends boolean, Default extends DefaultValue<Member>>(subschema: TypedMembers<Member>, required: Required, defaultValue: Default): EnumerationSchema<Member, Required, Default>;
39
- function DynamicObject<Subschema extends BaseSchemaAny>(subschema: Subschema): DynamicObjectSchema<Subschema, true, undefined>;
40
- function DynamicObject<Subschema extends BaseSchemaAny, Required extends boolean>(subschema: Subschema, required: Required): DynamicObjectSchema<Subschema, Required, undefined>;
41
- function DynamicObject<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<DynamicObjectSource<Subschema>>>(subschema: Subschema, required: Required, defaultValue: Default): DynamicObjectSchema<Subschema, Required, Default>;
42
150
  function OrSet<MemberSchema extends BaseSchemaAny>(subschema: TypedMembers<MemberSchema>): OrSetSchema<MemberSchema, true, undefined>;
43
151
  function OrSet<MemberSchema extends BaseSchemaAny, Required extends boolean>(subschema: TypedMembers<MemberSchema>, required: Required): OrSetSchema<MemberSchema, Required, undefined>;
44
152
  function OrSet<MemberSchema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<OrSetSchemaSource<MemberSchema>>>(subschema: TypedMembers<MemberSchema>, required: Required, defaultValue: Default): OrSetSchema<MemberSchema, Required, Default>;
153
+ function Constant<Constant extends ConstantSource>(constant: Constant): ConstantSchema<Constant, true, undefined>;
154
+ function Constant<Constant extends ConstantSource, Required extends boolean>(constant: Constant, required: Required): ConstantSchema<Constant, Required, undefined>;
155
+ function Constant<Constant extends ConstantSource, Required extends boolean, Default extends DefaultValue<Constant>>(constant: Constant, required: Required, defaultValue: Default): ConstantSchema<Constant, Required, Default>;
45
156
  type TypedMembers<Member extends any> = {
46
157
  $members: Member[];
47
158
  };
package/build/index.d.ts CHANGED
@@ -5,9 +5,11 @@ export * from "./schema/AnySchema";
5
5
  export * from "./schema/ArraySchema";
6
6
  export * from "./schema/BaseSchema";
7
7
  export * from "./schema/BooleanSchema";
8
+ export * from "./schema/ConstantSchema";
8
9
  export * from "./schema/DateSchema";
9
10
  export * from "./schema/DynamicObjectSchema";
10
11
  export * from "./schema/EnumerationSchema";
12
+ export * from "./schema/LenientObject";
11
13
  export * from "./schema/NumberSchema";
12
14
  export * from "./schema/ObjectSchema";
13
15
  export * from "./schema/OrSetSchema";
package/build/index.js CHANGED
@@ -99,28 +99,32 @@
99
99
  };
100
100
  try {
101
101
  result = this._executeAdditionalValidator(result, pass, "beforeAll");
102
+ // Handle Default
102
103
  result = this._executeAdditionalValidator(result, pass, "beforeDefault");
103
104
  if (!BaseSchema.isPresent(result) && this.hasDefault()) {
104
105
  result = this.getDefault(pass);
105
106
  }
106
107
  result = this._executeAdditionalValidator(result, pass, "afterDefault");
107
- result = this._executeAdditionalValidator(result, pass, "beforeConversion");
108
- if (BaseSchema.isPresent(result)) {
109
- if (BaseSchema.getType(result) !== this.type) {
110
- result = this.convert(result, pass);
111
- }
112
- result = this._validate(result, presentOptions, pass);
113
- result = this._executeAdditionalValidator(result, pass, "afterConversion");
114
- }
115
- else {
116
- if (this._required) {
117
- throw pass.causeError(pass.path.length > 0 ? `Missing required value at "${pass.path.join(".")}".` : "Missing required value.");
108
+ // Handle Required
109
+ if (!BaseSchema.isPresent(result)) {
110
+ if (this.isRequired()) {
111
+ throw pass.causeError(pass.path.length > 0 ? `Missing required ${this.type} at path "${pass.path.join(".")}".` : `Missing required ${this.type}.`);
118
112
  }
119
113
  else {
120
114
  result = undefined;
121
115
  }
122
116
  }
123
- if (this._required || result !== undefined) {
117
+ // Handle Conversion
118
+ if (result !== undefined) {
119
+ result = this._executeAdditionalValidator(result, pass, "beforeConversion");
120
+ if (BaseSchema.getType(result) !== this.type) {
121
+ result = this.convert(result, pass);
122
+ }
123
+ result = this._executeAdditionalValidator(result, pass, "afterConversion");
124
+ }
125
+ // Handle Validation
126
+ if (this.isRequired() || result !== undefined) {
127
+ result = this._validate(result, presentOptions, pass);
124
128
  result = this._executeAdditionalValidator(result, pass, "afterAll");
125
129
  }
126
130
  }
@@ -245,12 +249,12 @@
245
249
  }
246
250
  }
247
251
  /**
248
- * Checks to see if a value is present. (Not null or undefined)
252
+ * Checks to see if a value is present. (Not undefined)
249
253
  * @param value The value to check the presence of.
250
- * @returns true if value is not null or undefined, false otherwise.
254
+ * @returns true if value is undefined, false otherwise.
251
255
  */
252
256
  static isPresent(value) {
253
- return value !== undefined && value !== null;
257
+ return value !== undefined;
254
258
  }
255
259
  static getType(value) {
256
260
  if (value === null) {
@@ -315,6 +319,15 @@
315
319
  else if (typeof value === "string") {
316
320
  return [value];
317
321
  }
322
+ else if (typeof value === "object") {
323
+ return Object.entries(value).reduce((array, [key, item]) => {
324
+ const index = parseInt(key);
325
+ pass.assert(!isNaN(index), `Unable to convert ${BaseSchema.getType(value)} to array. ` +
326
+ `Encountered key that is not a valid array index: "${key}"`);
327
+ array[index] = item;
328
+ return array;
329
+ }, []);
330
+ }
318
331
  else {
319
332
  throw pass.causeError(`Unable to convert ${BaseSchema.getType(value)} to array.`);
320
333
  }
@@ -369,6 +382,31 @@
369
382
  }
370
383
  }
371
384
 
385
+ class ConstantSchema extends BaseSchema {
386
+ value;
387
+ constructor(subschema, required, defaultValue, additionalValidationPasses) {
388
+ super(required, defaultValue, additionalValidationPasses);
389
+ this.value = subschema;
390
+ }
391
+ get type() { return BaseSchema.getType(this.value); }
392
+ _validate(source, options, pass) {
393
+ pass.assert(this.value === source, `Supplied source (${JSON.stringify(source)}) did not match expected constant value (${JSON.stringify(this.value)}).`);
394
+ return source;
395
+ }
396
+ convert(value, pass) {
397
+ return value;
398
+ }
399
+ clone() {
400
+ return new ConstantSchema(this.value, this._required, this._default, this._additionalValidationPasses);
401
+ }
402
+ getJsonSchema() {
403
+ return {
404
+ type: "string",
405
+ description: this._getJsonSchemaDescription(),
406
+ };
407
+ }
408
+ }
409
+
372
410
  const StandardDate = globalThis.Date;
373
411
  class DateSchema extends BaseSchema {
374
412
  get type() { return "Date"; }
@@ -500,6 +538,97 @@
500
538
  }
501
539
  }
502
540
 
541
+ class LenientObjectSchema extends BaseSchema {
542
+ subschema;
543
+ constructor(subschema, required, defaultValue, additionalValidationPasses) {
544
+ super(required, defaultValue, additionalValidationPasses);
545
+ this.subschema = subschema;
546
+ }
547
+ get type() { return "object"; }
548
+ _validate(source, options, pass) {
549
+ const inputObject = source;
550
+ let outputObject = { ...inputObject };
551
+ for (const key in this.subschema) {
552
+ const nestedSchema = this.subschema[key];
553
+ const nestedValue = inputObject[key];
554
+ outputObject[key] = this.subschema[key].validate(inputObject[key], options, pass.next([...pass.path, key], nestedSchema, nestedValue));
555
+ }
556
+ return outputObject;
557
+ }
558
+ convert(value, pass) {
559
+ pass.assert(typeof value === "object", `Unable to convert ${LenientObjectSchema.getType(value)} to object.`);
560
+ const inputObject = value;
561
+ const outputObject = { ...inputObject };
562
+ for (const key in this.subschema) {
563
+ const nestedSchema = this.subschema[key];
564
+ const nestedValue = inputObject[key];
565
+ outputObject[key] = nestedSchema.convert(nestedValue, pass.next([...pass.path, key], nestedSchema, nestedValue));
566
+ }
567
+ return outputObject;
568
+ }
569
+ // Works at runtime, can't figure out typing.
570
+ // public extend<
571
+ // ExtensionSubschema extends LenientObjectSubschema,
572
+ // ExtensionSource extends LenientObjectSource<ExtensionSubschema>,
573
+ // ExtensionModel extends LenientObjectModel<ExtensionSubschema>,
574
+ // ExtensionDefault extends DefaultValue<ExtensionSource>
575
+ // >(schema: LenientObjectSchema<ExtensionSubschema, ExtensionSource, ExtensionModel, Required, ExtensionDefault>):
576
+ // // LenientObjectSchema<ExtensionSubschema & Subschema, ExtensionSource & Source, ExtensionModel & Model, Required, DefaultValue<ExtensionSource & Source>>
577
+ // ExtendedLenientObjectSchema<this, LenientObjectSchema<ExtensionSubschema, ExtensionSource, ExtensionModel, Required, ExtensionDefault>> {
578
+ // let defaultValue: any = undefined;
579
+ // if (this.hasDefault() !== schema.hasDefault()) {
580
+ // throw new Error("Both or neither default values must be specified in order to extend a schema!");
581
+ // }
582
+ // if (this.hasDefault() && schema.hasDefault()) {
583
+ // defaultValue = (pass: ValidationPass) => {
584
+ // return {
585
+ // ...this.getDefault(pass),
586
+ // ...schema.getDefault(pass)
587
+ // };
588
+ // };
589
+ // }
590
+ // const subschema: any = { ...this.subschema };
591
+ // for (const key in schema.subschema) {
592
+ // if (key in this.subschema) {
593
+ // this.subschema[key].extend(schema.subschema[key]);
594
+ // } else {
595
+ // subschema[key] = schema.subschema[key];
596
+ // }
597
+ // }
598
+ // return new LenientObjectSchema(subschema, this._required, defaultValue) as any;
599
+ // }
600
+ getJsonSchema() {
601
+ const properties = {};
602
+ for (const key in this.subschema) {
603
+ properties[key] = this.subschema[key].getJsonSchema();
604
+ }
605
+ return {
606
+ type: "object",
607
+ description: this._getJsonSchemaDescription(),
608
+ properties
609
+ };
610
+ }
611
+ clone() {
612
+ const subschema = {};
613
+ for (const key in this.subschema) {
614
+ subschema[key] = this.subschema[key].clone();
615
+ }
616
+ return new LenientObjectSchema(subschema, this._required, this._default, this._additionalValidationPasses);
617
+ }
618
+ toString(level = 0) {
619
+ const indent = " ";
620
+ const prefix = indent.repeat(level);
621
+ const pieces = [];
622
+ pieces.push(`${super.toString()}({\n`);
623
+ for (const schemaKey in this.subschema) {
624
+ const subschema = this.subschema[schemaKey];
625
+ pieces.push(`${prefix}${indent}${schemaKey}: ${subschema.toString(level + 1)}\n`);
626
+ }
627
+ pieces.push(`${prefix}})`);
628
+ return pieces.join("");
629
+ }
630
+ }
631
+
503
632
  class NumberSchema extends BaseSchema {
504
633
  get type() { return "number"; }
505
634
  _validate(source, options, pass) {
@@ -587,21 +716,6 @@
587
716
  }
588
717
  return model;
589
718
  }
590
- extend(schema) {
591
- let defaultValue = undefined;
592
- if (this.hasDefault() !== schema.hasDefault()) {
593
- throw new Error("Both or neither default values must be specified in order to extend a schema!");
594
- }
595
- if (this.hasDefault() && schema.hasDefault()) {
596
- defaultValue = (pass) => {
597
- return {
598
- ...this.getDefault(pass),
599
- ...schema.getDefault(pass)
600
- };
601
- };
602
- }
603
- return new ObjectSchema({ ...this.subschema, ...schema.subschema }, this._required, defaultValue);
604
- }
605
719
  getJsonSchema() {
606
720
  const properties = {};
607
721
  for (const key in this.subschema) {
@@ -644,34 +758,52 @@
644
758
  return "string";
645
759
  }
646
760
  _validate(source, options, pass) {
647
- let result = source;
648
- if (result !== undefined) {
649
- let done = false;
650
- const failureMessages = [];
651
- for (let i = 0; i < this.schemas.length && !done; i++) {
652
- const schema = this.schemas[i];
653
- try {
654
- if (BaseSchema.getType(result) === schema.type) {
655
- result = schema.validate(result, options, pass);
656
- done = true;
657
- }
658
- }
659
- catch (error) {
660
- if (error instanceof Error) {
661
- failureMessages.push(`Schema #${i + 1}: ${error.message}`);
662
- }
663
- else {
664
- failureMessages.push(`Schema #${i + 1}: ${String(error)}`);
665
- }
761
+ const errors = {};
762
+ for (let i = 0; i < this.schemas.length; i++) {
763
+ const schema = this.schemas[i];
764
+ try {
765
+ if (BaseSchema.getType(source) !== schema.type) {
766
+ throw new ValidationError(new ValidationPass(schema, source, undefined), `Conversions for schemas in an OrSet are disabled.`);
666
767
  }
768
+ return schema.validate(source);
667
769
  }
668
- if (!done) {
669
- failureMessages.push(`Conversions for schemas in an OrSet are disabled.`);
770
+ catch (error) {
771
+ errors[i] = error;
670
772
  }
671
- pass.assert(failureMessages.length === 0, `Provided value (${BaseSchema.getType(result)}) matched no schemas ` +
672
- `(${this.schemas.map((schema) => schema.type).join(", ")}).\n${failureMessages.join("\n")}`);
673
773
  }
674
- return result;
774
+ const errorMessages = Object.entries(errors).map(([index, error]) => `Schema #${parseInt(index) + 1}: ${error === undefined ? "Unknown error." : error.message}`);
775
+ throw pass.causeError(`Provided value (${BaseSchema.getType(source)}) matched no schemas ` +
776
+ `(${this.schemas.map((schema) => schema.type).join(", ")}).\n` +
777
+ `${errorMessages.join("\n")}`);
778
+ // let result = source;
779
+ // if (result !== undefined) {
780
+ // let done = false;
781
+ // const failureMessages: string[] = [];
782
+ // for (let i = 0; i < this.schemas.length && !done; i++) {
783
+ // const schema = this.schemas[i];
784
+ // try {
785
+ // if (BaseSchema.getType(result) === schema.type) {
786
+ // result = schema.validate(result, options, pass);
787
+ // done = true;
788
+ // }
789
+ // } catch (error) {
790
+ // if (error instanceof Error) {
791
+ // failureMessages.push(`Schema #${i + 1}: ${error.message}`);
792
+ // } else {
793
+ // failureMessages.push(`Schema #${i + 1}: ${String(error)}`);
794
+ // }
795
+ // }
796
+ // }
797
+ // if (!done) {
798
+ // failureMessages.push(`Conversions for schemas in an OrSet are disabled.`);
799
+ // }
800
+ // pass.assert(
801
+ // failureMessages.length === 0,
802
+ // `Provided value (${BaseSchema.getType(result)}) matched no schemas ` +
803
+ // `(${this.schemas.map((schema) => schema.type).join(", ")}).\n${failureMessages.join("\n")}`
804
+ // );
805
+ // }
806
+ // return result;
675
807
  }
676
808
  convert(value, pass) {
677
809
  return value;
@@ -753,6 +885,9 @@
753
885
  }
754
886
  }
755
887
 
888
+ /**
889
+ * A collection of helper functions used to create the standard set of schemas.
890
+ */
756
891
  exports.Schema = void 0;
757
892
  (function (Schema) {
758
893
  function String(required = true, defaultValue = undefined) {
@@ -779,6 +914,14 @@
779
914
  return new ObjectSchema(subschema, required, defaultValue);
780
915
  }
781
916
  Schema.Object = Object;
917
+ function LenientObject(subschema, required = true, defaultValue = undefined) {
918
+ return new LenientObjectSchema(subschema, required, defaultValue);
919
+ }
920
+ Schema.LenientObject = LenientObject;
921
+ function DynamicObject(subschema, required = true, defaultValue = undefined) {
922
+ return new DynamicObjectSchema(subschema, required, defaultValue);
923
+ }
924
+ Schema.DynamicObject = DynamicObject;
782
925
  function Array(subschema, required = true, defaultValue = undefined) {
783
926
  return new ArraySchema(subschema, required, defaultValue);
784
927
  }
@@ -787,14 +930,14 @@
787
930
  return new EnumerationSchema(members.$members, required, defaultValue);
788
931
  }
789
932
  Schema.Enumeration = Enumeration;
790
- function DynamicObject(subschema, required = true, defaultValue = undefined) {
791
- return new DynamicObjectSchema(subschema, required, defaultValue);
792
- }
793
- Schema.DynamicObject = DynamicObject;
794
933
  function OrSet(members, required = true, defaultValue = undefined) {
795
934
  return new OrSetSchema(members.$members, required, defaultValue);
796
935
  }
797
936
  Schema.OrSet = OrSet;
937
+ function Constant(value, required = true, defaultValue = undefined) {
938
+ return new ConstantSchema(value, required, defaultValue);
939
+ }
940
+ Schema.Constant = Constant;
798
941
  function Members(...members) {
799
942
  /*
800
943
  * HACK START: The hermes JS engine doesn't use globalThis.Array when interpreting `...members`
@@ -823,9 +966,11 @@
823
966
  exports.ArraySchema = ArraySchema;
824
967
  exports.BaseSchema = BaseSchema;
825
968
  exports.BooleanSchema = BooleanSchema;
969
+ exports.ConstantSchema = ConstantSchema;
826
970
  exports.DateSchema = DateSchema;
827
971
  exports.DynamicObjectSchema = DynamicObjectSchema;
828
972
  exports.EnumerationSchema = EnumerationSchema;
973
+ exports.LenientObjectSchema = LenientObjectSchema;
829
974
  exports.NumberSchema = NumberSchema;
830
975
  exports.ObjectSchema = ObjectSchema;
831
976
  exports.OrSetSchema = OrSetSchema;
@@ -2,7 +2,7 @@ import { ValidationPass } from "../error/ValidationPass";
2
2
  import { BaseSchemaAny } from "../typing/extended";
3
3
  import type { AdditionalValidationPasses, DefaultValue, ModelValue, SourceValue, ValidationOptions } from "../typing/toolbox";
4
4
  import { BaseSchema } from "./BaseSchema";
5
- export type ArraySource<Subschema extends BaseSchemaAny> = (Subschema extends BaseSchema<infer Source, any, infer Required, infer Default> ? (SourceValue<Source, Required, Default>[]) : never);
5
+ export type ArraySource<Subschema extends BaseSchemaAny> = (Subschema extends BaseSchema<infer Source, any, infer Required, infer Default> ? (Record<number, SourceValue<Source, Required, Default>> | SourceValue<Source, Required, Default>[]) : never);
6
6
  export type ArrayModel<Subschema extends BaseSchemaAny> = (Subschema extends BaseSchema<infer Source, infer Model, infer Required, infer Default> ? (ModelValue<Source, Model, Required, Default>[]) : never);
7
7
  export declare class ArraySchema<Subschema extends BaseSchemaAny, Required extends boolean, Default extends DefaultValue<ArraySource<Subschema>>> extends BaseSchema<ArraySource<Subschema>, ArrayModel<Subschema>, Required, Default> {
8
8
  readonly subschema: Subschema;
@@ -8,6 +8,17 @@ export declare abstract class BaseSchema<Source, Model, Required extends boolean
8
8
  constructor(required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<Source, Model>);
9
9
  abstract get type(): string;
10
10
  validate(source: SourceValue<Source, Required, Default>, options?: OptionalValidationOptions, pass?: ValidationPass): ModelValue<Source, Model, Required, Default>;
11
+ /**
12
+ * Responsible for validating that a supplied source matches the schema this class represents.
13
+ *
14
+ * Some assumptions can be made about `source`:
15
+ * * It will never be `null`/`undefined`.
16
+ * * Its type returned by `BaseSchema.getType` will match that of this classes `type` property.
17
+ *
18
+ * @param source Source to be validated.
19
+ * @param options Validation options.
20
+ * @param pass Validation pass.
21
+ */
11
22
  protected abstract _validate(source: ModelValue<Source, Model, Required, Default>, options: ValidationOptions, pass: ValidationPass): ModelValue<Source, Model, Required, Default>;
12
23
  abstract convert(value: Source, pass: ValidationPass): Model;
13
24
  custom(additionalValidator: AdditionalValidator<SourceValue<Source, Required, Default>>, type: AdditionalValidatorBeforeType): this;
@@ -47,10 +58,10 @@ export declare abstract class BaseSchema<Source, Model, Required extends boolean
47
58
  */
48
59
  toString(level?: number): string;
49
60
  /**
50
- * Checks to see if a value is present. (Not null or undefined)
61
+ * Checks to see if a value is present. (Not undefined)
51
62
  * @param value The value to check the presence of.
52
- * @returns true if value is not null or undefined, false otherwise.
63
+ * @returns true if value is undefined, false otherwise.
53
64
  */
54
- static isPresent(value: any): value is Exclude<Exclude<any, null>, undefined>;
65
+ static isPresent(value: any): value is Exclude<any, undefined>;
55
66
  static getType(value: any): any;
56
67
  }
@@ -0,0 +1,13 @@
1
+ import { ValidationPass } from "../error/ValidationPass";
2
+ import { AdditionalValidationPasses, DefaultValue, ModelValue, ValidationOptions } from "../typing/toolbox";
3
+ import { BaseSchema } from "./BaseSchema";
4
+ export type ConstantSource = string | number | boolean | null;
5
+ export declare class ConstantSchema<Constant extends ConstantSource, Required extends boolean, Default extends DefaultValue<Constant>> extends BaseSchema<Constant, Constant, Required, Default> {
6
+ readonly value: Constant;
7
+ constructor(subschema: Constant, required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<Constant, Constant>);
8
+ get type(): any;
9
+ protected _validate(source: ModelValue<Constant, Constant, Required, Default>, options: ValidationOptions, pass: ValidationPass): ModelValue<Constant, Constant, Required, Default>;
10
+ convert(value: Constant, pass: ValidationPass): Constant;
11
+ clone(): ConstantSchema<Constant, Required, Default>;
12
+ getJsonSchema(): object;
13
+ }
@@ -0,0 +1,31 @@
1
+ import { ValidationPass } from "../error/ValidationPass";
2
+ import { BaseSchemaAny } from "../typing/extended";
3
+ import type { AdditionalValidationPasses, DefaultValue, Merge, ModelRequirement, ModelValue, SourceRequirement, SourceValue, ValidationOptions } from "../typing/toolbox";
4
+ import { BaseSchema } from "./BaseSchema";
5
+ export type LenientObjectSubschema = {
6
+ [Key: string]: BaseSchemaAny;
7
+ };
8
+ export type LenientObjectSource<Subschema extends LenientObjectSubschema> = ({
9
+ [Key: string]: any;
10
+ } & Merge<{
11
+ [Key in keyof Subschema as SourceRequirement<Subschema[Key]> extends true ? Key : never]: (Subschema[Key] extends BaseSchema<infer Source, any, infer Required, infer Default> ? (SourceValue<Source, Required, Default>) : never);
12
+ }, {
13
+ [Key in keyof Subschema as SourceRequirement<Subschema[Key]> extends false ? Key : never]?: (Subschema[Key] extends BaseSchema<infer Source, any, infer Required, infer Default> ? (SourceValue<Source, Required, Default>) : never);
14
+ }>);
15
+ export type LenientObjectModel<Subschema extends LenientObjectSubschema> = ({
16
+ [Key: string]: any;
17
+ } & Merge<{
18
+ [Key in keyof Subschema as ModelRequirement<Subschema[Key]> extends true ? Key : never]: (Subschema[Key] extends BaseSchema<infer Source, infer Model, infer Required, infer Default> ? (ModelValue<Source, Model, Required, Default>) : never);
19
+ }, {
20
+ [Key in keyof Subschema as ModelRequirement<Subschema[Key]> extends false ? Key : never]?: (Subschema[Key] extends BaseSchema<infer Source, infer Model, infer Required, infer Default> ? (ModelValue<Source, Model, Required, Default>) : never);
21
+ }>);
22
+ export declare class LenientObjectSchema<Subschema extends LenientObjectSubschema, Source extends LenientObjectSource<Subschema>, Model extends LenientObjectModel<Subschema>, Required extends boolean, Default extends DefaultValue<Source>> extends BaseSchema<Source, Model, Required, Default> {
23
+ readonly subschema: Subschema;
24
+ constructor(subschema: Subschema, required: Required, defaultValue: Default, additionalValidationPasses?: AdditionalValidationPasses<Source, Model>);
25
+ get type(): string;
26
+ protected _validate(source: ModelValue<Source, Model, Required, Default>, options: ValidationOptions, pass: ValidationPass): ModelValue<Source, Model, Required, Default>;
27
+ convert(value: Source, pass: ValidationPass): Model;
28
+ getJsonSchema(): object;
29
+ clone(): LenientObjectSchema<Subschema, Source, Model, Required, Default>;
30
+ toString(level?: number): string;
31
+ }
@@ -21,7 +21,6 @@ export declare class ObjectSchema<Subschema extends ObjectSubschema, Required ex
21
21
  get type(): string;
22
22
  protected _validate(source: ModelValue<ObjectSource<Subschema>, ObjectModel<Subschema>, Required, Default>, options: ValidationOptions, pass: ValidationPass): ModelValue<ObjectSource<Subschema>, ObjectModel<Subschema>, Required, Default>;
23
23
  convert(value: ObjectSource<Subschema>, pass: ValidationPass): ObjectModel<Subschema>;
24
- extend<ExtensionSubschema extends ObjectSubschema, ExtensionDefault extends DefaultValue<ObjectSource<ExtensionSubschema>>>(schema: ObjectSchema<ExtensionSubschema, Required, ExtensionDefault>): ObjectSchema<Subschema & ExtensionSubschema, Required, any>;
25
24
  getJsonSchema(): object;
26
25
  clone(): ObjectSchema<Subschema, Required, Default>;
27
26
  toString(level?: number): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lucania/schema",
3
- "version": "3.0.1",
3
+ "version": "3.0.3",
4
4
  "description": "A schema module for compile-time and runtime type checking.",
5
5
  "main": "./build/index.js",
6
6
  "types": "./build/index.d.ts",