shelving 1.73.1 → 1.74.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.
@@ -4,12 +4,6 @@ import { Sortable, SortConstraints } from "./SortConstraints.js";
4
4
  import { Constraint } from "./Constraint.js";
5
5
  import { FilterList } from "./FilterConstraint.js";
6
6
  import { SortKeys, SortList } from "./SortConstraint.js";
7
- /** Set of props for a query defined as an object. */
8
- export declare type QueryProps<T extends Data> = {
9
- readonly filter?: FilterList<T>;
10
- readonly sort?: SortList<T>;
11
- readonly limit?: number | null;
12
- };
13
7
  /** Interface that combines Filterable, Sortable, Sliceable. */
14
8
  export interface Queryable<T extends Data> extends Filterable<T>, Sortable<T> {
15
9
  /**
@@ -31,15 +25,13 @@ export interface Queryable<T extends Data> extends Filterable<T>, Sortable<T> {
31
25
  readonly limit: number | null;
32
26
  /** Return a new instance of this class with a limit set. */
33
27
  max(max: number | null): this;
34
- /** Return a new instance of this class with new filters, sorts, limits set. */
35
- query(query: QueryProps<T>): this;
36
28
  }
37
29
  /** Allows filtering, sorting, and limiting on a set of results. */
38
30
  export declare class QueryConstraints<T extends Data = Data> extends Constraint<T> implements Queryable<T> {
39
31
  readonly filters: FilterConstraints<T>;
40
32
  readonly sorts: SortConstraints<T>;
41
33
  readonly limit: number | null;
42
- constructor({ filter, sort, limit }?: QueryProps<T>);
34
+ constructor(filters?: FilterList<T>, sorts?: SortList<T>, limit?: number | null);
43
35
  filter(...filters: FilterList<T>[]): this;
44
36
  get unfilter(): this;
45
37
  match(item: T): boolean;
@@ -49,7 +41,6 @@ export declare class QueryConstraints<T extends Data = Data> extends Constraint<
49
41
  after(item: T): this;
50
42
  before(item: T): this;
51
43
  max(limit: number | null): this;
52
- query({ sort, limit, filter }: QueryProps<T>): this;
53
44
  transform(items: Iterable<T>): Iterable<T>;
54
45
  toString(): string;
55
46
  }
@@ -8,13 +8,12 @@ import { FilterConstraint } from "./FilterConstraint.js";
8
8
  // Instances to save resources for the default case (empty query).
9
9
  const EMPTY_FILTERS = new FilterConstraints(); // eslint-disable-line @typescript-eslint/no-explicit-any
10
10
  const EMPTY_SORTS = new SortConstraints(); // eslint-disable-line @typescript-eslint/no-explicit-any
11
- const EMPTY_PROPS = { filter: EMPTY_FILTERS, sort: EMPTY_SORTS, limit: null }; // eslint-disable-line @typescript-eslint/no-explicit-any
12
11
  /** Allows filtering, sorting, and limiting on a set of results. */
