shelving 1.147.0 → 1.148.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/schema/ColorSchema.d.ts +1 -1
  3. package/schema/ColorSchema.js +2 -2
  4. package/schema/DataSchema.d.ts +12 -6
  5. package/schema/DataSchema.js +19 -2
  6. package/schema/DateSchema.d.ts +1 -1
  7. package/schema/DateSchema.js +2 -2
  8. package/schema/EmailSchema.d.ts +1 -1
  9. package/schema/EmailSchema.js +2 -2
  10. package/schema/EntitySchema.d.ts +1 -1
  11. package/schema/EntitySchema.js +2 -2
  12. package/schema/FileSchema.d.ts +1 -1
  13. package/schema/FileSchema.js +2 -2
  14. package/schema/KeySchema.d.ts +1 -1
  15. package/schema/KeySchema.js +2 -2
  16. package/schema/LinkSchema.d.ts +1 -1
  17. package/schema/LinkSchema.js +2 -2
  18. package/schema/NullableSchema.d.ts +15 -0
  19. package/schema/NullableSchema.js +14 -0
  20. package/schema/NumberSchema.d.ts +3 -3
  21. package/schema/NumberSchema.js +4 -4
  22. package/schema/OptionalSchema.d.ts +9 -12
  23. package/schema/OptionalSchema.js +9 -8
  24. package/schema/PhoneSchema.d.ts +1 -1
  25. package/schema/PhoneSchema.js +2 -2
  26. package/schema/SlugSchema.d.ts +1 -1
  27. package/schema/SlugSchema.js +2 -2
  28. package/schema/TextSchema.d.ts +2 -2
  29. package/schema/TextSchema.js +3 -3
  30. package/schema/ThroughSchema.d.ts +5 -4
  31. package/schema/ThroughSchema.js +1 -1
  32. package/schema/TimeSchema.d.ts +1 -1
  33. package/schema/TimeSchema.js +2 -2
  34. package/schema/UUIDSchema.d.ts +1 -1
  35. package/schema/UUIDSchema.js +2 -2
  36. package/schema/index.d.ts +1 -2
  37. package/schema/index.js +1 -2
  38. package/test/people.js +2 -2
  39. package/util/validate.d.ts +3 -1
  40. package/util/validate.js +10 -6
  41. package/schema/ItemSchema.d.ts +0 -33
  42. package/schema/ItemSchema.js +0 -21
  43. package/schema/PartialSchema.d.ts +0 -18
  44. package/schema/PartialSchema.js +0 -17
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.147.0",
14
+ "version": "1.148.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -15,4 +15,4 @@ export declare class ColorSchema extends TextSchema {
15
15
  /** Valid color hex string, e.g. `#00CCFF` (required because empty string is invalid). */
16
16
  export declare const COLOR: ColorSchema;
17
17
  /** Valid color hex string, e.g. `#00CCFF`, or `null` */
18
- export declare const OPTIONAL_COLOR: import("./OptionalSchema.js").OptionalSchema<string>;
18
+ export declare const NULLABLE_COLOR: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,4 +1,4 @@
1
- import { OPTIONAL } from "./OptionalSchema.js";
1
+ import { NULLABLE } from "./NullableSchema.js";
2
2
  import { TextSchema } from "./TextSchema.js";
3
3
  const COLOR_REGEXP = /^#[0-9A-F]{6}$/;
4
4
  const NOT_HEX_REGEXP = /[^0-9A-F]/g;
