shelving 1.91.2 → 1.92.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/db/Change.d.ts +19 -20
  2. package/db/Change.js +22 -21
  3. package/db/CollectionReference.d.ts +103 -0
  4. package/db/CollectionReference.js +127 -0
  5. package/db/Database.d.ts +21 -23
  6. package/db/Database.js +18 -18
  7. package/db/{Item.d.ts → ItemReference.d.ts} +13 -14
  8. package/db/{Item.js → ItemReference.js} +14 -17
  9. package/db/ItemState.d.ts +3 -3
  10. package/db/{Query.d.ts → QueryReference.d.ts} +25 -33
  11. package/db/QueryReference.js +101 -0
  12. package/db/QueryState.d.ts +10 -10
  13. package/db/QueryState.js +20 -18
  14. package/db/index.d.ts +3 -3
  15. package/db/index.js +3 -3
  16. package/firestore/client/FirestoreClientProvider.d.ts +13 -13
  17. package/firestore/client/FirestoreClientProvider.js +55 -84
  18. package/firestore/lite/FirestoreLiteProvider.d.ts +11 -11
  19. package/firestore/lite/FirestoreLiteProvider.js +52 -83
  20. package/firestore/server/FirestoreServerProvider.d.ts +13 -13
  21. package/firestore/server/FirestoreServerProvider.js +51 -84
  22. package/index.d.ts +0 -2
  23. package/index.js +0 -2
  24. package/package.json +1 -3
  25. package/provider/CacheProvider.d.ts +7 -7
  26. package/provider/CacheProvider.js +14 -14
  27. package/provider/DebugProvider.d.ts +11 -11
  28. package/provider/DebugProvider.js +28 -29
  29. package/provider/MemoryProvider.d.ts +17 -17
  30. package/provider/MemoryProvider.js +41 -43
  31. package/provider/Provider.d.ts +15 -15
  32. package/provider/ThroughProvider.d.ts +12 -12
  33. package/provider/ThroughProvider.js +20 -20
  34. package/provider/ValidationProvider.d.ts +11 -11
  35. package/provider/ValidationProvider.js +16 -12
  36. package/react/useData.d.ts +5 -5
  37. package/react/useData.js +2 -2
  38. package/state/DataState.d.ts +4 -8
  39. package/state/DataState.js +6 -16
  40. package/state/DictionaryState.d.ts +4 -6
  41. package/state/DictionaryState.js +4 -9
  42. package/test/basics.d.ts +1 -1
  43. package/test/people.d.ts +1 -1
  44. package/test/util.d.ts +1 -1
  45. package/test/util.js +1 -1
  46. package/util/data.d.ts +2 -2
  47. package/util/equal.js +5 -5
  48. package/util/hydrate.d.ts +13 -26
  49. package/util/hydrate.js +44 -60
  50. package/util/index.d.ts +2 -0
  51. package/util/index.js +2 -0
  52. package/util/iterate.d.ts +2 -0
  53. package/util/match.d.ts +2 -10
  54. package/util/match.js +4 -8
  55. package/util/query.d.ts +83 -0
  56. package/util/query.js +129 -0
  57. package/util/sort.d.ts +8 -15
  58. package/util/sort.js +15 -19
  59. package/util/transform.d.ts +26 -45
  60. package/util/transform.js +24 -29
  61. package/util/update.d.ts +22 -0
  62. package/util/update.js +42 -0
  63. package/util/validate.js +2 -2
  64. package/constraint/Constraint.d.ts +0 -7
  65. package/constraint/Constraint.js +0 -3
  66. package/constraint/Constraints.d.ts +0 -20
  67. package/constraint/Constraints.js +0 -34
  68. package/constraint/Filter.d.ts +0 -34
  69. package/constraint/Filter.js +0 -89
  70. package/constraint/Filters.d.ts +0 -27
  71. package/constraint/Filters.js +0 -41
  72. package/constraint/Sort.d.ts +0 -18
  73. package/constraint/Sort.js +0 -33
  74. package/constraint/Sorts.d.ts +0 -26
  75. package/constraint/Sorts.js +0 -47
  76. package/constraint/Statement.d.ts +0 -45
  77. package/constraint/Statement.js +0 -79
  78. package/constraint/index.d.ts +0 -7
  79. package/constraint/index.js +0 -7
  80. package/db/Collection.d.ts +0 -64
  81. package/db/Collection.js +0 -83
  82. package/db/Query.js +0 -94
  83. package/update/ArrayUpdate.d.ts +0 -17
  84. package/update/ArrayUpdate.js +0 -31
  85. package/update/DataUpdate.d.ts +0 -37
  86. package/update/DataUpdate.js +0 -44
  87. package/update/Delete.d.ts +0 -10
  88. package/update/Delete.js +0 -12
  89. package/update/DictionaryUpdate.d.ts +0 -31
  90. package/update/DictionaryUpdate.js +0 -62
  91. package/update/Increment.d.ts +0 -18
  92. package/update/Increment.js +0 -22
  93. package/update/Update.d.ts +0 -8
  94. package/update/Update.js +0 -6
  95. package/update/hydrations.d.ts +0 -3
  96. package/update/hydrations.js +0 -13
  97. package/update/index.d.ts +0 -7
  98. package/update/index.js +0 -7
