shelving 1.86.4 → 1.86.6

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/db/Query.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getFirstItem, getLastItem, getOptionalFirstItem, getOptionalLastItem, isArrayMin, countArray } from "../util/array.js";
1
+ import { getFirstItem, getLastItem, getOptionalFirstItem, getOptionalLastItem, isArrayLength, countArray } from "../util/array.js";
2
2
  import { runSequence } from "../util/sequence.js";
3
3
  import { QueryConstraints } from "../constraint/QueryConstraints.js";
4
4
  /** Reference to a set of items in a sync or async provider. */
@@ -68,7 +68,7 @@ export class AsyncQuery extends BaseQuery {
68
68
  return this.value.then(countArray);
69
69
  }
70
70
  get exists() {
71
- return this.max(1).value.then(isArrayMin);
71
+ return this.max(1).value.then(isArrayLength);
72
72
  }
73
73
  get firstValue() {
74
74
  return this.max(1).value.then(getOptionalFirstItem);
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.86.4",
14
+ "version": "1.86.6",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -6,7 +6,7 @@ export type ArraySchemaOptions<T> = SchemaOptions & {
6
6
  readonly value?: ImmutableArray;
7
7
  readonly items: Validator<T>;
8
8
  readonly min?: number;
9
- readonly max?: number | null;
9
+ readonly max?: number;
10
10
  readonly unique?: boolean;
11
11
  };
12
12
  /**
@@ -41,7 +41,7 @@ export declare class ArraySchema<T> extends Schema<ImmutableArray<T>> {
41
41
  readonly items: Validator<T>;
42
42
  readonly unique: boolean;
43
43
  readonly min: number;
44
- readonly max: number | null;
44
+ readonly max: number;
45
45
  constructor({ value, items, unique, min, max, ...options }: ArraySchemaOptions<T>);
46
46
  validate(unsafeValue?: unknown): ImmutableArray<T>;
47
47
  }
@@ -30,7 +30,7 @@ import { Schema } from "./Schema.js";
30
30
  * schema.validate(["a", null], schema); // Throws Invalids({ "1": Invalid('Must be a string') });
31
31
  */
32
32
  export class ArraySchema extends Schema {
33
- constructor({ value = [], items, unique = false, min = 0, max = null, ...options }) {
33
+ constructor({ value = [], items, unique = false, min = 0, max = Infinity, ...options }) {
34
34
  super(options);
35
35
  this.value = value;
36
36
  this.items = items;
@@ -43,9 +43,9 @@ export class ArraySchema extends Schema {
43
43
  throw new InvalidFeedback("Must be array", { value: unsafeValue });
44
44
  const safeArray = validateArray(unsafeValue, this.items);
45
45
  const dedupedArray = this.unique ? uniqueArray(safeArray) : safeArray;
46
- if (typeof this.min === "number" && dedupedArray.length < this.min)
46
+ if (dedupedArray.length < this.min)
47
47
  throw new InvalidFeedback(dedupedArray.length ? `Minimum ${this.min} items` : "Required", { value: dedupedArray });
48
- if (typeof this.max === "number" && dedupedArray.length > this.max)
48
+ if (dedupedArray.length > this.max)
49
49
  throw new InvalidFeedback(`Maximum ${this.max} items`, { value: dedupedArray });
50
50
  return dedupedArray;
51
51
  }
@@ -1,7 +1,7 @@
1
1
  import { Schema, SchemaOptions } from "./Schema.js";
2
2
  /** Allowed options for `BooleanSchema` */
3
3
  export type BooleanSchemaOptions = SchemaOptions & {
4
- readonly value?: boolean;
4
+ readonly value?: boolean | undefined;
5
5
  };
6
6
  /** Define a valid boolean. */
7
7
  export declare class BooleanSchema extends Schema<boolean> {
@@ -5,7 +5,7 @@ import { Schema, SchemaOptions } from "./Schema.js";
5
5
  /** Allowed options for `DataSchema` */
6
6
  export type DataSchemaOptions<T extends Data> = SchemaOptions & {
7
7
  readonly props: Validators<T>;
8
- readonly value?: Partial<T>;
8
+ readonly value?: Partial<T> | undefined;
9
9
  };
10
10
  /** Validate a data object. */
11
11
  export declare class DataSchema<T extends Data> extends Schema<T> {
@@ -1,10 +1,10 @@
1
- import { PossibleDate } from "../util/date.js";
1
+ import { PossibleDate, PossibleOptionalDate } from "../util/date.js";
2
2
  import { Schema, SchemaOptions } from "./Schema.js";
3
3
  /** Allowed options for `DateSchema` */
4
4
  export type DateSchemaOptions = SchemaOptions & {
5
- readonly value?: PossibleDate;
6
- readonly min?: PossibleDate | null;
7
- readonly max?: PossibleDate | null;
5
+ readonly value?: PossibleDate | undefined;
6
+ readonly min?: PossibleOptionalDate | undefined;
7
+ readonly max?: PossibleOptionalDate | undefined;
8
8
  };
9
9
  /** Define a valid date in YMD format, e.g. `2005-09-12` */
10
10
  export declare class DateSchema extends Schema<string> {
@@ -1,4 +1,4 @@
1
- import { formatDate, getDate, getOptionalDate, getYMD } from "../util/date.js";
1
+ import { formatDate, getOptionalDate, getYMD } from "../util/date.js";
2
2
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
3
3
  import { Schema } from "./Schema.js";
4
4
  import { OPTIONAL } from "./OptionalSchema.js";
@@ -7,8 +7,8 @@ export class DateSchema extends Schema {
7
7
  constructor({ value = "now", min = null, max = null, ...options }) {
8
8
  super(options);
9
9
  this.value = value;
10
- this.min = min !== null ? getDate(min) : null;
11
- this.max = max !== null ? getDate(max) : null;
10
+ this.min = getOptionalDate(min);
11
+ this.max = getOptionalDate(max);
12
12
  }
13
13
  validate(unsafeValue = this.value) {
14
14
  const date = getOptionalDate(unsafeValue);
@@ -4,16 +4,16 @@ import { Schema, SchemaOptions } from "./Schema.js";
4
4
  /** Allowed options for `DictionarySchema` */
5
5
  export type DictionarySchemaOptions<T> = SchemaOptions & {
6
6
  readonly items: Validator<T>;
7
- readonly value?: ImmutableDictionary;
8
- readonly min?: number | null;
9
- readonly max?: number | null;
7
+ readonly value?: ImmutableDictionary | undefined;
8
+ readonly min?: number | undefined;
9
+ readonly max?: number | undefined;
10
10
  };
11
11
  /** Validate a dictionary object (whose props are all the same with string keys). */
12
12
  export declare class DictionarySchema<T> extends Schema<ImmutableDictionary<T>> {
13
13
  readonly value: ImmutableDictionary;
14
14
  readonly items: Validator<T>;
15
- readonly min: number | null;
16
- readonly max: number | null;
15
+ readonly min: number;
16
+ readonly max: number;
17
17
  constructor({ value, items, min, max, ...rest }: DictionarySchemaOptions<T>);
18
18
  validate(unsafeValue?: unknown): ImmutableDictionary<T>;
19
19
  }
@@ -4,10 +4,8 @@ import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
4
4
  import { Schema } from "./Schema.js";
5
5
  /** Validate a dictionary object (whose props are all the same with string keys). */
6
6
  export class DictionarySchema extends Schema {
7
- constructor({ value = {}, items, min = null, max = null, ...rest }) {
7
+ constructor({ value = {}, items, min = 0, max = Infinity, ...rest }) {
8
8
  super(rest);
9
- this.min = null;
10
- this.max = null;
11
9
  this.items = items;
12
10
  this.value = value;
13
11
  this.min = min;
@@ -18,9 +16,9 @@ export class DictionarySchema extends Schema {
18
16
  throw new InvalidFeedback("Must be object", { value: unsafeValue });
19
17
  const unsafeEntries = Object.entries(unsafeValue);
20
18
  const safeObject = Object.fromEntries(validateEntries(unsafeEntries, this.items));
21
- if (typeof this.min === "number" && unsafeEntries.length < this.min)
19
+ if (unsafeEntries.length < this.min)
22
20
  throw new InvalidFeedback(unsafeEntries.length ? `Minimum ${this.min} items` : "Required", { value: safeObject });
23
- if (typeof this.max === "number" && unsafeEntries.length > this.max)
21
+ if (unsafeEntries.length > this.max)
24
22
  throw new InvalidFeedback(`Maximum ${this.max} items`, { value: safeObject });
25
23
  return safeObject;
26
24
  }
@@ -1,8 +1,8 @@
1
1
  import { StringSchema, StringSchemaOptions } from "./StringSchema.js";
2
2
  /** Allowed options for `LinkSchema` */
3
3
  export type LinkSchemaOptions = StringSchemaOptions & {
4
- readonly schemes?: string[];
5
- readonly hosts?: string[] | null;
4
+ readonly schemes?: string[] | undefined;
5
+ readonly hosts?: string[] | undefined;
6
6
  };
7
7
  /**
8
8
  * Type of `StringSchema` that defines a valid URL.
@@ -15,7 +15,7 @@ export declare class LinkSchema extends StringSchema {
15
15
  readonly min = 1;
16
16
  readonly max = 512;
17
17
  readonly schemes: string[];
18
- readonly hosts: string[] | null;
18
+ readonly hosts: string[] | undefined;
19
19
  constructor({ schemes, hosts, ...rest }: LinkSchemaOptions);
20
20
  validate(unsafeValue: unknown): string;
21
21
  }
@@ -9,13 +9,11 @@ import { StringSchema } from "./StringSchema.js";
9
9
  * - Falsy values are converted to `""` empty string.
10
10
  */
11
11
  export class LinkSchema extends StringSchema {
12
- constructor({ schemes = ["http:", "https:"], hosts = null, ...rest }) {
12
+ constructor({ schemes = ["http:", "https:"], hosts, ...rest }) {
13
13
  super(rest);
14
14
  this.type = "url";
15
15
  this.min = 1;
16
16
  this.max = 512;
17
- this.schemes = ["http:", "https:"];
18
- this.hosts = null;
19
17
  this.schemes = schemes;
20
18
  this.hosts = hosts;
21
19
  }
@@ -1,16 +1,16 @@
1
1
  import { Schema, SchemaOptions } from "./Schema.js";
2
2
  /** Allowed options for `NumberSchema` */
3
3
  export type NumberSchemaOptions = SchemaOptions & {
4
- readonly value?: number | null;
5
- readonly min?: number | null;
6
- readonly max?: number | null;
7
- readonly step?: number | null;
4
+ readonly value?: number | undefined;
5
+ readonly min?: number | undefined;
6
+ readonly max?: number | undefined;
7
+ readonly step?: number | null | undefined;
8
8
  };
9
9
  /** Schema that defines a valid number. */
10
10
  export declare class NumberSchema extends Schema<number> {
11
- readonly value: number | null;
12
- readonly min: number | null;
13
- readonly max: number | null;
11
+ readonly value: number;
12
+ readonly min: number;
13
+ readonly max: number;
14
14
  readonly step: number | null;
15
15
  constructor({ value, min, max, step, ...rest }: NumberSchemaOptions);
16
16
  validate(unsafeValue?: unknown): number;
@@ -4,7 +4,7 @@ import { Schema } from "./Schema.js";
4
4
  import { OPTIONAL } from "./OptionalSchema.js";
5
5
  /** Schema that defines a valid number. */
6
6
  export class NumberSchema extends Schema {
7
- constructor({ value = 0, min = null, max = null, step = null, ...rest }) {
7
+ constructor({ value = 0, min = -Infinity, max = Infinity, step = null, ...rest }) {
8
8
  super(rest);
9
9
  this.value = value;
10
10
  this.min = min;
@@ -16,9 +16,9 @@ export class NumberSchema extends Schema {
16
16
  if (typeof unsafeNumber !== "number")
17
17
  throw new InvalidFeedback("Must be number", { value: unsafeValue });
18
18
  const safeNumber = typeof this.step === "number" ? roundStep(unsafeNumber, this.step) : unsafeNumber;
19
- if (typeof this.max === "number" && safeNumber > this.max)
19
+ if (safeNumber > this.max)
20
20
  throw new InvalidFeedback(`Maximum ${formatNumber(this.max)}`, { value: safeNumber });
21
- if (typeof this.min === "number" && safeNumber < this.min)
21
+ if (safeNumber < this.min)
22
22
  throw new InvalidFeedback(`Minimum ${formatNumber(this.min)}`, { value: safeNumber });
23
23
  return safeNumber;
24
24
  }
@@ -2,11 +2,11 @@ import type { Validatable } from "../util/validate.js";
2
2
  /** Options allowed by a `Schema` instance. */
3
3
  export type SchemaOptions = {
4
4
  /** Title of the schema, e.g. for using as the title of a corresponding field. */
5
- readonly title?: string | null;
5
+ readonly title?: string | undefined;
6
6
  /** Description of the schema, e.g. for using as a description in a corresponding field. */
7
- readonly description?: string | null;
7
+ readonly description?: string | undefined;
8
8
  /** Placeholder of the schema, e.g. for using as a placeholder in a corresponding field. */
9
- readonly placeholder?: string | null;
9
+ readonly placeholder?: string | undefined;
10
10
  /** Default value for the schema if `validate()` is called with an `undefined` value. */
11
11
  readonly value?: unknown;
12
12
  };
@@ -17,14 +17,16 @@ export type SchemaOptions = {
17
17
  */
18
18
  export declare abstract class Schema<T extends unknown = unknown> implements Validatable<T> {
19
19
  /** Title of the schema, e.g. for using as the title of a corresponding field. */
20
- readonly title: string | null;
20
+ readonly title: string | undefined;
21
21
  /** Description of the schema, e.g. for using as a description in a corresponding field. */
22
- readonly description: string | null;
22
+ readonly description: string | undefined;
23
23
  /** Placeholder of the schema, e.g. for using as a placeholder in a corresponding field. */
24
- readonly placeholder: string | null;
24
+ readonly placeholder: string | undefined;
25
25
  /** Default value for the schema if `validate()` is called with an `undefined` value. */
26
26
  readonly value: unknown;
27
27
  constructor({ title, description, placeholder, value }: SchemaOptions);
28
28
  /** Every schema must implement a `validate()` method. */
29
29
  abstract validate(unsafeValue: unknown): T;
30
30
  }
31
+ /** Is an unknown value a `Schema` instance? */
32
+ export declare const isSchema: <T extends Schema<unknown>>(v: unknown) => v is T;
package/schema/Schema.js CHANGED
@@ -4,10 +4,12 @@
4
4
  * - `validate()` returns `Invalid` if value was not valid.
5
5
  */
6
6
  export class Schema {
7
- constructor({ title = null, description = null, placeholder = null, value }) {
7
+ constructor({ title, description, placeholder, value }) {
8
8
  this.title = title;
9
9
  this.description = description;
10
10
  this.placeholder = placeholder;
11
11
  this.value = value;
12
12
  }
13
13
  }
14
+ /** Is an unknown value a `Schema` instance? */
15
+ export const isSchema = (v) => v instanceof Schema;
@@ -5,13 +5,13 @@ export type HtmlInputType = "text" | "password" | "color" | "date" | "email" | "
5
5
  export type Sanitizer = (str: string) => string;
6
6
  /** Options for `StringSchema` */
7
7
  export type StringSchemaOptions = SchemaOptions & {
8
- readonly value?: string;
9
- readonly type?: HtmlInputType;
10
- readonly min?: number;
11
- readonly max?: number | null;
12
- readonly match?: RegExp | null;
13
- readonly sanitizer?: Sanitizer | null;
14
- readonly multiline?: boolean;
8
+ readonly value?: string | undefined;
9
+ readonly type?: HtmlInputType | undefined;
10
+ readonly min?: number | undefined;
11
+ readonly max?: number | undefined;
12
+ readonly match?: RegExp | undefined;
13
+ readonly sanitizer?: Sanitizer | undefined;
14
+ readonly multiline?: boolean | undefined;
15
15
  };
16
16
  /**
17
17
  * Schema that defines a valid string.
@@ -37,9 +37,9 @@ export declare class StringSchema extends Schema<string> {
37
37
  readonly value: string;
38
38
  readonly type: HtmlInputType;
39
39
  readonly min: number;
40
- readonly max: number | null;
41
- readonly match: RegExp | null;
42
- readonly sanitizer: Sanitizer | null;
40
+ readonly max: number;
41
+ readonly match: RegExp | undefined;
42
+ readonly sanitizer: Sanitizer | undefined;
43
43
  readonly multiline: boolean;
44
44
  constructor({ value, type, min, max, match, sanitizer, multiline, ...rest }: StringSchemaOptions);
45
45
  validate(unsafeValue?: unknown): string;
@@ -22,7 +22,7 @@ import { Schema } from "./Schema.js";
22
22
  * schema.validate('j'); // Throws 'Minimum 3 chaacters'
23
23
  */
24
24
  export class StringSchema extends Schema {
25
- constructor({ value = "", type = "text", min = 0, max = null, match = null, sanitizer = null, multiline = false, ...rest }) {
25
+ constructor({ value = "", type = "text", min = 0, max = Infinity, match, sanitizer, multiline = false, ...rest }) {
26
26
  super(rest);
27
27
  this.type = type;
28
28
  this.value = value;
@@ -39,7 +39,7 @@ export class StringSchema extends Schema {
39
39
  const safeString = this.sanitize(unsafeString);
40
40
  if (safeString.length < this.min)
41
41
  throw new InvalidFeedback(safeString ? `Minimum ${this.min} characters` : "Required", { value: safeString });
42
- if (this.max && safeString.length > this.max)
42
+ if (safeString.length > this.max)
43
43
  throw new InvalidFeedback(`Maximum ${this.max} characters`, { value: safeString });
44
44
  if (this.match && !this.match.test(safeString))
45
45
  throw new InvalidFeedback(safeString ? "Invalid format" : "Required", { value: safeString });
@@ -1,11 +1,11 @@
1
- import { PossibleTime, Time } from "../util/time.js";
1
+ import { PossibleTime, Time, PossibleOptionalTime } from "../util/time.js";
2
2
  import { Schema, SchemaOptions } from "./Schema.js";
3
3
  /** Allowed options for `TimeSchama` */
4
4
  export type TimeSchemaOptions = SchemaOptions & {
5
- readonly value?: PossibleTime;
6
- readonly min?: PossibleTime | null;
7
- readonly max?: PossibleTime | null;
8
- readonly step?: number | null;
5
+ readonly value?: PossibleTime | undefined;
6
+ readonly min?: PossibleOptionalTime | undefined;
7
+ readonly max?: PossibleOptionalTime | undefined;
8
+ readonly step?: number | null | undefined;
9
9
  };
10
10
  /** Define a valid time in 24h hh:mm:ss.fff format, e.g. `23:59` or `24:00 */
11
11
  export declare class TimeSchema extends Schema<string> {
@@ -1,6 +1,6 @@
1
1
  import { InvalidFeedback } from "../feedback/InvalidFeedback.js";
2
2
  import { roundStep } from "../util/number.js";
3
- import { getOptionalTime, Time, getTime } from "../util/time.js";
3
+ import { getOptionalTime, Time } from "../util/time.js";
4
4
  import { OPTIONAL } from "./OptionalSchema.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 */
@@ -8,8 +8,8 @@ export class TimeSchema extends Schema {
8
8
  constructor({ value = "now", min = null, max = null, step = 60, ...options }) {
9
9
  super(options);
10
10
  this.value = value;
11
- this.min = min !== null ? getTime(min) : null;
12
- this.max = max !== null ? getTime(max) : null;
11
+ this.min = getOptionalTime(min);
12
+ this.max = getOptionalTime(max);
13
13
  this.step = step;
14
14
  }
15
15
  validate(unsafeValue = this.value) {
package/util/array.d.ts CHANGED
@@ -60,20 +60,32 @@ export declare function limitArray<T>(items: Iterable<T>, limit: number): Immuta
60
60
  /** Count the items in an array. */
61
61
  export declare function countArray<T>(arr: ImmutableArray<T>): number;
62
62
  /** Does an array have the specified minimum length. */
63
- export declare function isArrayMin<T>(arr: ImmutableArray<T>, min?: 1): arr is [T, ...T[]];
64
- export declare function isArrayMin<T>(arr: ImmutableArray<T>, min: 2): arr is [T, T, ...T[]];
65
- export declare function isArrayMin<T>(arr: ImmutableArray<T>, min: 3): arr is [T, T, T, ...T[]];
66
- export declare function isArrayMin<T>(arr: ImmutableArray<T>, min: 4): arr is [T, T, T, T, ...T[]];
67
- export declare function isArrayMin<T>(arr: ImmutableArray<T>, min: number): boolean;
63
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min: 1, max: 1): arr is readonly [T];
64
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min: 2, max: 2): arr is readonly [T, T];
65
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min: 3, max: 3): arr is readonly [T, T, T];
66
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min: 4, max: 4): arr is readonly [T, T, T, T];
67
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min?: 1, max?: number): arr is readonly [T, ...T[]];
68
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min: 2, max?: number): arr is readonly [T, T, ...T[]];
69
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min: 3, max?: number): arr is readonly [T, T, T, ...T[]];
70
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min: 4, max?: number): arr is readonly [T, T, T, T, ...T[]];
71
+ export declare function isArrayLength<T>(arr: ImmutableArray<T>, min?: number, max?: number): boolean;
68
72
  /** Assert that a value has a specific length (or length is in a specific range). */
69
- export declare function assertArrayMin<T>(arr: ImmutableArray<T> | unknown, min?: 1): asserts arr is [T, ...T[]];
70
- export declare function assertArrayMin<T>(arr: ImmutableArray<T> | unknown, min: 2): asserts arr is [T, T, ...T[]];
71
- export declare function assertArrayMin<T>(arr: ImmutableArray<T> | unknown, min: 3): asserts arr is [T, T, T, ...T[]];
72
- export declare function assertArrayMin<T>(arr: ImmutableArray<T> | unknown, min: 4): asserts arr is [T, T, T, T, ...T[]];
73
- export declare function assertArrayMin<T>(arr: ImmutableArray<T> | unknown, min: number): asserts arr is ImmutableArray<T>;
73
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T>, min: 1, max: 1): asserts arr is readonly [T];
74
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T>, min: 2, max: 2): asserts arr is readonly [T, T];
75
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T>, min: 3, max: 3): asserts arr is readonly [T, T, T];
76
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T>, min: 4, max: 4): asserts arr is readonly [T, T, T, T];
77
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T> | unknown, min?: 1, max?: number): asserts arr is readonly [T, ...T[]];
78
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T> | unknown, min: 2, max?: number): asserts arr is readonly [T, T, ...T[]];
79
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T> | unknown, min: 3, max?: number): asserts arr is readonly [T, T, T, ...T[]];
80
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T> | unknown, min: 4, max?: number): asserts arr is readonly [T, T, T, T, ...T[]];
81
+ export declare function assertArrayLength<T>(arr: ImmutableArray<T> | unknown, min: number, max?: number): asserts arr is ImmutableArray<T>;
74
82
  /** Get an array if it has the specified minimum length. */
75
- export declare function getArrayMin<T>(arr: ImmutableArray<T>, min?: 1): [T, ...T[]];
76
- export declare function getArrayMin<T>(arr: ImmutableArray<T>, min: 2): [T, T, ...T[]];
77
- export declare function getArrayMin<T>(arr: ImmutableArray<T>, min: 3): [T, T, T, ...T[]];
78
- export declare function getArrayMin<T>(arr: ImmutableArray<T>, min: 4): [T, T, T, T, ...T[]];
79
- export declare function getArrayMin<T>(arr: ImmutableArray<T>, min: number): ImmutableArray<T>;
83
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min: 1, max: 1): readonly [T];
84
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min: 2, max: 2): readonly [T, T];
85
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min: 3, max: 3): readonly [T, T, T];
86
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min: 4, max: 4): readonly [T, T, T, T];
87
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min?: 1, max?: number): readonly [T, ...T[]];
88
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min: 2, max?: number): readonly [T, T, ...T[]];
89
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min: 3, max?: number): readonly [T, T, T, ...T[]];
90
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min: 4, max?: number): readonly [T, T, T, T, ...T[]];
91
+ export declare function getArrayLength<T>(arr: ImmutableArray<T>, min?: number, max?: number): ImmutableArray<T>;
package/util/array.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { AssertionError } from "../error/AssertionError.js";
2
2
  import { RequiredError } from "../error/RequiredError.js";
3
3
  import { filterItems, omitItems, pickItems } from "./iterate.js";
4
+ import { formatRange } from "./number.js";
4
5
  /** Is an unknown value an array? */
5
6
  export const isArray = (v) => Array.isArray(v);
6
7
  /** Assert that a value is an array. */
@@ -137,14 +138,14 @@ export function limitArray(items, limit) {
137
138
  export function countArray(arr) {
138
139
  return arr.length;
139
140
  }
140
- export function isArrayMin(arr, min = 1) {
141
- return arr.length >= min;
141
+ export function isArrayLength(arr, min = 1, max = Infinity) {
142
+ return arr.length >= min && arr.length <= max;
142
143
  }
143
- export function assertArrayMin(arr, min = 1) {
144
- if (!isArray(arr) || !isArrayMin(arr, min))
145
- throw new AssertionError(`Must be array with minimum length ${min}`, arr);
144
+ export function assertArrayLength(arr, min = 1, max = Infinity) {
145
+ if (!isArray(arr) || !isArrayLength(arr, min, max))
146
+ throw new AssertionError(`Must be array with length ${formatRange(min, max)}`, arr);
146
147
  }
147
- export function getArrayMin(arr, min = 1) {
148
- assertArrayMin(arr, min);
148
+ export function getArrayLength(arr, min = 1, max = Infinity) {
149
+ assertArrayLength(arr, min, max);
149
150
  return arr;
150
151
  }
package/util/number.d.ts CHANGED
@@ -70,6 +70,8 @@ export declare function boundNumber(num: number, min: number, max: number): numb
70
70
  export declare function wrapNumber(num: number, min: number, max: number): number;
71
71
  /** Format a number (based on the user's browser language settings). */
72
72
  export declare function formatNumber(num: number, precision?: number | null): string;
73
+ /** Format a number range (based on the user's browser language settings). */
74
+ export declare function formatRange(min: number, max: number, precision?: number | null): string;
73
75
  /** Format a number with a short suffix, e.g. `1,000 kg` */
74
76
  export declare const formatQuantity: (num: number, abbr: string, precision?: number | null) => string;
75
77
  /** Format a number with a longer full-word suffix. */
package/util/number.js CHANGED
@@ -110,10 +110,16 @@ export function wrapNumber(num, min, max) {
110
110
  }
111
111
  /** Format a number (based on the user's browser language settings). */
112
112
  export function formatNumber(num, precision = null) {
113
+ if (Number.isNaN(num))
114
+ return "None";
113
115
  if (!Number.isFinite(num))
114
- return Number.isNaN(num) ? "None" : "Infinity";
116
+ return "";
115
117
  return new Intl.NumberFormat(undefined, { minimumFractionDigits: precision !== null && precision !== void 0 ? precision : undefined, maximumFractionDigits: precision !== null && precision !== void 0 ? precision : 20 }).format(num);
116
118
  }
119
+ /** Format a number range (based on the user's browser language settings). */
120
+ export function formatRange(min, max, precision = null) {
121
+ return `${formatNumber(min, precision)}–${formatNumber(max, precision)}`;
122
+ }
117
123
  /** Format a number with a short suffix, e.g. `1,000 kg` */
118
124
  export const formatQuantity = (num, abbr, precision) => `${formatNumber(num, precision)}${NNBSP}${abbr}`;
119
125
  /** Format a number with a longer full-word suffix. */
package/util/string.d.ts CHANGED
@@ -27,6 +27,12 @@ export declare function assertString(value: unknown): asserts value is string;
27
27
  * - Everything else returns `"Unknown"`
28
28
  */
29
29
  export declare function getString(value: unknown): string;
30
+ /** Does a string have the specified minimum length. */
31
+ export declare const isStringLength: (str: string, min?: number, max?: number) => boolean;
32
+ /** Assert that a value has a specific length (or length is in a specific range). */
33
+ export declare function assertStringLength(str: string | unknown, min?: number, max?: number): asserts str is string;
34
+ /** Get a string if it has the specified minimum length. */
35
+ export declare function getStringLength(arr: string, min?: number, max?: number): string;
30
36
  /** Concatenate an iterable set of strings together. */
31
37
  export declare const joinStrings: (strs: Iterable<string> & NotString, joiner?: string) => string;
32
38
  /**
@@ -89,13 +95,17 @@ export declare function limitString(str: string, maxLength: number, append?: str
89
95
  * Divide a string into parts based on a separator.
90
96
  * - Like `String.prototype.split()` but with more useful arguments.
91
97
  * - Excess segments in `String.prototype.split()` is counterintuitive because further parts are thrown away.
92
- * - Excess segments in `splitString()` are concatenated onto the last segment (set `maxSegments` to `null` if you want infinite segments).
98
+ * - Excess segments in `splitString()` are concatenated onto the last segment (set `max` to `null` if you want infinite segments).
93
99
  *
94
- * @throws AssertionError if `minSegments` isn't met.
100
+ * @throws AssertionError if `min` isn't met.
95
101
  * @throws AssertionError if any of the segments are empty.
96
102
  */
97
- export declare function splitString(str: string, separator: string, minSegments: 1, maxSegments?: number): readonly [string, ...string[]];
98
- export declare function splitString(str: string, separator: string, minSegments: 2, maxSegments?: number): readonly [string, string, ...string[]];
99
- export declare function splitString(str: string, separator: string, minSegments: 3, maxSegments?: number): readonly [string, string, string, ...string[]];
100
- export declare function splitString(str: string, separator: string, minSegments: 4, maxSegments?: number): readonly [string, string, string, string, ...string[]];
101
- export declare function splitString(str: string, separator: string, minSegments?: number, maxSegments?: number | null): ImmutableArray<string>;
103
+ export declare function splitString(str: string, separator: string, min: 1, max: 1): readonly [string];
104
+ export declare function splitString(str: string, separator: string, min: 2, max: 2): readonly [string, string];
105
+ export declare function splitString(str: string, separator: string, min: 3, max: 3): readonly [string, string, string];
106
+ export declare function splitString(str: string, separator: string, min: 4, max: 4): readonly [string, string, string, string];
107
+ export declare function splitString(str: string, separator: string, min: 1, max?: number): readonly [string, ...string[]];
108
+ export declare function splitString(str: string, separator: string, min: 2, max?: number): readonly [string, string, ...string[]];
109
+ export declare function splitString(str: string, separator: string, min: 3, max?: number): readonly [string, string, string, ...string[]];
110
+ export declare function splitString(str: string, separator: string, min: 4, max?: number): readonly [string, string, string, string, ...string[]];
111
+ export declare function splitString(str: string, separator: string, min?: number, max?: number): ImmutableArray<string>;
package/util/string.js CHANGED
@@ -3,7 +3,7 @@ import { AssertionError } from "../error/AssertionError.js";
3
3
  import { formatDate, isDate } from "./date.js";
4
4
  import { formatObject, isObject } from "./object.js";
5
5
  import { getArray, isArray } from "./array.js";
6
- import { formatNumber, isBetween } from "./number.js";
6
+ import { formatNumber, formatRange, isBetween } from "./number.js";
7
7
  /** Is a value a string? */
8
8
  export const isString = (v) => typeof v === "string";
9
9
  /** Assert that a value is a string. */
@@ -45,6 +45,18 @@ export function getString(value) {
45
45
  return formatObject(value);
46
46
  return "Unknown";
47
47
  }
48
+ /** Does a string have the specified minimum length. */
49
+ export const isStringLength = (str, min = 1, max = Infinity) => str.length >= min && str.length <= max;
50
+ /** Assert that a value has a specific length (or length is in a specific range). */
51
+ export function assertStringLength(str, min = 1, max = Infinity) {
52
+ if (!isString(str) || !isStringLength(str, min, max))
53
+ throw new AssertionError(`Must be string with length ${formatRange(min, max)}`, str);
54
+ }
55
+ /** Get a string if it has the specified minimum length. */
56
+ export function getStringLength(arr, min = 1, max = Infinity) {
57
+ assertStringLength(arr, min, max);
58
+ return arr;
59
+ }
48
60
  /** Concatenate an iterable set of strings together. */
49
61
  export const joinStrings = (strs, joiner = "") => getArray(strs).join(joiner);
50
62
  /**
@@ -133,11 +145,11 @@ export function limitString(str, maxLength, append = "…") {
133
145
  const lastSpace = str.lastIndexOf(" ", maxLength);
134
146
  return `${str.slice(0, lastSpace > 0 ? lastSpace : maxLength).trimEnd()}${append}`;
135
147
  }
136
- export function splitString(str, separator, minSegments = 0, maxSegments = minSegments) {
148
+ export function splitString(str, separator, min = 1, max = Infinity) {
137
149
  const segments = str.split(separator);
138
- if (typeof maxSegments === "number" && segments.length > maxSegments)
139
- segments.splice(maxSegments - 1, segments.length, segments.slice(maxSegments - 1).join(separator));
140
- if (segments.length < minSegments || !segments.every(Boolean))
141
- throw new AssertionError(`Must be string with ${minSegments} non-empty segments separated by "${separator}"`, str);
150
+ if (segments.length > max)
151
+ segments.splice(max - 1, segments.length, segments.slice(max - 1).join(separator));
152
+ if (segments.length < min || !segments.every(Boolean))
153
+ throw new AssertionError(`Must be string with ${formatRange(min, max)} non-empty segments separated by "${separator}"`, str);
142
154
  return segments;
143
155
  }