shelving 1.151.0 → 1.152.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.
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.151.0",
14
+ "version": "1.152.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -1,4 +1,4 @@
1
- import { TextSchema, type TextSchemaOptions } from "./TextSchema.js";
1
+ import { StringSchema, type StringSchemaOptions } from "./StringSchema.js";
2
2
  /**
3
3
  * Define a valid color hex string, e.g `#00CCFF`
4
4
  *
@@ -8,8 +8,8 @@ import { TextSchema, type TextSchemaOptions } from "./TextSchema.js";
8
8
  *
9
9
  * Colors are limited to 512 characters (this can be changed with `max`), but generally these won't be data: URIs so this is a reasonable limit.
10
10
  */
11
- export declare class ColorSchema extends TextSchema {
12
- constructor({ title, value, ...options }: Omit<TextSchemaOptions, "type" | "min" | "max" | "multiline" | "match">);
11
+ export declare class ColorSchema extends StringSchema {
12
+ constructor({ title, value, ...options }: Omit<StringSchemaOptions, "type" | "min" | "max" | "multiline" | "match">);
13
13
  sanitize(insaneString: string): string;
14
14
  }
15
15
  /** Valid color hex string, e.g. `#00CCFF` (required because empty string is invalid). */
@@ -1,5 +1,5 @@
1
1
  import { NULLABLE } from "./NullableSchema.js";
2
- import { TextSchema } from "./TextSchema.js";
2
+ import { StringSchema } from "./StringSchema.js";
3
3
  const COLOR_REGEXP = /^#[0-9A-F]{6}$/;
4
4
  const NOT_HEX_REGEXP = /[^0-9A-F]/g;
5
5
  /**
@@ -11,16 +11,15 @@ const NOT_HEX_REGEXP = /[^0-9A-F]/g;
11
11
  *
12
12
  * Colors are limited to 512 characters (this can be changed with `max`), but generally these won't be data: URIs so this is a reasonable limit.
13
13
  */
14
- export class ColorSchema extends TextSchema {
14
+ export class ColorSchema extends StringSchema {
15
15
  constructor({ title = "Color", value = "#000000", ...options }) {
16
16
  super({
17
17
  title,
18
18
  value,
19
19
  ...options,
20
- type: "color",
20
+ input: "color",
21
21
  min: 1,
22
22
  max: 7,
23
- multiline: false,
24
23
  match: COLOR_REGEXP,
25
24
  });
26
25
  }
@@ -1,5 +1,5 @@
1
1
  import type { StringSchemaOptions } from "./StringSchema.js";
2
- import { TextSchema } from "./TextSchema.js";
2
+ import { StringSchema } from "./StringSchema.js";
3
3
  /**
4
4
  * Define a valid email address.
5
5
  *
@@ -17,7 +17,7 @@ import { TextSchema } from "./TextSchema.js";
17
17
  * - Up to 10 segments of up to 63 characters each, separated by `.`
18
18
  * - TLD is a segment of 2-63 characters, possibly in `xn--` international format.
19
19
  */
20
- export declare class EmailSchema extends TextSchema {
20
+ export declare class EmailSchema extends StringSchema {
21
21
  constructor({ title, ...options }: Omit<StringSchemaOptions, "type" | "min" | "max" | "match" | "multiline">);
22
22
  sanitize(str: string): string;
23
23
  }
@@ -1,5 +1,5 @@
1
1
  import { NULLABLE } from "./NullableSchema.js";
2
- import { TextSchema } from "./TextSchema.js";
2
+ import { StringSchema } from "./StringSchema.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
  /**
5
5
  * Define a valid email address.
@@ -18,16 +18,15 @@ const R_MATCH = /^[a-z0-9](?:[a-zA-Z0-9._+-]{0,62}[a-zA-Z0-9])?@(?:[a-z0-9](?:[a
18
18
  * - Up to 10 segments of up to 63 characters each, separated by `.`
19
19
  * - TLD is a segment of 2-63 characters, possibly in `xn--` international format.
20
20
  */
21
- export class EmailSchema extends TextSchema {
21
+ export class EmailSchema extends StringSchema {
22
22
  constructor({ title = "Email", ...options }) {
23
23
  super({
24
24
  title,
25
25
  ...options,
26
- type: "email",
26
+ input: "email",
27
27
  min: 1,
28
28
  max: 254,
29
29
  match: R_MATCH,
30
- multiline: false,
31
30
  });
32
31
  }
33
32
  sanitize(str) {
@@ -1,7 +1,7 @@
1
1
  import type { ImmutableArray } from "../util/array.js";
2
2
  import { type AbsoluteLink } from "../util/link.js";
3
3
  import type { StringSchemaOptions } from "./StringSchema.js";
4
- import { TextSchema } from "./TextSchema.js";
4
+ import { StringSchema } from "./StringSchema.js";
5
5
  /** Allowed options for `LinkSchema` */
6
6
  export interface LinkSchemaOptions extends Omit<StringSchemaOptions, "type" | "min" | "max" | "multiline"> {
7
7
  readonly base?: AbsoluteLink | undefined;
@@ -14,7 +14,7 @@ export interface LinkSchemaOptions extends Omit<StringSchemaOptions, "type" | "m
14
14
  * - URLs are limited to 512 characters, but generally these won't be data: URIs so this is a reasonable limit.
15
15
  * - Falsy values are converted to `""` empty string.
16
16
  */
17
- export declare class LinkSchema extends TextSchema {
17
+ export declare class LinkSchema extends StringSchema {
18
18
  readonly base: AbsoluteLink | undefined;
19
19
  readonly schemes: ImmutableArray<string> | undefined;
20
20
  readonly hosts: ImmutableArray<string> | undefined;
@@ -1,14 +1,14 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
2
  import { getLinkURL } from "../util/link.js";
3
3
  import { NULLABLE } from "./NullableSchema.js";
4
- import { TextSchema } from "./TextSchema.js";
4
+ import { StringSchema } from "./StringSchema.js";
5
5
  /**
6
6
  * Type of `StringSchema` that defines a valid URL link.
7
7
  * - Checks URL scheme against a whitelist (always), and checks URL domain against a whitelist (optional).
8
8
  * - URLs are limited to 512 characters, but generally these won't be data: URIs so this is a reasonable limit.
9
9
  * - Falsy values are converted to `""` empty string.
10
10
  */
11
- export class LinkSchema extends TextSchema {
11
+ export class LinkSchema extends StringSchema {
12
12
  base;
13
13
  schemes;
14
14
  hosts;
@@ -16,7 +16,7 @@ export class LinkSchema extends TextSchema {
16
16
  super({
17
17
  title,
18
18
  ...options,
19
- type: "url",
19
+ input: "url",
20
20
  min: 1,
21
21
  max: 512,
22
22
  multiline: false,
@@ -1,11 +1,11 @@
1
1
  import type { StringSchemaOptions } from "./StringSchema.js";
2
- import { TextSchema } from "./TextSchema.js";
2
+ import { StringSchema } from "./StringSchema.js";
3
3
  /**
4
4
  * Type of `StringSchema` that defines a valid phone number.
5
5
  * - Multiple string formats are automatically converted to E.164 format (starting with `+` plus).
6
6
  * - Falsy values are converted to `""` empty string.
7
7
  */
8
- export declare class PhoneSchema extends TextSchema {
8
+ export declare class PhoneSchema extends StringSchema {
9
9
  constructor({ title, ...options }: StringSchemaOptions);
10
10
  sanitize(insaneString: string): string;
11
11
  }
@@ -1,5 +1,5 @@
1
1
  import { NULLABLE } from "./NullableSchema.js";
2
- import { TextSchema } from "./TextSchema.js";
2
+ import { StringSchema } from "./StringSchema.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`).
5
5
  // - Subscriber number (5-12 digits — the Solomon Islands have five-digit phone numbers apparently).
@@ -9,16 +9,15 @@ const PHONE_REGEXP = /^\+[1-9][0-9]{0,2}[0-9]{5,12}$/;
9
9
  * - Multiple string formats are automatically converted to E.164 format (starting with `+` plus).
10
10
  * - Falsy values are converted to `""` empty string.
11
11
  */
12
- export class PhoneSchema extends TextSchema {
12
+ export class PhoneSchema extends StringSchema {
13
13
  constructor({ title = "Phone", ...options }) {
14
14
  super({
15
15
  title,
16
16
  ...options,
17
- type: "tel",
17
+ input: "tel",
18
18
  min: 1,
19
19
  max: 16, // Valid phone number is 16 digits or fewer (15 numerals with a leading `+` plus).
20
20
  match: PHONE_REGEXP,
21
- multiline: false,
22
21
  });
23
22
  }
24
23
  sanitize(insaneString) {
@@ -12,7 +12,7 @@ export class SlugSchema extends StringSchema {
12
12
  constructor(options) {
13
13
  super({
14
14
  ...options,
15
- min: 2,
15
+ min: 1,
16
16
  max: 32,
17
17
  });
18
18
  }
@@ -1,12 +1,16 @@
1
1
  import type { SchemaOptions } from "./Schema.js";
2
2
  import { Schema } from "./Schema.js";
3
- /** Function that sanitizes a string. */
4
- export type Sanitizer = (str: string) => string;
3
+ /** `type=""` prop for HTML `<input />` tags that are relevant for strings. */
4
+ export type HTMLInputType = "text" | "password" | "color" | "date" | "email" | "number" | "tel" | "search" | "url";
5
5
  /** Options for `StringSchema` */
6
6
  export interface StringSchemaOptions extends SchemaOptions {
7
7
  readonly value?: string | undefined;
8
8
  readonly min?: number | undefined;
9
9
  readonly max?: number | undefined;
10
+ readonly multiline?: boolean | undefined;
11
+ readonly match?: RegExp | undefined;
12
+ readonly case?: "upper" | "lower" | undefined;
13
+ readonly input?: HTMLInputType | undefined;
10
14
  }
11
15
  /**
12
16
  * Schema that defines a valid string.
@@ -25,9 +29,13 @@ export interface StringSchemaOptions extends SchemaOptions {
25
29
  */
26
30
  export declare class StringSchema extends Schema<string> {
27
31
  readonly value: string;
32
+ readonly input: HTMLInputType;
28
33
  readonly min: number;
29
34
  readonly max: number;
30
- constructor({ min, max, value, ...options }: StringSchemaOptions);
35
+ readonly multiline: boolean;
36
+ readonly match: RegExp | undefined;
37
+ readonly case: "upper" | "lower" | undefined;
38
+ constructor({ min, max, value, multiline, match, case: _case, input, ...options }: StringSchemaOptions);
31
39
  validate(unsafeValue?: unknown): string;
32
40
  /** Sanitize the string by removing unwanted characters. */
33
41
  sanitize(str: string): string;
@@ -35,4 +43,17 @@ export declare class StringSchema extends Schema<string> {
35
43
  /** Valid string, e.g. `Hello there!` */
36
44
  export declare const STRING: StringSchema;
37
45
  /** Valid string, `Hello there!`, with more than one character. */
38
- export declare const REQUIRED_STRING: StringSchema;
46
+ export declare const REQUIRED_STRING: StringSchema; /** Valid text, e.g. `Hello there!` */
47
+ export declare const TEXT: StringSchema;
48
+ /** Valid text, `Hello there!`, with more than one character. */
49
+ export declare const REQUIRED_TEXT: StringSchema;
50
+ /** Title string, e.g. `Title of something` */
51
+ export declare const TITLE: StringSchema;
52
+ /** Optional name string, e.g. `Title of something` or `null` */
53
+ export declare const NULLABLE_TITLE: import("./NullableSchema.js").NullableSchema<string>;
54
+ /** Name string, e.g. `Name of Something` */
55
+ export declare const NAME: StringSchema;
56
+ /** Optional name string, e.g. `Name of Something` or `null` */
57
+ export declare const NULLABLE_NAME: import("./NullableSchema.js").NullableSchema<string>;
58
+ /** Password string. */
59
+ export declare const PASSWORD: StringSchema;
@@ -1,4 +1,6 @@
1
1
  import { ValueFeedback } from "../feedback/Feedback.js";
2
+ import { sanitizeMultilineText, sanitizeText } from "../util/string.js";
3
+ import { NULLABLE } from "./NullableSchema.js";
2
4
  import { Schema } from "./Schema.js";
3
5
  /**
4
6
  * Schema that defines a valid string.
@@ -16,12 +18,20 @@ import { Schema } from "./Schema.js";
16
18
  * schema.validate('j'); // Throws 'Minimum 3 chaacters'
17
19
  */
18
20
  export class StringSchema extends Schema {
21
+ input;
19
22
  min;
20
23
  max;
21
- constructor({ min = 0, max = Number.POSITIVE_INFINITY, value = "", ...options }) {
24
+ multiline;
25
+ match;
26
+ case;
27
+ constructor({ min = 0, max = Number.POSITIVE_INFINITY, value = "", multiline = false, match, case: _case, input = "text", ...options }) {
22
28
  super({ value, ...options });
23
29
  this.min = min;
24
30
  this.max = max;
31
+ this.multiline = multiline;
32
+ this.match = match;
33
+ this.case = _case;
34
+ this.input = input;
25
35
  }
26
36
  validate(unsafeValue = this.value) {
27
37
  const possibleString = typeof unsafeValue === "number" ? unsafeValue.toString() : unsafeValue;
@@ -32,14 +42,34 @@ export class StringSchema extends Schema {
32
42
  throw new ValueFeedback(possibleString.length ? `Minimum ${this.min} characters` : "Required", saneString);
33
43
  if (saneString.length > this.max)
34
44
  throw new ValueFeedback(`Maximum ${this.max} characters`, saneString);
45
+ if (this.match && !this.match.test(saneString))
46
+ throw new ValueFeedback(saneString ? "Invalid format" : "Required", saneString);
35
47
  return saneString;
36
48
  }
37
49
  /** Sanitize the string by removing unwanted characters. */
38
50
  sanitize(str) {
39
- return str;
51
+ const sane = this.multiline ? sanitizeMultilineText(str) : sanitizeText(str);
52
+ if (this.case === "upper")
53
+ return sane.toUpperCase();
54
+ if (this.case === "lower")
55
+ return sane.toLowerCase();
56
+ return sane;
40
57
  }
41
58
  }
42
59
  /** Valid string, e.g. `Hello there!` */
43
60
  export const STRING = new StringSchema({});
44
61
  /** Valid string, `Hello there!`, with more than one character. */
45
- export const REQUIRED_STRING = new StringSchema({ min: 1 });
62
+ export const REQUIRED_STRING = new StringSchema({ min: 1 }); /** Valid text, e.g. `Hello there!` */
63
+ export const TEXT = new StringSchema({ title: "Text" });
64
+ /** Valid text, `Hello there!`, with more than one character. */
65
+ export const REQUIRED_TEXT = new StringSchema({ min: 1 });
66
+ /** Title string, e.g. `Title of something` */
67
+ export const TITLE = new StringSchema({ title: "Title", min: 1, max: 100 });
68
+ /** Optional name string, e.g. `Title of something` or `null` */
69
+ export const NULLABLE_TITLE = NULLABLE(TITLE);
70
+ /** Name string, e.g. `Name of Something` */
71
+ export const NAME = new StringSchema({ title: "Name", min: 1, max: 100 });
72
+ /** Optional name string, e.g. `Name of Something` or `null` */
73
+ export const NULLABLE_NAME = NULLABLE(NAME);
74
+ /** Password string. */
75
+ export const PASSWORD = new StringSchema({ title: "Password", min: 6, input: "password" });
package/schema/index.d.ts CHANGED
@@ -19,6 +19,5 @@ export * from "./NumberSchema.js";
19
19
  export * from "./PhoneSchema.js";
20
20
  export * from "./SlugSchema.js";
21
21
  export * from "./StringSchema.js";
22
- export * from "./TextSchema.js";
23
22
  export * from "./TimeSchema.js";
24
23
  export * from "./UUIDSchema.js";
package/schema/index.js CHANGED
@@ -21,6 +21,5 @@ export * from "./NumberSchema.js";
21
21
  export * from "./PhoneSchema.js";
22
22
  export * from "./SlugSchema.js";
23
23
  export * from "./StringSchema.js";
24
- export * from "./TextSchema.js";
25
24
  export * from "./TimeSchema.js";
26
25
  export * from "./UUIDSchema.js";
@@ -1,40 +0,0 @@
1
- import { type Sanitizer, StringSchema, type StringSchemaOptions } from "./StringSchema.js";
2
- /** `type=""` prop for HTML `<input />` tags that are relevant for strings. */
3
- export type TextSchemaType = "text" | "password" | "color" | "date" | "email" | "number" | "tel" | "search" | "url";
4
- /** Options for `TextSchema` */
5
- export interface TextSchemaOptions extends StringSchemaOptions {
6
- readonly type?: TextSchemaType | undefined;
7
- readonly match?: RegExp | undefined;
8
- readonly multiline?: boolean | undefined;
9
- }
10
- /**
11
- * Schema that defines a valid text string.
12
- *
13
- * Ensures value is string and optionally enforces min/max length, whether to trim whitespace, and regex match format.
14
- * Doesn't allow `null` to mean no value — empty string is the equivalent for StringSchema (because it means we'll never accidentally get `"null"` by converting the `null` to string).
15
- *
16
- * Defaults to a single line text string (newlines are stripped). Use `multiline=true` to allow newlines.
17
- */
18
- export declare class TextSchema extends StringSchema {
19
- readonly type: TextSchemaType;
20
- readonly match: RegExp | undefined;
21
- readonly sanitizer: Sanitizer | undefined;
22
- readonly multiline: boolean;
23
- constructor({ type, match, multiline, ...options }: TextSchemaOptions);
24
- validate(unsafeValue?: unknown): string;
25
- sanitize(str: string): string;
26
- }
27
- /** Valid text, e.g. `Hello there!` */
28
- export declare const TEXT: TextSchema;
29
- /** Valid text, `Hello there!`, with more than one character. */
30
- export declare const REQUIRED_TEXT: TextSchema;
31
- /** Title string, e.g. `Title of something` */
32
- export declare const TITLE: TextSchema;
33
- /** Optional name string, e.g. `Title of something` or `null` */
34
- export declare const NULLABLE_TITLE: import("./NullableSchema.js").NullableSchema<string>;
35
- /** Name string, e.g. `Name of Something` */
36
- export declare const NAME: TextSchema;
37
- /** Optional name string, e.g. `Name of Something` or `null` */
38
- export declare const NULLABLE_NAME: import("./NullableSchema.js").NullableSchema<string>;
39
- /** Password string. */
40
- export declare const PASSWORD: TextSchema;
@@ -1,47 +0,0 @@
1
- import { ValueFeedback } from "../feedback/Feedback.js";
2
- import { sanitizeMultilineText, sanitizeText } from "../util/string.js";
3
- import { NULLABLE } from "./NullableSchema.js";
4
- import { StringSchema } from "./StringSchema.js";
5
- /**
6
- * Schema that defines a valid text string.
7
- *
8
- * Ensures value is string and optionally enforces min/max length, whether to trim whitespace, and regex match format.
9
- * Doesn't allow `null` to mean no value — empty string is the equivalent for StringSchema (because it means we'll never accidentally get `"null"` by converting the `null` to string).
10
- *
11
- * Defaults to a single line text string (newlines are stripped). Use `multiline=true` to allow newlines.
12
- */
13
- export class TextSchema extends StringSchema {
14
- type;
15
- match;
16
- sanitizer;
17
- multiline;
18
- constructor({ type = "text", match, multiline = false, ...options }) {
19
- super(options);
20
- this.type = type;
21
- this.match = match;
22
- this.multiline = multiline;
23
- }
24
- validate(unsafeValue = this.value) {
25
- const str = super.validate(unsafeValue);
26
- if (this.match && !this.match.test(str))
27
- throw new ValueFeedback(str ? "Invalid format" : "Required", str);
28
- return str;
29
- }
30
- sanitize(str) {
31
- return this.multiline ? sanitizeMultilineText(str) : sanitizeText(str);
32
- }
33
- }
34
- /** Valid text, e.g. `Hello there!` */
35
- export const TEXT = new TextSchema({ title: "Text" });
36
- /** Valid text, `Hello there!`, with more than one character. */
37
- export const REQUIRED_TEXT = new TextSchema({ min: 1 });
38
- /** Title string, e.g. `Title of something` */
39
- export const TITLE = new TextSchema({ title: "Title", min: 1, max: 100 });
40
- /** Optional name string, e.g. `Title of something` or `null` */
41
- export const NULLABLE_TITLE = NULLABLE(TITLE);
42
- /** Name string, e.g. `Name of Something` */
43
- export const NAME = new TextSchema({ title: "Name", min: 1, max: 100 });
44
- /** Optional name string, e.g. `Name of Something` or `null` */
45
- export const NULLABLE_NAME = NULLABLE(NAME);
46
- /** Password string. */
47
- export const PASSWORD = new TextSchema({ title: "Password", min: 6, type: "password" });