@@ -0,0 +1,83 @@
1
+ import type { ImmutableArray } from "./array.js";
2
+ import type { Data, FlatData, FlatDataKey } from "./data.js";
3
+ /** Query that can be applied to a list of data objects. */
4
+ export type Query<T extends Data> = {
5
+ readonly [K in FlatDataKey<T> as K | `${K}` | `!${K}`]?: FlatData<T>[K] | ImmutableArray<FlatData<T>[K]> | undefined;
6
+ } & {
7
+ readonly [K in FlatDataKey<T> as `${K}<` | `${K}<=` | `${K}>` | `${K}>=`]?: FlatData<T>[K] | undefined;
8
+ } & {
9
+ readonly [K in FlatDataKey<T> as `${K}[]`]?: FlatData<T>[K] extends ImmutableArray<infer X> ? X | undefined : never;
10
+ } & {
11
+ readonly $order?: FlatDataKey<T> | `${FlatDataKey<T>}` | `!${FlatDataKey<T>}` | ImmutableArray<`${FlatDataKey<T>}` | `!${FlatDataKey<T>}`>;
12
+ readonly $limit?: number | undefined;
13
+ };
14
+ /** A single filter that can be applied to a list of data objects. */
15
+ export type Filter = {
16
+ keys: readonly [string, ...string[]];
17
+ operator: "is";
18
+ value: unknown;
19
+ } | {
20
+ keys: readonly [string, ...string[]];
21
+ operator: "not";
22
+ value: unknown;
23
+ } | {
24
+ keys: readonly [string, ...string[]];
25
+ operator: "in";
26
+ value: ImmutableArray;
27
+ } | {
28
+ keys: readonly [string, ...string[]];
29
+ operator: "out";
30
+ value: ImmutableArray;
31
+ } | {
32
+ keys: readonly [string, ...string[]];
33
+ operator: "contains";
34
+ value: unknown;
35
+ } | {
36
+ keys: readonly [string, ...string[]];
37
+ operator: "lt";
38
+ value: unknown;
39
+ } | {
40
+ keys: readonly [string, ...string[]];
41
+ operator: "lte";
42
+ value: unknown;
43
+ } | {
44
+ keys: readonly [string, ...string[]];
45
+ operator: "gt";
46
+ value: unknown;
47
+ } | {
48
+ keys: readonly [string, ...string[]];
49
+ operator: "gte";
50
+ value: unknown;
51
+ };
52
+ /** A single sort that can be applied to a list of data objects. */
53
+ export type Sort = {
54
+ keys: readonly [string, ...string[]];
55
+ direction: "asc" | "desc";
56
+ };
57
+ /** Get the `Filter` objects for a query. */
58
+ export declare function getFilters<T extends Data>(query: Query<T>): ImmutableArray<Filter>;
59
+ /** Get the `Sort` objects for a query. */
60
+ export declare function getSorts<T extends Data>({ $order }: Query<T>): ImmutableArray<Sort>;
61
+ /** Get the limit for a query. */
62
+ export declare const getLimit: <T extends Data>({ $limit }: Query<T>) => number | undefined;
63
+ /** Query a set of data items using a query. */
64
+ export declare function queryItems<T extends Data>(items: Iterable<T>, query: Query<T>): Iterable<T>;
65
+ /**
66
+ * Query a set of data items for writing using a query.
67
+ * - If no limit is set on the data sorting can be avoided too for performance reasons.
68
+ */
69
+ export declare function queryWritableItems<T extends Data>(items: Iterable<T>, query: Query<T>): Iterable<T>;
70
+ /** Match a single data item againt a set of filters. */
71
+ export declare function matchQueryItem<T extends Data>(item: T, filters: ImmutableArray<Filter>): boolean;
72
+ /** Filter a set of data items using a set of filters. */
73
+ export declare function filterQueryItems<T extends Data>(items: Iterable<T>, filters: ImmutableArray<Filter>): Iterable<T>;
74
+ /** Compare two data items using a set of sorts. */
75
+ export declare function compareQueryItems<T extends Data>(left: T, right: T, sorts: ImmutableArray<Sort>): number;
76
+ /** Sort a set of data items using a set of sorts. */
77
+ export declare function sortQueryItems<T extends Data>(items: Iterable<T>, sorts: ImmutableArray<Sort>): Iterable<T>;
78
+ /** LImit a set of data items using a set of limit. */
79
+ export declare function limitQueryItems<T extends Data>(items: ImmutableArray<T> | Iterable<T>, limit: number | undefined): Iterable<T>;
80
+ /** Get a query for items that appear before a specified item. */
81
+ export declare function getBeforeQuery<T extends Data>(query: Query<T>, item: T): Query<T>;
82
+ /** Get a query for items that appear after a specified item. */
83
+ export declare function getAfterQuery<T extends Data>(query: Query<T>, item: T): Query<T>;
package/util/query.js ADDED
@@ -0,0 +1,129 @@
1
+ import { getLastItem, isArray, limitArray } from "./array.js";
2
+ import { isArrayWith, isEqual, isEqualGreater, isEqualLess, isGreater, isInArray, isLess, notEqual, notInArray } from "./equal.js";
3
+ import { limitItems } from "./iterate.js";
4
+ import { getProp, getProps } from "./object.js";
5
+ import { compareAscending, compareDescending, sortArray } from "./sort.js";
6
+ import { splitString } from "./string.js";
7
+ import { isDefined } from "./undefined.js";
8
+ /** Map `Filter` operators to corresponding `Match` function. */
9
+ const MATCHERS = {
10
+ is: isEqual,
11
+ not: notEqual,
12
+ in: isInArray,
13
+ out: notInArray,
14
+ contains: isArrayWith,
15
+ lt: isLess,
16
+ lte: isEqualLess,
17
+ gt: isGreater,
18
+ gte: isEqualGreater,
19
+ };
20
+ /** Get the `Filter` objects for a query. */
21
+ export function getFilters(query) {
22
+ return getProps(query).map(_getFilters).filter(isDefined);
23
+ }
24
+ function _getFilters([key, value]) {
25
+ if (key === "$order" || key === "$limit")
26
+ return;
27
+ else if (key.startsWith("!"))
28
+ return isArray(value) ? { keys: splitString(key.slice(1), "."), operator: "out", value } : { keys: splitString(key.slice(1), "."), operator: "not", value };
29
+ else if (key.endsWith("[]"))
30
+ return { keys: splitString(key.slice(0, -2), "."), operator: "contains", value };
31
+ else if (key.endsWith(">"))
32
+ return { keys: splitString(key.slice(0, -1), "."), operator: "gt", value };
33
+ else if (key.endsWith(">="))
34
+ return { keys: splitString(key.slice(0, -2), "."), operator: "gte", value };
35
+ else if (key.endsWith("<"))
36
+ return { keys: splitString(key.slice(0, -1), "."), operator: "lt", value };
37
+ else if (key.endsWith("<="))
38
+ return { keys: splitString(key.slice(0, -2), "."), operator: "lte", value };
39
+ else
40
+ return isArray(value) ? { keys: splitString(key, "."), operator: "in", value } : { keys: splitString(key, "."), operator: "is", value };
41
+ }
42
+ /** Get the `Sort` objects for a query. */
43
+ export function getSorts({ $order }) {
44
+ return !$order ? [] : isArray($order) ? $order.map(_getSort) : [_getSort($order)];
45
+ }
46
+ function _getSort(key) {
47
+ if (key.startsWith("!"))
48
+ return { keys: splitString(key.slice(1), "."), direction: "desc" };
49
+ else
50
+ return { keys: splitString(key, "."), direction: "asc" };
51
+ }
52
+ /** Get the limit for a query. */
53
+ export const getLimit = ({ $limit }) => $limit;
54
+ /** Query a set of data items using a query. */
55
+ export function queryItems(items, query) {
56
+ return limitQueryItems(sortQueryItems(filterQueryItems(items, getFilters(query)), getSorts(query)), getLimit(query));
57
+ }
58
+ /**
59
+ * Query a set of data items for writing using a query.
60
+ * - If no limit is set on the data sorting can be avoided too for performance reasons.
61
+ */
62
+ export function queryWritableItems(items, query) {
63
+ return getLimit(query) === undefined ? filterQueryItems(items, getFilters(query)) : queryItems(items, query);
64
+ }
65
+ /** Match a single data item againt a set of filters. */
66
+ export function matchQueryItem(item, filters) {
67
+ for (const { keys, operator, value } of filters) {
68
+ const matcher = MATCHERS[operator];
69
+ if (!matcher(getProp(item, ...keys), value))
70
+ return false;
71
+ }
72
+ return true;
73
+ }
74
+ /** Filter a set of data items using a set of filters. */
75
+ export function* filterQueryItems(items, filters) {
76
+ if (filters.length) {
77
+ for (const item of items)
78
+ if (matchQueryItem(item, filters))
79
+ yield item;
80
+ }
81
+ else {
82
+ yield* items;
83
+ }
84
+ }
85
+ /** Compare two data items using a set of sorts. */
86
+ export function compareQueryItems(left, right, sorts) {
87
+ for (const { keys, direction } of sorts) {
88
+ const l = getProp(left, ...keys);
89
+ const r = getProp(right, ...keys);
90
+ const c = direction === "asc" ? compareAscending(l, r) : compareDescending(l, r);
91
+ if (c !== 0)
92
+ return c;
93
+ }
94
+ return 0;
95
+ }
96
+ /** Sort a set of data items using a set of sorts. */
97
+ export function sortQueryItems(items, sorts) {
98
+ return sorts.length ? sortArray(items, compareQueryItems, sorts) : items;
99
+ }
100
+ /** LImit a set of data items using a set of limit. */
101
+ export function limitQueryItems(items, limit) {
102
+ return typeof limit !== "number" ? items : isArray(items) ? limitArray(items, limit) : limitItems(items, limit);
103
+ }
104
+ /** Get a query for items that appear before a specified item. */
105
+ export function getBeforeQuery(query, item) {
106
+ const sorts = getSorts(query);
107
+ const lastSort = getLastItem(sorts);
108
+ const newQuery = { ...query };
109
+ for (const sort of sorts) {
110
+ const { keys, direction } = sort;
111
+ const key = keys.join(".");
112
+ const value = getProp(item, ...keys);
113
+ newQuery[direction === "asc" ? (sort === lastSort ? `${key}>` : `${key}>=`) : sort === lastSort ? `${key}<` : `${key}<=`] = value;
114
+ }
115
+ return newQuery;
116
+ }
117
+ /** Get a query for items that appear after a specified item. */
118
+ export function getAfterQuery(query, item) {
119
+ const sorts = getSorts(query);
120
+ const lastSort = getLastItem(sorts);
121
+ const newQuery = { ...query };
122
+ for (const sort of sorts) {
123
+ const { keys, direction } = sort;
124
+ const key = keys.join(".");
125
+ const value = getProp(item, ...keys);
126
+ newQuery[direction === "asc" ? (sort === lastSort ? `${key}<` : `${key}<=`) : sort === lastSort ? `${key}>` : `${key}>=`] = value;
127
+ }
128
+ return newQuery;
129
+ }
package/util/sort.d.ts CHANGED
@@ -1,16 +1,9 @@
1
1
  import type { ImmutableArray } from "./array.js";
