shelving 1.160.4 → 1.161.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.
@@ -1,5 +1,5 @@
1
1
  import { DeferredSequence } from "../sequence/DeferredSequence.js";
2
- import { getArray } from "../util/array.js";
2
+ import { requireArray } from "../util/array.js";
3
3
  import { isArrayEqual } from "../util/equal.js";
4
4
  import { getItem } from "../util/item.js";
5
5
  import { countItems } from "../util/iterate.js";
@@ -163,7 +163,7 @@ export class MemoryTable {
163
163
  return query ? countItems(queryItems(this._data.values(), query)) : this._data.size;
164
164
  }
165
165
  getQuery(query) {
166
- return getArray(query ? queryItems(this._data.values(), query) : this._data.values());
166
+ return requireArray(query ? queryItems(this._data.values(), query) : this._data.values());
167
167
  }
168
168
  async *getQuerySequence(query) {
169
169
  let lastItems = this.getQuery(query);
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.160.4",
14
+ "version": "1.161.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -3,11 +3,18 @@ import type { SchemaOptions } from "./Schema.js";
3
3
  import { Schema } from "./Schema.js";
4
4
  /** Allowed options for `ArraySchema` */
5
5
  export interface ArraySchemaOptions<T> extends SchemaOptions {
6
+ /** The default value */
6
7
  readonly value?: ImmutableArray;
8
+ /** A schema all the array items in the value must conform to */
7
9
  readonly items: Schema<T>;
10
+ /** Minimum number of items. */
8
11
  readonly min?: number;
12
+ /** Maximum number of items. */
9
13
  readonly max?: number;
14
+ /** Whether to deduplicate the items. */
10
15
  readonly unique?: boolean;
16
+ /** Separator is used */
17
+ readonly separator?: string | RegExp;
11
18
  }
12
19
  /**
13
20
  * Define a valid array.
@@ -15,13 +22,6 @@ export interface ArraySchemaOptions<T> extends SchemaOptions {
15
22
  * Validates arrays and ensures the array's items match a specified format.
16
23
  * Only returns a new instance of the object if it changes (for immutability).
17
24
  *
18
- * Schema options:
19
- * - `value` The default value
20
- * - `length` The exact array length required
21
- * - `min` The minimum array length required
22
- * - `max` The maximum array length required
23
- * - `values` A schema all the array items in the value must conform to
24
- *
25
25
  * @example
26
26
  * const schema = new ArraySchema({ min: 1, max: 2, default: [10,11,12], required: true });
27
27
  * schema.validate([1,2,3], schema); // Returns [1,2,3]
@@ -42,7 +42,8 @@ export declare class ArraySchema<T> extends Schema<ImmutableArray<T>> {
42
42
  readonly unique: boolean;
43
43
  readonly min: number;
44
44
  readonly max: number;
45
- constructor({ items, one, many, title, placeholder, unique, min, max, value, ...options }: ArraySchemaOptions<T>);
45
+ readonly separator: string | RegExp;
46
+ constructor({ items, one, many, title, placeholder, unique, min, max, separator, value, ...options }: ArraySchemaOptions<T>);
46
47
  validate(unsafeValue?: unknown): ImmutableArray<T>;
47
48
  }
48
49
  /** Valid array with specifed items. */
@@ -8,13 +8,6 @@ import { Schema } from "./Schema.js";
8
8
  * Validates arrays and ensures the array's items match a specified format.
9
9
  * Only returns a new instance of the object if it changes (for immutability).
10
10
  *
11
- * Schema options:
12
- * - `value` The default value
13
- * - `length` The exact array length required
14
- * - `min` The minimum array length required
15
- * - `max` The maximum array length required
16
- * - `values` A schema all the array items in the value must conform to
17
- *
18
11
  * @example
19
12
  * const schema = new ArraySchema({ min: 1, max: 2, default: [10,11,12], required: true });
20
13
  * schema.validate([1,2,3], schema); // Returns [1,2,3]
@@ -34,17 +27,20 @@ export class ArraySchema extends Schema {
34
27
  unique;
35
28
  min;
36
29
  max;
37
- constructor({ items, one = items.one, many = items.many, title = "Items", placeholder = `No ${many}`, unique = false, min = 0, max = Number.POSITIVE_INFINITY, value = [], ...options }) {
30
+ separator;
31
+ constructor({ items, one = items.one, many = items.many, title = "Items", placeholder = `No ${many}`, unique = false, min = 0, max = Number.POSITIVE_INFINITY, separator = ",", value = [], ...options }) {
38
32
  super({ one, many, title, placeholder, value, ...options });
39
33
  this.items = items;
40
34
  this.unique = unique;
41
35
  this.min = min;
42
36
  this.max = max;
37
+ this.separator = separator;
43
38
  }
44
39
  validate(unsafeValue = this.value) {
45
- if (!isArray(unsafeValue))
40
+ const unsafeArray = typeof unsafeValue === "string" ? unsafeValue.split(this.separator).filter(Boolean) : unsafeValue;
41
+ if (!isArray(unsafeArray))
46
42
  throw new ValueFeedback("Must be array", unsafeValue);
47
- const validArray = validateArray(unsafeValue, this.items);
43
+ const validArray = validateArray(unsafeArray, this.items);
48
44
  const uniqueArray = this.unique ? getUniqueArray(validArray) : validArray;
49
45
  if (uniqueArray.length < this.min)
50
46
  throw new ValueFeedback(uniqueArray.length ? `Minimum ${this.min} ${this.many}` : "Required", uniqueArray);
@@ -1,10 +1,13 @@
1
1
  import { Schema } from "./Schema.js";
2
+ const NEGATIVE = ["", "false", "0", "no", "n", "off"];
2
3
  /** Define a valid boolean. */
3
4
  export class BooleanSchema extends Schema {
4
5
  constructor({ value = false, ...options }) {
5
6
  super({ value, ...options });
6
7
  }
7
8
  validate(unsafeValue = this.value) {
9
+ if (typeof unsafeValue === "string")
10
+ return !NEGATIVE.includes(unsafeValue.toLowerCase().trim());
8
11
  return !!unsafeValue;
9
12
  }
10
13
  }
@@ -1,4 +1,4 @@
1
- import type { Data, Database } from "../util/data.js";
1
+ import type { Data, Database, PartialData } from "../util/data.js";
2
2
  import type { Identifier, Item } from "../util/item.js";
3
3
  import type { NullableSchema } from "./NullableSchema.js";
4
4
  import type { SchemaOptions, Schemas } from "./Schema.js";
@@ -25,6 +25,6 @@ export declare const DATA: <T extends Data>(props: Schemas<T>) => DataSchema<T>;
25
25
  /** Valid data object with specifed properties, or `null` */
26
26
  export declare const NULLABLE_DATA: <T extends Data>(props: Schemas<T>) => NullableSchema<T>;
27
27
  /** Create a `DataSchema` that validates partially, i.e. properties can be their value, or `undefined` */
28
- export declare function PARTIAL<T extends Data>(source: Schemas<T> | DataSchema<T>): DataSchema<Partial<T>>;
28
+ export declare function PARTIAL<T extends Data>(source: Schemas<T> | DataSchema<T>): DataSchema<PartialData<T>>;
29
29
  /** Create a `DataSchema` that validates a data item, i.e. it has a string or number `.id` identifier property. */
30
30
  export declare function ITEM<I extends Identifier, T extends Data>(id: Schema<I>, schemas: Schemas<T> | DataSchema<T>): DataSchema<Item<I, T>>;
@@ -14,8 +14,8 @@ export declare class DictionaryStore<T> extends Store<ImmutableDictionary<T>> im
14
14
  get(name: string): T | undefined;
15
15
  /** Set an item in this dictionary. */
16
16
  set(name: string, value: T): void;
17
- /** Delete an item in this dictionary. */
18
- delete(name: string): void;
17
+ /** Delete an item (or several items) in this dictionary. */
18
+ delete(name: string, ...names: string[]): void;
19
19
  /** Iterate over the entries of the object. */
20
20
  [Symbol.iterator](): Iterator<DictionaryItem<T>>;
21
21
  }
@@ -27,9 +27,9 @@ export class DictionaryStore extends Store {
27
27
  set(name, value) {
28
28
  this.value = withProp(this.value, name, value);
29
29
  }
30
- /** Delete an item in this dictionary. */
31
- delete(name) {
32
- this.value = omitProps(this.value, name);
30
+ /** Delete an item (or several items) in this dictionary. */
31
+ delete(name, ...names) {
32
+ this.value = omitProps(this.value, name, ...names);
33
33
  }
34
34
  /** Iterate over the entries of the object. */
35
35
  [Symbol.iterator]() {
@@ -11,4 +11,5 @@ export declare class PathStore extends Store<AbsolutePath> {
11
11
  isProud(path: AbsolutePath): boolean;
12
12
  /** Get an absolute path from a path relative to the current store path. */
13
13
  getPath(path: string): AbsolutePath;
14
+ toString(): string;
14
15
  }
@@ -24,4 +24,7 @@ export class PathStore extends Store {
24
24
  getPath(path) {
25
25
  return requirePath(path, this.value);
26
26
  }
27
+ toString() {
28
+ return this.value;
29
+ }
27
30
  }
@@ -0,0 +1,46 @@
1
+ import type { Data } from "../util/data.js";
2
+ import { type ImmutableDictionary } from "../util/dictionary.js";
3
+ import { type PossibleURL } from "../util/url.js";
4
+ import { Store } from "./Store.js";
5
+ /** Store a URL, e.g. `https://top.com/a/b/c` */
6
+ export declare class URLStore extends Store<URL> {
7
+ readonly base: URL | undefined;
8
+ constructor(url: PossibleURL, base?: PossibleURL);
9
+ set value(url: PossibleURL);
10
+ get value(): URL;
11
+ get href(): string;
12
+ set href(href: string);
13
+ get origin(): string;
14
+ get protocol(): string;
15
+ get username(): string;
16
+ get password(): string;
17
+ get hostname(): string;
18
+ get host(): string;
19
+ get port(): string;
20
+ get pathname(): string;
21
+ /** Get the URL params as a string. */
22
+ get search(): string;
23
+ /** Get the URL params as a dictionary. */
24
+ get params(): ImmutableDictionary<string>;
25
+ /** Return a single param in this URL, or `undefined` if it could not be found. */
26
+ getParam(key: string): string | undefined;
27
+ /** Require a single param in this URL, or throw `RequiredError` if it could not be found. */
28
+ requireParam(key: string): string | undefined;
29
+ /** Update a single param in this URL. */
30
+ setParam(key: string, value: unknown): void;
31
+ /** Update several params in this URL. */
32
+ setParams(params: Data): void;
33
+ /** Delete one or more params in this URL. */
34
+ deleteParam(key: string, ...keys: string[]): void;
35
+ /** Delete one or more params in this URL. */
36
+ deleteParams(key: string, ...keys: string[]): void;
37
+ /** Return the current URL with an additional param. */
38
+ withParam(key: string, value: unknown): URL;
39
+ /** Return the current URL with an additional param. */
40
+ withParams(params: Data): URL;
41
+ /** Return the current URL with an additional param. */
42
+ omitParams(...keys: string[]): URL;
43
+ /** Return the current URL with an additional param. */
44
+ omitParam(key: string): URL;
45
+ toString(): string;
46
+ }
@@ -0,0 +1,100 @@
1
+ import { getSetter } from "../util/class.js";
2
+ import { getDictionary } from "../util/dictionary.js";
3
+ import { getURL, getURLParam, omitURLParams, requireURL, requireURLParam, withURLParam, withURLParams, } from "../util/url.js";
4
+ import { Store } from "./Store.js";
5
+ /** Store a URL, e.g. `https://top.com/a/b/c` */
6
+ export class URLStore extends Store {
7
+ base;
8
+ constructor(url, base) {
9
+ const baseURL = getURL(base);
10
+ super(requireURL(url, baseURL));
11
+ this.base = baseURL;
12
+ }
13
+ set value(url) {
14
+ super.value = requireURL(url, this.base, getSetter(this, "value"));
15
+ }
16
+ get value() {
17
+ return super.value;
18
+ }
19
+ get href() {
20
+ return this.value.href;
21
+ }
22
+ set href(href) {
23
+ this.value = requireURL(href, this.base, getSetter(this, "href"));
24
+ }
25
+ get origin() {
26
+ return this.value.origin;
27
+ }
28
+ get protocol() {
29
+ return this.value.protocol;
30
+ }
31
+ get username() {
32
+ return this.value.username;
33
+ }
34
+ get password() {
35
+ return this.value.password;
36
+ }
37
+ get hostname() {
38
+ return this.value.hostname;
39
+ }
40
+ get host() {
41
+ return this.value.host;
42
+ }
43
+ get port() {
44
+ return this.value.port;
45
+ }
46
+ get pathname() {
47
+ return this.value.pathname;
48
+ }
49
+ /** Get the URL params as a string. */
50
+ get search() {
51
+ return this.value.search;
52
+ }
53
+ /** Get the URL params as a dictionary. */
54
+ get params() {
55
+ return getDictionary(this.value.searchParams);
56
+ }
57
+ /** Return a single param in this URL, or `undefined` if it could not be found. */
58
+ getParam(key) {
59
+ return getURLParam(this.value.searchParams, key);
60
+ }
61
+ /** Require a single param in this URL, or throw `RequiredError` if it could not be found. */
62
+ requireParam(key) {
63
+ return requireURLParam(this.value.searchParams, key, getSetter(this, "requireParam"));
64
+ }
65
+ /** Update a single param in this URL. */
66
+ setParam(key, value) {
67
+ this.value = withURLParam(this.value, key, value, this.setParams);
68
+ }
69
+ /** Update several params in this URL. */
70
+ setParams(params) {
71
+ this.value = withURLParams(this.value, params, this.setParams);
72
+ }
73
+ /** Delete one or more params in this URL. */
74
+ deleteParam(key, ...keys) {
75
+ this.value = omitURLParams(this.value, key, ...keys);
76
+ }
77
+ /** Delete one or more params in this URL. */
78
+ deleteParams(key, ...keys) {
79
+ this.value = omitURLParams(this.value, key, ...keys);
80
+ }
81
+ /** Return the current URL with an additional param. */
82
+ withParam(key, value) {
83
+ return withURLParam(this.value, key, value, this.withParam);
84
+ }
85
+ /** Return the current URL with an additional param. */
86
+ withParams(params) {
87
+ return withURLParams(this.value, params, this.withParams);
88
+ }
89
+ /** Return the current URL with an additional param. */
90
+ omitParams(...keys) {
91
+ return omitURLParams(this.value, ...keys);
92
+ }
93
+ /** Return the current URL with an additional param. */
94
+ omitParam(key) {
95
+ return omitURLParams(this.value, key);
96
+ }
97
+ toString() {
98
+ return this.href;
99
+ }
100
+ }
package/store/index.d.ts CHANGED
@@ -4,3 +4,4 @@ export * from "./DataStore.js";
4
4
  export * from "./DictionaryStore.js";
5
5
  export * from "./PathStore.js";
6
6
  export * from "./Store.js";
7
+ export * from "./URLStore.js";
package/store/index.js CHANGED
@@ -4,3 +4,4 @@ export * from "./DataStore.js";
4
4
  export * from "./DictionaryStore.js";
5
5
  export * from "./PathStore.js";
6
6
  export * from "./Store.js";
7
+ export * from "./URLStore.js";
package/util/array.d.ts CHANGED
@@ -42,9 +42,9 @@ export declare function assertArray<T>(arr: ImmutableArray<T>, min: 3, max: 3, c
42
42
  export declare function assertArray<T>(arr: ImmutableArray<T>, min?: 1, max?: number, caller?: AnyCaller): asserts arr is readonly [T, ...T[]];
43
43
  export declare function assertArray<T>(arr: ImmutableArray<T>, min: 2, max?: number, caller?: AnyCaller): asserts arr is readonly [T, T, ...T[]];
44
44
  export declare function assertArray<T>(arr: ImmutableArray<T>, min: 3, max?: number, caller?: AnyCaller): asserts arr is readonly [T, T, T, ...T[]];
45
- export declare function assertArray<T>(value: unknown, min?: number, max?: number, caller?: AnyCaller): asserts value is ImmutableArray<T>;
45
+ export declare function assertArray<T>(arr: ImmutableArray<T>, min?: number, max?: number, caller?: AnyCaller): asserts arr is ImmutableArray<T>;
46
46
  /** Convert a possible array to an array. */
47
- export declare function getArray<T>(list: PossibleArray<T>): ImmutableArray<T>;
47
+ export declare function getArray(list: unknown): ImmutableArray<unknown> | undefined;
48
48
  /** Convert a possible array to an array (optionally with specified min/max length), or throw `RequiredError` if conversion fails. */
49
49
  export declare function requireArray<T>(arr: MutableArray<T>, min: 1, max: 1, caller?: AnyCaller): [T];
50
50
  export declare function requireArray<T>(arr: MutableArray<T>, min: 2, max: 2, caller?: AnyCaller): [T, T];
@@ -66,12 +66,20 @@ export declare function isArrayItem<T>(list: PossibleArray<T>, item: unknown): i
66
66
  export declare function assertArrayItem<T>(arr: PossibleArray<T>, item: unknown, caller?: AnyCaller): asserts item is T;
67
67
  /** Add multiple items to an array (immutably) and return a new array with those items (or the same array if no changes were made). */
68
68
  export declare function withArrayItems<T>(list: PossibleArray<T>, ...add: T[]): ImmutableArray<T>;
69
+ /** Add an item to an array (immutably) and return a new array with that item (or the same array if no changes were made). */
70
+ export declare const withArrayItem: <T>(items: PossibleArray<T>, add: T) => ImmutableArray<T>;
69
71
  /** Pick multiple items from an array (immutably) and return a new array with those items (or the same array if no changes were made). */
70
72
  export declare function pickArrayItems<T>(items: PossibleArray<T>, ...pick: T[]): ImmutableArray<T>;
73
+ /** Pick an item from an array (immutably) and return a new array with that item (or the same array if no changes were made). */
74
+ export declare const pickArrayItem: <T>(items: PossibleArray<T>, pick: T) => ImmutableArray<T>;
71
75
  /** Remove multiple items from an array (immutably) and return a new array without those items (or the same array if no changes were made). */
72
76
  export declare function omitArrayItems<T>(items: PossibleArray<T>, ...omit: T[]): ImmutableArray<T>;
77
+ /** Remove an item from an array (immutably) and return a new array without those items (or the same array if no changes were made). */
78
+ export declare const omitArrayItem: <T>(items: PossibleArray<T>, omit: T) => ImmutableArray<T>;
73
79
  /** Toggle an item in and out of an array (immutably) and return a new array with or without the specified items (or the same array if no changes were made). */
74
80
  export declare function toggleArrayItems<T>(items: PossibleArray<T>, ...toggle: T[]): ImmutableArray<T>;
81
+ /** Toggle an item in and out of an array (immutably) and return a new array with or without the specified item (or the same array if no changes were made). */
82
+ export declare const toggleArrayItem: <T>(items: PossibleArray<T>, toggle: T) => ImmutableArray<T>;
75
83
  /** Return a shuffled version of an array or iterable. */
76
84
  export declare function shuffleArray<T>(items: PossibleArray<T>): ImmutableArray<T>;
77
85
  /**
@@ -84,11 +92,10 @@ export declare function addArrayItem<T>(arr: MutableArray<T>, item: T): T;
84
92
  * - Skip items that already exist.
85
93
  */
86
94
  export declare function addArrayItems<T>(arr: MutableArray<T>, ...items: T[]): void;
87
- /**
88
- * Remove multiple items from an array (by reference).
89
- * - Skip items that already exist.
90
- */
95
+ /** Remove multiple items from an array (by reference). */
91
96
  export declare function deleteArrayItems<T>(arr: MutableArray<T>, ...items: T[]): void;
97
+ /** Remove an item from an array (by reference). */
98
+ export declare const deleteArrayItem: <T>(arr: MutableArray<T>, item: T) => void;
92
99
  /** Return an array of the unique items in an array. */
93
100
  export declare function getUniqueArray<T>(list: PossibleArray<T>): ImmutableArray<T>;
94
101
  /** Apply a limit to an array. */
package/util/array.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RequiredError } from "../error/RequiredError.js";
2
- import { interleaveItems, omitItems, pickItems } from "./iterate.js";
2
+ import { interleaveItems, isIterable, omitItems, pickItems } from "./iterate.js";
3
3
  export function isArray(value, min = 0, max = Number.POSITIVE_INFINITY) {
4
4
  return Array.isArray(value) && value.length >= min && value.length <= max;
5
5
  }
@@ -12,10 +12,10 @@ export function assertArray(value, min, max, caller = assertArray) {
12
12
  }
13
13
  /** Convert a possible array to an array. */
14
14
  export function getArray(list) {
15
- return Array.isArray(list) ? list : Array.from(list);
15
+ return Array.isArray(list) ? list : isIterable(list) ? Array.from(list) : undefined;
16
16
  }
17
17
  export function requireArray(list, min, max, caller = requireArray) {
18
- const arr = getArray(list);
18
+ const arr = Array.isArray(list) ? list : Array.from(list);
19
19
  assertArray(arr, min, max, caller);
20
20
  return arr;
21
21
  }
@@ -42,16 +42,22 @@ export function withArrayItems(list, ...add) {
42
42
  function _doesNotInclude(value) {
43
43
  return !this.includes(value);
44
44
  }
45
+ /** Add an item to an array (immutably) and return a new array with that item (or the same array if no changes were made). */
46
+ export const withArrayItem = withArrayItems;
45
47
  /** Pick multiple items from an array (immutably) and return a new array with those items (or the same array if no changes were made). */
46
48
  export function pickArrayItems(items, ...pick) {
47
49
  const arr = Array.from(pickItems(items, ...pick));
48
50
  return isArray(items) && arr.length === items.length ? items : arr;
49
51
  }
52
+ /** Pick an item from an array (immutably) and return a new array with that item (or the same array if no changes were made). */
53
+ export const pickArrayItem = pickArrayItems;
50
54
  /** Remove multiple items from an array (immutably) and return a new array without those items (or the same array if no changes were made). */
51
55
  export function omitArrayItems(items, ...omit) {
52
56
  const filtered = Array.from(omitItems(items, ...omit));
53
57
  return isArray(items) && filtered.length === items.length ? items : filtered;
54
58
  }
59
+ /** Remove an item from an array (immutably) and return a new array without those items (or the same array if no changes were made). */
60
+ export const omitArrayItem = omitArrayItems;
55
61
  /** Toggle an item in and out of an array (immutably) and return a new array with or without the specified items (or the same array if no changes were made). */
56
62
  export function toggleArrayItems(items, ...toggle) {
57
63
  const arr = Array.from(items);
@@ -59,6 +65,8 @@ export function toggleArrayItems(items, ...toggle) {
59
65
  const filtered = arr.filter(_doesNotInclude, toggle);
60
66
  return extras.length ? [...filtered, ...extras] : filtered.length !== arr.length ? filtered : isArray(items) ? items : arr;
61
67
  }
68
+ /** Toggle an item in and out of an array (immutably) and return a new array with or without the specified item (or the same array if no changes were made). */
69
+ export const toggleArrayItem = toggleArrayItems;
62
70
  /** Return a shuffled version of an array or iterable. */
63
71
  export function shuffleArray(items) {
64
72
  const arr = Array.from(items);
@@ -86,15 +94,14 @@ export function addArrayItems(arr, ...items) {
86
94
  if (arr.indexOf(item) < 0)
87
95
  arr.push(item);
88
96
  }
89
- /**
90
- * Remove multiple items from an array (by reference).
91
- * - Skip items that already exist.
92
- */
97
+ /** Remove multiple items from an array (by reference). */
93
98
  export function deleteArrayItems(arr, ...items) {
94
99
  for (let i = arr.length - 1; i >= 0; i--)
95
100
  if (i in arr && items.includes(arr[i]))
96
101
  arr.splice(i, 1);
97
102
  }
103
+ /** Remove an item from an array (by reference). */
104
+ export const deleteArrayItem = deleteArrayItems;
98
105
  /** Return an array of the unique items in an array. */
99
106
  export function getUniqueArray(list) {
100
107
  const output = [];
@@ -35,6 +35,8 @@ export declare const withDictionaryItem: <T>(dict: ImmutableDictionary<T>, key:
35
35
  export declare const withDictionaryItems: <T>(dict: ImmutableDictionary<T>, props: PossibleDictionary<T>) => ImmutableDictionary<T>;
36
36
  /** Remove several key/value entries from a dictionary object (immutably) and return a new object without those props. */
37
37
  export declare const omitDictionaryItems: <T>(dict: ImmutableDictionary<T>, ...keys: string[]) => ImmutableDictionary<T>;
38
+ /** Remove a key/value entry from a dictionary object (immutably) and return a new object without that prop. */
39
+ export declare const omitDictionaryItem: <T>(dict: ImmutableDictionary<T>, key: string) => ImmutableDictionary<T>;
38
40
  /** Pick several props from a dictionary object and return a new object with only thos props. */
39
41
  export declare const pickDictionaryItems: <T>(dict: ImmutableDictionary<T>, ...keys: string[]) => ImmutableDictionary<T>;
40
42
  /** Set a single named prop on a dictionary object (by reference) and return its value. */
@@ -43,6 +45,8 @@ export declare const setDictionaryItem: <T>(dict: MutableDictionary<T>, key: str
43
45
  export declare const setDictionaryItems: <T>(dict: MutableDictionary<T>, entries: PossibleDictionary<T>) => void;
44
46
  /** Remove several key/value entries from a dictionary object (by reference). */
45
47
  export declare const deleteDictionaryItems: <T extends MutableDictionary>(dict: T, ...keys: string[]) => void;
48
+ /** Remove a key/value entry from a dictionary object (by reference). */
49
+ export declare const deleteDictionaryItem: <T extends MutableDictionary>(dict: T, key: string) => void;
46
50
  /** Type that represents an empty dictionary object. */
47
51
  export type EmptyDictionary = {
48
52
  readonly [K in never]: never;
@@ -42,6 +42,8 @@ export const withDictionaryItem = withProp;
42
42
  export const withDictionaryItems = withProps;
43
43
  /** Remove several key/value entries from a dictionary object (immutably) and return a new object without those props. */
44
44
  export const omitDictionaryItems = omitProps;
45
+ /** Remove a key/value entry from a dictionary object (immutably) and return a new object without that prop. */
46
+ export const omitDictionaryItem = omitProps;
45
47
  /** Pick several props from a dictionary object and return a new object with only thos props. */
46
48
  export const pickDictionaryItems = pickProps;
47
49
  /** Set a single named prop on a dictionary object (by reference) and return its value. */
@@ -50,5 +52,7 @@ export const setDictionaryItem = setProp;
50
52
  export const setDictionaryItems = setProps;
51
53
  /** Remove several key/value entries from a dictionary object (by reference). */
52
54
  export const deleteDictionaryItems = deleteProps;
55
+ /** Remove a key/value entry from a dictionary object (by reference). */
56
+ export const deleteDictionaryItem = deleteProps;
53
57
  /** An empty dictionary object. */
54
58
  export const EMPTY_DICTIONARY = { __proto__: null };
package/util/object.d.ts CHANGED
@@ -90,6 +90,8 @@ export declare function withProps<T>(input: T, props: Partial<T>): T;
90
90
  export declare function withProps<T>(input: T, props: T | Partial<T> | Iterable<Prop<T>>): T;
91
91
  /** Remove several props from an object (immutably) and return a new object without those props. */
92
92
  export declare function omitProps<T, K extends Key<T>>(input: T, ...keys: K[]): Omit<T, K>;
93
+ /** Remove a prop from an object (immutably) and return a new object without that prop. */
94
+ export declare const omitProp: <T, K extends Key<T>>(input: T, key: K) => Omit<T, K>;
93
95
  /** Pick several props from an object and return a new object with only thos props. */
94
96
  export declare function pickProps<T, K extends Key<T>>(obj: T, ...keys: K[]): Pick<T, K>;
95
97
  /** Set a single named prop on an object (by reference) and return its value. */
@@ -98,6 +100,8 @@ export declare function setProp<T extends MutableObject, K extends Key<T>>(obj:
98
100
  export declare function setProps<T extends MutableObject>(obj: T, entries: T | Partial<T> | Iterable<Prop<T>>): void;
99
101
  /** Remove several key/value entries from an object (by reference). */
100
102
  export declare function deleteProps<T extends MutableObject>(obj: T, ...keys: Key<T>[]): void;
103
+ /** Remove a key/value entry from an object (by reference). */
104
+ export declare const deleteProp: <T extends MutableObject>(input: T, key: Key<T>) => void;
101
105
  /**
102
106
  * Get the prototype of an object instance.
103
107
  * - Recommend to use this because Typescript's default lib specifies `Object.getPrototypeOf()` returning `any`.
package/util/object.js CHANGED
@@ -66,6 +66,8 @@ export function omitProps(input, ...keys) {
66
66
  function _hasntKey([key]) {
67
67
  return !this.includes(key);
68
68
  }
69
+ /** Remove a prop from an object (immutably) and return a new object without that prop. */
70
+ export const omitProp = omitProps;
69
71
  export function pickProps(input, ...keys) {
70
72
  return Object.fromEntries(Object.entries(input).filter(_hasKey, keys));
71
73
  }
@@ -87,6 +89,8 @@ export function deleteProps(obj, ...keys) {
87
89
  for (const key of keys)
88
90
  delete obj[key];
89
91
  }
92
+ /** Remove a key/value entry from an object (by reference). */
93
+ export const deleteProp = deleteProps;
90
94
  /**
91
95
  * Get the prototype of an object instance.
92
96
  * - Recommend to use this because Typescript's default lib specifies `Object.getPrototypeOf()` returning `any`.
package/util/path.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AnyCaller } from "./function.js";
2
- import { type Nullish } from "./null.js";
2
+ import type { Nullish } from "./null.js";
3
3
  /** Absolute path string starts with `/` slash. */
4
4
  export type AbsolutePath = `/` | `/${string}`;
5
5
  /** Relative path string starts with `./` or `../` */
@@ -19,7 +19,7 @@ export declare function isRelativePath(path: string): path is RelativePath;
19
19
  * @param base Absolute path used for resolving relative paths in `possible`
20
20
  * @return Absolute path with a leading trailing slash, e.g. `/a/c/b`
21
21
  */
22
- export declare function getPath(value: Nullish<string | URL>, base?: AbsolutePath | undefined): AbsolutePath | undefined;
22
+ export declare function getPath(value: Nullish<string | URL>, base?: AbsolutePath): AbsolutePath | undefined;
23
23
  /**
24
24
  * Resolve a relative or absolute path and return the path, or throw `RequiredError` if not a valid path.
25
25
  * - Internally uses `new URL` to do path processing but shouldn't ever reveal that fact.
package/util/path.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { RequiredError } from "../error/RequiredError.js";
2
- import { notNullish } from "./null.js";
2
+ import { getURL } from "./url.js";
3
3
  /** Is a string path an absolute path? */
4
4
  export function isAbsolutePath(path) {
5
5
  return path.startsWith("/");
@@ -23,15 +23,11 @@ function _cleanPath(path) {
23
23
  * @return Absolute path with a leading trailing slash, e.g. `/a/c/b`
24
24
  */
25
25
  export function getPath(value, base = "/") {
26
- if (notNullish(value)) {
27
- try {
28
- const { pathname, search, hash } = new URL(value, `http://j.com${base}/`);
29
- if (isAbsolutePath(pathname))
30
- return `${_cleanPath(pathname)}${search}${hash}`;
31
- }
32
- catch {
33
- //
34
- }
26
+ const url = getURL(value, `http://j.com${base === "/" || base.endsWith("/") ? base : `${base}/`}`);
27
+ if (url) {
28
+ const { pathname, search, hash } = url;
29
+ if (isAbsolutePath(pathname))
30
+ return `${_cleanPath(pathname)}${search}${hash}`;
35
31
  }
36
32
  }
37
33
  /**
package/util/regexp.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { RequiredError } from "../error/RequiredError.js";
2
2
  import { ValueError } from "../error/ValueError.js";
3
- import { getArray } from "./array.js";
3
+ import { requireArray } from "./array.js";
4
4
  /** Regular expression that always matches everything. */
5
5
  export const ALWAYS_REGEXP = /^.*$/;
6
6
  /** Regular expression that never matches anything. */
@@ -28,21 +28,21 @@ export function escapeRegExp(pattern) {
28
28
  const REPLACE_ESCAPED = /[-[\]/{}()*+?.\\^$|]/g;
29
29
  /** Create regular expression that matches any of a list of other expressions. */
30
30
  export function createRegExpAny(patterns, flags) {
31
- const arr = getArray(patterns).filter(Boolean);
31
+ const arr = requireArray(patterns).filter(Boolean);
32
32
  // If there are no patterns to match against then _no_ string can ever match against any of nothing.
33
33
  if (!arr.length)
34
34
  return NEVER_REGEXP;
35
35
  // Create RegExp using multiple joined matches like `(?:AAA)|(?:BBB)`
36
- return new RegExp(`(?:${getArray(patterns).map(getRegExpSource).join(")|(?:")})`, flags);
36
+ return new RegExp(`(?:${requireArray(patterns).map(getRegExpSource).join(")|(?:")})`, flags);
37
37
  }
38
38
  /** Create regular expression that matches all of a list of other expressions. */
39
39
  export function createRegExpAll(patterns, flags) {
40
- const arr = getArray(patterns).filter(Boolean);
40
+ const arr = requireArray(patterns).filter(Boolean);
41
41
  // If there are no patterns to match against then _every_ string will match against the entire list of nothing.
42
42
  if (!arr.length)
43
43
  return ALWAYS_REGEXP;
44
44
  // Create RegExp using multiple lookaheads like `^(?=.*?(?:AAA))(?=.*?(?:BBB))`
45
- return new RegExp(`^(?=.*?(?:${getArray(patterns).map(getRegExpSource).join("))(?=.*?(?:")}))`, flags);
45
+ return new RegExp(`^(?=.*?(?:${requireArray(patterns).map(getRegExpSource).join("))(?=.*?(?:")}))`, flags);
46
46
  }
47
47
  /** Match function for finding strings that match against regular expressions (use with `filter()` to positively filter iterable sets of items). */
48
48
  export function isMatch(str, regexp) {
package/util/string.d.ts CHANGED
@@ -10,8 +10,8 @@ export type NotString = {
10
10
  toUpperCase?: never;
11
11
  toLowerCase?: never;
12
12
  };
13
- /** Things that can be easily converted to a string. */
14
- export type PossibleString = string | number | Date;
13
+ /** Things that can be reliably converted to a string with no confusion. */
14
+ export type PossibleString = boolean | string | number | Date;
15
15
  /** Is a value a string (optionally with specified min/max length). */
16
16
  export declare function isString(value: unknown, min?: number, max?: number): value is string;
17
17
  /** Assert that a value is a string (optionally with specified min/max length). */
package/util/string.js CHANGED
@@ -20,8 +20,12 @@ export function getString(value) {
20
20
  return value;
21
21
  if (typeof value === "number")
22
22
  return value.toString();
23
+ if (typeof value === "boolean")
24
+ return value ? "true" : "false";
23
25
  if (value instanceof Date)
24
26
  return value.toISOString();
27
+ if (Array.isArray(value))
28
+ return value.map(getString).filter(Boolean).join(",");
25
29
  return undefined;
26
30
  }
27
31
  /** Convert a possible string to a string (optionally with specified min/max length), or throw `RequiredError` if conversion fails. */
package/util/url.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { Data } from "./data.js";
2
+ import type { ImmutableDictionary } from "./dictionary.js";
1
3
  import type { AnyCaller } from "./function.js";
2
4
  import { type Nullish } from "./null.js";
3
5
  /** Values that can be converted to a URL instance. */
@@ -10,3 +12,20 @@ export declare function assertURL(value: unknown, caller?: AnyCaller): asserts v
10
12
  export declare function getURL(possible: Nullish<PossibleURL>, base?: PossibleURL | undefined): URL | undefined;
11
13
  /** Convert a possible URL to a URL, or throw `RequiredError` if conversion fails. */
12
14
  export declare function requireURL(possible: PossibleURL, base?: PossibleURL, caller?: AnyCaller): URL;
15
+ /**
16
+ * Get the params of a URL as a dictionary of strings.
17
+ * - Note: The same param cannot exist multiple times in the returned dictionary, so the first value for a param will be used (which matches `URLSearchParams.get()` behavior).
18
+ */
19
+ export declare function getURLParams(params: URLSearchParams): ImmutableDictionary<string>;
20
+ /** Get a param from a URL. */
21
+ export declare function getURLParam(params: URLSearchParams, key: string): string | undefined;
22
+ /** Get a param from a URL. */
23
+ export declare function requireURLParam(params: URLSearchParams, key: string, caller?: AnyCaller): string | undefined;
24
+ /** Return a URL with a new param set (or same URL if no changes were made). */
25
+ export declare function withURLParam(url: PossibleURL, key: string, value: unknown, caller?: AnyCaller): URL;
26
+ /** Return a URL with several new params set (or same URL if no changes were made). */
27
+ export declare function withURLParams(url: PossibleURL, params: Data, caller?: AnyCaller): URL;
28
+ /** Return a URL without one or more params (or same URL if no changes were made). */
29
+ export declare function omitURLParams(url: PossibleURL, ...keys: string[]): URL;
30
+ /** Return a URL without a param (or same URL if no changes were made). */
31
+ export declare const omitURLParam: (url: PossibleURL, key: string) => URL;
package/util/url.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { RequiredError } from "../error/RequiredError.js";
2
2
  import { notNullish } from "./null.js";
3
+ import { getProps } from "./object.js";
4
+ import { requireString } from "./string.js";
3
5
  /** Is an unknown value a URL object? */
4
6
  export function isURL(value) {
5
7
  return value instanceof URL;
@@ -27,3 +29,50 @@ export function requireURL(possible, base, caller = requireURL) {
27
29
  assertURL(url, caller);
28
30
  return url;
29
31
  }
32
+ /**
33
+ * Get the params of a URL as a dictionary of strings.
34
+ * - Note: The same param cannot exist multiple times in the returned dictionary, so the first value for a param will be used (which matches `URLSearchParams.get()` behavior).
35
+ */
36
+ export function getURLParams(params) {
37
+ const output = {};
38
+ for (const [key, value] of params)
39
+ if (!Object.hasOwn(output, key))
40
+ output[key] = value;
41
+ return output;
42
+ }
43
+ /** Get a param from a URL. */
44
+ export function getURLParam(params, key) {
45
+ return params.get(key) || undefined;
46
+ }
47
+ /** Get a param from a URL. */
48
+ export function requireURLParam(params, key, caller = requireURLParam) {
49
+ const value = params.get(key);
50
+ if (value === null)
51
+ throw new RequiredError(`URL param "${key}" is required`, { received: params.toString(), caller });
52
+ return value;
53
+ }
54
+ /** Return a URL with a new param set (or same URL if no changes were made). */
55
+ export function withURLParam(url, key, value, caller = withURLParam) {
56
+ const input = requireURL(url);
57
+ const output = new URL(input);
58
+ output.searchParams.set(key, requireString(value, undefined, undefined, caller));
59
+ return input.href === output.href ? input : output;
60
+ }
61
+ /** Return a URL with several new params set (or same URL if no changes were made). */
62
+ export function withURLParams(url, params, caller = withURLParams) {
63
+ const input = requireURL(url);
64
+ const output = requireURL(url);
65
+ for (const [key, value] of getProps(params))
66
+ output.searchParams.set(key, requireString(value, undefined, undefined, caller));
67
+ return input.href === output.href ? input : output;
68
+ }
69
+ /** Return a URL without one or more params (or same URL if no changes were made). */
70
+ export function omitURLParams(url, ...keys) {
71
+ const input = requireURL(url);
72
+ const output = requireURL(url);
73
+ for (const key of keys)
74
+ output.searchParams.delete(key);
75
+ return input.href === output.href ? input : output;
76
+ }
77
+ /** Return a URL without a param (or same URL if no changes were made). */
78
+ export const omitURLParam = omitURLParams;