13
12
  export class QueryConstraints extends Constraint {
14
- constructor({ filter = EMPTY_FILTERS, sort = EMPTY_SORTS, limit = null } = EMPTY_PROPS) {
13
+ constructor(filters = EMPTY_FILTERS, sorts = EMPTY_SORTS, limit = null) {
15
14
  super();
16
- this.filters = filter instanceof FilterConstraints ? filter : new FilterConstraints(filter);
17
- this.sorts = sort instanceof SortConstraints ? sort : new SortConstraints(sort);
15
+ this.filters = filters instanceof FilterConstraints ? filters : new FilterConstraints(filters);
16
+ this.sorts = sorts instanceof SortConstraints ? sorts : new SortConstraints(sorts);
18
17
  this.limit = limit;
19
18
  }
20
19
  // Implement `Filterable`
@@ -81,15 +80,6 @@ export class QueryConstraints extends Constraint {
81
80
  limit,
82
81
  };
83
82
  }
84
- query({ sort, limit, filter }) {
85
- return {
86
- __proto__: Object.getPrototypeOf(this),
87
- ...this,
88
- filters: filter ? this.filters.filter(filter) : this.filters,
89
- sorts: sort ? this.sorts.sort(sort) : this.sorts,
90
- limit: limit !== undefined ? limit : this.limit,
91
- };
92
- }
93
83
  // Implement `Rule`
94
84
  transform(items) {
95
85
  const sorted = this.sorts.transform(this.filters.transform(items));
@@ -97,7 +87,7 @@ export class QueryConstraints extends Constraint {
97
87
  }
98
88
  // Implement toString()
99
89
  toString() {
100
- return `{"filter":${this.filters.toString()}},"sort":${this.sorts.toString()},"limit":${this.limit}}`;
90
+ return `{"filters":${this.filters.toString()}},"sorts":${this.sorts.toString()},"limit":${this.limit}}`;
101
91
  }
102
92
  }
103
93
  function* _getAfterFilters(sorts, item) {
package/db/Changes.d.ts CHANGED
@@ -1,7 +1,8 @@
1
- import type { Datas, Key } from "../util/data.js";
1
+ import type { Data, Datas, Key } from "../util/data.js";
2
2
  import { DataUpdate } from "../update/DataUpdate.js";
3
3
  import type { Provider, AsyncProvider } from "../provider/Provider.js";
4
4
  import type { Database, AsyncDatabase } from "./Database.js";
5
+ import { ItemConstraints, ItemData } from "./Item.js";
5
6
  /**
6
7
  * Change set of operations to run against a database in `{ "collection/id": data | DataUpdate | null }` format.
7
8
  * - If data is an object, sets the item.
@@ -9,8 +10,8 @@ import type { Database, AsyncDatabase } from "./Database.js";
9
10
  * - If data is null, deletes the item.
10
11
  * - If data is undefined, skip the item.
11
12
  */
12
- export declare type Changes<DB extends Datas> = {
13
- [K in Key<DB> as `${K}/${string}`]: DB[K] | DataUpdate<DB[K]> | null | undefined;
13
+ export declare type Changes<T extends Datas> = {
14
+ [K in Key<T> as `${K}/${string}`]: T[K] | DataUpdate<T[K]> | null | undefined;
14
15
  };
15
16
  /** Apply a set of changes to a synchronous database. */
16
17
  export declare function changeDatabase<T extends Datas>({ provider }: Database<T>, changes: Changes<T>): Changes<T>;
@@ -20,3 +21,4 @@ export declare function changeAsyncDatabase<T extends Datas>({ provider }: Async
20
21
  export declare function changeProvider<T extends Datas>(provider: Provider<T>, changes: Changes<T>): Changes<T>;
21
22
  /** Apply a set of changes to an asynchronous provider. */
22
23
  export declare function changeAsyncProvider<T extends Datas>(provider: AsyncProvider<T>, changes: Changes<T>): Promise<Changes<T>>;
24
+ export declare function _getItemConstraint<T extends Data>(id: string): ItemConstraints<ItemData<T>>;
package/db/Changes.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { splitString } from "../util/string.js";
2
2
  import { DataUpdate } from "../update/DataUpdate.js";
3
+ import { QueryConstraints } from "../constraint/QueryConstraints.js";
4
+ import { FilterConstraint } from "../constraint/FilterConstraint.js";
3
5
  /** Apply a set of changes to a synchronous database. */
4
6
  export function changeDatabase({ provider }, changes) {
5
7
  return changeProvider(provider, changes);
@@ -17,7 +19,7 @@ export function changeProvider(provider, changes) {
17
19
  else if (change === null)
18
20
  provider.deleteItem(collection, id);
19
21
  else if (change instanceof DataUpdate)
20
- provider.updateItem(collection, id, change);
22
+ provider.updateQuery(collection, _getItemConstraint(id), change);
21
23
  else
22
24
  provider.setItem(collection, id, change);
23
25
  }
@@ -32,9 +34,12 @@ export async function changeAsyncProvider(provider, changes) {
32
34
  else if (change === null)
33
35
  await provider.deleteItem(collection, id);
34
36
  else if (change instanceof DataUpdate)
35
- await provider.updateItem(collection, id, change);
37
+ await provider.updateQuery(collection, _getItemConstraint(id), change);
36
38
  else
37
39
  await provider.setItem(collection, id, change);
38
40
  }
39
41
  return changes;
40
42
  }
43
+ export function _getItemConstraint(id) {
44
+ return new QueryConstraints(new FilterConstraint("id", id), undefined, 1);
45
+ }
@@ -1,11 +1,35 @@
1
- import { DataUpdate } from "../index.js";
1
+ import { AsyncDatabase, DataUpdate } from "../index.js";
2
2
  test("Typescript", () => {
3
- const operations = {
4
- "collection1/a1": { a: 1, one: false },
3
+ const operations1 = {
4
+ "collection1/a1": { one: 1, bool: false },
5
5
  "collection1/b2": null,
6
- "collection1/b3": new DataUpdate({ one: true }),
7
- "collection2/a1": { b: 1, one: false },
6
+ "collection1/b3": new DataUpdate({ bool: true }),
7
+ "collection2/a1": { two: 1, bool: false },
8
8
  "collection2/b2": null,
9
- "collection2/c3": new DataUpdate({ one: true }),
9
+ "collection2/c3": new DataUpdate({ bool: true }),
10
10
  };
11
+ // Check the keys.
12
+ const operations2 = {
13
+ // @ts-expect-error
14
+ "NOTCOLLECTION/a1": { a: 1, bool: false },
15
+ };
16
+ // Check the values.
17
+ const operations3 = {
18
+ "collection1/a1": {
19
+ one: 1,
20
+ // @ts-expect-error
21
+ bool: "NOTBOOL",
22
+ },
23
+ };
24
+ // Check some objects.
25
+ const db = new AsyncDatabase(undefined);
26
+ const changes = {
27
+ ...db.item("collection1", "a1").getUpdate({ bool: true }),
28
+ ...db.item("collection2", "b2").getSet({ two: 123, bool: false }),
29
+ ...db.item("collection2", "b2").getDelete(),
30
+ };
31
+ // Merging changes.
32
+ const changes1 = {};
33
+ const changes2 = {};
34
+ const changes3 = { ...changes1, ...changes2 };
11
35
  });
@@ -1,38 +1,37 @@
1
- import type { QueryProps } from "../constraint/QueryConstraints.js";
2
1
  import type { Datas, Key } from "../util/data.js";
2
+ import type { FilterList } from "../constraint/FilterConstraint.js";
3
+ import type { SortList } from "../constraint/SortConstraint.js";
3
4
  import type { ItemData, AsyncItem, Item } from "./Item.js";
4
5
  import type { AsyncDatabase, Database } from "./Database.js";
5
6
  import type { AsyncQuery, Query } from "./Query.js";
6
7
  /** Reference to a collection in a synchronous or asynchronous provider. */
7
- interface CollectionInterface<T extends Datas = Datas, K extends Key<T> = Key<T>> {
8
- readonly db: Database<T> | AsyncDatabase<T>;
9
- readonly collection: K;
8
+ declare abstract class BaseCollection<T extends Datas = Datas, K extends Key<T> = Key<T>> {
9
+ abstract readonly db: Database<T> | AsyncDatabase<T>;
10
+ abstract readonly collection: K;
10
11
  /** Create a query on this item's collection. */
11
- query(query?: QueryProps<ItemData<T[K]>>): Query<T, K> | AsyncQuery<T, K>;
12
+ abstract query(filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null): Query<T, K> | AsyncQuery<T, K>;
12
13
  /** Create a query on this item's collection. */
13
- item(id: string): Item<T, K> | AsyncItem<T, K>;
14
+ abstract item(id: string): Item<T, K> | AsyncItem<T, K>;
14
15
  /** Add an item to this collection. */
15
- add(data: T[K]): string | Promise<string>;
16
+ abstract add(data: T[K]): string | Promise<string>;
16
17
  toString(): K;
17
18
  }
18
19
  /** Reference to a collection in a synchronous provider. */
19
- export declare class Collection<T extends Datas = Datas, K extends Key<T> = Key<T>> implements CollectionInterface<T, K> {
20
+ export declare class Collection<T extends Datas = Datas, K extends Key<T> = Key<T>> extends BaseCollection<T, K> {
20
21
  readonly db: Database<T>;
21
22
  readonly collection: K;
22
23
  constructor(db: Database<T>, collection: K);
23
- query(query?: QueryProps<ItemData<T[K]>>): Query<T, K>;
24
+ query(filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null): Query<T, K>;
24
25
  item(id: string): Item<T, K>;
25
26
  add(data: T[K]): string;
26
- toString(): K;
27
27
  }
28
28
  /** Reference to a collection in an asynchronous provider. */
29
- export declare class AsyncCollection<T extends Datas = Datas, K extends Key<T> = Key<T>> implements CollectionInterface<T, K> {
29
+ export declare class AsyncCollection<T extends Datas = Datas, K extends Key<T> = Key<T>> extends BaseCollection<T, K> {
30
30
  readonly db: AsyncDatabase<T>;
31
31
  readonly collection: K;
32
32
  constructor(db: AsyncDatabase<T>, collection: K);
33
- query(query?: QueryProps<ItemData<T[K]>>): AsyncQuery<T, K>;
33
+ query(filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null): AsyncQuery<T, K>;
34
34
  item(id: string): AsyncItem<T, K>;
35
35
  add(data: T[K]): Promise<string>;
36
- toString(): K;
37
36
  }
38
37
  export {};
package/db/Collection.js CHANGED
@@ -1,11 +1,19 @@
1
+ /** Reference to a collection in a synchronous or asynchronous provider. */
2
+ class BaseCollection {
3
+ // Implement toString()
4
+ toString() {
5
+ return this.collection;
6
+ }
7
+ }
1
8
  /** Reference to a collection in a synchronous provider. */
2
- export class Collection {
9
+ export class Collection extends BaseCollection {
3
10
  constructor(db, collection) {
11
+ super();
4
12
  this.db = db;
5
13
  this.collection = collection;
6
14
  }
7
- query(query) {
8
- return this.db.query(this.collection, query);
15
+ query(filters, sorts, limit) {
16
+ return this.db.query(this.collection, filters, sorts, limit);
9
17
  }
10
18
  item(id) {
11
19
  return this.db.item(this.collection, id);
@@ -13,18 +21,16 @@ export class Collection {
13
21
  add(data) {
14
22
  return this.db.provider.addItem(this.collection, data);
15
23
  }
16
- toString() {
17
- return this.collection;
18
- }
19
24
  }
20
25
  /** Reference to a collection in an asynchronous provider. */
21
- export class AsyncCollection {
26
+ export class AsyncCollection extends BaseCollection {
22
27
  constructor(db, collection) {
28
+ super();
23
29
  this.db = db;
24
30
  this.collection = collection;
25
31
  }
26
- query(query) {
27
- return this.db.query(this.collection, query);
32
+ query(filters, sorts, limit) {
33
+ return this.db.query(this.collection, filters, sorts, limit);
28
34
  }
29
35
  item(id) {
30
36
  return this.db.item(this.collection, id);
@@ -32,7 +38,4 @@ export class AsyncCollection {
32
38
  add(data) {
33
39
  return this.db.provider.addItem(this.collection, data);
34
40
  }
35
- toString() {
36
- return this.collection;
37
- }
38
41
  }
package/db/Database.d.ts CHANGED
@@ -1,17 +1,18 @@
1
1
  import type { Key, Datas } from "../util/data.js";
2
- import { ItemData } from "../db/Item.js";
3
2
  import type { AsyncProvider, Provider } from "../provider/Provider.js";
4
- import type { QueryProps } from "../constraint/QueryConstraints.js";
3
+ import type { FilterList } from "../constraint/FilterConstraint.js";
4
+ import type { SortList } from "../constraint/SortConstraint.js";
5
+ import type { ItemData } from "../db/Item.js";
5
6
  import { Item, AsyncItem } from "./Item.js";
6
7
  import { Query, AsyncQuery } from "./Query.js";
7
- import { AsyncCollection, Collection } from "./Collection.js";
8
+ import { Collection, AsyncCollection } from "./Collection.js";
8
9
  /** Database with a synchronous or asynchronous provider. */
9
10
  interface AbstractDatabase<T extends Datas> {
10
11
  readonly provider: Provider<T> | AsyncProvider<T>;
11
12
  /** Create a query on a collection in this database. */
12
13
  collection<K extends Key<T>>(collection: K): Collection<T, K> | AsyncCollection<T, K>;
13
14
  /** Create a query on a collection in this database. */
14
- query<K extends Key<T>>(collection: K, query?: QueryProps<ItemData<T[K]>>): Query<T, K> | AsyncQuery<T, K>;
15
+ query<K extends Key<T>>(collection: K, filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null): Query<T, K> | AsyncQuery<T, K>;
15
16
  /** Reference an item in a collection in this database. */
16
17
  item<K extends Key<T>>(collection: K, id: string): Item<T, K> | AsyncItem<T, K>;
17
18
  }
@@ -20,7 +21,7 @@ export declare class Database<T extends Datas = Datas> implements AbstractDataba
20
21
  readonly provider: Provider<T>;
21
22
  constructor(provider: Provider<T>);
22
23
  collection<K extends Key<T>>(collection: K): Collection<T, K>;
23
- query<K extends Key<T>>(collection: K, query?: QueryProps<ItemData<T[K]>>): Query<T, K>;
24
+ query<K extends Key<T>>(collection: K, filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null): Query<T, K>;
24
25
  item<K extends Key<T>>(collection: K, id: string): Item<T, K>;
25
26
  }
26
27
  /** Database with a synchronous provider. */
@@ -28,7 +29,7 @@ export declare class AsyncDatabase<T extends Datas = Datas> implements AbstractD
28
29
  readonly provider: AsyncProvider<T>;
29
30
  constructor(provider: AsyncProvider<T>);
30
31
  collection<K extends Key<T>>(collection: K): AsyncCollection<T, K>;
31
- query<K extends Key<T>>(collection: K, query?: QueryProps<ItemData<T[K]>>): AsyncQuery<T, K>;
32
+ query<K extends Key<T>>(collection: K, filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null): AsyncQuery<T, K>;
32
33
  item<K extends Key<T>>(collection: K, id: string): AsyncItem<T, K>;
33
34
  }
34
35
  export {};
package/db/Database.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Item, AsyncItem } from "./Item.js";
2
2
  import { Query, AsyncQuery } from "./Query.js";
3
- import { AsyncCollection, Collection } from "./Collection.js";
3
+ import { Collection, AsyncCollection } from "./Collection.js";
4
4
  /** Database with a synchronous provider. */
5
5
  export class Database {
6
6
  constructor(provider) {
@@ -9,8 +9,8 @@ export class Database {
9
9
  collection(collection) {
10
10
  return new Collection(this, collection);
11
11
  }
12
- query(collection, query) {
13
- return new Query(this, collection, query);
12
+ query(collection, filters, sorts, limit) {
13
+ return new Query(this, collection, filters, sorts, limit);
14
14
  }
15
15
  item(collection, id) {
16
16
  return new Item(this, collection, id);
@@ -24,8 +24,8 @@ export class AsyncDatabase {
24
24
  collection(collection) {
25
25
  return new AsyncCollection(this, collection);
26
26
  }
27
- query(collection, query) {
28
- return new AsyncQuery(this, collection, query);
27
+ query(collection, filters, sorts, limit) {
28
+ return new AsyncQuery(this, collection, filters, sorts, limit);
29
29
  }
30
30
  item(collection, id) {
31
31
  return new AsyncItem(this, collection, id);
package/db/Item.d.ts CHANGED
@@ -7,6 +7,7 @@ import { DataUpdate, PropUpdates } from "../update/DataUpdate.js";
7
7
  import type { QueryConstraints } from "../constraint/QueryConstraints.js";
8
8
  import type { AsyncDatabase, Database } from "./Database.js";
9
9
  import type { AsyncQuery, Query } from "./Query.js";
10
+ import type { Changes } from "./Changes.js";
10
11
  /** Item data with a string ID that uniquely identifies it. */
11
12
  export declare type ItemData<T extends Data = Data> = T & {
12
13
  id: string;
@@ -18,21 +19,22 @@ export declare type ItemArray<T extends Data = Data> = ImmutableArray<ItemData<T
18
19
  /** A set of query constraints for item data. */
19
20
  export declare type ItemConstraints<T extends Data = Data> = QueryConstraints<ItemData<T>>;
20
21
  /** Reference to an item in a synchronous or asynchronous provider. */
21
- interface ItemInterface<T extends Datas = Datas, K extends Key<T> = Key<T>> extends Observable<ItemValue<T[K]>> {
22
- readonly collection: K;
23
- readonly id: string;
22
+ declare abstract class BaseItem<T extends Datas = Datas, K extends Key<T> = Key<T>> implements Observable<ItemValue<T[K]>> {
23
+ abstract readonly db: Database<T> | AsyncDatabase<T>;
24
+ abstract readonly collection: K;
25
+ abstract readonly id: string;
24
26
  /** Get an 'optional' reference to this item (uses a `ModelQuery` with an `id` filter). */
25
- optional: Query<T, K> | AsyncQuery<T, K>;
27
+ abstract optional: Query<T, K> | AsyncQuery<T, K>;
26
28
  /**
27
29
  * Does this item exist?
28
30
  * @return `true` if an item exists or `false` otherwise (possibly promised).
29
31
  */
30
- exists: boolean | PromiseLike<boolean>;
32
+ abstract exists: boolean | PromiseLike<boolean>;
31
33
  /**
32
34
  * Get the optional data of this item.
33
35
  * @return Document's data, or `null` if the item doesn't exist (possibly promised).
34
36
  */
35
- value: ItemValue<T[K]> | PromiseLike<ItemValue<T[K]>>;
37
+ abstract value: ItemValue<T[K]> | PromiseLike<ItemValue<T[K]>>;
36
38
  /**
37
39
  * Get the data of this item.
38
40
  * - Useful for destructuring, e.g. `{ name, title } = await itemThatMustExist.asyncData`
@@ -40,7 +42,7 @@ interface ItemInterface<T extends Datas = Datas, K extends Key<T> = Key<T>> exte
40
42
  * @return Document's data (possibly promised).
41
43
  * @throws RequiredError if the item does not exist.
42
44
  */
43
- data: ItemData<T[K]> | PromiseLike<ItemData<T[K]>>;
45
+ abstract data: ItemData<T[K]> | PromiseLike<ItemData<T[K]>>;
44
46
  /**
45
47
  * Subscribe to the result of this item (indefinitely).
46
48
  * - `next()` is called once with the initial result, and again any time the result changes.
@@ -50,15 +52,21 @@ interface ItemInterface<T extends Datas = Datas, K extends Key<T> = Key<T>> exte
50
52
  */
51
53
  subscribe(next: PartialObserver<ItemValue<T[K]>> | Dispatch<[ItemValue<T[K]>]>): Unsubscribe;
52
54
  /** Set the complete data of this item. */
53
- set(data: T[K]): void | PromiseLike<void>;
55
+ abstract set(data: T[K]): void | PromiseLike<void>;
56
+ /** Get a set change for this item. */
57
+ getSet(data: T[K]): Changes<T>;
54
58
  /** Update this item. */
55
- update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): void | PromiseLike<void>;
59
+ abstract update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): void | PromiseLike<void>;
60
+ /** Get an update change for this item. */
61
+ getUpdate(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): Changes<T>;
56
62
  /** Delete this item. */
57
- delete(): void | PromiseLike<void>;
63
+ abstract delete(): void | PromiseLike<void>;
64
+ /** Get a delete change for this item. */
65
+ getDelete(): Changes<T>;
58
66
  toString(): `${K}/${string}`;
59
67
  }
60
68
  /** Reference to an item in a synchronous provider. */
61
- export declare class Item<T extends Datas = Datas, K extends Key<T> = Key<T>> implements ItemInterface<T, K> {
69
+ export declare class Item<T extends Datas = Datas, K extends Key<T> = Key<T>> extends BaseItem<T, K> {
62
70
  readonly db: Database<T>;
63
71
  readonly collection: K;
64
72
  readonly id: string;
@@ -67,14 +75,12 @@ export declare class Item<T extends Datas = Datas, K extends Key<T> = Key<T>> im
67
75
  get exists(): boolean;
68
76
  get value(): ItemValue<T[K]>;
69
77
  get data(): ItemData<T[K]>;
70
- subscribe(next: PartialObserver<ItemValue<T[K]>> | Dispatch<[ItemValue<T[K]>]>): Unsubscribe;
71
78
  set(data: T[K]): void;
72
79
  update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): void;
73
80
  delete(): void;
74
- toString(): `${K}/${string}`;
75
81
  }
76
82
  /** Reference to an item in an asynchronous provider. */
77
- export declare class AsyncItem<T extends Datas = Datas, K extends Key<T> = Key<T>> implements ItemInterface<T, K> {
83
+ export declare class AsyncItem<T extends Datas = Datas, K extends Key<T> = Key<T>> extends BaseItem<T, K> {
78
84
  readonly db: AsyncDatabase<T>;
79
85
  readonly collection: K;
80
86
  readonly id: string;
@@ -83,11 +89,9 @@ export declare class AsyncItem<T extends Datas = Datas, K extends Key<T> = Key<T
83
89
  get exists(): Promise<boolean>;
84
90
  get value(): Promise<ItemValue<T[K]>>;
85
91
  get data(): Promise<ItemData<T[K]>>;
86
- subscribe(next: PartialObserver<ItemValue<T[K]>> | Dispatch<[ItemValue<T[K]>]>): Unsubscribe;
87
92
  set(data: T[K]): Promise<void>;
88
93
  update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): Promise<void>;
89
94
  delete(): Promise<void>;
90
- toString(): `${K}/${string}`;
91
95
  }
92
96
  /** Get the ID from item data. */
93
97
  export declare const getID: <T extends Data>({ id }: ItemData<T>) => string;
package/db/Item.js CHANGED
@@ -1,14 +1,46 @@
1
1
  import { getData } from "../util/data.js";
2
+ import { FilterConstraint } from "../constraint/FilterConstraint.js";
2
3
  import { DataUpdate } from "../update/DataUpdate.js";
4
+ import { isTransformable } from "../util/transform.js";
5
+ /** Reference to an item in a synchronous or asynchronous provider. */
6
+ class BaseItem {
7
+ /**
8
+ * Subscribe to the result of this item (indefinitely).
9
+ * - `next()` is called once with the initial result, and again any time the result changes.
10
+ *
11
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
12
+ * @return Function that ends the subscription.
13
+ */
14
+ subscribe(next) {
15
+ return this.db.provider.subscribeItem(this.collection, this.id, typeof next === "function" ? { next } : next);
16
+ }
17
+ /** Get a set change for this item. */
18
+ getSet(data) {
19
+ return { [this.toString()]: data };
20
+ }
21
+ /** Get an update change for this item. */
22
+ getUpdate(updates) {
23
+ return { [this.toString()]: updates instanceof DataUpdate ? updates : new DataUpdate(updates) };
24
+ }
25
+ /** Get a delete change for this item. */
26
+ getDelete() {
27
+ return { [this.toString()]: null };
28
+ }
29
+ // Implement toString()
30
+ toString() {
31
+ return `${this.collection}/${this.id}`;
32
+ }
33
+ }
3
34
  /** Reference to an item in a synchronous provider. */
4
- export class Item {
35
+ export class Item extends BaseItem {
5
36
  constructor(db, collection, id) {
37
+ super();
6
38
  this.db = db;
7
39
  this.collection = collection;
8
40
  this.id = id;
9
41
  }
10
42
  get optional() {
11
- return this.db.query(this.collection, { filter: { id: this.id }, limit: 1 });
43
+ return this.db.query(this.collection, new FilterConstraint("id", this.id), undefined, 1);
12
44
  }
13
45
  get exists() {
14
46
  return !!this.db.provider.getItem(this.collection, this.id);
@@ -19,31 +51,26 @@ export class Item {
19
51
  get data() {
20
52
  return getData(this.value);
21
53
  }
22
- subscribe(next) {
23
- return this.db.provider.subscribeItem(this.collection, this.id, typeof next === "function" ? { next } : next);
24
- }
25
54
  set(data) {
26
55
  return this.db.provider.setItem(this.collection, this.id, data);
27
56
  }
28
57
  update(updates) {
29
- return this.db.provider.updateItem(this.collection, this.id, updates instanceof DataUpdate ? updates : new DataUpdate(updates));
58
+ return this.db.provider.updateItem(this.collection, this.id, isTransformable(updates) ? updates : new DataUpdate(updates));
30
59
  }
31
60
  delete() {
32
61
  return this.db.provider.deleteItem(this.collection, this.id);
33
62
  }
34
- toString() {
35
- return `${this.collection}/${this.id}`;
36
- }
37
63
  }
38
64
  /** Reference to an item in an asynchronous provider. */
39
- export class AsyncItem {
65
+ export class AsyncItem extends BaseItem {
40
66
  constructor(provider, collection, id) {
67
+ super();
41
68
  this.db = provider;
42
69
  this.collection = collection;
43
70
  this.id = id;
44
71
  }
45
72
  get optional() {
46
- return this.db.query(this.collection, { filter: { id: this.id }, limit: 1 });
73
+ return this.db.query(this.collection, new FilterConstraint("id", this.id), undefined, 1);
47
74
  }
48
75
  get exists() {
49
76
  return this.db.provider.getItem(this.collection, this.id).then(Boolean);
@@ -54,21 +81,15 @@ export class AsyncItem {
54
81
  get data() {
55
82
  return this.value.then(getData);
56
83
  }
57
- subscribe(next) {
58
- return this.db.provider.subscribeItem(this.collection, this.id, typeof next === "function" ? { next } : next);
59
- }
60
84
  set(data) {
61
85
  return this.db.provider.setItem(this.collection, this.id, data);
62
86
  }
63
87
  update(updates) {
64
- return this.db.provider.updateItem(this.collection, this.id, updates instanceof DataUpdate ? updates : new DataUpdate(updates));
88
+ return this.db.provider.updateItem(this.collection, this.id, isTransformable(updates) ? updates : new DataUpdate(updates));
65
89
  }
66
90
  delete() {
67
91
  return this.db.provider.deleteItem(this.collection, this.id);
68
92
  }
69
- toString() {
70
- return `${this.collection}/${this.id}`;
71
- }
72
93
  }
73
94
  /** Get the ID from item data. */
74
95
  export const getID = ({ id }) => id;
package/db/Query.d.ts CHANGED
@@ -2,48 +2,51 @@ import type { Dispatch } from "../util/function.js";
2
2
  import type { Key, Datas } from "../util/data.js";
3
3
  import type { PartialObserver } from "../observe/Observer.js";
4
4
  import type { Observable, Unsubscribe } from "../observe/Observable.js";
5
- import { QueryConstraints, QueryProps } from "../constraint/QueryConstraints.js";
5
+ import type { FilterList } from "../constraint/FilterConstraint.js";
6
+ import type { SortList } from "../constraint/SortConstraint.js";
7
+ import { QueryConstraints } from "../constraint/QueryConstraints.js";
6
8
  import { DataUpdate, PropUpdates } from "../update/DataUpdate.js";
7
9
  import type { ItemArray, ItemValue, ItemData } from "./Item.js";
8
10
  import type { AsyncDatabase, Database } from "./Database.js";
9
11
  /** Reference to a set of items in a sync or async provider. */
10
- interface QueryInterface<T extends Datas = Datas, K extends Key<T> = Key<T>> extends QueryConstraints<ItemData<T[K]>>, Observable<ItemArray<T[K]>> {
11
- readonly collection: K;
12
+ declare abstract class BaseQuery<T extends Datas = Datas, K extends Key<T> = Key<T>> extends QueryConstraints<ItemData<T[K]>> implements Observable<ItemArray<T[K]>> {
13
+ abstract readonly db: Database<T> | AsyncDatabase<T>;
14
+ abstract readonly collection: K;
12
15
  /**
13
16
  * Get array of entities for this query.
14
17
  * @return Array of entities.
15
18
  */
16
- value: ItemArray<T[K]> | PromiseLike<ItemArray<T[K]>>;
19
+ abstract value: ItemArray<T[K]> | PromiseLike<ItemArray<T[K]>>;
17
20
  /**
18
21
  * Count the number of results of this set of items.
19
22
  * @return Number of items matching the query (possibly promised).
20
23
  */
21
- count: number | PromiseLike<number>;
24
+ abstract count: number | PromiseLike<number>;
22
25
  /**
23
26
  * Does at least one item exist for this query?
24
27
  * @return `true` if a item exists or `false` otherwise (possibly promised).
25
28
  */
26
- exists: boolean | PromiseLike<boolean>;
29
+ abstract exists: boolean | PromiseLike<boolean>;
27
30
  /**
28
31
  * Get the first item matched by this query or `null` if this query has no results.
29
32
  * @throws RequiredError if there were no results for this query.
30
33
  */
31
- firstValue: ItemValue<T[K]> | PromiseLike<ItemValue<T[K]>>;
34
+ abstract firstValue: ItemValue<T[K]> | PromiseLike<ItemValue<T[K]>>;
32
35
  /**
33
36
  * Get the first item matched by this query.
34
37
  * @throws RequiredError if there were no results for this query.
35
38
  */
36
- firstData: ItemData<T[K]> | PromiseLike<ItemData<T[K]>>;
39
+ abstract firstData: ItemData<T[K]> | PromiseLike<ItemData<T[K]>>;
37
40
  /**
38
41
  * Get the last item matched by this query or `null` if this query has no results.
39
42
  * @throws RequiredError if there were no results for this query.
40
43
  */
41
- lastValue: ItemValue<T[K]> | PromiseLike<ItemValue<T[K]>>;
44
+ abstract lastValue: ItemValue<T[K]> | PromiseLike<ItemValue<T[K]>>;
42
45
  /**
43
46
  * Get the last item matched by this query.
44
47
  * @throws RequiredError if there were no results for this query.
45
48
  */
46
- lastData: ItemData<T[K]> | PromiseLike<ItemData<T[K]>>;
49
+ abstract lastData: ItemData<T[K]> | PromiseLike<ItemData<T[K]>>;
47
50
  /**
48
51
  * Subscribe to all matching items.
49
52
  * - `next()` is called once with the initial results, and again any time the results change.
@@ -58,25 +61,26 @@ interface QueryInterface<T extends Datas = Datas, K extends Key<T> = Key<T>> ext
58
61
  * @param data Complete data to set the item to.
59
62
  * @return Nothing (possibly promised).
60
63
  */
61
- set(data: T[K]): number | PromiseLike<number>;
64
+ abstract set(data: T[K]): number | PromiseLike<number>;
62
65
  /**
63
66
  * Update all matching items with the same partial value.
64
67
  *
65
68
  * @param updates `Update` instance or set of updates to apply to every matching item.
66
69
  * @return Nothing (possibly promised).
67
70
  */
68
- update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): number | PromiseLike<number>;
71
+ abstract update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): number | PromiseLike<number>;
69
72
  /**
70
73
  * Delete all matching items.
71
74
  * @return Nothing (possibly promised).
72
75
  */
73
- delete(): number | PromiseLike<number>;
76
+ abstract delete(): number | PromiseLike<number>;
77
+ toString(): string;
74
78
  }
75
79
  /** Reference to a set of items in a provider. */
76
- export declare class Query<T extends Datas = Datas, K extends Key<T> = Key<T>> extends QueryConstraints<ItemData<T[K]>> implements QueryInterface<T, K> {
80
+ export declare class Query<T extends Datas = Datas, K extends Key<T> = Key<T>> extends BaseQuery<T, K> implements BaseQuery<T, K> {
77
81
  readonly db: Database<T>;
78
82
  readonly collection: K;
79
- constructor(db: Database<T>, collection: K, props?: QueryProps<ItemData<T[K]>>);
83
+ constructor(db: Database<T>, collection: K, filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null);
80
84
  get value(): ItemArray<T[K]>;
81
85
  get count(): number;
82
86
  get exists(): boolean;
@@ -84,18 +88,15 @@ export declare class Query<T extends Datas = Datas, K extends Key<T> = Key<T>> e
84
88
  get firstData(): ItemData<T[K]>;
85
89
  get lastValue(): ItemValue<T[K]>;
86
90
  get lastData(): ItemData<T[K]>;
87
- subscribe(next: PartialObserver<ItemArray<T[K]>> | Dispatch<[ItemArray<T[K]>]>): Unsubscribe;
88
91
  set(data: T[K]): number;
89
92
  update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): number;
90
93
  delete(): number;
91
- toString(): string;
92
94
  }
93
95
  /** Reference to a set of items in a provider. */
94
- export declare class AsyncQuery<T extends Datas = Datas, K extends Key<T> = Key<T>> extends QueryConstraints<ItemData<T[K]>> implements QueryInterface<T, K> {
96
+ export declare class AsyncQuery<T extends Datas = Datas, K extends Key<T> = Key<T>> extends BaseQuery<T, K> implements BaseQuery<T, K> {
95
97
  readonly db: AsyncDatabase<T>;
96
98
  readonly collection: K;
97
- constructor(db: AsyncDatabase<T>, collection: K, props?: QueryProps<ItemData<T[K]>>);
98
- closed?: boolean;
99
+ constructor(db: AsyncDatabase<T>, collection: K, filters?: FilterList<ItemData<T[K]>>, sorts?: SortList<ItemData<T[K]>>, limit?: number | null);
99
100
  get value(): Promise<ItemArray<T[K]>>;
100
101
  get count(): Promise<number>;
101
102
  get exists(): Promise<boolean>;
@@ -103,10 +104,8 @@ export declare class AsyncQuery<T extends Datas = Datas, K extends Key<T> = Key<
103
104
  get firstData(): Promise<ItemData<T[K]>>;
104
105
  get lastValue(): Promise<ItemValue<T[K]>>;
105
106
  get lastData(): Promise<ItemData<T[K]>>;
106
- subscribe(next: PartialObserver<ItemArray<T[K]>> | Dispatch<[ItemArray<T[K]>]>): Unsubscribe;
107
107
  set(data: T[K]): Promise<number>;
108
108
  update(updates: DataUpdate<T[K]> | PropUpdates<T[K]>): PromiseLike<number>;
109
109
  delete(): PromiseLike<number>;
110
- toString(): string;
111
110
  }
112
111
  export {};
package/db/Query.js CHANGED
@@ -2,10 +2,27 @@ import { getFirstItem, getLastItem, getOptionalFirstItem, getOptionalLastItem }
2
2
  import { QueryConstraints } from "../constraint/QueryConstraints.js";
3
3
  import { countItems, hasItems } from "../util/iterate.js";
4
4
  import { DataUpdate } from "../update/DataUpdate.js";
5
+ /** Reference to a set of items in a sync or async provider. */
6
+ class BaseQuery extends QueryConstraints {
7
+ /**
8
+ * Subscribe to all matching items.
9
+ * - `next()` is called once with the initial results, and again any time the results change.
10
+ *
11
+ * @param next Observer with `next`, `error`, or `complete` methods or a `next()` dispatcher.
12
+ * @return Function that ends the subscription.
13
+ */
14
+ subscribe(next) {
15
+ return this.db.provider.subscribeQuery(this.collection, this, typeof next === "function" ? { next } : next);
16
+ }
17
+ // Override to include the collection name.
18
+ toString() {
19
+ return `${this.collection}?${super.toString()}`;
20
+ }
21
+ }
5
22
  /** Reference to a set of items in a provider. */
6
- export class Query extends QueryConstraints {
7
- constructor(db, collection, props) {
8
- super(props);
23
+ export class Query extends BaseQuery {
24
+ constructor(db, collection, filters, sorts, limit) {
25
+ super(filters, sorts, limit);
9
26
  this.db = db;
10
27
  this.collection = collection;
11
28
  }
@@ -30,9 +47,6 @@ export class Query extends QueryConstraints {
30
47
  get lastData() {
31
48
  return getLastItem(this.value);
32
49
  }
33
- subscribe(next) {
34
- return this.db.provider.subscribeQuery(this.collection, this, typeof next === "function" ? { next } : next);
35
- }
36
50
  set(data) {
37
51
  return this.db.provider.setQuery(this.collection, this, data);
38
52
  }
@@ -42,14 +56,11 @@ export class Query extends QueryConstraints {
42
56
  delete() {
43
57
  return this.db.provider.deleteQuery(this.collection, this);
44
58
  }
45
- toString() {
46
- return `${this.collection}?${super.toString()}`;
47
- }
48
59
  }
49
60
  /** Reference to a set of items in a provider. */
50
- export class AsyncQuery extends QueryConstraints {
51
- constructor(db, collection, props) {
52
- super(props);
61
+ export class AsyncQuery extends BaseQuery {
62
+ constructor(db, collection, filters, sorts, limit) {
63
+ super(filters, sorts, limit);
53
64
  this.db = db;
54
65
  this.collection = collection;
55
66
  }
@@ -74,9 +85,6 @@ export class AsyncQuery extends QueryConstraints {
74
85
  get lastData() {
75
86
  return this.value.then(getLastItem);
76
87
  }
77
- subscribe(next) {
78
- return this.db.provider.subscribeQuery(this.collection, this, typeof next === "function" ? { next } : next);
79
- }
80
88
  set(data) {
81
89
  return this.db.provider.setQuery(this.collection, this, data);
82
90
  }
@@ -86,7 +94,4 @@ export class AsyncQuery extends QueryConstraints {
86
94
  delete() {
87
95
  return this.db.provider.deleteQuery(this.collection, this);
88
96
  }
89
- toString() {
90
- return `${this.collection}?${super.toString()}`;
91
- }
92
97
  }
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "state-management",
12
12
  "query-builder"
13
13
  ],
14
- "version": "1.73.1",
14
+ "version": "1.74.0",
15
15
  "repository": "https://github.com/dhoulb/shelving",
16
16
  "author": "Dave Houlbrooke <dave@shax.com>",
17
17
  "license": "0BSD",
@@ -72,4 +72,4 @@ export class BatchProvider extends AsyncThroughProvider {
72
72
  }
73
73
  }
74
74
  const _getItemKey = (collection, id) => `${collection}/${id}`;
75
- const _getQueryKey = (collection, constraints) => `${collection}:${QueryConstraints.prototype.toString.call(constraints)}`;
75
+ const _getQueryKey = (collection, constraints) => `${collection}?${QueryConstraints.prototype.toString.call(constraints)}`;
@@ -238,4 +238,4 @@ export class AsyncDebugProvider extends AbstractDebugProvider {
238
238
  }
239
239
  }
240
240
  const _getItemKey = (collection, id) => `${collection}/${id}`;
241
- const _getQueryKey = (collection, constraints) => `${collection}:${QueryConstraints.prototype.toString.call(constraints)}`;
241
+ const _getQueryKey = (collection, constraints) => `${collection}?${QueryConstraints.prototype.toString.call(constraints)}`;
@@ -223,4 +223,4 @@ export class MemoryTable extends Subject {
223
223
  // When we're writing data, if there's no limit set the results don't need to be sorted (for performance).
224
224
  const _getWriteConstraints = (constraints) => (constraints.limit ? constraints : constraints.filters);
225
225
  // Queries that have no limit don't care about sorting either.
226
- const _getQueryKey = (query) => (query.limit ? `{filters:${query.filters.toString()}}` : QueryConstraints.prototype.toString.call(query));
226
+ const _getQueryKey = (query) => (query.limit ? `"filters":${query.filters.toString()}}` : QueryConstraints.prototype.toString.call(query));
package/util/index.d.ts CHANGED
@@ -28,6 +28,7 @@ export * from "./random.js";
28
28
  export * from "./regexp.js";
29
29
  export * from "./serialise.js";
30
30
  export * from "./sort.js";
31
+ export * from "./source.js";
31
32
  export * from "./string.js";
32
33
  export * from "./template.js";
33
34
  export * from "./timeout.js";
package/util/index.js CHANGED
@@ -28,6 +28,7 @@ export * from "./random.js";
28
28
  export * from "./regexp.js";
29
29
  export * from "./serialise.js";
30
30
  export * from "./sort.js";
31
+ export * from "./source.js";
31
32
  export * from "./string.js";
32
33
  export * from "./template.js";
33
34
  export * from "./timeout.js";
@@ -6,7 +6,7 @@ import type { ArrayType, ImmutableArray } from "./array.js";
6
6
  export interface Transformable<I, O> {
7
7
  transform(input: I): O;
8
8
  }
9
- /** Is an unknown value a derivable. */
9
+ /** Is an unknown value a transformable. */
10
10
  export declare const isTransformable: <T extends Transformable<unknown, unknown>>(v: unknown) => v is T;
11
11
  /** Function that can transform an input value into an output value. */
12
12
  export declare type Transform<I, O> = (input: I) => O;
package/util/transform.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { isFunction } from "./function.js";
2
2
  import { getProps, isData } from "./data.js";
3
3
  import { getEntries } from "./entry.js";
4
- /** Is an unknown value a derivable. */
4
+ /** Is an unknown value a transformable. */
5
5
  export const isTransformable = (v) => isData(v) && typeof v.transform === "function";
6
6
  export function transform(input, transformer) {
7
7
  return isFunction(transformer) ? transformer(input) : isTransformable(transformer) ? transformer.transform(input) : transformer;