2
- /** Object that can rank two values using its `rank()` function. */
3
- export interface Rankable<T> {
4
- rank(left: T, right: T): number;
5
- }
6
- /** Function that can rank two values. */
7
- export type Rank<T> = (left: T, right: T) => number;
8
- /** Something that can rank two values. */
9
- export type Ranker<T> = Rankable<T> | Rank<T>;
10
- /** Rank two values with a `Ranker`. */
11
- export declare function rank<T>(left: T, ranker: Ranker<T>, right: T): number;
2
+ import type { Arguments } from "./function.js";
3
+ /** Function that can compare two values for sorting. */
4
+ export type Compare<T, A extends Arguments = []> = (left: T, right: T, ...args: A) => number;
12
5
  /**
13
- * Rank two values in ascending order.
6
+ * Compare two unknown values in ascending order.
14
7
  * - Allows values of different types to be ranked for sorting.
15
8
  *
16
9
  * 1. Numbers and dates (ascending order: -Infinity, negative, zero, positive, Infinity, NaN)
@@ -26,8 +19,8 @@ export declare function rank<T>(left: T, ranker: Ranker<T>, right: T): number;
26
19
  *
27
20
  * @returns Number below zero if `a` is higher, number above zero if `b` is higher, or `0` if they're equally sorted.
28
21
  */
