shelving 1.46.1 → 1.47.3

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/Database.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Entry, Observable, Observer, Result, Unsubscriber, Results, Validatable, Validator, Key, Data, Entries, Datas, Validators, ValidatorType, Dispatcher } from "../util/index.js";
2
2
  import { PropUpdates, Update } from "../update/index.js";
3
3
  import type { Provider } from "../provider/Provider.js";
4
- import { Filters, Sorts, Query } from "../query/index.js";
4
+ import { Filters, Sorts, Query, FilterProps, SortKeys } from "../query/index.js";
5
5
  /**
6
6
  * Combines a database model and a provider.
7
7
  *
@@ -14,7 +14,7 @@ export declare class Database<V extends Validators<Datas> = Validators<Datas>> {
14
14
  readonly provider: Provider;
15
15
  constructor(validators: V, provider: Provider);
16
16
  /** Create a query on a collection in this model. */
17
- query<K extends Key<V>>(collection: K, filters?: Filters<ValidatorType<V[K]>>, sorts?: Sorts<ValidatorType<V[K]>>, limit?: number | null): DatabaseQuery<ValidatorType<V[K]>>;
17
+ query<K extends Key<V>>(collection: K, filters?: FilterProps<ValidatorType<V[K]>>, sorts?: SortKeys<ValidatorType<V[K]>>, limit?: number | null): DatabaseQuery<ValidatorType<V[K]>>;
18
18
  /** Reference a document in a collection in this model. */
19
19
  doc<K extends Key<V>>(collection: K, id: string): DatabaseDocument<ValidatorType<V[K]>>;
20
20
  /**
@@ -122,7 +122,7 @@ export declare class DatabaseDocument<T extends Data = Data> implements Observab
122
122
  readonly id: string;
123
123
  constructor(db: Database, validator: Validator<T>, collection: string, id: string);
124
124
  /** Create a query on this document's collection. */
125
- query(filters?: Filters<T>, sorts?: Sorts<T>, limit?: number | null): DatabaseQuery<T>;
125
+ query(filters?: FilterProps<T>, sorts?: SortKeys<T>, limit?: number | null): DatabaseQuery<T>;
126
126
  /** Get an 'optional' reference to this document (uses a `ModelQuery` with an `id` filter). */
127
127
  get optional(): DatabaseQuery<T>;
128
128
  /**
@@ -163,8 +163,8 @@ export declare class DatabaseDocument<T extends Data = Data> implements Observab
163
163
  }
164
164
  /** Database data embeds the corresponding `Document` instance and string ID into the data. */
165
165
  export declare type DocumentData<T extends Data> = T & {
166
- _id: string;
167
- _doc: DatabaseDocument<T>;
166
+ id: string;
167
+ doc: DatabaseDocument<T>;
168
168
  };
169
169
  /** Get the data for a document from a result for that document. */
170
170
  export declare function getDocumentData<T extends Data>(result: Result<T>, ref: DatabaseDocument<T>): DocumentData<T>;
package/db/Database.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { callAsync, getFirstItem, throwAsync, validate, getMap, countItems, hasItems, ResultsObserver, } from "../util/index.js";
2
2
  import { DataUpdate, Update } from "../update/index.js";
3
3
  import { Feedback, InvalidFeedback } from "../feedback/index.js";
4
- import { Filters, Query, EqualFilter } from "../query/index.js";
4
+ import { Filters, Sorts, Query, Filter } from "../query/index.js";
5
5
  import { DocumentRequiredError, DocumentValidationError, QueryRequiredError, QueryValidationError } from "./errors.js";
6
6
  /**
7
7
  * Combines a database model and a provider.
@@ -18,7 +18,7 @@ export class Database {
18
18
  }
19
19
  /** Create a query on a collection in this model. */
20
20
  query(collection, filters, sorts, limit) {
21
- return new DatabaseQuery(this, this.validators[collection], collection, filters, sorts, limit);
21
+ return new DatabaseQuery(this, this.validators[collection], collection, filters && Filters.on(filters), sorts && Sorts.on(sorts), limit);
22
22
  }
23
23
  /** Reference a document in a collection in this model. */