@@ -32,4 +32,4 @@ export class ColorSchema extends TextSchema {
32
32
  /** Valid color hex string, e.g. `#00CCFF` (required because empty string is invalid). */
33
33
  export const COLOR = new ColorSchema({});
34
34
  /** Valid color hex string, e.g. `#00CCFF`, or `null` */
35
- export const OPTIONAL_COLOR = OPTIONAL(COLOR);
35
+ export const NULLABLE_COLOR = NULLABLE(COLOR);
@@ -1,15 +1,17 @@
1
1
  import type { Data, Database } from "../util/data.js";
2
- import type { Validators } from "../util/validate.js";
3
- import type { OptionalSchema } from "./OptionalSchema.js";
2
+ import type { Item } from "../util/item.js";
3
+ import type { Validator, Validators } from "../util/validate.js";
4
+ import type { NullableSchema } from "./NullableSchema.js";
4
5
  import type { SchemaOptions } from "./Schema.js";
5
6
  import { Schema } from "./Schema.js";
6
- /** Allowed options for `DataSchema` */
7
+ /** Allowed options for `PropsSchema` (a schema that has props). */
7
8
  export interface DataSchemaOptions<T extends Data> extends SchemaOptions {
9
+ readonly id?: Validator<string>;
8
10
  readonly props: Validators<T>;
9
11
  readonly value?: Partial<T> | undefined;
10
12
  }
11
13
  /** Validate a data object. */
12
- export declare class DataSchema<T extends Data> extends Schema<T> {
14
+ export declare class DataSchema<T extends Data> extends Schema<unknown> {
13
15
  readonly value: Partial<T>;
14
16
  readonly props: Validators<T>;
15
17
  constructor({ props, title, value, ...options }: DataSchemaOptions<T>);
@@ -19,7 +21,11 @@ export declare class DataSchema<T extends Data> extends Schema<T> {
19
21
  export type DataSchemas<T extends Database> = {
20
22
  [K in keyof T]: DataSchema<T[K]>;
21
23
  };
22
- /** Valid data object with specifed properties. */
24
+ /** Create a `DataSchema` for a set of properties. */
23
25
  export declare const DATA: <T extends Data>(props: Validators<T>) => DataSchema<T>;
24
26
  /** Valid data object with specifed properties, or `null` */
25
- export declare const OPTIONAL_DATA: <T extends Data>(props: Validators<T>) => OptionalSchema<T>;
27
+ export declare const NULLABLE_DATA: <T extends Data>(props: Validators<T>) => NullableSchema<T>;
28
+ /** Create a `DataSchema` that validates partially, i.e. properties can be their value, or `undefined` */
29
+ export declare function PARTIAL<T extends Data>(source: Validators<T> | DataSchema<T>): DataSchema<Partial<T>>;
30
+ /** Create a `DataSchema` that validates a data item, i.e. it has a string `.id` property. */
31
+ export declare function ITEM<T extends Data>(validators: Validators<T> | DataSchema<T>, id?: Validator<string>): DataSchema<Item<T>>;
@@ -1,6 +1,9 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { isData } from "../util/data.js";
3
+ import { mapObject } from "../util/transform.js";
3
4
  import { validateData } from "../util/validate.js";
5
+ import { KEY } from "./KeySchema.js";
6
+ import { NULLABLE } from "./NullableSchema.js";
4
7
  import { OPTIONAL } from "./OptionalSchema.js";
5
8
  import { Schema } from "./Schema.js";
6
9
  /** Validate a data object. */
@@ -16,7 +19,21 @@ export class DataSchema extends Schema {
16
19
  return validateData(unsafeValue, this.props);
17
20
  }
18
21
  }
19
- /** Valid data object with specifed properties. */
22
+ /** Create a `DataSchema` for a set of properties. */
20
23
  export const DATA = (props) => new DataSchema({ props });
21
24
  /** Valid data object with specifed properties, or `null` */
22
- export const OPTIONAL_DATA = (props) => OPTIONAL(new DataSchema({ props }));
25
+ export const NULLABLE_DATA = (props) => NULLABLE(new DataSchema({ props }));
26
+ /** Create a `DataSchema` that validates partially, i.e. properties can be their value, or `undefined` */
27
+ export function PARTIAL(source) {
28
+ const props = source instanceof DataSchema ? source.props : source;
29
+ return new DataSchema({
30
+ props: mapObject(props, OPTIONAL),
31
+ });
32
+ }
33
+ /** Create a `DataSchema` that validates a data item, i.e. it has a string `.id` property. */
34
+ export function ITEM(validators, id = KEY) {
35
+ const props = validators instanceof DataSchema ? validators.props : validators;
36
+ return new DataSchema({
37
+ props: { id, ...props },
38
+ });
39
+ }
@@ -19,4 +19,4 @@ export declare class DateSchema extends Schema<string> {
19
19
  /** Valid date, e.g. `2005-09-12` (required because falsy values are invalid). */
20
20
  export declare const DATE: DateSchema;
21
21
  /** Valid date, e.g. `2005-09-12`, or `null` */
22
- export declare const OPTIONAL_DATE: import("./OptionalSchema.js").OptionalSchema<string>;
22
+ export declare const NULLABLE_DATE: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,7 +1,7 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { getDate, requireYMD } from "../util/date.js";
3
3
  import { formatDate } from "../util/format.js";
4
- import { OPTIONAL } from "./OptionalSchema.js";
4
+ import { NULLABLE } from "./NullableSchema.js";
5
5
  import { Schema } from "./Schema.js";
6
6
  /** Define a valid date in YMD format, e.g. `2005-09-12` */
7
7
  export class DateSchema extends Schema {
@@ -26,4 +26,4 @@ export class DateSchema extends Schema {
26
26
  /** Valid date, e.g. `2005-09-12` (required because falsy values are invalid). */
27
27
  export const DATE = new DateSchema({});
28
28
  /** Valid date, e.g. `2005-09-12`, or `null` */
29
- export const OPTIONAL_DATE = OPTIONAL(DATE);
29
+ export const NULLABLE_DATE = NULLABLE(DATE);
@@ -24,4 +24,4 @@ export declare class EmailSchema extends TextSchema {
24
24
  /** Valid email, e.g. `test@test.com` */
25
25
  export declare const EMAIL: EmailSchema;
26
26
  /** Valid optional email, e.g. `test@test.com`, or `null` */
27
- export declare const OPTIONAL_EMAIL: import("./OptionalSchema.js").OptionalSchema<string>;
27
+ export declare const NULLABLE_EMAIL: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,4 +1,4 @@
1
- import { OPTIONAL } from "./OptionalSchema.js";
1
+ import { NULLABLE } from "./NullableSchema.js";
2
2
  import { TextSchema } from "./TextSchema.js";
3
3
  const R_MATCH = /^[a-z0-9](?:[a-zA-Z0-9._+-]{0,62}[a-zA-Z0-9])?@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.){1,3}(?:[a-z]{2,63}|xn--[a-z0-9-]{0,58}[a-z0-9])$/;
4
4
  /**
@@ -37,4 +37,4 @@ export class EmailSchema extends TextSchema {
37
37
  /** Valid email, e.g. `test@test.com` */
38
38
  export const EMAIL = new EmailSchema({});
39
39
  /** Valid optional email, e.g. `test@test.com`, or `null` */
40
- export const OPTIONAL_EMAIL = OPTIONAL(EMAIL);
40
+ export const NULLABLE_EMAIL = NULLABLE(EMAIL);
@@ -14,4 +14,4 @@ export declare class EntitySchema<T extends string> extends StringSchema {
14
14
  /** Valid file, e.g. `challenge:a1b2c3` */
15
15
  export declare const ENTITY: EntitySchema<string>;
16
16
  /** Valid optional file, e.g. `file.txt`, or `null` */
17
- export declare const OPTIONAL_ENTITY: import("./OptionalSchema.js").OptionalSchema<`${string}:${string}`>;
17
+ export declare const NULLABLE_ENTITY: import("./NullableSchema.js").NullableSchema<`${string}:${string}`>;
@@ -1,7 +1,7 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { isItem } from "../util/array.js";
3
3
  import { getEntity } from "../util/entity.js";
4
- import { OPTIONAL } from "./OptionalSchema.js";
4
+ import { NULLABLE } from "./NullableSchema.js";
5
5
  import { StringSchema } from "./StringSchema.js";
6
6
  /** Validate a file name matching one or more extensions. */
7
7
  export class EntitySchema extends StringSchema {
@@ -23,4 +23,4 @@ export class EntitySchema extends StringSchema {
23
23
  /** Valid file, e.g. `challenge:a1b2c3` */
24
24
  export const ENTITY = new EntitySchema({});
25
25
  /** Valid optional file, e.g. `file.txt`, or `null` */
26
- export const OPTIONAL_ENTITY = OPTIONAL(ENTITY);
26
+ export const NULLABLE_ENTITY = NULLABLE(ENTITY);
@@ -13,4 +13,4 @@ export declare class FileSchema extends StringSchema {
13
13
  /** Valid file, e.g. `file.txt` */
14
14
  export declare const FILE: FileSchema;
15
15
  /** Valid optional file, e.g. `file.txt`, or `null` */
16
- export declare const OPTIONAL_FILE: import("./OptionalSchema.js").OptionalSchema<string>;
16
+ export declare const NULLABLE_FILE: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,7 +1,7 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { getFileExtension } from "../util/file.js";
3
3
  import { isProp } from "../util/object.js";
4
- import { OPTIONAL } from "./OptionalSchema.js";
4
+ import { NULLABLE } from "./NullableSchema.js";
5
5
  import { StringSchema } from "./StringSchema.js";
6
6
  /** Validate a file name matching one or more extensions. */
7
7
  export class FileSchema extends StringSchema {
@@ -23,4 +23,4 @@ export class FileSchema extends StringSchema {
23
23
  /** Valid file, e.g. `file.txt` */
24
24
  export const FILE = new FileSchema({});
25
25
  /** Valid optional file, e.g. `file.txt`, or `null` */
26
- export const OPTIONAL_FILE = OPTIONAL(FILE);
26
+ export const NULLABLE_FILE = NULLABLE(FILE);
@@ -14,4 +14,4 @@ export declare class KeySchema extends StringSchema {
14
14
  /** Valid database key. */
15
15
  export declare const KEY: KeySchema;
16
16
  /** Valid optional database key. */
17
- export declare const OPTIONAL_KEY: import("./OptionalSchema.js").OptionalSchema<string>;
17
+ export declare const NULLABLE_KEY: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,4 +1,4 @@
1
- import { OPTIONAL } from "./OptionalSchema.js";
1
+ import { NULLABLE } from "./NullableSchema.js";
2
2
  import { StringSchema } from "./StringSchema.js";
3
3
  const R_NOT_CHAR = /[^a-zA-Z0-9]/g;
4
4
  /**
@@ -20,4 +20,4 @@ export class KeySchema extends StringSchema {
20
20
  /** Valid database key. */
21
21
  export const KEY = new KeySchema({ title: "ID" });
22
22
  /** Valid optional database key. */
23
- export const OPTIONAL_KEY = OPTIONAL(KEY);
23
+ export const NULLABLE_KEY = NULLABLE(KEY);
@@ -24,4 +24,4 @@ export declare class LinkSchema extends TextSchema {
24
24
  /** Valid link, e.g. `https://www.google.com` */
25
25
  export declare const LINK: LinkSchema;
26
26
  /** Valid link, e.g. `https://www.google.com`, or `null` */
27
- export declare const OPTIONAL_LINK: import("./OptionalSchema.js").OptionalSchema<`${string}:${string}`>;
27
+ export declare const NULLABLE_LINK: import("./NullableSchema.js").NullableSchema<`${string}:${string}`>;
@@ -1,6 +1,6 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { getLinkURL } from "../util/link.js";
3
- import { OPTIONAL } from "./OptionalSchema.js";
3
+ import { NULLABLE } from "./NullableSchema.js";
4
4
  import { TextSchema } from "./TextSchema.js";
5
5
  /**
6
6
  * Type of `StringSchema` that defines a valid URL link.
@@ -37,4 +37,4 @@ export class LinkSchema extends TextSchema {
37
37
  /** Valid link, e.g. `https://www.google.com` */
38
38
  export const LINK = new LinkSchema({});
39
39
  /** Valid link, e.g. `https://www.google.com`, or `null` */
40
- export const OPTIONAL_LINK = OPTIONAL(LINK);
40
+ export const NULLABLE_LINK = NULLABLE(LINK);
@@ -0,0 +1,15 @@
1
+ import type { Schema, SchemaOptions } from "./Schema.js";
2
+ import { ThroughSchema } from "./ThroughSchema.js";
3
+ /** Allowed options for `NullableSchema` */
4
+ export interface NullableSchemaOptions<T> extends SchemaOptions {
5
+ readonly source: Schema<T>;
6
+ readonly value?: T | null;
7
+ }
8
+ /** Validate a value of a specific type or `null`. */
9
+ export declare class NullableSchema<T> extends ThroughSchema<T | null> {
10
+ readonly value: T | null;
11
+ constructor({ value, ...options }: NullableSchemaOptions<T>);
12
+ validate(unsafeValue?: unknown): T | null;
13
+ }
14
+ /** Create a new nullable schema from a source schema. */
15
+ export declare const NULLABLE: <T>(source: Schema<T>) => NullableSchema<T>;
@@ -0,0 +1,14 @@
1
+ import { ThroughSchema } from "./ThroughSchema.js";
2
+ /** Validate a value of a specific type or `null`. */
3
+ export class NullableSchema extends ThroughSchema {
4
+ constructor({ value = null, ...options }) {
5
+ super({ value, ...options });
6
+ }
7
+ validate(unsafeValue = this.value) {
8
+ if (unsafeValue === null || unsafeValue === undefined || unsafeValue === "" || Number.isNaN(unsafeValue))
9
+ return null;
10
+ return super.validate(unsafeValue);
11
+ }
12
+ }
13
+ /** Create a new nullable schema from a source schema. */
14
+ export const NULLABLE = (source) => new NullableSchema({ source });
@@ -19,7 +19,7 @@ export declare class NumberSchema extends Schema<number> {
19
19
  /** Valid number, e.g. `2048.12345` or `0` zero. */
20
20
  export declare const NUMBER: NumberSchema;
21
21
  /** Valid optional number, e.g. `2048.12345` or `0` zero, or `null` */
22
- export declare const OPTIONAL_NUMBER: import("./OptionalSchema.js").OptionalSchema<number>;
22
+ export declare const NULLABLE_NUMBER: import("./NullableSchema.js").NullableSchema<number>;
23
23
  /** Valid integer number, e.g. `2048` or `0` zero. */
24
24
  export declare const INTEGER: NumberSchema;
25
25
  /** Valid positive integer number, e.g. `1,2,3` (not including zero). */
@@ -31,8 +31,8 @@ export declare const NEGATIVE_INTEGER: NumberSchema;
31
31
  /** Valid non-positive integer number, e.g. `0,-1,-2,-3` (including zero). */
32
32
  export declare const NON_POSITIVE_INTEGER: NumberSchema;
33
33
  /** Valid optional integer number, e.g. `2048` or `0` zero, or `null` */
34
- export declare const OPTIONAL_INTEGER: import("./OptionalSchema.js").OptionalSchema<number>;
34
+ export declare const NULLABLE_INTEGER: import("./NullableSchema.js").NullableSchema<number>;
35
35
  /** Valid Unix timestamp (including milliseconds). */
36
36
  export declare const TIMESTAMP: NumberSchema;
37
37
  /** Valid Unix timestamp (including milliseconds). */
38
- export declare const OPTIONAL_TIMESTAMP: import("./OptionalSchema.js").OptionalSchema<number>;
38
+ export declare const NULLABLE_TIMESTAMP: import("./NullableSchema.js").NullableSchema<number>;
@@ -1,7 +1,7 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { formatNumber } from "../util/format.js";
3
3
  import { getNumber, roundStep } from "../util/number.js";
4
- import { OPTIONAL } from "./OptionalSchema.js";
4
+ import { NULLABLE } from "./NullableSchema.js";
5
5
  import { Schema } from "./Schema.js";
6
6
  /** Schema that defines a valid number. */
7
7
  export class NumberSchema extends Schema {
@@ -29,7 +29,7 @@ export class NumberSchema extends Schema {
29
29
  /** Valid number, e.g. `2048.12345` or `0` zero. */
30
30
  export const NUMBER = new NumberSchema({ title: "Number" });
31
31
  /** Valid optional number, e.g. `2048.12345` or `0` zero, or `null` */
32
- export const OPTIONAL_NUMBER = OPTIONAL(NUMBER);
32
+ export const NULLABLE_NUMBER = NULLABLE(NUMBER);
33
33
  /** Valid integer number, e.g. `2048` or `0` zero. */
34
34
  export const INTEGER = new NumberSchema({ step: 1, min: Number.MIN_SAFE_INTEGER, max: Number.MAX_SAFE_INTEGER, value: 0 });
35
35
  /** Valid positive integer number, e.g. `1,2,3` (not including zero). */
@@ -41,7 +41,7 @@ export const NEGATIVE_INTEGER = new NumberSchema({ step: 1, min: Number.MIN_SAFE
41
41
  /** Valid non-positive integer number, e.g. `0,-1,-2,-3` (including zero). */
42
42
  export const NON_POSITIVE_INTEGER = new NumberSchema({ step: 1, min: Number.MIN_SAFE_INTEGER, max: 0, value: 0 });
43
43
  /** Valid optional integer number, e.g. `2048` or `0` zero, or `null` */
44
- export const OPTIONAL_INTEGER = OPTIONAL(INTEGER);
44
+ export const NULLABLE_INTEGER = NULLABLE(INTEGER);
45
45
  /** Valid Unix timestamp (including milliseconds). */
46
46
  export const TIMESTAMP = new NumberSchema({
47
47
  title: "Timestamp",
@@ -51,4 +51,4 @@ export const TIMESTAMP = new NumberSchema({
51
51
  value: 0,
52
52
  });
53
53
  /** Valid Unix timestamp (including milliseconds). */
54
- export const OPTIONAL_TIMESTAMP = OPTIONAL_INTEGER;
54
+ export const NULLABLE_TIMESTAMP = NULLABLE_INTEGER;
@@ -1,15 +1,12 @@
1
- import type { Schema, SchemaOptions } from "./Schema.js";
1
+ import type { Schema } from "./Schema.js";
2
2
  import { ThroughSchema } from "./ThroughSchema.js";
3
- /** Allowed options for `OptionalSchema` */
4
- export interface OptionalSchemaOptions<T> extends SchemaOptions {
5
- readonly source: Schema<T>;
6
- readonly value?: T | null;
3
+ /**
4
+ * Validate a property in an optional way, i.e. it can be the value, or `undefined`
5
+ * - If the prop is `undefined`, then `undefined` is returned.
6
+ * - When used with `validateData()` this means the prop can be silently skipped.
7
+ */
8
+ export declare class OptionalSchema<T> extends ThroughSchema<T | undefined> {
9
+ validate(unsafeValue: unknown): T | undefined;
7
10
  }
8
- /** Validate a value of a specific type or `null`. */
9
- export declare class OptionalSchema<T> extends ThroughSchema<T | null> {
10
- readonly value: T | null;
11
- constructor({ value, ...options }: OptionalSchemaOptions<T>);
12
- validate(unsafeValue?: unknown): T | null;
13
- }
14
- /** Create a new optional schema from a source schema. */
11
+ /** Make a property of a set of data optional, i.e. it can be the value or `undefined` */
15
12
  export declare const OPTIONAL: <T>(source: Schema<T>) => OptionalSchema<T>;
@@ -1,14 +1,15 @@
1
1
  import { ThroughSchema } from "./ThroughSchema.js";
2
- /** Validate a value of a specific type or `null`. */
2
+ /**
3
+ * Validate a property in an optional way, i.e. it can be the value, or `undefined`
4
+ * - If the prop is `undefined`, then `undefined` is returned.
5
+ * - When used with `validateData()` this means the prop can be silently skipped.
6
+ */
3
7
  export class OptionalSchema extends ThroughSchema {
4
- constructor({ value = null, ...options }) {
5
- super({ value, ...options });
6
- }
7
- validate(unsafeValue = this.value) {
8
- if (unsafeValue === null || unsafeValue === undefined || unsafeValue === "" || Number.isNaN(unsafeValue))
9
- return null;
8
+ validate(unsafeValue) {
9
+ if (unsafeValue === undefined)
10
+ return undefined;
10
11
  return super.validate(unsafeValue);
11
12
  }
12
13
  }
13
- /** Create a new optional schema from a source schema. */
14
+ /** Make a property of a set of data optional, i.e. it can be the value or `undefined` */
14
15
  export const OPTIONAL = (source) => new OptionalSchema({ source });
@@ -12,4 +12,4 @@ export declare class PhoneSchema extends TextSchema {
12
12
  /** Valid phone number, e.g. `+441234567890` */
13
13
  export declare const PHONE: PhoneSchema;
14
14
  /** Valid phone number, e.g. `+441234567890`, or `null` */
15
- export declare const OPTIONAL_PHONE: import("./OptionalSchema.js").OptionalSchema<string>;
15
+ export declare const NULLABLE_PHONE: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,4 +1,4 @@
1
- import { OPTIONAL } from "./OptionalSchema.js";
1
+ import { NULLABLE } from "./NullableSchema.js";
2
2
  import { TextSchema } from "./TextSchema.js";
3
3
  // Valid phone number is max 16 digits made up of:
4
4
  // - Country code (`+` plus character and 1-3 digits, e.g. `+44` or `+1`).
@@ -31,4 +31,4 @@ export class PhoneSchema extends TextSchema {
31
31
  /** Valid phone number, e.g. `+441234567890` */
32
32
  export const PHONE = new PhoneSchema({});
33
33
  /** Valid phone number, e.g. `+441234567890`, or `null` */
34
- export const OPTIONAL_PHONE = OPTIONAL(PHONE);
34
+ export const NULLABLE_PHONE = NULLABLE(PHONE);
@@ -13,4 +13,4 @@ export declare class SlugSchema extends StringSchema {
13
13
  /** Valid slug, e.g. `this-is-a-slug` */
14
14
  export declare const SLUG: SlugSchema;
15
15
  /** Valid slug, e.g. `this-is-a-slug`, or `null` */
16
- export declare const OPTIONAL_SLUG: import("./OptionalSchema.js").OptionalSchema<string>;
16
+ export declare const NULLABLE_SLUG: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,5 +1,5 @@
1
1
  import { getSlug } from "../util/string.js";
2
- import { OPTIONAL } from "./OptionalSchema.js";
2
+ import { NULLABLE } from "./NullableSchema.js";
3
3
  import { StringSchema } from "./StringSchema.js";
4
4
  /**
5
5
  * Define a valid slug, e.g. `this-is-a-slug`
@@ -23,4 +23,4 @@ export class SlugSchema extends StringSchema {
23
23
  /** Valid slug, e.g. `this-is-a-slug` */
24
24
  export const SLUG = new SlugSchema({});
25
25
  /** Valid slug, e.g. `this-is-a-slug`, or `null` */
26
- export const OPTIONAL_SLUG = OPTIONAL(SLUG);
26
+ export const NULLABLE_SLUG = NULLABLE(SLUG);
@@ -31,10 +31,10 @@ export declare const REQUIRED_TEXT: TextSchema;
31
31
  /** Title string, e.g. `Title of something` */
32
32
  export declare const TITLE: TextSchema;
33
33
  /** Optional name string, e.g. `Title of something` or `null` */
34
- export declare const OPTIONAL_TITLE: import("./OptionalSchema.js").OptionalSchema<string>;
34
+ export declare const NULLABLE_TITLE: import("./NullableSchema.js").NullableSchema<string>;
35
35
  /** Name string, e.g. `Name of Something` */
36
36
  export declare const NAME: TextSchema;
37
37
  /** Optional name string, e.g. `Name of Something` or `null` */
38
- export declare const OPTIONAL_NAME: import("./OptionalSchema.js").OptionalSchema<string>;
38
+ export declare const NULLABLE_NAME: import("./NullableSchema.js").NullableSchema<string>;
39
39
  /** Password string. */
40
40
  export declare const PASSWORD: TextSchema;
@@ -1,6 +1,6 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { sanitizeMultilineText, sanitizeText } from "../util/string.js";
3
- import { OPTIONAL } from "./OptionalSchema.js";
3
+ import { NULLABLE } from "./NullableSchema.js";
4
4
  import { StringSchema } from "./StringSchema.js";
5
5
  /**
6
6
  * Schema that defines a valid text string.
@@ -38,10 +38,10 @@ export const REQUIRED_TEXT = new TextSchema({ min: 1 });
38
38
  /** Title string, e.g. `Title of something` */
39
39
  export const TITLE = new TextSchema({ title: "Title", min: 1, max: 100 });
40
40
  /** Optional name string, e.g. `Title of something` or `null` */
41
- export const OPTIONAL_TITLE = OPTIONAL(TITLE);
41
+ export const NULLABLE_TITLE = NULLABLE(TITLE);
42
42
  /** Name string, e.g. `Name of Something` */
43
43
  export const NAME = new TextSchema({ title: "Name", min: 1, max: 100 });
44
44
  /** Optional name string, e.g. `Name of Something` or `null` */
45
- export const OPTIONAL_NAME = OPTIONAL(NAME);
45
+ export const NULLABLE_NAME = NULLABLE(NAME);
46
46
  /** Password string. */
47
47
  export const PASSWORD = new TextSchema({ title: "Password", min: 6, type: "password" });
@@ -1,13 +1,14 @@
1
1
  import type { Sourceable } from "../util/source.js";
2
+ import type { Validator } from "../util/validate.js";
2
3
  import type { SchemaOptions } from "./Schema.js";
3
4
  import { Schema } from "./Schema.js";
4
- /** Allowed options for `ThroughSchama` */
5
+ /** Allowed options for `ThroughSchema` */
5
6
  export interface ThroughSchemaOptions<T> extends SchemaOptions {
6
- source: Schema<T>;
7
+ source: Validator<T>;
7
8
  }
8
9
  /** Schema that passes through to a source schema. */
9
- export declare abstract class ThroughSchema<T> extends Schema<T> implements Sourceable<Schema<T>> {
10
- readonly source: Schema<T>;
10
+ export declare abstract class ThroughSchema<T> extends Schema<T> implements Sourceable<Validator<T>> {
11
+ readonly source: Validator<T>;
11
12
  constructor({ source, ...options }: ThroughSchemaOptions<T>);
12
13
  validate(unsafeValue: unknown): T;
13
14
  }
@@ -3,7 +3,7 @@ import { Schema } from "./Schema.js";
3
3
  export class ThroughSchema extends Schema {
4
4
  source;
5
5
  constructor({ source, ...options }) {
6
- super({ ...source, ...options });
6
+ super(source instanceof Schema ? { ...source, ...options } : options);
7
7
  this.source = source;
8
8
  }
9
9
  validate(unsafeValue) {
@@ -26,4 +26,4 @@ export declare class TimeSchema extends Schema<string> {
26
26
  /** Valid time, e.g. `2005-09-12` (required because falsy values are invalid). */
27
27
  export declare const TIME: TimeSchema;
28
28
  /** Valid time, e.g. `2005-09-12`, or `null` */
29
- export declare const OPTIONAL_TIME: import("./OptionalSchema.js").OptionalSchema<string>;
29
+ export declare const NULLABLE_TIME: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,7 +1,7 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { roundStep } from "../util/number.js";
3
3
  import { Time, getTime } from "../util/time.js";
4
- import { OPTIONAL } from "./OptionalSchema.js";
4
+ import { NULLABLE } from "./NullableSchema.js";
5
5
  import { Schema } from "./Schema.js";
6
6
  /** Define a valid time in 24h hh:mm:ss.fff format, e.g. `23:59` or `24:00 */
7
7
  export class TimeSchema extends Schema {
@@ -33,4 +33,4 @@ export class TimeSchema extends Schema {
33
33
  /** Valid time, e.g. `2005-09-12` (required because falsy values are invalid). */
34
34
  export const TIME = new TimeSchema({});
35
35
  /** Valid time, e.g. `2005-09-12`, or `null` */
36
- export const OPTIONAL_TIME = OPTIONAL(TIME);
36
+ export const NULLABLE_TIME = NULLABLE(TIME);
@@ -11,4 +11,4 @@ export declare class UUIDSchema extends StringSchema {
11
11
  /** Any valid UUID (versions 1-5) */
12
12
  export declare const UUID: UUIDSchema;
13
13
  /** Any valid UUID (versions 1-5) or null */
14
- export declare const OPTIONAL_UUID: import("./OptionalSchema.js").OptionalSchema<string>;
14
+ export declare const NULLABLE_UUID: import("./NullableSchema.js").NullableSchema<string>;
@@ -1,5 +1,5 @@
1
1
  import { getUUID } from "../util/uuid.js";
2
- import { OPTIONAL } from "./OptionalSchema.js";
2
+ import { NULLABLE } from "./NullableSchema.js";
3
3
  import { StringSchema } from "./StringSchema.js";
4
4
  /**
5
5
  * Type of `StringSchema` that defines a valid UUID (versions 1-5). Defaults to any-version validation.
@@ -23,4 +23,4 @@ export class UUIDSchema extends StringSchema {
23
23
  /** Any valid UUID (versions 1-5) */
24
24
  export const UUID = new UUIDSchema({ title: "ID" });
25
25
  /** Any valid UUID (versions 1-5) or null */
26
- export const OPTIONAL_UUID = OPTIONAL(UUID);
26
+ export const NULLABLE_UUID = NULLABLE(UUID);
package/schema/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export * from "./Schema.js";
2
2
  export * from "./ThroughSchema.js";
3
+ export * from "./NullableSchema.js";
3
4
  export * from "./OptionalSchema.js";
4
5
  export * from "./RequiredSchema.js";
5
- export * from "./PartialSchema.js";
6
6
  export * from "./AllowSchema.js";
7
7
  export * from "./ArraySchema.js";
8
8
  export * from "./BooleanSchema.js";
@@ -13,7 +13,6 @@ export * from "./DictionarySchema.js";
13
13
  export * from "./EmailSchema.js";
14
14
  export * from "./EntitySchema.js";
15
15
  export * from "./FileSchema.js";
16
- export * from "./ItemSchema.js";
17
16
  export * from "./KeySchema.js";
18
17
  export * from "./LinkSchema.js";
19
18
  export * from "./NumberSchema.js";
package/schema/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  export * from "./Schema.js";
2
2
  // Utility schemas.
3
3
  export * from "./ThroughSchema.js";
4
+ export * from "./NullableSchema.js";
4
5
  export * from "./OptionalSchema.js";
5
6
  export * from "./RequiredSchema.js";
6
- export * from "./PartialSchema.js";
7
7
  // Field schemas.
8
8
  export * from "./AllowSchema.js";
9
9
  export * from "./ArraySchema.js";
@@ -15,7 +15,6 @@ export * from "./DictionarySchema.js";
15
15
  export * from "./EmailSchema.js";
16
16
  export * from "./EntitySchema.js";
17
17
  export * from "./FileSchema.js";
18
- export * from "./ItemSchema.js";
19
18
  export * from "./KeySchema.js";
20
19
  export * from "./LinkSchema.js";
21
20
  export * from "./NumberSchema.js";
package/test/people.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { DATA } from "../schema/DataSchema.js";
2
- import { OPTIONAL_DATE } from "../schema/DateSchema.js";
2
+ import { NULLABLE_DATE } from "../schema/DateSchema.js";
3
3
  import { REQUIRED_STRING } from "../schema/StringSchema.js";
4
4
  export const PERSON_SCHEMA = DATA({
5
5
  name: DATA({ first: REQUIRED_STRING, last: REQUIRED_STRING }),
6
- birthday: OPTIONAL_DATE,
6
+ birthday: NULLABLE_DATE,
7
7
  });
8
8
  export const person1 = { id: "person1", name: { first: "Dave", last: "Brook" }, birthday: "1985-12-06" };
9
9
  export const person2 = { id: "person2", name: { first: "Sally", last: "Callister" }, birthday: "1973-11-19" };
@@ -58,8 +58,10 @@ export declare function validateDictionary<T>(unsafeDictionary: ImmutableDiction
58
58
  * Validate a data object with a set of validators.
59
59
  * - Defined props in the object will be validated against the corresponding validator.
60
60
  * - `undefined` props in the object will be set to the default value of that prop.
61
+ * - `undefined` props after validation will not be set in the output object.
62
+ *
63
+ * @param partial Whether we're validating a partial match or not. This allows props to be missing without error.
61
64
  *
62
- * @param partial Whether we're validating a partial match, or not.
63
65
  * @return Valid object.
64
66
  * @throw Feedback if one or more props did not validate.
65
67
  * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
package/util/validate.js CHANGED
@@ -2,7 +2,7 @@ import { ValueError } from "../error/ValueError.js";
2
2
  import { Feedback } from "../feedback/Feedback.js";
3
3
  import { ValueFeedbacks } from "../feedback/Feedbacks.js";
4
4
  import { isArray } from "./array.js";
5
- import { getDataProps } from "./data.js";
5
+ import { getDataKeys, getDataProps } from "./data.js";
6
6
  import { getDictionaryItems } from "./dictionary.js";
7
7
  import { PASSTHROUGH } from "./function.js";
8
8
  import { isIterable } from "./iterate.js";
@@ -55,7 +55,7 @@ export function* validateItems(unsafeItems, validator) {
55
55
  export function validateArray(unsafeArray, validator) {
56
56
  let index = 0;
57
57
  let valid = true;
58
- let changed = true;
58
+ let changed = false; // start false so we can reuse original if nothing changes
59
59
  const safeArray = [];
60
60
  const messages = {};
61
61
  for (const unsafeItem of unsafeArray) {
@@ -110,18 +110,20 @@ export function validateDictionary(unsafeDictionary, validator) {
110
110
  let isDeeplyPartial = false;
111
111
  export function validateData(unsafeData, validators, partial = isDeeplyPartial) {
112
112
  let valid = true;
113
- let changed = true;
113
+ let changed = false;
114
114
  const safeData = {};
115
115
  const messages = {};
116
116
  try {
117
117
  isDeeplyPartial = partial;
118
- for (const [key, validator] of getDataProps(validators)) {
118
+ const props = getDataProps(validators);
119
+ for (const [key, validator] of props) {
119
120
  const unsafeValue = unsafeData[key];
120
121
  if (unsafeValue === undefined && partial)
121
122
  continue; // Silently skip `undefined` props if in partial mode.
122
123
  try {
123
124
  const safeValue = validator.validate(unsafeValue);
124
- safeData[key] = safeValue;
125
+ if (safeValue !== undefined)
126
+ safeData[key] = safeValue;
125
127
  if (!changed && safeValue !== unsafeValue)
126
128
  changed = true;
127
129
  }
@@ -134,7 +136,9 @@ export function validateData(unsafeData, validators, partial = isDeeplyPartial)
134
136
  }
135
137
  if (!valid)
136
138
  throw new ValueFeedbacks(messages, unsafeData);
137
- return changed ? safeData : unsafeData;
139
+ if (changed || getDataKeys(unsafeData).length > props.length)
140
+ return safeData;
141
+ return unsafeData;
138
142
  }
139
143
  finally {
140
144
  isDeeplyPartial = false;
@@ -1,33 +0,0 @@
1
- import type { Data, Database } from "../util/data.js";
2
- import type { Item } from "../util/item.js";
3
- import type { Validators } from "../util/validate.js";
4
- import { DataSchema } from "./DataSchema.js";
5
- import type { KeySchema } from "./KeySchema.js";
6
- import { type OptionalSchema } from "./OptionalSchema.js";
7
- import type { SchemaOptions } from "./Schema.js";
8
- /** Allowed options for `ItemSchema` */
9
- export interface ItemSchemaOptions<T extends Data> extends SchemaOptions {
10
- readonly id?: KeySchema | undefined;
11
- readonly props: Validators<T>;
12
- readonly value?: Partial<Item<T>> | undefined;
13
- }
14
- /** Validate an item object. */
15
- export declare class ItemSchema<T extends Data> extends DataSchema<Item<T>> {
16
- constructor({ id, props, ...options }: ItemSchemaOptions<T>);
17
- }
18
- /** Set of named item schemas. */
19
- export type ItemSchemas<T extends Database> = {
20
- [K in keyof T]: DataSchema<Item<T[K]>>;
21
- };
22
- /**
23
- * Valid item object with specifed properties.
24
- * - An `Item` is a `Data` object with a string `.id` prop.
25
- * - Optional validator for `.id` must be a `KeySchema` and defaults to `KEY`.
26
- */
27
- export declare const ITEM: <T extends Data>(props: Validators<T> | DataSchema<T>, id?: KeySchema) => ItemSchema<Item<T>>;
28
- /**
29
- * Valid item object or `null`.
30
- * - An `Item` is a `Data` object with a string `.id` prop.
31
- * - Optional validator for `.id` must be a `KeySchema` and defaults to `KEY`.
32
- */
33
- export declare const OPTIONAL_ITEM: <T extends Data>(props: Validators<T> | DataSchema<T>, id?: KeySchema) => OptionalSchema<Item<T>>;
@@ -1,21 +0,0 @@
1
- import { DataSchema } from "./DataSchema.js";
2
- import { KEY } from "./KeySchema.js";
3
- import { OPTIONAL } from "./OptionalSchema.js";
4
- /** Validate an item object. */
5
- export class ItemSchema extends DataSchema {
6
- constructor({ id = KEY, props, ...options }) {
7
- super({ props: { id, ...props }, ...options });
8
- }
9
- }
10
- /**
11
- * Valid item object with specifed properties.
12
- * - An `Item` is a `Data` object with a string `.id` prop.
13
- * - Optional validator for `.id` must be a `KeySchema` and defaults to `KEY`.
14
- */
15
- export const ITEM = (props, id) => props instanceof DataSchema ? new ItemSchema({ id, ...props }) : new ItemSchema({ props, id });
16
- /**
17
- * Valid item object or `null`.
18
- * - An `Item` is a `Data` object with a string `.id` prop.
19
- * - Optional validator for `.id` must be a `KeySchema` and defaults to `KEY`.
20
- */
21
- export const OPTIONAL_ITEM = (props, id) => OPTIONAL(ITEM(props, id));
@@ -1,18 +0,0 @@
1
- import type { Data } from "../util/data.js";
2
- import type { DataSchema } from "./DataSchema.js";
3
- import type { SchemaOptions } from "./Schema.js";
4
- import { ThroughSchema } from "./ThroughSchema.js";
5
- /** Allowed options for `PartialSchema` */
6
- export interface PartialSchemaOptions<T extends Data> extends SchemaOptions {
7
- readonly source: DataSchema<T>;
8
- readonly value?: Partial<T> | undefined;
9
- }
10
- /** Validate a partial value for a given `DataSchema` source. */
11
- export declare class PartialSchema<T extends Data> extends ThroughSchema<Partial<T>> {
12
- readonly source: DataSchema<T>;
13
- readonly value: Partial<T>;
14
- constructor({ value, ...options }: PartialSchemaOptions<T>);
15
- validate(unsafeValue?: unknown): Partial<T>;
16
- }
17
- /** Create a new partial schema from a `DataSchema` source. */
18
- export declare const PARTIAL: <T extends Data>(source: DataSchema<T>) => PartialSchema<T>;
@@ -1,17 +0,0 @@
1
- import { ValueFeedback } from "../feedback/Feedback.js";
2
- import { isData } from "../util/data.js";
3
- import { validateData } from "../util/validate.js";
4
- import { ThroughSchema } from "./ThroughSchema.js";
5
- /** Validate a partial value for a given `DataSchema` source. */
6
- export class PartialSchema extends ThroughSchema {
7
- constructor({ value = {}, ...options }) {
8
- super({ value, ...options });
9
- }
10
- validate(unsafeValue = this.value) {
11
- if (!isData(unsafeValue))
12
- throw new ValueFeedback("Must be object", unsafeValue);
13
- return validateData(unsafeValue, this.source.props, true);
14
- }
15
- }
16
- /** Create a new partial schema from a `DataSchema` source. */
17
- export const PARTIAL = (source) => new PartialSchema({ source });