29
- export declare function rankAsc(left: unknown, right: unknown): number;
30
- /** Rank two values in descending order. */
31
- export declare const rankDesc: (left: unknown, right: unknown) => number;
22
+ export declare function compareAscending(left: unknown, right: unknown): number;
23
+ /** Compare two unknown values in descending order. */
24
+ export declare const compareDescending: (left: unknown, right: unknown) => number;
32
25
  /** Sort an iterable set of items using a ranker (defaults to sorting in ascending order). */
33
- export declare function sortItems<T>(input: ImmutableArray<T> | Iterable<T>, ranker?: Ranker<T>): ImmutableArray<T>;
26
+ export declare function sortArray<T, A extends Arguments = []>(input: ImmutableArray<T> | Iterable<T>, compare?: Compare<T, A>, ...args: A): ImmutableArray<T>;
package/util/sort.js CHANGED
@@ -1,10 +1,6 @@
1
1
  import { isArray } from "./array.js";
2
- /** Rank two values with a `Ranker`. */
3
- export function rank(left, ranker, right) {
4
- return typeof ranker === "function" ? ranker(left, right) : ranker.rank(left, right);
5
- }
6
2
  /**
7
- * Rank two values in ascending order.
3
+ * Compare two unknown values in ascending order.
8
4
  * - Allows values of different types to be ranked for sorting.
9
5
  *
10
6
  * 1. Numbers and dates (ascending order: -Infinity, negative, zero, positive, Infinity, NaN)
@@ -20,7 +16,7 @@ export function rank(left, ranker, right) {
20
16
  *
21
17
  * @returns Number below zero if `a` is higher, number above zero if `b` is higher, or `0` if they're equally sorted.
22
18
  */