24
24
  doc(collection, id) {
@@ -190,11 +190,11 @@ export class DatabaseDocument {
190
190
  }
191
191
  /** Create a query on this document's collection. */
192
192
  query(filters, sorts, limit) {
193
- return new DatabaseQuery(this.db, this.validator, this.collection, filters, sorts, limit);
193
+ return new DatabaseQuery(this.db, this.validator, this.collection, filters && Filters.on(filters), sorts && Sorts.on(sorts), limit);
194
194
  }
195
195
  /** Get an 'optional' reference to this document (uses a `ModelQuery` with an `id` filter). */
196
196
  get optional() {
197
- return new DatabaseQuery(this.db, this.validator, this.collection, new Filters(new EqualFilter("id", this.id)));
197
+ return new DatabaseQuery(this.db, this.validator, this.collection, new Filters(new Filter("id", "IS", this.id)));
198
198
  }
199
199
  /**
200
200
  * Does this document exist?
@@ -259,7 +259,7 @@ export class DatabaseDocument {
259
259
  /** Get the data for a document from a result for that document. */
260
260
  export function getDocumentData(result, ref) {
261
261
  if (result)
262
- return { ...result, _id: ref.id, _doc: ref };
262
+ return { ...result, id: ref.id, doc: ref };
263
263
  throw new DocumentRequiredError(ref);
264
264
  }
265
265
  /** Get the data for a document from a result for that document. */
package/db/Operation.d.ts CHANGED
@@ -8,10 +8,8 @@ export declare abstract class Operation {
8
8
  }
9
9
  /** Represent a list of write operations on a database run in series. */
10
10
  export declare class Operations extends Operation {
11
- /** Return a new write operations list with a set of write operations. */
12
- static with(...operations: Nullish<Operation>[]): Operations;
13
11
  readonly operations: ImmutableArray<Operation>;
14
- constructor(operations: ImmutableArray<Nullish<Operation>>);
12
+ constructor(...operations: Nullish<Operation>[]);
15
13
  run(db: Database): Promise<Operations>;
16
14
  /** Return a new write operations list with an additional write operation added. */
17
15
  with(...operations: Nullish<Operation>[]): {
package/db/Operation.js CHANGED
@@ -1,26 +1,23 @@
1
- import { callAsyncSeries, isNotNullish, isNullish } from "../util/index.js";
1
+ import { callAsyncSeries, notNullish, isNullish } from "../util/index.js";
2
2
  /** Represent a write operation on a database. */
3
3
  export class Operation {
4
4
  }
5
5
  /** Represent a list of write operations on a database run in series. */
6
6
  export class Operations extends Operation {
7
- constructor(operations) {
7
+ constructor(...operations) {
8
8
  super();
9
- this.operations = operations.filter(isNotNullish);
10
- }
11
- /** Return a new write operations list with a set of write operations. */
12
- static with(...operations) {
13
- return new Operations(operations);
9
+ this.operations = operations.filter(notNullish);
14
10
  }
15
11
  async run(db) {
16
- return new Operations(await callAsyncSeries(_write, this.operations, db));
12
+ const ops = await callAsyncSeries(_run, this.operations, db);
13
+ return new Operations(...ops);
17
14
  }
18
15
  /** Return a new write operations list with an additional write operation added. */
19
16
  with(...operations) {
20
17
  return { __proto__: Object.getPrototypeOf(this), ...this, operations: [...this.operations, operations] };
21
18
  }
22
19
  }
23
- const _write = (operation, db) => operation.run(db);
20
+ const _run = (operation, db) => operation.run(db);
24
21
  /** Represent a add operation made to a collection in a database. */
25
22
  export class AddOperation extends Operation {
26
23
  constructor(collection, data) {
@@ -8,6 +8,7 @@ const OPERATORS = {
8
8
  IS: "==",
9
9
  NOT: "!=",
10
10
  IN: "in",
11
+ OUT: "not-in",
11
12
  GT: ">",
12
13
  GTE: ">=",
13
14
  LT: "<",
@@ -8,6 +8,7 @@ const OPERATORS = {
8
8
  IS: "==",
9
9
  NOT: "!=",
10
10
  IN: "in",
11
+ OUT: "not-in",
11
12
  GT: ">",
12
13
  GTE: ">=",
13
14
  LT: "<",
@@ -8,6 +8,7 @@ const OPERATORS = {
8
8
  IS: "==",
9
9
  NOT: "!=",
10
10
  IN: "in",
11
+ OUT: "not-in",
11
12
  GT: ">",
12
13
  GTE: ">=",
13
14
  LT: "<",
package/markup/render.js CHANGED
@@ -40,7 +40,7 @@ function renderString(content, options) {
40
40
  if (matchedRule && matchedResult && matchedResult[0]) {
41
41
  // If index is more than zero, then add a string node before this one.
42
42
  if (matchedIndex) {
43
- const prefix = content.substr(0, matchedIndex);
43
+ const prefix = content.slice(0, matchedIndex);
44
44
  appendNode(nodes, renderString(prefix, options));
45
45
  }
46
46
  // Call the rule's `render()` function to generate the node.
@@ -49,7 +49,7 @@ function renderString(content, options) {
49
49
  element.rule = matchedRule;
50
50
  appendNode(nodes, renderNode(element, childOptions));
51
51
  // Decrement the content.
52
- content = content.substr(matchedIndex + matchedResult[0].length);
52
+ content = content.slice(matchedIndex + matchedResult[0].length);
53
53
  }
54
54
  else {
55
55
  // If nothing else matched add the rest of the string as a node (presumably it doesn't have any punctuation).
package/markup/rules.js CHANGED
@@ -82,9 +82,9 @@ const OL = {
82
82
  const SPLIT_OL_ITEMS = new RegExp(`\\n+(?=${ORDERED})`, "g");
83
83
  const mapOrderedItem = (item, key) => {
84
84
  const firstSpace = item.indexOf(" ");
85
- const value = parseInt(item.substr(0, firstSpace), 10);
85
+ const value = parseInt(item.slice(0, firstSpace), 10);
86
86
  const children = item
87
- .substr(firstSpace + 1)
87
+ .slice(firstSpace + 1)
88
88
  .trimStart()
89
89
  .replace(REPLACE_INDENT, "");
90
90
  return { type: "li", key, props: { value, children } };
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.46.1",
14
+ "version": "1.47.3",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
package/query/Filter.d.ts CHANGED
@@ -1,59 +1,21 @@
1
- import { Data, Entry, Matchable, Entries, ImmutableArray } from "../util/index.js";
1
+ import { Data, Entry, Matchable, Entries, Key } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
- import { FilterOperator, QueryKey } from "./types.js";
3
+ import { FilterKey, FilterOperator } from "./types.js";
4
4
  /**
5
5
  * Filter: filters a list of documents.
6
6
  *
7
7
  * @param key The name of a property that might exist on documents in the collection.
8
- * @param type MatchType reference, e.g. `is` or `contains`
8
+ * @param operator FilterOperator, e.g. `IS` or `CONTAINS`
9
9
  * @param value Value the specified property should be matched against.
10
10
  */
11
- export declare abstract class Filter<T extends Data, V extends unknown = unknown> extends Rule<T> implements Matchable<Entry<T>, void> {
12
- abstract readonly operator: FilterOperator;
13
- readonly key: QueryKey<T>;
14
- readonly value: V;
15
- constructor(key: QueryKey<T>, value: V);
16
- abstract match([id, data]: Entry<T>, target: void): boolean;
11
+ export declare class Filter<T extends Data> extends Rule<T> implements Matchable<Entry<T>, void> {
12
+ /** Parse a set of FilterProps and return the corresponding array of `Filter` instances. */
13
+ static on<X extends Data>(key: FilterKey<X>, value: unknown): Filter<X>;
14
+ readonly key: "id" | Key<T>;
15
+ readonly operator: FilterOperator;
16
+ readonly value: unknown;
17
+ constructor(key: "id" | Key<T>, operator: FilterOperator, value: unknown);
18
+ match([id, data]: Entry<T>): boolean;
17
19
  transform(entries: Entries<T>): Entries<T>;
18
20
  toString(): string;
19
21
  }
20
- /** Filter a set of values with an `IS` clause. */
21
- export declare class EqualFilter<T extends Data> extends Filter<T> {
22
- readonly operator = "IS";
23
- match([id, data]: Entry<T>): boolean;
24
- }
25
- /** Filter a set of values with an `NOT` clause. */
26
- export declare class NotEqualFilter<T extends Data> extends Filter<T> {
27
- readonly operator = "NOT";
28
- match([id, data]: Entry<T>): boolean;
29
- }
30
- /** Filter a set of values with an `IS` clause. */
31
- export declare class InArrayFilter<T extends Data> extends Filter<T, ImmutableArray> {
32
- readonly operator = "IN";
33
- match([id, data]: Entry<T>): boolean;
34
- }
35
- /** Filter a set of values with a `CONTAINS` clause. */
36
- export declare class ArrayWithFilter<T extends Data> extends Filter<T> {
37
- readonly operator = "CONTAINS";
38
- match([id, data]: Entry<T>): boolean;
39
- }
40
- /** Filter a set of values with an `LT` clause. */
41
- export declare class LessFilter<T extends Data> extends Filter<T> {
42
- readonly operator = "LT";
43
- match([id, data]: Entry<T>): boolean;
44
- }
45
- /** Filter a set of values with an `LTE` clause. */
46
- export declare class EqualLessFilter<T extends Data> extends Filter<T> {
47
- readonly operator = "LTE";
48
- match([id, data]: Entry<T>): boolean;
49
- }
50
- /** Filter a set of values with an `GT` clause. */
51
- export declare class GreaterFilter<T extends Data> extends Filter<T> {
52
- readonly operator = "GT";
53
- match([id, data]: Entry<T>): boolean;
54
- }
55
- /** Filter a set of values with an `GTE` clause. */
56
- export declare class EqualGreaterFilter<T extends Data> extends Filter<T> {
57
- readonly operator = "GTE";
58
- match([id, data]: Entry<T>): boolean;
59
- }
package/query/Filter.js CHANGED
@@ -1,103 +1,55 @@
1
- import { match, isArrayWith, isEqual, isGreater, isEqualGreater, isInArray, isLess, isEqualLess, isNotEqual, yieldFiltered } from "../util/index.js";
1
+ import { match, isArrayWith, isEqual, isGreater, isEqualGreater, isLess, isEqualLess, notEqual, yieldFiltered, notInArray, isInArray } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
3
  import { getQueryProp } from "./helpers.js";
4
+ /** Map `FilterOperator` to its corresponding `Matcher` function. */
5
+ const MATCHERS = {
6
+ IS: isEqual,
7
+ NOT: notEqual,
8
+ IN: isInArray,
9
+ OUT: notInArray,
10
+ CONTAINS: isArrayWith,
11
+ LT: isLess,
12
+ LTE: isEqualLess,
13
+ GT: isGreater,
14
+ GTE: isEqualGreater,
15
+ };
4
16
  /**
5
17
  * Filter: filters a list of documents.
6
18
  *
7
19
  * @param key The name of a property that might exist on documents in the collection.
8
- * @param type MatchType reference, e.g. `is` or `contains`
20
+ * @param operator FilterOperator, e.g. `IS` or `CONTAINS`
9
21
  * @param value Value the specified property should be matched against.
10
22
  */
11
23
  export class Filter extends Rule {
12
- constructor(key, value) {
24
+ constructor(key, operator, value) {
13
25
  super();
14
26
  this.key = key;
27
+ this.operator = operator;
15
28
  this.value = value;
16
29
  }
30
+ /** Parse a set of FilterProps and return the corresponding array of `Filter` instances. */
31
+ static on(key, value) {
32
+ return key.startsWith("!")
33
+ ? new Filter(key.slice(1), value instanceof Array ? "OUT" : "NOT", value)
34
+ : key.endsWith(">")
35
+ ? new Filter(key.slice(0, -1), "GT", value)
36
+ : key.endsWith(">=")
37
+ ? new Filter(key.slice(0, -2), "GTE", value)
38
+ : key.endsWith("<")
39
+ ? new Filter(key.slice(0, -1), "LT", value)
40
+ : key.endsWith("<=")
41
+ ? new Filter(key.slice(0, -2), "LTE", value)
42
+ : key.endsWith("[]")
43
+ ? new Filter(key.slice(0, -2), "CONTAINS", value)
44
+ : new Filter(key, value instanceof Array ? "IN" : "IS", value);
45
+ }
46
+ match([id, data]) {
47
+ return match(getQueryProp(id, data, this.key), MATCHERS[this.operator], this.value);
48
+ }
17
49
  transform(entries) {
18
50
  return yieldFiltered(entries, this);
19
51
  }
20
52
  toString() {
21
- return `${this.key}:${this.operator}=${JSON.stringify(this.value)}`;
22
- }
23
- }
24
- /** Filter a set of values with an `IS` clause. */
25
- export class EqualFilter extends Filter {
26
- constructor() {
27
- super(...arguments);
28
- this.operator = "IS";
29
- }
30
- match([id, data]) {
31
- return match(getQueryProp(id, data, this.key), isEqual, this.value);
32
- }
33
- }
34
- /** Filter a set of values with an `NOT` clause. */
35
- export class NotEqualFilter extends Filter {
36
- constructor() {
37
- super(...arguments);
38
- this.operator = "NOT";
39
- }
40
- match([id, data]) {
41
- return match(getQueryProp(id, data, this.key), isNotEqual, this.value);
42
- }
43
- }
44
- /** Filter a set of values with an `IS` clause. */
45
- export class InArrayFilter extends Filter {
46
- constructor() {
47
- super(...arguments);
48
- this.operator = "IN";
49
- }
50
- match([id, data]) {
51
- return match(getQueryProp(id, data, this.key), isInArray, this.value);
52
- }
53
- }
54
- /** Filter a set of values with a `CONTAINS` clause. */
55
- export class ArrayWithFilter extends Filter {
56
- constructor() {
57
- super(...arguments);
58
- this.operator = "CONTAINS";
59
- }
60
- match([id, data]) {
61
- return match(getQueryProp(id, data, this.key), isArrayWith, this.value);
62
- }
63
- }
64
- /** Filter a set of values with an `LT` clause. */
65
- export class LessFilter extends Filter {
66
- constructor() {
67
- super(...arguments);
68
- this.operator = "LT";
69
- }
70
- match([id, data]) {
71
- return match(getQueryProp(id, data, this.key), isLess, this.value);
72
- }
73
- }
74
- /** Filter a set of values with an `LTE` clause. */
75
- export class EqualLessFilter extends Filter {
76
- constructor() {
77
- super(...arguments);
78
- this.operator = "LTE";
79
- }
80
- match([id, data]) {
81
- return match(getQueryProp(id, data, this.key), isEqualLess, this.value);
82
- }
83
- }
84
- /** Filter a set of values with an `GT` clause. */
85
- export class GreaterFilter extends Filter {
86
- constructor() {
87
- super(...arguments);
88
- this.operator = "GT";
89
- }
90
- match([id, data]) {
91
- return match(getQueryProp(id, data, this.key), isGreater, this.value);
92
- }
93
- }
94
- /** Filter a set of values with an `GTE` clause. */
95
- export class EqualGreaterFilter extends Filter {
96
- constructor() {
97
- super(...arguments);
98
- this.operator = "GTE";
99
- }
100
- match([id, data]) {
101
- return match(getQueryProp(id, data, this.key), isEqualGreater, this.value);
53
+ return `${this.key}:${this.operator}:${JSON.stringify(this.value)}`;
102
54
  }
103
55
  }
@@ -1,18 +1,17 @@
1
1
  import { Entry, Data, Key, Entries, ImmutableArray, ArrayType } from "../util/index.js";
2
- import type { Filterable, QueryKey } from "./types.js";
2
+ import type { Filterable, FilterProps } from "./types.js";
3
3
  import { Filter } from "./Filter.js";
4
4
  import { Rules } from "./Rules.js";
5
5
  /** A set of filters. */
6
6
  export declare class Filters<T extends Data> extends Rules<T, Filter<T>> implements Filterable<T> {
7
- is<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
8
- not<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
9
- in<K extends QueryKey<T>>(key: K, value: K extends "id" ? readonly string[] : readonly T[K][]): this;
10
- contains<K extends Key<T>>(key: K, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
11
- lt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
12
- lte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
13
- gt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
14
- gte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
7
+ /** Create a `Filters` instance from a set of `FilterProps` */
8
+ static on<X extends Data>(filters: FilterProps<X>): Filters<X>;
9
+ filter(props: FilterProps<T>): this;
10
+ filter(key: "id" | "!id" | "id>" | "id>=" | "id<" | "id<=", value: string): this;
11
+ filter(key: "id" | "!id", value: ImmutableArray<string>): this;
12
+ filter<K extends Key<T>>(key: K | `!${K}` | `${K}>` | `${K}>=` | `${K}<` | `${K}<=`, value: T[K]): this;
13
+ filter<K extends Key<T>>(key: K | `!${K}`, value: ImmutableArray<string>): this;
14
+ filter<K extends Key<T>>(key: `${K}[]`, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
15
15
  match(entry: Entry<T>): boolean;
16
- get unfiltered(): this;
17
16
  transform(iterable: Entries<T>): Entries<T>;
18
17
  }
package/query/Filters.js CHANGED
@@ -1,32 +1,18 @@
1
1
  import { yieldFiltered } from "../util/index.js";
2
- import { ArrayWithFilter, EqualFilter, EqualGreaterFilter, GreaterFilter, InArrayFilter, EqualLessFilter, LessFilter, NotEqualFilter } from "./Filter.js";
2
+ import { Filter } from "./Filter.js";
3
3
  import { Rules } from "./Rules.js";
4
+ function* _yieldFilters(props) {
5
+ for (const [key, value] of Object.entries(props))
6
+ yield Filter.on(key, value);
7
+ }
4
8
  /** A set of filters. */
5
9
  export class Filters extends Rules {
6
- // Implement `Filterable`
7
- is(key, value) {
8
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new EqualFilter(key, value)] };
9
- }
10
- not(key, value) {
11
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new NotEqualFilter(key, value)] };
12
- }
13
- in(key, value) {
14
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new InArrayFilter(key, value)] };
15
- }
16
- contains(key, value) {
17
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new ArrayWithFilter(key, value)] };
10
+ /** Create a `Filters` instance from a set of `FilterProps` */
11
+ static on(filters) {
12
+ return new Filters(..._yieldFilters(filters));
18
13
  }
19
- lt(key, value) {
20
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new LessFilter(key, value)] };
21
- }
22
- lte(key, value) {
23
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new EqualLessFilter(key, value)] };
24
- }
25
- gt(key, value) {
26
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new GreaterFilter(key, value)] };
27
- }
28
- gte(key, value) {
29
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new EqualGreaterFilter(key, value)] };
14
+ filter(input, value) {
15
+ return typeof input === "string" ? this.with(Filter.on(input, value)) : this.with(..._yieldFilters(input));
30
16
  }
31
17
  match(entry) {
32
18
  for (const rule of this._rules)
@@ -34,9 +20,6 @@ export class Filters extends Rules {
34
20
  return false;
35
21
  return true;
36
22
  }
37
- get unfiltered() {
38
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [] };
39
- }
40
23
  // Implement `Rule`
41
24
  transform(iterable) {
42
25
  return this._rules.length ? yieldFiltered(iterable, this) : iterable;
package/query/Query.d.ts CHANGED
@@ -1,27 +1,26 @@
1
1
  import { Data, Entry, Key, Entries, ArrayType, ImmutableArray } from "../util/index.js";
2
- import type { Queryable, QueryKey } from "./types.js";
2
+ import type { Queryable, SortKey, FilterProps, SortKeys } from "./types.js";
3
3
  import { Filters } from "./Filters.js";
4
4
  import { Sorts } from "./Sorts.js";
5
5
  import { Rule } from "./Rule.js";
6
6
  /** Allows filtering, sorting, and limiting on a set of results. */
7
7
  export declare class Query<T extends Data> extends Rule<T> implements Queryable<T> {
8
+ /** Create a new `Query` object from a set of `QueryProps` */
9
+ static on<X extends Data>(filters?: FilterProps<X>, sorts?: SortKey<X> | ImmutableArray<SortKey<X>>, limit?: number | null): Query<X>;
8
10
  readonly filters: Filters<T>;
9
11
  readonly sorts: Sorts<T>;
10
12
  readonly limit: number | null;
11
13
  constructor(filters?: Filters<T>, sorts?: Sorts<T>, limit?: number | null);
12
- is<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
13
- not<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
14
- in<K extends QueryKey<T>>(key: K, value: K extends "id" ? readonly string[] : readonly T[K][]): this;
15
- contains<K extends Key<T>>(key: K, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
16
- lt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
17
- lte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
18
- gt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
19
- gte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
20
- get unfiltered(): this;
14
+ filter(props: FilterProps<T>): this;
15
+ filter(key: "id" | "!id" | "id>" | "id>=" | "id<" | "id<=", value: string): this;
16
+ filter(key: "id" | "!id", value: ImmutableArray<string>): this;
17
+ filter<K extends Key<T>>(key: K | `!${K}` | `${K}>` | `${K}>=` | `${K}<` | `${K}<=`, value: T[K]): this;
18
+ filter<K extends Key<T>>(key: K | `!${K}`, value: ImmutableArray<T[K]>): this;
19
+ filter<K extends Key<T>>(key: `${K}[]`, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
20
+ get unfilter(): this;
21
21
  match(entry: Entry<T>): boolean;
22
- asc(key: QueryKey<T>): this;
23
- desc(key: QueryKey<T>): this;
24
- get unsorted(): this;
22
+ sort(...keys: SortKeys<T>[]): this;
23
+ get unsort(): this;
25
24
  rank(left: Entry<T>, right: Entry<T>): number;
26
25
  /** Return a new instance of this class with a limit defined. */
27
26
  max(limit: number | null): this;
package/query/Query.js CHANGED
@@ -3,7 +3,7 @@ import { Filters } from "./Filters.js";
3
3
  import { Sorts } from "./Sorts.js";
4
4
  import { Rule } from "./Rule.js";
5
5
  import { getQueryProp } from "./helpers.js";
6
- import { EqualGreaterFilter as GTE, GreaterFilter as GT, EqualLessFilter as LTE, LessFilter as LT } from "./Filter.js";
6
+ import { Filter } from "./Filter.js";
7
7
  // Instances to save resources for the default case (empty query).
8
8
  const EMPTY_FILTERS = new Filters(); // eslint-disable-line @typescript-eslint/no-explicit-any
9
9
  const EMPTY_SORTS = new Sorts(); // eslint-disable-line @typescript-eslint/no-explicit-any
@@ -15,46 +15,25 @@ export class Query extends Rule {
15
15
  this.sorts = sorts;
16
16
  this.limit = limit;
17
17
  }
18
- // Implement `Filterable`
19
- is(key, value) {
20
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.is(key, value) };
18
+ /** Create a new `Query` object from a set of `QueryProps` */
19
+ static on(filters, sorts, limit) {
20
+ return new Query(filters && Filters.on(filters), sorts && Sorts.on(sorts), limit);
21
21
  }
22
- not(key, value) {
23
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.not(key, value) };
22
+ filter(input, value) {
23
+ return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.filter(input, value) };
24
24
  }
25
- in(key, value) {
26
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.in(key, value) };
27
- }
28
- contains(key, value) {
29
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.contains(key, value) };
30
- }
31
- lt(key, value) {
32
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.lt(key, value) };
33
- }
34
- lte(key, value) {
35
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.lte(key, value) };
36
- }
37
- gt(key, value) {
38
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.gt(key, value) };
39
- }
40
- gte(key, value) {
41
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.gte(key, value) };
42
- }
43
- get unfiltered() {
44
- return { __proto__: Object.getPrototypeOf(this), ...this, filters: this.filters.unfiltered };
25
+ get unfilter() {
26
+ return { __proto__: Object.getPrototypeOf(this), ...this, filters: EMPTY_FILTERS };
45
27
  }
46
28
  match(entry) {
47
29
  return this.filters.match(entry);
48
30
  }
49
31
  // Implement `Sortable`
50
- asc(key) {
51
- return { __proto__: Object.getPrototypeOf(this), ...this, sorts: this.sorts.asc(key) };
52
- }
53
- desc(key) {
54
- return { __proto__: Object.getPrototypeOf(this), ...this, sorts: this.sorts.desc(key) };
32
+ sort(...keys) {
33
+ return { __proto__: Object.getPrototypeOf(this), ...this, sorts: this.sorts.sort(...keys) };
55
34
  }
56
- get unsorted() {
57
- return { __proto__: Object.getPrototypeOf(this), ...this, sorts: this.sorts.unsorted };
35
+ get unsort() {
36
+ return { __proto__: Object.getPrototypeOf(this), ...this, sorts: EMPTY_SORTS };
58
37
  }
59
38
  rank(left, right) {
60
39
  return this.sorts.rank(left, right);
@@ -69,8 +48,8 @@ export class Query extends Rule {
69
48
  const lastSort = this.sorts.last;
70
49
  assert(lastSort);
71
50
  for (const sort of this.sorts) {
72
- const AppliedFilter = sort.direction === "ASC" ? (sort === lastSort ? GT : GTE) : sort === lastSort ? LT : LTE;
73
- filters.push(new AppliedFilter(sort.key, getQueryProp(id, data, sort.key)));
51
+ const { key, direction } = sort;
52
+ filters.push(new Filter(key, direction === "ASC" ? (sort === lastSort ? "GT" : "GTE") : sort === lastSort ? "LT" : "LTE", getQueryProp(id, data, key)));
74
53
  }
75
54
  return { __proto__: Object.getPrototypeOf(this), ...this, filters: new Filters(...filters) };
76
55
  }
@@ -79,8 +58,8 @@ export class Query extends Rule {
79
58
  const lastSort = this.sorts.last;
80
59
  assert(lastSort);
81
60
  for (const sort of this.sorts) {
82
- const AppliedFilter = sort.direction === "ASC" ? (sort === lastSort ? LT : LTE) : sort === lastSort ? GT : GTE;
83
- filters.push(new AppliedFilter(sort.key, getQueryProp(id, data, sort.key)));
61
+ const { key, direction } = sort;
62
+ filters.push(new Filter(key, direction === "ASC" ? (sort === lastSort ? "LT" : "LTE") : sort === lastSort ? "GT" : "GTE", getQueryProp(id, data, key)));
84
63
  }
85
64
  return { __proto__: Object.getPrototypeOf(this), ...this, filters: new Filters(...filters) };
86
65
  }
package/query/Rules.d.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Data, MutableArray } from "../util/index.js";
1
+ import { Data, ImmutableArray } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
3
  /** Type of Rule that is powered by several sub-rules (e.g. `Filters` and `Sorts` and `Query` itself extend this). */
4
4
  export declare abstract class Rules<T extends Data, R extends Rule<T>> extends Rule<T> implements Iterable<R> {
5
- protected readonly _rules: MutableArray<R>;
5
+ protected readonly _rules: ImmutableArray<R>;
6
6
  /** Get the first rule. */
7
7
  get first(): R | undefined;
8
8
  /** Get the last rule. */
@@ -11,6 +11,10 @@ export declare abstract class Rules<T extends Data, R extends Rule<T>> extends R
11
11
  get size(): number;
12
12
  constructor(...rules: R[]);
13
13
  toString(): string;
14
+ /** Clone this set of rules but add additional rules. */
15
+ with(...rules: R[]): this;
16
+ /** Clone this set of rules but remove specific rules. */
17
+ without(...rules: R[]): this;
14
18
  /** Iterate over the rules. */
15
19
  [Symbol.iterator](): Iterator<R, void>;
16
20
  }
package/query/Rules.js CHANGED
@@ -1,4 +1,4 @@
1
- import { toString } from "../util/index.js";
1
+ import { toString, withItems, withoutItems } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
3
  /** Type of Rule that is powered by several sub-rules (e.g. `Filters` and `Sorts` and `Query` itself extend this). */
4
4
  export class Rules extends Rule {
@@ -21,6 +21,16 @@ export class Rules extends Rule {
21
21
  toString() {
22
22
  return this._rules.map(toString).join("&");
23
23
  }
24
+ /** Clone this set of rules but add additional rules. */
25
+ with(...rules) {
26
+ const _rules = withItems(this._rules, rules);
27
+ return _rules !== this._rules ? { __proto__: Object.getPrototypeOf(this), ...this, _rules } : this;
28
+ }
29
+ /** Clone this set of rules but remove specific rules. */
30
+ without(...rules) {
31
+ const _rules = withoutItems(this._rules, rules);
32
+ return _rules !== this._rules ? { __proto__: Object.getPrototypeOf(this), ...this, _rules } : this;
33
+ }
24
34
  /** Iterate over the rules. */
25
35
  [Symbol.iterator]() {
26
36
  return this._rules.values();
package/query/Sort.d.ts CHANGED
@@ -1,22 +1,14 @@
1
- import { Data, Entry, Rankable, Entries } from "../util/index.js";
1
+ import { Data, Entry, Rankable, Entries, Key } from "../util/index.js";
2
2
  import { Rule } from "./Rule.js";
3
- import { QueryKey, SortDirection } from "./types.js";
3
+ import { SortDirection, SortKey } from "./types.js";
4
4
  /** Sort a list of values. */
5
- export declare abstract class Sort<T extends Data> extends Rule<T> implements Rankable<Entry<T>> {
6
- abstract readonly direction: SortDirection;
7
- readonly key: QueryKey<T>;
8
- constructor(key: QueryKey<T>);
9
- abstract rank(left: Entry<T>, right: Entry<T>): number;
5
+ export declare class Sort<T extends Data> extends Rule<T> implements Rankable<Entry<T>> {
6
+ /** Create a sort on a specified field. */
7
+ static on<X extends Data>(sort: SortKey<X> | Sort<X>): Sort<X>;
8
+ readonly key: "id" | Key<T>;
9
+ readonly direction: SortDirection;
10
+ constructor(key: "id" | Key<T>, direction: SortDirection);
11
+ rank([leftId, leftData]: Entry<T>, [rightId, rightData]: Entry<T>): number;
10
12
  transform(iterable: Entries<T>): Entries<T>;
11
13
  toString(): string;
12
14
  }
13
- /** Sort a list of values in ascending order. */
14
- export declare class AscendingSort<T extends Data> extends Sort<T> {
15
- readonly direction = "ASC";
16
- rank([leftId, leftData]: Entry<T>, [rightId, rightData]: Entry<T>): number;
17
- }
18
- /** Sort a list of values in descending order. */
19
- export declare class DescendingSort<T extends Data> extends Sort<T> {
20
- readonly direction = "DESC";
21
- rank([leftId, leftData]: Entry<T>, [rightId, rightData]: Entry<T>): number;
22
- }
package/query/Sort.js CHANGED
@@ -1,36 +1,24 @@
1
- import { rankAscending, rank, rankDesc, sortItems } from "../util/index.js";
1
+ import { rankAsc, rank, sortItems, rankDesc } from "../util/index.js";
2
2
  import { getQueryProp } from "./helpers.js";
3
3
  import { Rule } from "./Rule.js";
4
4
  /** Sort a list of values. */
5
5
  export class Sort extends Rule {
6
- constructor(key) {
6
+ constructor(key, direction) {
7
7
  super();
8
8
  this.key = key;
9
+ this.direction = direction;
9
10
  }
10
- transform(iterable) {
11
- return sortItems(iterable, this);
12
- }
13
- toString() {
14
- return `${this.key}:${this.direction}`;
15
- }
16
- }
17
- /** Sort a list of values in ascending order. */
18
- export class AscendingSort extends Sort {
19
- constructor() {
20
- super(...arguments);
21
- this.direction = "ASC";
11
+ /** Create a sort on a specified field. */
12
+ static on(sort) {
13
+ return sort instanceof Sort ? sort : sort.startsWith("!") ? new Sort(sort.slice(1), "DESC") : new Sort(sort, "ASC");
22
14
  }
23
15
  rank([leftId, leftData], [rightId, rightData]) {
24
- return rank(getQueryProp(leftId, leftData, this.key), rankAscending, getQueryProp(rightId, rightData, this.key));
16
+ return rank(getQueryProp(leftId, leftData, this.key), this.direction === "ASC" ? rankAsc : rankDesc, getQueryProp(rightId, rightData, this.key));
25
17
  }
26
- }
27
- /** Sort a list of values in descending order. */
28
- export class DescendingSort extends Sort {
29
- constructor() {
30
- super(...arguments);
31
- this.direction = "DESC";
18
+ transform(iterable) {
19
+ return sortItems(iterable, this);
32
20
  }
33
- rank([leftId, leftData], [rightId, rightData]) {
34
- return rank(getQueryProp(leftId, leftData, this.key), rankDesc, getQueryProp(rightId, rightData, this.key));
21
+ toString() {
22
+ return `${this.direction === "DESC" ? "!" : ""}${this.key}`;
35
23
  }
36
24
  }
package/query/Sorts.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { Entry, Data, Entries } from "../util/index.js";
2
- import type { QueryKey, Sortable } from "./types.js";
2
+ import type { Sortable, SortKeys } from "./types.js";
3
3
  import { Sort } from "./Sort.js";
4
4
  import { Rules } from "./Rules.js";
5
5
  /** A set of sorts. */
6
6
  export declare class Sorts<T extends Data> extends Rules<T, Sort<T>> implements Sortable<T> {
7
- asc(key: QueryKey<T>): this;
8
- desc(key: QueryKey<T>): this;
9
- get unsorted(): this;
7
+ /** Create a new `Sorts` object from an array of `SortKey` strings. */
8
+ static on<X extends Data>(...keys: SortKeys<X>[]): Sorts<X>;
9
+ sort(...keys: SortKeys<T>[]): this;
10
10
  rank(left: Entry<T>, right: Entry<T>): number;
11
11
  transform(iterable: Entries<T>): Entries<T>;
12
12
  }
package/query/Sorts.js CHANGED
@@ -1,17 +1,15 @@
1
1
  import { sortItems } from "../util/index.js";
2
- import { AscendingSort, DescendingSort } from "./Sort.js";
2
+ import { Sort } from "./Sort.js";
3
3
  import { Rules } from "./Rules.js";
4
4
  /** A set of sorts. */
5
5
  export class Sorts extends Rules {
6
- // Implement `Sortable`
7
- asc(key) {
8
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new AscendingSort(key)] };
9
- }
10
- desc(key) {
11
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [...this._rules, new DescendingSort(key)] };
6
+ /** Create a new `Sorts` object from an array of `SortKey` strings. */
7
+ static on(...keys) {
8
+ return new Sorts(...keys.flat().map(Sort.on));
12
9
  }
13
- get unsorted() {
14
- return { __proto__: Object.getPrototypeOf(this), ...this, _rules: [] };
10
+ // Implement `Sortable`
11
+ sort(...keys) {
12
+ return this.with(...keys.flat().map(Sort.on));
15
13
  }
16
14
  rank(left, right) {
17
15
  for (const rule of this._rules) {
@@ -1,9 +1,8 @@
1
- import { Data } from "../util/index.js";
2
- import type { QueryKey } from "./types.js";
1
+ import { Data, Key } from "../util/index.js";
3
2
  /**
4
3
  * Get a named property value from some data.
5
4
  * - For using in `Query` and `Filter` and `Sort` etc.
6
5
  * - `"id"` is a special case and always returns the ID.
7
6
  * - Anything else assumes the entry value is an object and looks up that named prop in the data.
8
7
  */
9
- export declare const getQueryProp: <T extends Data, K extends QueryKey<T>>(id: string, data: T, key: K) => K extends "id" ? string : T[K];
8
+ export declare const getQueryProp: <T extends Data, K extends "id" | Key<T>>(id: string, data: T, key: K) => K extends "id" ? string : T[K];
package/query/types.d.ts CHANGED
@@ -1,40 +1,52 @@
1
1
  import type { Data, Key, Matchable, Entry, Rankable, ImmutableArray, ArrayType } from "../util/index.js";
2
- /** Type for a key in a query, either `id` for the unique ID of the document or any other string key that exists in data. */
3
- export declare type QueryKey<T extends Data> = "id" | Key<T>;
4
2
  /** Possible operator references. */
5
- export declare type FilterOperator = "IS" | "NOT" | "IN" | "CONTAINS" | "LT" | "LTE" | "GT" | "GTE";
3
+ export declare type FilterOperator = "IS" | "NOT" | "IN" | "OUT" | "CONTAINS" | "LT" | "LTE" | "GT" | "GTE";
4
+ /** Format that allows filters to be specified as a string, e.g. `!name` means `name is not` and `age>` means `age is more than` and `tags[]` means `tags array contains` */
5
+ export declare type FilterKey<T extends Data> = "id" | "!id" | "id>" | "id>=" | "id<" | "id<=" | Key<T> | `!${Key<T>}` | `${Key<T>}[]` | `${Key<T>}<` | `${Key<T>}<=` | `${Key<T>}>` | `${Key<T>}>=`;
6
+ /** Format that allows multiple filters to be specified as a plain object. */
7
+ export declare type FilterProps<T extends Data> = {
8
+ "id"?: string | ImmutableArray<string>;
9
+ "!id"?: string | ImmutableArray<string>;
10
+ "id>"?: string;
11
+ "id>="?: string;
12
+ "id<"?: string;
13
+ "id<="?: string;
14
+ } & {
15
+ [K in Key<T> as K | `!${K}`]?: T[K] | ImmutableArray<T[K]>;
16
+ } & {
17
+ [K in Key<T> as `${K}[]`]?: T[K] extends ImmutableArray ? ArrayType<T[K]> : never;
18
+ } & {
19
+ [K in Key<T> as `${K}<` | `${K}<=` | `${K}>` | `${K}>=`]?: T[K];
20
+ };
6
21
  /**
7
22
  * Interface to make sure an object implements all matchers.
8
23
  * - Extends `Matchable` so this object itself can be directly be used in `filterItems()` and `filterEntries()`
9
24
  */
10
25
  export interface Filterable<T extends Data> extends Matchable<Entry<T>, void> {
11
- is<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
12
- not<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
13
- in<K extends QueryKey<T>>(key: K, value: K extends "id" ? readonly string[] : readonly T[K][]): this;
14
- contains<K extends Key<T>>(key: K, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
15
- lt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
16
- lte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
17
- gt<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
18
- gte<K extends QueryKey<T>>(key: K, value: K extends "id" ? string : T[K]): this;
19
- /** Return a new instance of this class with no sorts specified. */
20
- unfiltered: this;
26
+ /** Add a filter to this filterable. */
27
+ filter(props: FilterProps<T>): this;
28
+ filter(key: "id" | "!id" | "id>" | "id>=" | "id<" | "id<=", value: string): this;
29
+ filter(key: "id" | "!id", value: ImmutableArray<string>): this;
30
+ filter<K extends Key<T>>(key: K | `!${K}` | `${K}>` | `${K}>=` | `${K}<` | `${K}<=`, value: T[K]): this;
31
+ filter<K extends Key<T>>(key: K | `!${K}`, value: ImmutableArray<string>): this;
32
+ filter<K extends Key<T>>(key: `${K}[]`, value: T[K] extends ImmutableArray ? ArrayType<T[K]> : never): this;
21
33
  /** Match an entry against the filters specified for this object. */
22
34
  match(entry: Entry<T>): boolean;
23
35
  }
36
+ /** Format that allows sorts to be set as a plain string, e.g. `name` sorts by name in ascending order and `!date` sorts by date in descending order. */
37
+ export declare type SortKey<T extends Data> = "id" | "!id" | Key<T> | `!${Key<T>}`;
38
+ /** One or more sort keys. */
39
+ export declare type SortKeys<T extends Data> = SortKey<T> | ImmutableArray<SortKey<T>>;
40
+ /** Possible operator references. */
41
+ export declare type SortDirection = "ASC" | "DESC";
24
42
  /**
25
43
  * Interface to make sure an object implements all directions.
26
44
  * - Extends `Matchable` so this object itself can be directly be used in `filterItems()` and `filterEntries()`
27
45
  */
28
46
  export interface Sortable<T extends Data> extends Rankable<Entry<T>> {
29
- /** Return a new instance of this class with an ascending order sort defined. */
30
- asc(key: QueryKey<T>): this;
31
- /** Return a new instance of this class with a descending order sort defined. */
32
- desc(key: QueryKey<T>): this;
33
- /** Return a new instance of this class with no sorts specified. */
34
- unsorted: this;
47
+ /** Add one or more sorts to this sortable. */
48
+ sort(...keys: SortKeys<T>[]): this;
35
49
  }
36
- /** Possible operator references. */
37
- export declare type SortDirection = "ASC" | "DESC";
38
50
  /** Interface for an object that can have a limit set. */
39
51
  export interface Limitable {
40
52
  /** The maximum number of items allowed by the limit. */
@@ -55,4 +67,8 @@ export interface Queryable<T extends Data> extends Filterable<T>, Sortable<T>, L
55
67
  after(id: string, data: T): this;
56
68
  /** Return a new instance of this class with a before offset defined. */
57
69
  before(id: string, data: T): this;
70
+ /** Return a new instance of this class with no filters specified. */
71
+ unfilter: this;
72
+ /** Return a new instance of this class with no sorts specified. */
73
+ unsort: this;
58
74
  }
@@ -21,7 +21,7 @@ export class PhoneSchema extends StringSchema {
21
21
  // Strip characters that aren't 0-9 or `+` plus (including whitespace).
22
22
  const digits = str.replace(/[^0-9+]/g, "");
23
23
  // Allow `+` plus only if it's first character.
24
- return digits.substr(0, 1) + digits.substr(1).replace(/[^0-9]/g, "");
24
+ return digits.slice(0, 1) + digits.slice(1).replace(/[^0-9]/g, "");
25
25
  }
26
26
  }
27
27
  /** Valid phone number, e.g. `+441234567890` */
package/test/util.js CHANGED
@@ -34,7 +34,7 @@ function popErrorStack(error, count = 1) {
34
34
  const prefix = message ? `${name}: ${message}\n` : `${name}\n`;
35
35
  if (stack.startsWith(prefix)) {
36
36
  // In Chrome and Node the name and message of the error is the first line of the stack (so we need to skip over the first line).
37
- const lines = stack.substr(prefix.length).split("\n");
37
+ const lines = stack.slice(prefix.length).split("\n");
38
38
  lines.splice(0, count);
39
39
  error.stack = prefix + lines.join("\n");
40
40
  }
package/util/date.js CHANGED
@@ -64,11 +64,11 @@ export function getDate(target = "now") {
64
64
  /** Convert an unknown value to a YMD date string like "2015-09-12", or `null` if it couldn't be converted. */
65
65
  export function toYmd(target) {
66
66
  const date = toDate(target);
67
- return date ? date.toISOString().substr(0, 10) : null;
67
+ return date ? date.toISOString().slice(0, 10) : null;
68
68
  }
69
69
  /** Convert a `Date` instance to a YMD string like "2015-09-12", or throw `AssertionError` if it couldn't be converted. */
70
70
  export function getYmd(target = "now") {
71
- return getDate(target).toISOString().substr(0, 10);
71
+ return getDate(target).toISOString().slice(0, 10);
72
72
  }
73
73
  /** List of day-of-week strings. */
74
74
  export const days = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"];
package/util/debug.js CHANGED
@@ -16,7 +16,7 @@ export const debug = (value) => typeof value === "function"
16
16
  : typeof value === "string"
17
17
  ? debugString(value)
18
18
  : typeof value;
19
- const debugString = (value) => (value.length > DEBUG_STRING_MAX ? `"${value.substr(0, DEBUG_STRING_MAX)}…"` : `"${value}"`);
19
+ const debugString = (value) => (value.length > DEBUG_STRING_MAX ? `"${value.slice(0, DEBUG_STRING_MAX)}…"` : `"${value}"`);
20
20
  const DEBUG_STRING_MAX = 23;
21
21
  const debugObject = (value) => {
22
22
  const prototype = Object.getPrototypeOf(value);
package/util/filter.d.ts CHANGED
@@ -13,23 +13,26 @@ export declare type Matcher<L, R> = Matchable<L, R> | ((item: L, target: R) => b
13
13
  export declare function match<L>(item: L, matcher: Matcher<L, void>): boolean;
14
14
  export declare function match<L, R>(item: L, matcher: Matcher<L, R>, target: R): boolean;
15
15
  export declare const isEqual: <T>(item: unknown, target: T) => item is T;
16
- export declare const isNotEqual: <T, N>(item: T | N, target: T) => item is N;
16
+ export declare const notEqual: <T, N>(item: T | N, target: T) => item is N;
17
17
  export declare const isInArray: <T>(item: unknown, targets: ImmutableArray<T>) => item is T;
18
+ export declare const notInArray: <T, N>(item: T | N, targets: ImmutableArray<T>) => item is N;
18
19
  export declare const isArrayWith: (items: unknown, target: unknown) => boolean;
19
20
  export declare const isLess: (item: unknown, target: unknown) => boolean;
20
21
  export declare const isEqualLess: (item: unknown, target: unknown) => boolean;
21
22
  export declare const isGreater: (item: unknown, target: unknown) => boolean;
22
23
  export declare const isEqualGreater: (item: unknown, target: unknown) => boolean;
23
24
  export declare const isKeyEqual: ([key]: Entry<unknown>, target: string) => boolean;
24
- export declare const isKeyNotEqual: ([key]: Entry<unknown>, targets: ImmutableArray<string>) => boolean;
25
+ export declare const notKeyEqual: ([key]: Entry<unknown>, targets: string) => boolean;
25
26
  export declare const isKeyInArray: ([key]: Entry<unknown>, targets: ImmutableArray<string>) => boolean;
27
+ export declare const notKeyInArray: ([key]: Entry<unknown>, targets: ImmutableArray<string>) => boolean;
26
28
  export declare const isKeyLess: ([key]: Entry<unknown>, target: unknown) => boolean;
27
29
  export declare const isKeyEqualLess: ([key]: Entry<unknown>, target: unknown) => boolean;
28
30
  export declare const isKeyGreater: ([key]: Entry<unknown>, target: unknown) => boolean;
29
31
  export declare const isKeyEqualGreater: ([key]: Entry<unknown>, target: unknown) => boolean;
30
32
  export declare const isValueEqual: ([, value]: Entry<unknown>, target: unknown) => boolean;
31
- export declare const isValueNotEqual: ([, value]: Entry<unknown>, target: unknown) => boolean;
33
+ export declare const notValueEqual: ([, value]: Entry<unknown>, target: unknown) => boolean;
32
34
  export declare const isValueInArray: ([, value]: Entry<unknown>, targets: ImmutableArray<unknown>) => boolean;
35
+ export declare const notValueInArray: ([, value]: Entry<unknown>, targets: ImmutableArray<unknown>) => boolean;
33
36
  export declare const isValueLess: ([, value]: Entry<unknown>, target: unknown) => boolean;
34
37
  export declare const isValueEqualLess: ([, value]: Entry<unknown>, target: unknown) => boolean;
35
38
  export declare const isValueGreater: ([, value]: Entry<unknown>, target: unknown) => boolean;
package/util/filter.js CHANGED
@@ -1,29 +1,32 @@
1
- import { rankAscending } from "./sort.js";
1
+ import { rankAsc } from "./sort.js";
2
2
  import { transform } from "./transform.js";
3
3
  export function match(item, matcher, target) {
4
4
  return typeof matcher === "function" ? matcher(item, target) : matcher.match(item, target);
5
5
  }
6
6
  // Regular matchers.
7
7
  export const isEqual = (item, target) => item === target;
8
- export const isNotEqual = (item, target) => item !== target;
8
+ export const notEqual = (item, target) => item !== target;
9
9
  export const isInArray = (item, targets) => targets.includes(item);
10
+ export const notInArray = (item, targets) => !targets.includes(item);
10
11
  export const isArrayWith = (items, target) => items instanceof Array && items.includes(target);
11
- export const isLess = (item, target) => rankAscending(item, target) < 0;
12
- export const isEqualLess = (item, target) => rankAscending(item, target) <= 0;
13
- export const isGreater = (item, target) => rankAscending(item, target) > 0;
14
- export const isEqualGreater = (item, target) => rankAscending(item, target) >= 0;
12
+ export const isLess = (item, target) => rankAsc(item, target) < 0;
13
+ export const isEqualLess = (item, target) => rankAsc(item, target) <= 0;
14
+ export const isGreater = (item, target) => rankAsc(item, target) > 0;
15
+ export const isEqualGreater = (item, target) => rankAsc(item, target) >= 0;
15
16
  // Entry key matchers.
16
17
  export const isKeyEqual = ([key], target) => isEqual(key, target);
17
- export const isKeyNotEqual = ([key], targets) => isNotEqual(key, targets);
18
+ export const notKeyEqual = ([key], targets) => notEqual(key, targets);
18
19
  export const isKeyInArray = ([key], targets) => isInArray(key, targets);
20
+ export const notKeyInArray = ([key], targets) => notInArray(key, targets);
19
21
  export const isKeyLess = ([key], target) => isLess(key, target);
20
22
  export const isKeyEqualLess = ([key], target) => isEqualLess(key, target);
21
23
  export const isKeyGreater = ([key], target) => isGreater(key, target);
22
24
  export const isKeyEqualGreater = ([key], target) => isEqualGreater(key, target);
23
25
  // Entry value matchers.
24
26
  export const isValueEqual = ([, value], target) => isEqual(value, target);
25
- export const isValueNotEqual = ([, value], target) => isNotEqual(value, target);
27
+ export const notValueEqual = ([, value], target) => notEqual(value, target);
26
28
  export const isValueInArray = ([, value], targets) => isInArray(value, targets);
29
+ export const notValueInArray = ([, value], targets) => notInArray(value, targets);
27
30
  export const isValueLess = ([, value], target) => isLess(value, target);
28
31
  export const isValueEqualLess = ([, value], target) => isEqualLess(value, target);
29
32
  export const isValueGreater = ([, value], target) => isGreater(value, target);
package/util/null.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /** Is a value null? */
2
2
  export declare const isNull: (v: unknown) => v is null;
3
3
  /** Is a value not null? */
4
- export declare const isNotNull: <T>(v: T | null) => v is T;
4
+ export declare const notNull: <T>(v: T | null) => v is T;
5
5
  /** Function that always returns null. */
6
6
  export declare const NULL: () => null;
7
7
  /** Nullish is `null` or `undefined` */
@@ -11,7 +11,7 @@ export declare type NotNullish<T> = Exclude<T, null | undefined>;
11
11
  /** Is a value nullish? */
12
12
  export declare const isNullish: <T>(v: Nullish<T>) => v is null | undefined;
13
13
  /** Is a value not nullish? */
14
- export declare const isNotNullish: <T>(v: Nullish<T>) => v is T;
14
+ export declare const notNullish: <T>(v: Nullish<T>) => v is T;
15
15
  /** Get a required value (returns value or throws `RequiredError` if value is `null` or `undefined`). */
16
16
  export declare function getRequired<T>(v: T): NotNullish<T>;
17
17
  export declare function getRequired<T>(v: Nullish<T>): T;
package/util/null.js CHANGED
@@ -2,13 +2,13 @@ import { RequiredError } from "../error/index.js";
2
2
  /** Is a value null? */
3
3
  export const isNull = (v) => v === null;
4
4
  /** Is a value not null? */
5
- export const isNotNull = (v) => v !== null;
5
+ export const notNull = (v) => v !== null;
6
6
  /** Function that always returns null. */
7
7
  export const NULL = () => null;
8
8
  /** Is a value nullish? */
9
9
  export const isNullish = (v) => v === null || v === undefined;
10
10
  /** Is a value not nullish? */
11
- export const isNotNullish = (v) => v !== null && v !== undefined;
11
+ export const notNullish = (v) => v !== null && v !== undefined;
12
12
  export function getRequired(v) {
13
13
  if (v === undefined || v === null)
14
14
  throw new RequiredError("Required value is missing");
package/util/sort.d.ts CHANGED
@@ -28,17 +28,17 @@ export declare function rank<T>(left: T, ranker: Ranker<T>, right: T): number;
28
28
  *
29
29
  * @returns Number below zero if `a` is higher, number above zero if `b` is higher, or `0` if they're equally sorted.
30
30
  */
31
- export declare function rankAscending(left: unknown, right: unknown): number;
31
+ export declare function rankAsc(left: unknown, right: unknown): number;
32
32
  /** Rank two values in descending order. */
33
33
  export declare const rankDesc: (left: unknown, right: unknown) => number;
34
34
  /** Rank the keys of two entries in ascending order. */
35
- export declare const rankEntryKeyAsc: ([l]: Entry<unknown>, [r]: Entry<unknown>) => number;
35
+ export declare const rankKeyAsc: ([l]: Entry<unknown>, [r]: Entry<unknown>) => number;
36
36
  /** Rank the keys of two entries in descending order. */
37
- export declare const rankEntryKeyDesc: ([l]: Entry<unknown>, [r]: Entry<unknown>) => number;
37
+ export declare const rankKeyDesc: ([l]: Entry<unknown>, [r]: Entry<unknown>) => number;
38
38
  /** Rank the values of two entries in ascending order. */
39
- export declare const rankEntryValueAsc: ([, l]: Entry<unknown>, [, r]: Entry<unknown>) => number;
39
+ export declare const rankValueAsc: ([, l]: Entry<unknown>, [, r]: Entry<unknown>) => number;
40
40
  /** Rank the values of two entries in descending order. */
41
- export declare const rankEntryValueDesc: ([, l]: Entry<unknown>, [, r]: Entry<unknown>) => number;
41
+ export declare const rankValueDesc: ([, l]: Entry<unknown>, [, r]: Entry<unknown>) => number;
42
42
  /** Sort an iterable set of items using a ranker (defaults to sorting in ascending order). */
43
43
  export declare function sortItems<T>(input: Iterable<T>, ranker?: Ranker<T>): ImmutableArray<T>;
44
44
  /** Sort an array using a ranker (defaults to sorting in ascending order) */
package/util/sort.js CHANGED
@@ -20,7 +20,7 @@ export function rank(left, ranker, right) {
20
20
  *
21
21
  * @returns Number below zero if `a` is higher, number above zero if `b` is higher, or `0` if they're equally sorted.
22
22
  */
23
- export function rankAscending(left, right) {
23
+ export function rankAsc(left, right) {
24
24
  // Exactly equal is easy.
25
25
  if (left === right)
26
26
  return 0;
@@ -73,15 +73,15 @@ export function rankAscending(left, right) {
73
73
  return -1;
74
74
  }
75
75
  /** Rank two values in descending order. */
76
- export const rankDesc = (left, right) => 0 - rankAscending(left, right);
76
+ export const rankDesc = (left, right) => 0 - rankAsc(left, right);
77
77
  /** Rank the keys of two entries in ascending order. */
78
- export const rankEntryKeyAsc = ([l], [r]) => rankAscending(l, r);
78
+ export const rankKeyAsc = ([l], [r]) => rankAsc(l, r);
79
79
  /** Rank the keys of two entries in descending order. */
80
- export const rankEntryKeyDesc = ([l], [r]) => rankDesc(l, r);
80
+ export const rankKeyDesc = ([l], [r]) => rankDesc(l, r);
81
81
  /** Rank the values of two entries in ascending order. */
82
- export const rankEntryValueAsc = ([, l], [, r]) => rankAscending(l, r);
82
+ export const rankValueAsc = ([, l], [, r]) => rankAsc(l, r);
83
83
  /** Rank the values of two entries in descending order. */
84
- export const rankEntryValueDesc = ([, l], [, r]) => rankDesc(l, r);
84
+ export const rankValueDesc = ([, l], [, r]) => rankDesc(l, r);
85
85
  /**
86
86
  * Quick sort algorithm.
87
87
  * DH: We implement our own sorting algorithm so that:
@@ -130,13 +130,13 @@ function _quicksort(items, ranker, leftPointer = 0, rightPointer = items.length
130
130
  return changed;
131
131
  }
132
132
  /** Sort an iterable set of items using a ranker (defaults to sorting in ascending order). */
133
- export function sortItems(input, ranker = rankAscending) {
133
+ export function sortItems(input, ranker = rankAsc) {
134
134
  const array = Array.from(input);
135
135
  _quicksort(array, ranker);
136
136
  return array;
137
137
  }
138
138
  /** Sort an array using a ranker (defaults to sorting in ascending order) */
139
- export function sortArray(input, ranker = rankAscending) {
139
+ export function sortArray(input, ranker = rankAsc) {
140
140
  const output = Array.from(input);
141
141
  return _quicksort(output, ranker) ? output : input;
142
142
  }
@@ -144,24 +144,24 @@ export function sortArray(input, ranker = rankAscending) {
144
144
  * Sort an iterable set of entries (defaults to sorting by key in ascending order).
145
145
  * - Always returns an array
146
146
  */
147
- export function sortEntries(input, ranker = rankEntryKeyAsc) {
147
+ export function sortEntries(input, ranker = rankKeyAsc) {
148
148
  const array = Array.from(input);
149
149
  _quicksort(array, ranker);
150
150
  return array;
151
151
  }
152
152
  /** Sort a map-like object using a ranker (defaults to sorting by key in ascending order). */
153
- export function sortObject(input, ranker = rankEntryKeyAsc) {
153
+ export function sortObject(input, ranker = rankKeyAsc) {
154
154
  const array = Object.entries(input);
155
155
  return _quicksort(array, ranker) ? Object.fromEntries(array) : input;
156
156
  }
157
157
  /** Sort a map using a ranker (defaults to sorting by key in ascending order). */
158
- export function sortMap(input, ranker = rankEntryKeyAsc) {
158
+ export function sortMap(input, ranker = rankKeyAsc) {
159
159
  const array = Array.from(input);
160
160
  return _quicksort(array, ranker) ? new Map(array) : input;
161
161
  }
162
162
  /** Derive a value and match it against a target value. */
163
163
  export class TransformRanker {
164
- constructor(transformer, ranker = rankAscending) {
164
+ constructor(transformer, ranker = rankAsc) {
165
165
  this._transformer = transformer;
166
166
  this._ranker = ranker;
167
167
  }
package/util/template.js CHANGED
@@ -83,7 +83,7 @@ const matchInternal = (template, target) => {
83
83
  const stopIndex = !post ? Infinity : target.indexOf(post, startIndex);
84
84
  if (stopIndex < 0)
85
85
  return undefined; // Target doesn't match template because chunk post wasn't found.
86
- const value = target.substring(startIndex, stopIndex);
86
+ const value = target.slice(startIndex, stopIndex);
87
87
  if (!value.length)
88
88
  return undefined; // Target doesn't match template because chunk value was missing.
89
89
  values[name] = value;