shelving 1.121.1 → 1.123.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 (45) hide show
  1. package/package.json +1 -1
  2. package/schema/AllowSchema.d.ts +4 -2
  3. package/schema/AllowSchema.js +2 -1
  4. package/schema/ArraySchema.d.ts +3 -3
  5. package/schema/ArraySchema.js +2 -3
  6. package/schema/BooleanSchema.d.ts +3 -3
  7. package/schema/BooleanSchema.js +2 -2
  8. package/schema/ColorSchema.d.ts +1 -1
  9. package/schema/ColorSchema.js +3 -3
  10. package/schema/DataSchema.d.ts +3 -3
  11. package/schema/DataSchema.js +3 -3
  12. package/schema/DateSchema.d.ts +3 -3
  13. package/schema/DateSchema.js +2 -3
  14. package/schema/DictionarySchema.d.ts +3 -3
  15. package/schema/DictionarySchema.js +2 -3
  16. package/schema/EmailSchema.d.ts +2 -2
  17. package/schema/EmailSchema.js +3 -3
  18. package/schema/EntitySchema.d.ts +17 -0
  19. package/schema/EntitySchema.js +26 -0
  20. package/schema/FileSchema.d.ts +16 -0
  21. package/schema/FileSchema.js +26 -0
  22. package/schema/KeySchema.d.ts +1 -1
  23. package/schema/KeySchema.js +2 -2
  24. package/schema/LinkSchema.d.ts +3 -3
  25. package/schema/LinkSchema.js +4 -4
  26. package/schema/NumberSchema.d.ts +3 -3
  27. package/schema/NumberSchema.js +2 -3
  28. package/schema/OptionalSchema.d.ts +3 -3
  29. package/schema/OptionalSchema.js +2 -2
  30. package/schema/PhoneSchema.d.ts +1 -1
  31. package/schema/PhoneSchema.js +2 -2
  32. package/schema/StringSchema.d.ts +3 -3
  33. package/schema/StringSchema.js +2 -3
  34. package/schema/ThroughSchema.d.ts +3 -3
  35. package/schema/ThroughSchema.js +1 -2
  36. package/schema/TimeSchema.d.ts +3 -3
  37. package/schema/TimeSchema.js +2 -3
  38. package/schema/index.d.ts +2 -0
  39. package/schema/index.js +2 -0
  40. package/util/entity.d.ts +16 -0
  41. package/util/entity.js +17 -0
  42. package/util/file.d.ts +8 -0
  43. package/util/file.js +13 -0
  44. package/util/index.d.ts +2 -0
  45. package/util/index.js +2 -0
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.121.1",
14
+ "version": "1.123.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -3,9 +3,10 @@ import type { Entry } from "../util/entry.js";
3
3
  import type { ImmutableMap, PossibleMap, PossibleStringMap } from "../util/map.js";
4
4
  import { Schema } from "./Schema.js";
5
5
  /** Allowed options for `AllowSchama` */
6
- export type AllowSchemaOptions<K, T> = Omit<SchemaOptions, "value"> & {
6
+ export interface AllowSchemaOptions<K, T> extends Omit<SchemaOptions, "value"> {
7
+ /** Specify correct options using a `Map` or iterable set of entries. */
7
8
  allow: PossibleMap<K, T>;
8
- };
9
+ }
9
10
  /** Define a valid value from an allowed set of values. */
10
11
  export declare class AllowSchema<K, T> extends Schema<K> implements Iterable<Entry<K, T>> {
11
12
  readonly value: K;
@@ -17,6 +18,7 @@ export declare class AllowSchema<K, T> extends Schema<K> implements Iterable<Ent
17
18
  }
18
19
  /** Allowed options for `AllowStringSchama` */
19
20
  export type AllowStringSchemaOptions<K extends string, T> = Omit<SchemaOptions, "value"> & {
21
+ /** Specify correct options using a `Map`, iterable set of entries, or an object with string keys. */
20
22
  allow: PossibleStringMap<K, T>;
21
23
  };
22
24
  /** Define a valid string value from an allowed set of string values. */