23
- export function rankAsc(left, right) {
19
+ export function compareAscending(left, right) {
24
20
  // Exactly equal is easy.
25
21
  if (left === right)
26
22
  return 0;
@@ -72,8 +68,8 @@ export function rankAsc(left, right) {
72
68
  // Otherwise a is higher.
73
69
  return -1;
74
70
  }
75
- /** Rank two values in descending order. */
76
- export const rankDesc = (left, right) => 0 - rankAsc(left, right);
71
+ /** Compare two unknown values in descending order. */
72
+ export const compareDescending = (left, right) => 0 - compareAscending(left, right);
77
73
  /**
78
74
  * Quick sort algorithm.
79
75
  * DH: We implement our own sorting algorithm so that:
@@ -81,13 +77,13 @@ export const rankDesc = (left, right) => 0 - rankAsc(left, right);
81
77
  * 2. We can use our own comparison function by default.
82
78
  *
83
79
  * @param items The actual list of items that's sorted in place.
84
- * @param ranker A rank function that takes a left/right value and returns 1/0/-1 (like `Array.prototype.sort()`).
80
+ * @param compare A rank function that takes a left/right value and returns 1/0/-1 (like `Array.prototype.sort()`).
85
81
  * @param leftPointer Index in the set of items to start sorting on.
86
82
  * @param rightPointer Index in the set of items to stop sorting on.
87
83
  *
88
84
  * @return `true` if the array order changed, and `false` otherwise.
89
85
  */
90
- function _quicksort(items, ranker, leftPointer = 0, rightPointer = items.length - 1) {
86
+ function _quicksort(items, compare, args, leftPointer = 0, rightPointer = items.length - 1) {
91
87
  // Have any swaps been made?
92
88
  let changed = false;
93
89
  // Calculate the middle value.
@@ -97,9 +93,9 @@ function _quicksort(items, ranker, leftPointer = 0, rightPointer = items.length
97
93
  let l = leftPointer;
98
94
  let r = rightPointer;
99
95
  while (l <= r) {
100
- while (rank(items[l], ranker, middle) < 0)
96
+ while (compare(items[l], middle, ...args) < 0)
101
97
  l++; // eslint-disable-line @typescript-eslint/no-non-null-assertion
102
- while (rank(items[r], ranker, middle) > 0)
98
+ while (compare(items[r], middle, ...args) > 0)
103
99
  r--; // eslint-disable-line @typescript-eslint/no-non-null-assertion
104
100
  if (l <= r) {
105
101
  if (l < r) {
@@ -111,22 +107,22 @@ function _quicksort(items, ranker, leftPointer = 0, rightPointer = items.length
111
107
  }
112
108
  }
113
109
  // Sort the lower and upper segments.
114
- if (leftPointer < l - 1 && _quicksort(items, ranker, leftPointer, l - 1))
110
+ if (leftPointer < l - 1 && _quicksort(items, compare, args, leftPointer, l - 1))
115
111
  changed = true;
116
- if (l < rightPointer && _quicksort(items, ranker, l, rightPointer))
112
+ if (l < rightPointer && _quicksort(items, compare, args, l, rightPointer))
117
113
  changed = true;
118
114
  // Changes were made.
119
115
  return changed;
120
116
  }
121
117
  /** Sort an iterable set of items using a ranker (defaults to sorting in ascending order). */
122
- export function sortItems(input, ranker = rankAsc) {
118
+ export function sortArray(input, compare = compareAscending, ...args) {
123
119
  if (isArray(input)) {
124
120
  if (input.length < 2)
125
121
  return input;
126
122
  const output = Array.from(input);
127
- return _quicksort(output, ranker) ? output : input;
123
+ return _quicksort(output, compare, args) ? output : input;
128
124
  }
129
- const array = Array.from(input);
130
- _quicksort(array, ranker);
131
- return array;
125
+ const output = Array.from(input);
126
+ _quicksort(output, compare, args);
127
+ return output;
132
128
  }
@@ -3,57 +3,38 @@ import type { ImmutableDictionary, PossibleDictionary } from "./dictionary.js";
3
3
  import type { Entry } from "./entry.js";
4
4
  import type { Arguments } from "./function.js";
5
5
  import type { ImmutableObject, ObjectValue } from "./object.js";
6
- /** Object that transforms an input value into an output value with its `transform()` method. */
7
- export interface Transformable<I, O, A extends Arguments = []> {
8
- transform(input: I, ...args: A): O;
9
- }
10
- /** Object that transforms an input value into an output value with its `transform()` method. */
11
- export interface AsyncTransformable<I, O, A extends Arguments = []> {
12
- transform(input: I, ...args: A): O | PromiseLike<O>;
13
- }
14
- /** Is an unknown value a transformable. */
15
- export declare const isTransformable: <T extends Transformable<unknown, unknown, Arguments>>(value: unknown) => value is T;
16
6
  /** Function that can transform an input value into an output value. */
17
7
  export type Transform<I, O, A extends Arguments = []> = (input: I, ...args: A) => O;
18
8
  /** Function that can transform an input value into an output value. */
19
9
  export type AsyncTransform<I, O, A extends Arguments = []> = (input: I, ...args: A) => O | PromiseLike<O>;
20
- /** Something that can transform an input value into an output value (or a plain value). */
21
- export type Transformer<I, O, A extends Arguments = []> = Transformable<I, O, A> | Transform<I, O, A> | O;
22
- /** Something that can transform an input value into an output value (or a plain value). */
23
- export type AsyncTransformer<I, O, A extends Arguments = []> = AsyncTransformable<I, O, A> | AsyncTransform<I, O, A> | O;
24
- /** Set of named transformers for a data object (or `undefined` to skip the transform). */
25
- export type Transformers<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []> = {
26
- readonly [K in keyof I]?: Transformer<I[K], O[K], A>;
10
+ /** Set of named transforms for a data object (or `undefined` to skip the transform). */
11
+ export type Transforms<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []> = {
12
+ readonly [K in keyof I]?: Transform<I[K], O[K], A>;
27
13
  };
28
- /** Transform a value using a transformer. */
29
- export declare function transform<I, O, A extends Arguments = []>(input: I, transformer: (v: I) => O, ...args: A): O;
30
- export declare function transform<I, O, A extends Arguments = []>(input: I, transformer: (v: I) => O | PromiseLike<O>, ...args: A): O | PromiseLike<O>;
31
- export declare function transform<I, O, A extends Arguments = []>(input: I, transformer: Transformer<I, O, A>, ...args: A): O;
32
- export declare function transform<I, O, A extends Arguments = []>(input: I, transformer: AsyncTransformer<I, O, A>, ...args: A): O;
33
- /** Modify a set of items using a transformer. */
34
- export declare function mapItems<I, O, A extends Arguments = []>(items: Iterable<I>, transformer: (v: I, ...args: A) => O, ...args: A): Iterable<O>;
35
- export declare function mapItems<I, O, A extends Arguments = []>(items: Iterable<I>, transformer: Transformer<I, O, A>, ...args: A): Iterable<O>;
36
- /** Modify the items of an array using a transformer. */
37
- export declare function mapArray<T extends ImmutableArray>(arr: T, transformer: Transformer<ArrayItem<T>, ArrayItem<T>>): T;
38
- export declare function mapArray<I, O, A extends Arguments = []>(arr: Iterable<I>, transformer: (v: I, ...args: A) => O, ...args: A): ImmutableArray<O>;
39
- export declare function mapArray<I, O, A extends Arguments = []>(arr: Iterable<I>, transformer: Transformer<I, O, A>, ...args: A): ImmutableArray<O>;
40
- /** Modify the values of the props of an object using a transformer. */
41
- export declare function mapObject<T extends ImmutableObject>(obj: T, transformer: Transformer<ObjectValue<T>, ObjectValue<T>>): T;
42
- export declare function mapObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I, transformer: (v: ObjectValue<I>, ...args: A) => ObjectValue<O>, ...args: A): O;
43
- export declare function mapObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I, transformer: Transformer<ObjectValue<I>, ObjectValue<O>>, ...args: A): O;
44
- /** Modify the values of a dictionary using a transformer. */
45
- export declare const mapDictionary: <I, O, A extends Arguments = []>(dictionary: PossibleDictionary<I>, transformer: Transformer<I, O, A>, ...args: A) => ImmutableDictionary<O>;
46
- /** Modify the values of a set of entries using a transformer. */
47
- export declare function mapEntries<K, I, O, A extends Arguments = []>(entries: Iterable<Entry<K, I>>, transformer: Transformer<I, O, A>, ...args: A): Iterable<Entry<K, O>>;
14
+ /** Modify a set of items using a transform. */
15
+ export declare function mapItems<I, O, A extends Arguments = []>(items: Iterable<I>, transform: (v: I, ...args: A) => O, ...args: A): Iterable<O>;
16
+ export declare function mapItems<I, O, A extends Arguments = []>(items: Iterable<I>, transform: Transform<I, O, A>, ...args: A): Iterable<O>;
17
+ /** Modify the items of an array using a transform. */
18
+ export declare function mapArray<T extends ImmutableArray>(arr: T, transform: Transform<ArrayItem<T>, ArrayItem<T>>): T;
19
+ export declare function mapArray<I, O, A extends Arguments = []>(arr: Iterable<I>, transform: (v: I, ...args: A) => O, ...args: A): ImmutableArray<O>;
20
+ export declare function mapArray<I, O, A extends Arguments = []>(arr: Iterable<I>, transform: Transform<I, O, A>, ...args: A): ImmutableArray<O>;
21
+ /** Modify the values of the props of an object using a transform. */
22
+ export declare function mapObject<T extends ImmutableObject>(obj: T, transform: Transform<ObjectValue<T>, ObjectValue<T>>): T;
23
+ export declare function mapObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I, transform: (v: ObjectValue<I>, ...args: A) => ObjectValue<O>, ...args: A): O;
24
+ export declare function mapObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I, transform: Transform<ObjectValue<I>, ObjectValue<O>, A>, ...args: A): O;
25
+ /** Modify the values of a dictionary using a transform. */
26
+ export declare const mapDictionary: <I, O, A extends Arguments = []>(dictionary: PossibleDictionary<I>, transform: Transform<I, O, A>, ...args: A) => ImmutableDictionary<O>;
27
+ /** Modify the values of a set of entries using a transform. */
28
+ export declare function mapEntries<K, I, O, A extends Arguments = []>(entries: Iterable<Entry<K, I>>, transform: Transform<I, O, A>, ...args: A): Iterable<Entry<K, O>>;
48
29
  /**
49
- * Transform an object using a set of named transformers.
30
+ * Transform an object using a set of named transforms.
50
31
  *
51
32
  * @returns Transformed object (or same object if no changes were made).
52
33
  */
53
- export declare function transformObject<T extends ImmutableObject, A extends Arguments = []>(obj: T, transforms: Transformers<T, T | Partial<T>, A>, ...args: A): T;
54
- export declare function transformObject<T extends ImmutableObject, A extends Arguments = []>(obj: T | Partial<T>, transforms: Transformers<T, T | Partial<T>, A>, ...args: A): Partial<T>;
55
- export declare function transformObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I, transforms: Transformers<I, O | Partial<O>, A>, ...args: A): O;
56
- export declare function transformObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I | Partial<I>, transforms: Transformers<I, O | Partial<O>, A>, ...args: A): Partial<O>;
57
- /** Transform items in a sequence as they are yielded using a (potentially async) transformer. */
58
- export declare function mapSequence<I, O, A extends Arguments = []>(sequence: AsyncIterable<I>, transformer: (input: I, ...args: A) => O | PromiseLike<O>, ...args: A): AsyncIterable<O>;
59
- export declare function mapSequence<I, O, A extends Arguments = []>(sequence: AsyncIterable<I>, transformer: AsyncTransformer<I, O, A>, ...args: A): AsyncIterable<O>;
34
+ export declare function transformObject<T extends ImmutableObject, A extends Arguments = []>(obj: T, transforms: Transforms<T, T | Partial<T>, A>, ...args: A): T;
35
+ export declare function transformObject<T extends ImmutableObject, A extends Arguments = []>(obj: T | Partial<T>, transforms: Transforms<T, T | Partial<T>, A>, ...args: A): Partial<T>;
36
+ export declare function transformObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I, transforms: Transforms<I, O | Partial<O>, A>, ...args: A): O;
37
+ export declare function transformObject<I extends ImmutableObject, O extends ImmutableObject, A extends Arguments = []>(obj: I | Partial<I>, transforms: Transforms<I, O | Partial<O>, A>, ...args: A): Partial<O>;
38
+ /** Transform items in a sequence as they are yielded using a (potentially async) transform. */
39
+ export declare function mapSequence<I, O, A extends Arguments = []>(sequence: AsyncIterable<I>, transform: (input: I, ...args: A) => O | PromiseLike<O>, ...args: A): AsyncIterable<O>;
40
+ export declare function mapSequence<I, O, A extends Arguments = []>(sequence: AsyncIterable<I>, transform: AsyncTransform<I, O, A>, ...args: A): AsyncIterable<O>;
package/util/transform.js CHANGED
@@ -1,46 +1,41 @@
1
- import { isFunction } from "./function.js";
2
- import { getProps, isObject } from "./object.js";
3
- /** Is an unknown value a transformable. */
4
- export const isTransformable = (value) => isObject(value) && typeof value.transform === "function";
5
- export function transform(input, transformer, ...args) {
6
- return isFunction(transformer) ? transformer(input, ...args) : isTransformable(transformer) ? transformer.transform(input, ...args) : transformer;
7
- }
8
- export function* mapItems(items, transformer, ...args) {
1
+ import { getProps } from "./object.js";
2
+ export function* mapItems(items, transform, ...args) {
9
3
  for (const item of items)
10
- yield transform(item, transformer, ...args);
4
+ yield transform(item, ...args);
11
5
  }
12
- export function mapArray(arr, transformer, ...args) {
13
- return Array.from(mapItems(arr, transformer, ...args));
6
+ export function mapArray(arr, transform, ...args) {
7
+ return Array.from(mapItems(arr, transform, ...args));
14
8
  }
15
- export function mapObject(obj, transformer, ...args) {
16
- return Object.fromEntries(mapEntries(getProps(obj), transformer, ...args));
9
+ export function mapObject(obj, transform, ...args) {
10
+ return Object.fromEntries(mapEntries(getProps(obj), transform, ...args));
17
11
  }
18
- /** Modify the values of a dictionary using a transformer. */
12
+ /** Modify the values of a dictionary using a transform. */
19
13
  export const mapDictionary = mapObject;
20
- /** Modify the values of a set of entries using a transformer. */
21
- export function* mapEntries(entries, transformer, ...args) {
14
+ /** Modify the values of a set of entries using a transform. */
15
+ export function* mapEntries(entries, transform, ...args) {
22
16
  for (const [k, v] of entries)
23
- yield [k, transform(v, transformer, ...args)];
17
+ yield [k, transform(v, ...args)];
24
18
  }
25
19
  export function transformObject(input, transforms, ...args) {
26
20
  let changed = false;
27
21
  const output = { ...input };
28
22
  for (const [k, t] of getProps(transforms)) {
29
- // if (t !== undefined) {
30
- const i = input[k];
31
- const o = transform(i, t, ...args);
32
- if (t === undefined) {
33
- delete output[k];
34
- }
35
- else {
36
- output[k] = o;
37
- if (!changed && i !== o)
38
- changed = true;
23
+ if (t) {
24
+ const i = input[k];
25
+ const o = t(i, ...args);
26
+ if (t === undefined) {
27
+ delete output[k];
28
+ }
29
+ else {
30
+ output[k] = o;
31
+ if (!changed && i !== o)
32
+ changed = true;
33
+ }
39
34
  }
40
35
  }
41
36
  return changed ? output : input;
42
37
  }
43
- export async function* mapSequence(sequence, transformer, ...args) {
38
+ export async function* mapSequence(sequence, transform, ...args) {
44
39
  for await (const item of sequence)
45
- yield transform(item, transformer, ...args);
40
+ yield transform(item, ...args);
46
41
  }
@@ -0,0 +1,22 @@
1
+ import type { ImmutableArray } from "./array.js";
2
+ import type { Data, FlatData, FlatDataKey } from "./data.js";
3
+ /** Set of named updates for a data object. */
4
+ export type Updates<T extends Data = Data> = {
5
+ readonly [K in FlatDataKey<T> as K | `${K}`]?: FlatData<T>[K];
6
+ } & {
7
+ readonly [K in FlatDataKey<T> as `${K}+=` | `${K}-=`]?: FlatData<T>[K] extends number ? FlatData<T>[K] : never;
8
+ };
9
+ /** A single update to a keyed property in an object. */
10
+ export type Update = {
11
+ action: "set";
12
+ keys: ImmutableArray<string>;
13
+ value: unknown;
14
+ } | {
15
+ action: "sum";
16
+ keys: ImmutableArray<string>;
17
+ value: number;
18
+ };
19
+ /** Yield the prop updates in an `Updates` object as a set of `Update` objects. */
20
+ export declare function getUpdates<T extends Data>(data: Updates<T>): ImmutableArray<Update>;
21
+ /** Update a data object with a set of updates. */
22
+ export declare function updateData<T extends Data>(data: T, updates: Updates<T>): T;
package/util/update.js ADDED
@@ -0,0 +1,42 @@
1
+ import { AssertionError } from "../error/AssertionError.js";
2
+ import { reduceItems } from "./iterate.js";
3
+ import { getNumber } from "./number.js";
4
+ import { getProps, isObject } from "./object.js";
5
+ /** Yield the prop updates in an `Updates` object as a set of `Update` objects. */
6
+ export function getUpdates(data) {
7
+ return getProps(data).map(_getUpdate);
8
+ }
9
+ function _getUpdate([keys, value]) {
10
+ if (keys.endsWith("+="))
11
+ return { action: "sum", keys: keys.slice(0, -2).split("."), value: getNumber(value) };
12
+ else if (keys.endsWith("-="))
13
+ return { action: "sum", keys: keys.slice(0, -2).split("."), value: 0 - getNumber(value) };
14
+ else
15
+ return { action: "set", keys: keys.split("."), value };
16
+ }
17
+ /** Update a data object with a set of updates. */
18
+ export function updateData(data, updates) {
19
+ return reduceItems(getUpdates(updates), _updateProp, data);
20
+ }
21
+ function _updateProp(obj, update, i = 0) {
22
+ const { action, keys, value } = update;
23
+ const key = keys[i];
24
+ if (!key)
25
+ return obj; // Shouldn't happen.
26
+ const oldValue = obj[key];
27
+ let newValue = oldValue;
28
+ if (i === keys.length - 1) {
29
+ if (action === "sum")
30
+ newValue = typeof oldValue === "number" ? oldValue + value : value;
31
+ else if (action === "set")
32
+ newValue = value;
33
+ else
34
+ return action; // Never happens.
35
+ }
36
+ else {
37
+ if (!isObject(oldValue))
38
+ throw new AssertionError(`Prop "${keys.slice(0, i + 1).join(".")}" is not an object`);
39
+ newValue = _updateProp(oldValue, update, i + 1);
40
+ }
41
+ return oldValue === newValue ? obj : { ...obj, [key]: newValue };
42
+ }
package/util/validate.js CHANGED
@@ -105,10 +105,10 @@ export function validateDictionary(unsafeDictionary, validator) {
105
105
  * - `feedback.details` will contain an entry for each invalid item (keyed by their count in the input iterable).
106
106
  */
107
107
  export function validateData(unsafeData, validators) {
108
- const { partial = false } = getValidationContext();
108
+ const { partial = false, id = false } = getValidationContext();
109
109
  let valid = true;
110
110
  let changed = true;
111
- const safeData = {};
111
+ const safeData = id && typeof unsafeData.id === "string" ? { id: unsafeData.id } : {};
112
112
  const feedbacks = {};
113
113
  for (const [key, validator] of getDataProps(validators)) {
114
114
  const unsafeValue = unsafeData[key];
@@ -1,7 +0,0 @@
1
- import type { Data } from "../util/data.js";
2
- import type { Transformable } from "../util/transform.js";
3
- /** Something that can be used to constrain a query. */
4
- export declare abstract class Constraint<T extends Data> implements Transformable<Iterable<T>, Iterable<T>> {
5
- abstract transform(items: Iterable<T>): Iterable<T>;
6
- abstract toString(): string;
7
- }
@@ -1,3 +0,0 @@
1
- /** Something that can be used to constrain a query. */
2
- export class Constraint {
3
- }
@@ -1,20 +0,0 @@
1
- import type { ImmutableArray } from "../util/array.js";
2
- import type { Data } from "../util/data.js";
3
- import { Constraint } from "./Constraint.js";
4
- /** Type of Rule that is powered by several sub-constraints (e.g. `Filters` and `Sorts` and `Query` itself extend this). */
5
- export declare abstract class Constraints<T extends Data, C extends Constraint<Partial<T>>> extends Constraint<T> implements Iterable<C> {
6
- protected readonly _constraints: ImmutableArray<C>;
7
- /** Get the first constraint. */
8
- get first(): C | undefined;
9
- /** Get the last constraint. */
10
- get last(): C | undefined;
11
- /** Get the number of constraints. */
12
- get size(): number;
13
- constructor(...constraints: C[]);
14
- /** Clone this set of constraints but add additional constraints. */
15
- with(...constraints: C[]): this;
16
- /** Clone this set of constraints but remove specific constraints. */
17
- omit(...constraints: C[]): this;
18
- /** Iterate over the constraints. */
19
- [Symbol.iterator](): Iterator<C, void>;
20
- }