@@ -8,7 +8,8 @@ export class AllowSchema extends Schema {
8
8
  allow;
9
9
  constructor(options) {
10
10
  const allow = getMap(options.allow);
11
- super({ value: getFirstItem(allow.keys()), ...options });
11
+ const value = getFirstItem(allow.keys());
12
+ super({ value, ...options });
12
13
  this.allow = allow;
13
14
  }
14
15
  validate(unsafeValue = this.value) {
@@ -3,13 +3,13 @@ import type { ImmutableArray } from "../util/array.js";
3
3
  import type { Validator } from "../util/validate.js";
4
4
  import { Schema } from "./Schema.js";
5
5
  /** Allowed options for `ArraySchema` */
6
- export type ArraySchemaOptions<T> = SchemaOptions & {
6
+ export interface ArraySchemaOptions<T> extends SchemaOptions {
7
7
  readonly value?: ImmutableArray;
8
8
  readonly items: Validator<T>;
9
9
  readonly min?: number;
10
10
  readonly max?: number;
11
11
  readonly unique?: boolean;
12
- };
12
+ }
13
13
  /**
14
14
  * Define a valid array.
15
15
  *
@@ -43,7 +43,7 @@ export declare class ArraySchema<T> extends Schema<ImmutableArray<T>> {
43
43
  readonly unique: boolean;
44
44
  readonly min: number;
45
45
  readonly max: number;
46
- constructor(options: ArraySchemaOptions<T>);
46
+ constructor({ items, unique, min, max, title, value, ...options }: ArraySchemaOptions<T>);
47
47
  validate(unsafeValue?: unknown): ImmutableArray<T>;
48
48
  }
49
49
  /** Valid array with specifed items. */
@@ -34,9 +34,8 @@ export class ArraySchema extends Schema {
34
34
  unique;
35
35
  min;
36
36
  max;
37
- constructor(options) {
38
- super({ value: [], ...options });
39
- const { items, unique = false, min = 0, max = Infinity } = options;
37
+ constructor({ items, unique = false, min = 0, max = Infinity, title = "Items", value = [], ...options }) {
38
+ super({ title, value, ...options });
40
39
  this.items = items;
41
40
  this.unique = unique;
42
41
  this.min = min;
@@ -1,13 +1,13 @@
1
1
  import type { SchemaOptions } from "./Schema.js";
2
2
  import { Schema } from "./Schema.js";
3
3
  /** Allowed options for `BooleanSchema` */
4
- export type BooleanSchemaOptions = SchemaOptions & {
4
+ export interface BooleanSchemaOptions extends SchemaOptions {
5
5
  readonly value?: boolean | undefined;
6
- };
6
+ }
7
7
  /** Define a valid boolean. */
8
8
  export declare class BooleanSchema extends Schema<boolean> {
9
9
  readonly value: boolean;
10
- constructor(options: BooleanSchemaOptions);
10
+ constructor({ value, ...options }: BooleanSchemaOptions);
11
11
  validate(unsafeValue?: unknown): boolean;
12
12
  }
13
13
  /** Valid boolean. */
@@ -1,8 +1,8 @@
1
1
  import { Schema } from "./Schema.js";
2
2
  /** Define a valid boolean. */
3
3
  export class BooleanSchema extends Schema {
4
- constructor(options) {
5
- super({ value: false, ...options });
4
+ constructor({ value = false, ...options }) {
5
+ super({ value, ...options });
6
6
  }
7
7
  validate(unsafeValue = this.value) {
8
8
  return !!unsafeValue;
@@ -10,7 +10,7 @@ import { StringSchema } from "./StringSchema.js";
10
10
  * 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.
11
11
  */
12
12
  export declare class ColorSchema extends StringSchema {
13
- constructor(options: Omit<StringSchemaOptions, "type" | "min" | "max" | "multiline" | "match">);
13
+ constructor({ title, value, ...options }: Omit<StringSchemaOptions, "type" | "min" | "max" | "multiline" | "match">);
14
14
  sanitize(insaneString: string): string;
15
15
  }
16
16
  /** Valid color hex string, e.g. `#00CCFF` (required because empty string is invalid). */
@@ -12,10 +12,10 @@ const NOT_HEX_REGEXP = /[^0-9A-F]/g;
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
14
  export class ColorSchema extends StringSchema {
15
- constructor(options) {
15
+ constructor({ title = "Color", value = "#000000", ...options }) {
16
16
  super({
17
- title: "Color",
18
- value: "#000000",
17
+ title,
18
+ value,
19
19
  ...options,
20
20
  type: "color",
21
21
  min: 1,
@@ -4,15 +4,15 @@ import type { Data, Database } from "../util/data.js";
4
4
  import type { Validators } from "../util/validate.js";
5
5
  import { Schema } from "./Schema.js";
6
6
  /** Allowed options for `DataSchema` */
7
- export type DataSchemaOptions<T extends Data> = SchemaOptions & {
7
+ export interface DataSchemaOptions<T extends Data> extends SchemaOptions {
8
8
  readonly props: Validators<T>;
9
9
  readonly value?: Partial<T> | undefined;
10
- };
10
+ }
11
11
  /** Validate a data object. */
12
12
  export declare class DataSchema<T extends Data> extends Schema<T> {
13
13
  readonly value: Partial<T>;
14
14
  readonly props: Validators<T>;
15
- constructor(options: DataSchemaOptions<T>);
15
+ constructor({ props, title, value, ...options }: DataSchemaOptions<T>);
16
16
  validate(unsafeValue?: unknown): T;
17
17
  }
18
18
  /** Set of named data schemas. */
@@ -6,9 +6,9 @@ import { Schema } from "./Schema.js";
6
6
  /** Validate a data object. */
7
7
  export class DataSchema extends Schema {
8
8
  props;
9
- constructor(options) {
10
- super({ value: {}, ...options });
11
- this.props = options.props;
9
+ constructor({ props, title = "Data", value = {}, ...options }) {
10
+ super({ title, value, ...options });
11
+ this.props = props;
12
12
  }
13
13
  validate(unsafeValue = this.value) {
14
14
  if (!isData(unsafeValue))
@@ -3,17 +3,17 @@ import type { PossibleDate } from "../util/date.js";
3
3
  import type { Optional } from "../util/optional.js";
4
4
  import { Schema } from "./Schema.js";
5
5
  /** Allowed options for `DateSchema` */
6
- export type DateSchemaOptions = SchemaOptions & {
6
+ export interface DateSchemaOptions extends SchemaOptions {
7
7
  readonly value?: PossibleDate | undefined;
8
8
  readonly min?: Optional<PossibleDate> | undefined;
9
9
  readonly max?: Optional<PossibleDate> | undefined;
10
- };
10
+ }
11
11
  /** Define a valid date in YMD format, e.g. `2005-09-12` */
12
12
  export declare class DateSchema extends Schema<string> {
13
13
  readonly value: PossibleDate;
14
14
  readonly min: Date | undefined;
15
15
  readonly max: Date | undefined;
16
- constructor(options: DateSchemaOptions);
16
+ constructor({ min, max, title, value, ...options }: DateSchemaOptions);
17
17
  validate(unsafeValue?: unknown): string;
18
18
  }
19
19
  /** Valid date, e.g. `2005-09-12` (required because falsy values are invalid). */
@@ -6,9 +6,8 @@ import { Schema } from "./Schema.js";
6
6
  export class DateSchema extends Schema {
7
7
  min;
8
8
  max;
9
- constructor(options) {
10
- super({ title: "Date", value: "now", ...options });
11
- const { min = null, max = null } = options;
9
+ constructor({ min = null, max = null, title = "Date", value = "now", ...options }) {
10
+ super({ title, value, ...options });
12
11
  this.min = getOptionalDate(min);
13
12
  this.max = getOptionalDate(max);
14
13
  }
@@ -3,19 +3,19 @@ import type { ImmutableDictionary } from "../util/dictionary.js";
3
3
  import type { Validator } from "../util/validate.js";
4
4
  import { Schema } from "./Schema.js";
5
5
  /** Allowed options for `DictionarySchema` */
6
- export type DictionarySchemaOptions<T> = SchemaOptions & {
6
+ export interface DictionarySchemaOptions<T> extends SchemaOptions {
7
7
  readonly items: Validator<T>;
8
8
  readonly value?: ImmutableDictionary | undefined;
9
9
  readonly min?: number | undefined;
10
10
  readonly max?: number | undefined;
11
- };
11
+ }
12
12
  /** Validate a dictionary object (whose props are all the same with string keys). */
13
13
  export declare class DictionarySchema<T> extends Schema<ImmutableDictionary<T>> {
14
14
  readonly value: ImmutableDictionary;
15
15
  readonly items: Validator<T>;
16
16
  readonly min: number;
17
17
  readonly max: number;
18
- constructor(options: DictionarySchemaOptions<T>);
18
+ constructor({ items, min, max, title, value, ...options }: DictionarySchemaOptions<T>);
19
19
  validate(unsafeValue?: unknown): ImmutableDictionary<T>;
20
20
  }
21
21
  /** Valid dictionary object with specifed items. */
@@ -7,9 +7,8 @@ export class DictionarySchema extends Schema {
7
7
  items;
8
8
  min;
9
9
  max;
10
- constructor(options) {
11
- super({ value: {}, ...options });
12
- const { items, min = 0, max = Infinity } = options;
10
+ constructor({ items, min = 0, max = Infinity, title = "Items", value = {}, ...options }) {
11
+ super({ title, value, ...options });
13
12
  this.items = items;
14
13
  this.min = min;
15
14
  this.max = max;
@@ -18,10 +18,10 @@ import { StringSchema } from "./StringSchema.js";
18
18
  * - TLD is a segment of 2-63 characters, possibly in `xn--` international format.
19
19
  */
20
20
  export declare class EmailSchema extends StringSchema {
21
- constructor(options: Omit<StringSchemaOptions, "type" | "min" | "max" | "match" | "multiline">);
21
+ constructor({ title, ...options }: Omit<StringSchemaOptions, "type" | "min" | "max" | "match" | "multiline">);
22
22
  sanitize(uncleanString: string): string;
23
23
  }
24
24
  /** Valid email, e.g. `test@test.com` */
25
25
  export declare const EMAIL: EmailSchema;
26
- /** Valid email, e.g. `test@test.com`, or `null` */
26
+ /** Valid optional email, e.g. `test@test.com`, or `null` */
27
27
  export declare const OPTIONAL_EMAIL: import("./OptionalSchema.js").OptionalSchema<string>;
@@ -19,9 +19,9 @@ const R_MATCH = /^[a-z0-9](?:[a-zA-Z0-9._+-]{0,62}[a-zA-Z0-9])?@(?:[a-z0-9](?:[a
19
19
  * - TLD is a segment of 2-63 characters, possibly in `xn--` international format.
20
20
  */
21
21
  export class EmailSchema extends StringSchema {
22
- constructor(options) {
22
+ constructor({ title = "Email", ...options }) {
23
23
  super({
24
- title: "Email",
24
+ title,
25
25
  ...options,
26
26
  type: "email",
27
27
  min: 1,
@@ -37,5 +37,5 @@ export class EmailSchema extends StringSchema {
37
37
  }
38
38
  /** Valid email, e.g. `test@test.com` */
39
39
  export const EMAIL = new EmailSchema({});
40
- /** Valid email, e.g. `test@test.com`, or `null` */
40
+ /** Valid optional email, e.g. `test@test.com`, or `null` */
41
41
  export const OPTIONAL_EMAIL = OPTIONAL(EMAIL);
@@ -0,0 +1,17 @@
1
+ import { type ImmutableArray } from "../util/array.js";
2
+ import { type Entity } from "../util/entity.js";
3
+ import { StringSchema, type StringSchemaOptions } from "./StringSchema.js";
4
+ /** Allowed options for `EntitySchema` with specific types */
5
+ export interface EntitySchemaOptions<T extends string> extends StringSchemaOptions {
6
+ readonly types?: ImmutableArray<T> | undefined;
7
+ }
8
+ /** Validate a file name matching one or more extensions. */
9
+ export declare class EntitySchema<T extends string> extends StringSchema {
10
+ readonly types: ImmutableArray<T> | undefined;
11
+ constructor({ types, title, ...options }: EntitySchemaOptions<T>);
12
+ validate(unsafeValue?: unknown): Entity<T>;
13
+ }
14
+ /** Valid file, e.g. `challenge:a1b2c3` */
15
+ export declare const ENTITY: EntitySchema<string>;
16
+ /** Valid optional file, e.g. `file.txt`, or `null` */
17
+ export declare const OPTIONAL_ENTITY: import("./OptionalSchema.js").OptionalSchema<`${string}:${string}`>;
@@ -0,0 +1,26 @@
1
+ import { Feedback } from "../feedback/Feedback.js";
2
+ import { isArrayItem } from "../util/array.js";
3
+ import { splitOptionalEntity } from "../util/entity.js";
4
+ import { OPTIONAL } from "./OptionalSchema.js";
5
+ import { StringSchema } from "./StringSchema.js";
6
+ /** Validate a file name matching one or more extensions. */
7
+ export class EntitySchema extends StringSchema {
8
+ types;
9
+ constructor({ types, title = "Entity", ...options }) {
10
+ super({ title, ...options });
11
+ this.types = types;
12
+ }
13
+ validate(unsafeValue = this.value) {
14
+ const entity = super.validate(unsafeValue);
15
+ const [type] = splitOptionalEntity(entity);
16
+ if (!type)
17
+ throw new Feedback("Must be entity", unsafeValue);
18
+ if (this.types && !isArrayItem(this.types, type))
19
+ throw new Feedback("Invalid entity type", type);
20
+ return entity;
21
+ }
22
+ }
23
+ /** Valid file, e.g. `challenge:a1b2c3` */
24
+ export const ENTITY = new EntitySchema({});
25
+ /** Valid optional file, e.g. `file.txt`, or `null` */
26
+ export const OPTIONAL_ENTITY = OPTIONAL(ENTITY);
@@ -0,0 +1,16 @@
1
+ import type { FileTypes } from "../util/file.js";
2
+ import { StringSchema, type StringSchemaOptions } from "./StringSchema.js";
3
+ /** Allowed options for `FileSchema` */
4
+ export interface FileSchemaOptions extends StringSchemaOptions {
5
+ readonly types?: FileTypes | undefined;
6
+ }
7
+ /** Validate a file name matching one or more extensions. */
8
+ export declare class FileSchema extends StringSchema {
9
+ readonly types: FileTypes | undefined;
10
+ constructor({ types, title, ...options }: FileSchemaOptions);
11
+ validate(unsafeValue?: unknown): string;
12
+ }
13
+ /** Valid file, e.g. `file.txt` */
14
+ export declare const FILE: FileSchema;
15
+ /** Valid optional file, e.g. `file.txt`, or `null` */
16
+ export declare const OPTIONAL_FILE: import("./OptionalSchema.js").OptionalSchema<string>;
@@ -0,0 +1,26 @@
1
+ import { Feedback } from "../feedback/Feedback.js";
2
+ import { getOptionalFileExtension } from "../util/file.js";
3
+ import { isProp } from "../util/object.js";
4
+ import { OPTIONAL } from "./OptionalSchema.js";
5
+ import { StringSchema } from "./StringSchema.js";
6
+ /** Validate a file name matching one or more extensions. */
7
+ export class FileSchema extends StringSchema {
8
+ types;
9
+ constructor({ types, title = "File", ...options }) {
10
+ super({ title, ...options });
11
+ this.types = types;
12
+ }
13
+ validate(unsafeValue = this.value) {
14
+ const path = super.validate(unsafeValue);
15
+ const extension = getOptionalFileExtension(path);
16
+ if (!extension)
17
+ throw new Feedback("Must be file name with extension", unsafeValue);
18
+ if (this.types && !isProp(this.types, extension))
19
+ throw new Feedback("Invalid file type", extension);
20
+ return path;
21
+ }
22
+ }
23
+ /** Valid file, e.g. `file.txt` */
24
+ export const FILE = new FileSchema({});
25
+ /** Valid optional file, e.g. `file.txt`, or `null` */
26
+ export const OPTIONAL_FILE = OPTIONAL(FILE);
@@ -6,7 +6,7 @@ import { StringSchema, type StringSchemaOptions } from "./StringSchema.js";
6
6
  * - Default maximum key length is 64 characters.
7
7
  */
8
8
  export declare class KeySchema extends StringSchema {
9
- constructor(options: StringSchemaOptions);
9
+ constructor({ min, max, ...options }: StringSchemaOptions);
10
10
  }
11
11
  /** Valid color hex string, e.g. `#00CCFF` (required because empty string is invalid). */
12
12
  export declare const KEY: KeySchema;
@@ -7,8 +7,8 @@ import { StringSchema } from "./StringSchema.js";
7
7
  * - Default maximum key length is 64 characters.
8
8
  */
9
9
  export class KeySchema extends StringSchema {
10
- constructor(options) {
11
- super({ min: 1, max: 64, ...options });
10
+ constructor({ min = 1, max = 64, ...options }) {
11
+ super({ min, max, ...options });
12
12
  }
13
13
  }
14
14
  /** Valid color hex string, e.g. `#00CCFF` (required because empty string is invalid). */
@@ -2,10 +2,10 @@ import type { StringSchemaOptions } from "./StringSchema.js";
2
2
  import type { ImmutableArray } from "../util/array.js";
3
3
  import { StringSchema } from "./StringSchema.js";
4
4
  /** Allowed options for `LinkSchema` */
5
- export type LinkSchemaOptions = Omit<StringSchemaOptions, "type" | "min" | "max" | "multiline"> & {
5
+ export interface LinkSchemaOptions extends Omit<StringSchemaOptions, "type" | "min" | "max" | "multiline"> {
6
6
  readonly schemes?: ImmutableArray<string> | undefined;
7
7
  readonly hosts?: ImmutableArray<string> | undefined;
8
- };
8
+ }
9
9
  /**
10
10
  * Type of `StringSchema` that defines a valid URL.
11
11
  * - Checks URL scheme against a whitelist (always), and checks URL domain against a whitelist (optional).
@@ -15,7 +15,7 @@ export type LinkSchemaOptions = Omit<StringSchemaOptions, "type" | "min" | "max"
15
15
  export declare class LinkSchema extends StringSchema {
16
16
  readonly schemes: ImmutableArray<string>;
17
17
  readonly hosts: ImmutableArray<string> | undefined;
18
- constructor(options: LinkSchemaOptions);
18
+ constructor({ schemes, hosts, title, ...options }: LinkSchemaOptions);
19
19
  validate(unsafeValue: unknown): string;
20
20
  }
21
21
  /** Valid link, e.g. `https://www.google.com` */
@@ -11,17 +11,17 @@ import { StringSchema } from "./StringSchema.js";
11
11
  export class LinkSchema extends StringSchema {
12
12
  schemes;
13
13
  hosts;
14
- constructor(options) {
14
+ constructor({ schemes = ["http:", "https:"], hosts, title = "Link", ...options }) {
15
15
  super({
16
- title: "Link",
16
+ title,
17
17
  ...options,
18
18
  type: "url",
19
19
  min: 1,
20
20
  max: 512,
21
21
  multiline: false,
22
22
  });
23
- this.schemes = options.schemes || ["http:", "https:"];
24
- this.hosts = options.hosts;
23
+ this.schemes = schemes;
24
+ this.hosts = hosts;
25
25
  }
26
26
  // Override to clean the URL using the builtin `URL` class and check the schemes and hosts against the whitelists.
27
27
  validate(unsafeValue) {
@@ -1,19 +1,19 @@
1
1
  import type { SchemaOptions } from "./Schema.js";
2
2
  import { Schema } from "./Schema.js";
3
3
  /** Allowed options for `NumberSchema` */
4
- export type NumberSchemaOptions = SchemaOptions & {
4
+ export interface NumberSchemaOptions extends SchemaOptions {
5
5
  readonly value?: number | undefined;
6
6
  readonly min?: number | undefined;
7
7
  readonly max?: number | undefined;
8
8
  readonly step?: number | undefined;
9
- };
9
+ }
10
10
  /** Schema that defines a valid number. */
11
11
  export declare class NumberSchema extends Schema<number> {
12
12
  readonly value: number;
13
13
  readonly min: number;
14
14
  readonly max: number;
15
15
  readonly step: number | undefined;
16
- constructor(options: NumberSchemaOptions);
16
+ constructor({ min, max, step, title, value, ...options }: NumberSchemaOptions);
17
17
  validate(unsafeValue?: unknown): number;
18
18
  }
19
19
  /** Valid number, e.g. `2048.12345` or `0` zero. */
@@ -7,9 +7,8 @@ export class NumberSchema extends Schema {
7
7
  min;
8
8
  max;
9
9
  step;
10
- constructor(options) {
11
- super({ title: "Number", value: 0, ...options });
12
- const { min = -Infinity, max = Infinity, step } = options;
10
+ constructor({ min = -Infinity, max = Infinity, step, title = "Number", value = 0, ...options }) {
11
+ super({ title, value, ...options });
13
12
  this.min = min;
14
13
  this.max = max;
15
14
  this.step = step;
@@ -1,14 +1,14 @@
1
1
  import type { Schema, SchemaOptions } from "./Schema.js";
2
2
  import { ThroughSchema } from "./ThroughSchema.js";
3
3
  /** Allowed options for `OptionalSchema` */
4
- export type OptionalSchemaOptions<T> = SchemaOptions & {
4
+ export interface OptionalSchemaOptions<T> extends SchemaOptions {
5
5
  readonly source: Schema<T>;
6
6
  readonly value?: T | null;
7
- };
7
+ }
8
8
  /** Validate a value of a specific type or `null`. */
9
9
  export declare class OptionalSchema<T> extends ThroughSchema<T | null> {
10
10
  readonly value: T | null;
11
- constructor(options: OptionalSchemaOptions<T>);
11
+ constructor({ value, ...options }: OptionalSchemaOptions<T>);
12
12
  validate(unsafeValue?: unknown): T | null;
13
13
  }
14
14
  /** Create a new optional schema from a source schema. */
@@ -1,8 +1,8 @@
1
1
  import { ThroughSchema } from "./ThroughSchema.js";
2
2
  /** Validate a value of a specific type or `null`. */
3
3
  export class OptionalSchema extends ThroughSchema {
4
- constructor(options) {
5
- super({ value: null, ...options });
4
+ constructor({ value = null, ...options }) {
5
+ super({ value, ...options });
6
6
  }
7
7
  validate(unsafeValue = this.value) {
8
8
  if (unsafeValue === null || unsafeValue === undefined || unsafeValue === "" || Number.isNaN(unsafeValue))
@@ -6,7 +6,7 @@ import { StringSchema } from "./StringSchema.js";
6
6
  * - Falsy values are converted to `""` empty string.
7
7
  */
8
8
  export declare class PhoneSchema extends StringSchema {
9
- constructor(options: StringSchemaOptions);
9
+ constructor({ title, ...options }: StringSchemaOptions);
10
10
  sanitize(insaneString: string): string;
11
11
  }
12
12
  /** Valid phone number, e.g. `+441234567890` */
@@ -10,9 +10,9 @@ const PHONE_REGEXP = /^\+[1-9][0-9]{0,2}[0-9]{5,12}$/;
10
10
  * - Falsy values are converted to `""` empty string.
11
11
  */
12
12
  export class PhoneSchema extends StringSchema {
13
- constructor(options) {
13
+ constructor({ title = "Phone", ...options }) {
14
14
  super({
15
- title: "Phone",
15
+ title,
16
16
  ...options,
17
17
  type: "tel",
18
18
  min: 1,
@@ -5,7 +5,7 @@ export type HtmlInputType = "text" | "password" | "color" | "date" | "email" | "
5
5
  /** Function that sanitizes a string. */
6
6
  export type Sanitizer = (str: string) => string;
7
7
  /** Options for `StringSchema` */
8
- export type StringSchemaOptions = SchemaOptions & {
8
+ export interface StringSchemaOptions extends SchemaOptions {
9
9
  readonly value?: string | undefined;
10
10
  readonly type?: HtmlInputType | undefined;
11
11
  readonly min?: number | undefined;
@@ -13,7 +13,7 @@ export type StringSchemaOptions = SchemaOptions & {
13
13
  readonly match?: RegExp | undefined;
14
14
  readonly sanitizer?: Sanitizer | undefined;
15
15
  readonly multiline?: boolean | undefined;
16
- };
16
+ }
17
17
  /**
18
18
  * Schema that defines a valid string.
19
19
  *
@@ -42,7 +42,7 @@ export declare class StringSchema extends Schema<string> {
42
42
  readonly match: RegExp | undefined;
43
43
  readonly sanitizer: Sanitizer | undefined;
44
44
  readonly multiline: boolean;
45
- constructor(options: StringSchemaOptions);
45
+ constructor({ type, min, max, match, sanitizer, multiline, value, ...options }: StringSchemaOptions);
46
46
  validate(unsafeValue?: unknown): string;
47
47
  /**
48
48
  * Clean a string by removing characters that aren't digits.
@@ -29,9 +29,8 @@ export class StringSchema extends Schema {
29
29
  match;
30
30
  sanitizer;
31
31
  multiline;
32
- constructor(options) {
33
- super({ value: "", ...options });
34
- const { type = "text", min = 0, max = Infinity, match, sanitizer, multiline = false } = options;
32
+ constructor({ type = "text", min = 0, max = Infinity, match, sanitizer, multiline = false, value = "", ...options }) {
33
+ super({ value, ...options });
35
34
  this.type = type;
36
35
  this.min = min;
37
36
  this.max = max;
@@ -2,12 +2,12 @@ import type { SchemaOptions } from "./Schema.js";
2
2
  import type { Sourceable } from "../util/source.js";
3
3
  import { Schema } from "./Schema.js";
4
4
  /** Allowed options for `ThroughSchama` */
5
- export type ThroughSchemaOptions<T> = SchemaOptions & {
5
+ export interface ThroughSchemaOptions<T> extends SchemaOptions {
6
6
  source: Schema<T>;
7
- };
7
+ }
8
8
  /** Schema that passes through to a source schema. */
9
9
  export declare abstract class ThroughSchema<T> extends Schema<T> implements Sourceable<Schema<T>> {
10
10
  readonly source: Schema<T>;
11
- constructor(options: ThroughSchemaOptions<T>);
11
+ constructor({ source, ...options }: ThroughSchemaOptions<T>);
12
12
  validate(unsafeValue: unknown): T;
13
13
  }
@@ -2,8 +2,7 @@ import { Schema } from "./Schema.js";
2
2
  /** Schema that passes through to a source schema. */
3
3
  export class ThroughSchema extends Schema {
4
4
  source;
5
- constructor(options) {
6
- const source = options.source;
5
+ constructor({ source, ...options }) {
7
6
  super({ ...source, ...options });
8
7
  this.source = source;
9
8
  }
@@ -4,12 +4,12 @@ import type { PossibleTime } from "../util/time.js";
4
4
  import { Time } from "../util/time.js";
5
5
  import { Schema } from "./Schema.js";
6
6
  /** Allowed options for `TimeSchama` */
7
- export type TimeSchemaOptions = SchemaOptions & {
7
+ export interface TimeSchemaOptions extends SchemaOptions {
8
8
  readonly value?: PossibleTime | undefined;
9
9
  readonly min?: Optional<PossibleTime> | undefined;
10
10
  readonly max?: Optional<PossibleTime> | undefined;
11
11
  readonly step?: number | undefined;
12
- };
12
+ }
13
13
  /** Define a valid time in 24h hh:mm:ss.fff format, e.g. `23:59` or `24:00 */
14
14
  export declare class TimeSchema extends Schema<string> {
15
15
  readonly value: PossibleTime;
@@ -20,7 +20,7 @@ export declare class TimeSchema extends Schema<string> {
20
20
  * - Note: `<input type="time">` elements expect `step=""` to be in _seconds_ so you need to multiply this by `1000`
21
21
  */
22
22
  readonly step: number | undefined;
23
- constructor(options: TimeSchemaOptions);
23
+ constructor({ min, max, step, title, value, ...options }: TimeSchemaOptions);
24
24
  validate(unsafeValue?: unknown): string;
25
25
  }
26
26
  /** Valid time, e.g. `2005-09-12` (required because falsy values are invalid). */
@@ -12,9 +12,8 @@ export class TimeSchema extends Schema {
12
12
  * - Note: `<input type="time">` elements expect `step=""` to be in _seconds_ so you need to multiply this by `1000`
13
13
  */
14
14
  step;
15
- constructor(options) {
16
- super({ title: "Time", value: "now", ...options });
17
- const { min = null, max = null, step = 60 } = options;
15
+ constructor({ min = null, max = null, step = 60, title = "Time", value = "now", ...options }) {
16
+ super({ title, value, ...options });
18
17
  this.min = getOptionalTime(min);
19
18
  this.max = getOptionalTime(max);
20
19
  this.step = step;
package/schema/index.d.ts CHANGED
@@ -7,6 +7,8 @@ export * from "./DataSchema.js";
7
7
  export * from "./DateSchema.js";
8
8
  export * from "./DictionarySchema.js";
9
9
  export * from "./EmailSchema.js";
10
+ export * from "./EntitySchema.js";
11
+ export * from "./FileSchema.js";
10
12
  export * from "./KeySchema.js";
11
13
  export * from "./LinkSchema.js";
12
14
  export * from "./NumberSchema.js";
package/schema/index.js CHANGED
@@ -7,6 +7,8 @@ export * from "./DataSchema.js";
7
7
  export * from "./DateSchema.js";
8
8
  export * from "./DictionarySchema.js";
9
9
  export * from "./EmailSchema.js";
10
+ export * from "./EntitySchema.js";
11
+ export * from "./FileSchema.js";
10
12
  export * from "./KeySchema.js";
11
13
  export * from "./LinkSchema.js";
12
14
  export * from "./NumberSchema.js";
@@ -0,0 +1,16 @@
1
+ import type { Optional } from "./optional.js";
2
+ /** Entity strings combine a type and ID, e.g. `challenge:a1b2c3` */
3
+ export type Entity<T extends string = string> = `${T}:${string}`;
4
+ /** Extract the type from an `Entity` string. */
5
+ export type EntityType<E extends Entity> = E extends Entity<infer T> ? T : never;
6
+ /** Empty entity has undefined type and ID. */
7
+ export type EmptyEntity = [type: undefined, id: undefined];
8
+ /** Empty entity. */
9
+ export declare const EMPTY_ENTITY: EmptyEntity;
10
+ /** Split an entity tag like `challenge:a1b2c3` into `type` and `id` */
11
+ export declare function splitEntity<T extends string>(entity: Entity<T>): [type: T, id: string];
12
+ export declare function splitEntity(entity: string): [type: string, id: string];
13
+ /** Split an optional entity tag like `challenge:a1b2c3` into `["challenge", "a1b2c3"]`, or `undefined` or `null` */
14
+ export declare function splitOptionalEntity<T extends string>(entity: Entity<T>): [type: T, id: string];
15
+ export declare function splitOptionalEntity<T extends string>(entity: Optional<Entity<T>>): [type: T, id: string] | EmptyEntity;
16
+ export declare function splitOptionalEntity(entity: Optional<string>): [type: string, id: string] | EmptyEntity;
package/util/entity.js ADDED
@@ -0,0 +1,17 @@
1
+ import { ValueError } from "../error/ValueError.js";
2
+ /** Empty entity. */
3
+ export const EMPTY_ENTITY = [undefined, undefined];
4
+ export function splitEntity(entity) {
5
+ const bits = entity.split(":", 2);
6
+ if (bits[0] && bits[1])
7
+ return bits;
8
+ throw new ValueError("Invalid entity");
9
+ }
10
+ export function splitOptionalEntity(entity) {
11
+ if (entity) {
12
+ const bits = entity.split(":", 2);
13
+ if (bits[0] && bits[1])
14
+ return bits;
15
+ }
16
+ return EMPTY_ENTITY;
17
+ }
package/util/file.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /** List of file types in `extension: mime` format. */
2
+ export type FileTypes = {
3
+ [extension: string]: string;
4
+ };
5
+ /** Get the file extension from a file path, or return `undefined` if the input has no extension. */
6
+ export declare function getOptionalFileExtension(file: string): string | undefined;
7
+ /** Get the file extension from a file path, or throw `ValueError` if the input has no extension. */
8
+ export declare function getFileExtension(path: string): string;
package/util/file.js ADDED
@@ -0,0 +1,13 @@
1
+ import { ValueError } from "../error/ValueError.js";
2
+ /** Get the file extension from a file path, or return `undefined` if the input has no extension. */
3
+ export function getOptionalFileExtension(file) {
4
+ const i = file.lastIndexOf(".");
5
+ return (i >= 0 && file.slice(i + 1)) || undefined;
6
+ }
7
+ /** Get the file extension from a file path, or throw `ValueError` if the input has no extension. */
8
+ export function getFileExtension(path) {
9
+ const extension = getOptionalFileExtension(path);
10
+ if (!extension)
11
+ throw new ValueError("File extension is required", path);
12
+ return extension;
13
+ }
package/util/index.d.ts CHANGED
@@ -13,9 +13,11 @@ export * from "./dictionary.js";
13
13
  export * from "./diff.js";
14
14
  export * from "./dispose.js";
15
15
  export * from "./duration.js";
16
+ export * from "./entity.js";
16
17
  export * from "./entry.js";
17
18
  export * from "./equal.js";
18
19
  export * from "./error.js";
20
+ export * from "./file.js";
19
21
  export * from "./focus.js";
20
22
  export * from "./function.js";
21
23
  export * from "./hash.js";
package/util/index.js CHANGED
@@ -13,9 +13,11 @@ export * from "./dictionary.js";
13
13
  export * from "./diff.js";
14
14
  export * from "./dispose.js";
15
15
  export * from "./duration.js";
16
+ export * from "./entity.js";
16
17
  export * from "./entry.js";
17
18
  export * from "./equal.js";
18
19
  export * from "./error.js";
20
+ export * from "./file.js";
19
21
  export * from "./focus.js";
20
22
  export * from "./function.js";
21
23
  export * from "./hash.js";