shelving 1.181.2 → 1.182.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.
- package/api/cache/APICache.d.ts +1 -0
- package/api/cache/APICache.js +8 -6
- package/api/cache/EndpointCache.d.ts +5 -6
- package/api/cache/EndpointCache.js +7 -13
- package/api/endpoint/Endpoint.d.ts +1 -1
- package/api/index.d.ts +0 -1
- package/api/index.js +0 -1
- package/api/provider/APIProvider.d.ts +39 -4
- package/api/provider/APIProvider.js +76 -0
- package/api/provider/MockAPIProvider.d.ts +5 -5
- package/api/provider/MockAPIProvider.js +2 -2
- package/api/provider/ThroughAPIProvider.d.ts +6 -0
- package/api/provider/ThroughAPIProvider.js +15 -0
- package/bun/BunPostgreSQLProvider.d.ts +3 -2
- package/cloudflare/CloudflareD1Provider.d.ts +3 -2
- package/cloudflare/CloudflareKVProvider.d.ts +16 -18
- package/cloudflare/CloudflareKVProvider.js +11 -13
- package/db/cache/CollectionCache.d.ts +37 -0
- package/db/cache/CollectionCache.js +62 -0
- package/db/cache/DBCache.d.ts +38 -0
- package/db/cache/DBCache.js +59 -0
- package/db/collection/Collection.d.ts +12 -10
- package/db/index.d.ts +2 -0
- package/db/index.js +2 -0
- package/db/provider/CacheDBProvider.d.ts +18 -18
- package/db/provider/ChangesDBProvider.d.ts +11 -11
- package/db/provider/DBProvider.d.ts +17 -17
- package/db/provider/DBProvider.js +2 -2
- package/db/provider/DebugDBProvider.d.ts +15 -15
- package/db/provider/MemoryDBProvider.d.ts +28 -26
- package/db/provider/MemoryDBProvider.js +10 -5
- package/db/provider/MockDBProvider.d.ts +17 -17
- package/db/provider/PostgreSQLProvider.d.ts +4 -2
- package/db/provider/PostgreSQLProvider.js +1 -1
- package/db/provider/SQLProvider.d.ts +23 -23
- package/db/provider/SQLProvider.js +24 -23
- package/db/provider/SQLiteProvider.d.ts +6 -2
- package/db/provider/SQLiteProvider.js +14 -1
- package/db/provider/ThroughDBProvider.d.ts +19 -19
- package/db/provider/ValidationDBProvider.d.ts +14 -14
- package/db/provider/ValidationDBProvider.js +4 -8
- package/db/store/QueryStore.d.ts +3 -3
- package/firestore/client/FirestoreClientProvider.d.ts +21 -15
- package/firestore/client/FirestoreClientProvider.js +40 -37
- package/firestore/lite/FirestoreLiteProvider.d.ts +21 -15
- package/firestore/lite/FirestoreLiteProvider.js +38 -29
- package/firestore/server/FirestoreServerProvider.d.ts +21 -15
- package/firestore/server/FirestoreServerProvider.js +67 -71
- package/package.json +1 -1
- package/react/createAPIContext.d.ts +4 -2
- package/react/createAPIContext.js +22 -15
- package/react/createDBContext.d.ts +31 -0
- package/react/createDBContext.js +35 -0
- package/react/index.d.ts +1 -2
- package/react/index.js +1 -2
- package/util/http.d.ts +3 -2
- package/util/http.js +6 -6
- package/util/item.d.ts +4 -4
- package/util/query.d.ts +1 -4
- package/util/uri.d.ts +9 -4
- package/util/uri.js +4 -0
- package/api/provider/ClientAPIProvider.d.ts +0 -37
- package/api/provider/ClientAPIProvider.js +0 -51
- package/react/createCacheContext.d.ts +0 -13
- package/react/createCacheContext.js +0 -22
- package/react/createDataContext.d.ts +0 -26
- package/react/createDataContext.js +0 -31
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ImmutableArray } from "../../util/array.js";
|
|
2
2
|
import type { Data, DataPath } from "../../util/data.js";
|
|
3
|
-
import type { Items, OptionalItem } from "../../util/item.js";
|
|
4
|
-
import { type
|
|
3
|
+
import type { Identifier, Item, Items, OptionalItem } from "../../util/item.js";
|
|
4
|
+
import { type Query, type QueryFilter, type QueryOrder } from "../../util/query.js";
|
|
5
5
|
import { type Update, type Updates } from "../../util/update.js";
|
|
6
6
|
import type { Collection } from "../collection/Collection.js";
|
|
7
7
|
import { DBProvider } from "./DBProvider.js";
|
|
@@ -11,20 +11,20 @@ export interface SQLFragment {
|
|
|
11
11
|
readonly values: ImmutableArray<unknown>;
|
|
12
12
|
}
|
|
13
13
|
/** Shared SQL execution and CRUD/query behavior. */
|
|
14
|
-
export declare abstract class SQLProvider extends DBProvider<
|
|
15
|
-
abstract exec<
|
|
16
|
-
getItem<
|
|
17
|
-
getItemSequence<
|
|
18
|
-
addItem<
|
|
19
|
-
setItem<
|
|
20
|
-
updateItem<
|
|
21
|
-
deleteItem<
|
|
22
|
-
countQuery<
|
|
23
|
-
getQuery<
|
|
24
|
-
getQuerySequence<
|
|
25
|
-
setQuery<
|
|
26
|
-
updateQuery<
|
|
27
|
-
deleteQuery<
|
|
14
|
+
export declare abstract class SQLProvider<I extends Identifier = Identifier, T extends Data = Data> extends DBProvider<I, T> {
|
|
15
|
+
abstract exec<X extends Data>(strings: TemplateStringsArray, ...values: ImmutableArray<unknown>): Promise<ImmutableArray<X>>;
|
|
16
|
+
getItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): Promise<OptionalItem<II, TT>>;
|
|
17
|
+
getItemSequence<II extends I, TT extends T>(_collection: Collection<string, II, TT>, _id: II): AsyncIterable<OptionalItem<II, TT>>;
|
|
18
|
+
addItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, data: TT): Promise<II>;
|
|
19
|
+
setItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II, data: TT): Promise<void>;
|
|
20
|
+
updateItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II, updates: Updates<Item<II, TT>>): Promise<void>;
|
|
21
|
+
deleteItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): Promise<void>;
|
|
22
|
+
countQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<number>;
|
|
23
|
+
getQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<Items<II, TT>>;
|
|
24
|
+
getQuerySequence<II extends I, TT extends T>(_collection: Collection<string, II, TT>, _query?: Query<Item<II, TT>>): AsyncIterable<Items<II, TT>>;
|
|
25
|
+
setQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, data: TT): Promise<void>;
|
|
26
|
+
updateQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, updates: Updates<TT>): Promise<void>;
|
|
27
|
+
deleteQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<void>;
|
|
28
28
|
/**
|
|
29
29
|
* Define an SQL fragment using Javascript template literal format.
|
|
30
30
|
* @example this.sql`SELECT * FROM ${table}`; // SQLFragment
|
|
@@ -37,9 +37,9 @@ export declare abstract class SQLProvider extends DBProvider<number> {
|
|
|
37
37
|
/** Define an SQL fragment to generate a series of values with a separator, e.g. `"a" = 1 AND "b" = 2` */
|
|
38
38
|
sqlConcat(values: ImmutableArray<SQLFragment>, separator?: string, before?: string, after?: string): SQLFragment;
|
|
39
39
|
/** Define an SQL fragment for setting a list of values, e.g. `"a" = 1, "b" = 2` */
|
|
40
|
-
sqlSetters<
|
|
40
|
+
sqlSetters<TT extends Data>(data: TT): SQLFragment;
|
|
41
41
|
/** Define an SQL fragment for updates, e.g. `"a" = 1, "b" = "b" + 5` */
|
|
42
|
-
sqlUpdates<
|
|
42
|
+
sqlUpdates<TT extends Data>(updates: Updates<TT>): SQLFragment;
|
|
43
43
|
/**
|
|
44
44
|
* Define an SQL fragment for a single update action.
|
|
45
45
|
* - Handles flat `set` and `sum` only (single-segment key).
|
|
@@ -48,11 +48,11 @@ export declare abstract class SQLProvider extends DBProvider<number> {
|
|
|
48
48
|
*/
|
|
49
49
|
sqlUpdate({ action, key, value }: Update): SQLFragment;
|
|
50
50
|
/** Define an SQL fragment for `VALUES` syntax, e.g. `("a", "b") VALUES (1, 2)` */
|
|
51
|
-
sqlValues
|
|
51
|
+
sqlValues(data: Data): SQLFragment;
|
|
52
52
|
/** Define an SQL for the `WHERE`, `ORDER BY` and `LIMIT` clauses of an SQL query, e.g. e.g. ` WHERE x = 1 ORDER BY "name" LIMIT 0, 50` */
|
|
53
|
-
sqlClauses
|
|
53
|
+
sqlClauses(query: Query<Item>): SQLFragment;
|
|
54
54
|
/** Define an SQL fragment for a `WHERE` clause, e.g. ` WHERE x = 1 AND y <= 100` */
|
|
55
|
-
sqlWhere
|
|
55
|
+
sqlWhere(query: Query<Item>): SQLFragment;
|
|
56
56
|
/**
|
|
57
57
|
* Define an SQL fragment for a filter clause on a column.
|
|
58
58
|
*/
|
|
@@ -61,9 +61,9 @@ export declare abstract class SQLProvider extends DBProvider<number> {
|
|
|
61
61
|
* Define an SQL fragment for an `ORDER BY` clause, e.g. ` ORDER BY "a" ASC, "b" DESC`
|
|
62
62
|
* - Nested keys (multi-segment) throw `UnimplementedError`.
|
|
63
63
|
*/
|
|
64
|
-
sqlOrder
|
|
64
|
+
sqlOrder(query: Query<Item>): SQLFragment;
|
|
65
65
|
/** Define an SQL fragment for an individual column in an `ORDER BY`, e.g. `"a" ASC` */
|
|
66
66
|
sqlSort({ key, direction }: QueryOrder): SQLFragment;
|
|
67
67
|
/** Define an SQL fragment for an `LIMIT` clause, e.g. ` LIMIT 50, 100` */
|
|
68
|
-
sqlLimit
|
|
68
|
+
sqlLimit(query: Query<Item>): SQLFragment;
|
|
69
69
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
+
import { RequiredError } from "../../error/RequiredError.js";
|
|
1
2
|
import { UnimplementedError } from "../../error/UnimplementedError.js";
|
|
2
|
-
import { assertNumber } from "../../util/number.js";
|
|
3
3
|
import { getQueryFilters, getQueryLimit, getQueryOrders } from "../../util/query.js";
|
|
4
4
|
import { getUpdates } from "../../util/update.js";
|
|
5
5
|
import { DBProvider } from "./DBProvider.js";
|
|
6
6
|
/** Shared SQL execution and CRUD/query behavior. */
|
|
7
7
|
export class SQLProvider extends DBProvider {
|
|
8
|
-
async getItem(
|
|
8
|
+
async getItem(collection, id) {
|
|
9
9
|
const rows = await this.exec `
|
|
10
|
-
SELECT * FROM ${this.sqlIdentifier(name)}
|
|
10
|
+
SELECT * FROM ${this.sqlIdentifier(collection.name)}
|
|
11
11
|
WHERE ${this.sqlIdentifier("id")} = ${id}
|
|
12
12
|
LIMIT 1
|
|
13
13
|
`;
|
|
@@ -16,55 +16,56 @@ export class SQLProvider extends DBProvider {
|
|
|
16
16
|
getItemSequence(_collection, _id) {
|
|
17
17
|
throw new UnimplementedError(`SQLProvider does not support realtime subscriptions`);
|
|
18
18
|
}
|
|
19
|
-
async addItem(
|
|
19
|
+
async addItem(collection, data) {
|
|
20
20
|
const rows = await this.exec `
|
|
21
|
-
INSERT INTO ${this.sqlIdentifier(name)} ${this.sqlValues(data)}
|
|
21
|
+
INSERT INTO ${this.sqlIdentifier(collection.name)} ${this.sqlValues(data)}
|
|
22
22
|
RETURNING ${this.sqlIdentifier("id")}
|
|
23
23
|
`;
|
|
24
24
|
const id = rows[0]?.id;
|
|
25
|
-
|
|
25
|
+
if (id === undefined)
|
|
26
|
+
throw new RequiredError(`No id returned from INSERT into "${collection.name}"`, { provider: this });
|
|
26
27
|
return id;
|
|
27
28
|
}
|
|
28
|
-
async setItem(
|
|
29
|
+
async setItem(collection, id, data) {
|
|
29
30
|
await this.exec `
|
|
30
|
-
INSERT INTO ${this.sqlIdentifier(name)} ${this.sqlValues({ id, ...data })}
|
|
31
|
+
INSERT INTO ${this.sqlIdentifier(collection.name)} ${this.sqlValues({ id, ...data })}
|
|
31
32
|
ON CONFLICT (${this.sqlIdentifier("id")}) DO UPDATE SET ${this.sqlSetters(data)}
|
|
32
33
|
`;
|
|
33
34
|
}
|
|
34
|
-
async updateItem(
|
|
35
|
+
async updateItem(collection, id, updates) {
|
|
35
36
|
await this.exec `
|
|
36
|
-
UPDATE ${this.sqlIdentifier(name)}
|
|
37
|
+
UPDATE ${this.sqlIdentifier(collection.name)}
|
|
37
38
|
SET ${this.sqlUpdates(updates)}
|
|
38
39
|
WHERE ${this.sqlIdentifier("id")} = ${id}
|
|
39
40
|
`;
|
|
40
41
|
}
|
|
41
|
-
async deleteItem(
|
|
42
|
-
await this.exec `DELETE FROM ${this.sqlIdentifier(name)} WHERE ${this.sqlIdentifier("id")} = ${id}`;
|
|
42
|
+
async deleteItem(collection, id) {
|
|
43
|
+
await this.exec `DELETE FROM ${this.sqlIdentifier(collection.name)} WHERE ${this.sqlIdentifier("id")} = ${id}`;
|
|
43
44
|
}
|
|
44
|
-
async countQuery(
|
|
45
|
+
async countQuery(collection, query) {
|
|
45
46
|
const rows = await this.exec `
|
|
46
|
-
SELECT COUNT(*) AS "count" FROM ${this.sqlIdentifier(name)}
|
|
47
|
+
SELECT COUNT(*) AS "count" FROM ${this.sqlIdentifier(collection.name)}
|
|
47
48
|
${query ? this.sqlClauses(query) : this.sql ``}
|
|
48
49
|
`;
|
|
49
50
|
return rows[0]?.count ?? 0;
|
|
50
51
|
}
|
|
51
|
-
async getQuery(
|
|
52
|
+
async getQuery(collection, query) {
|
|
52
53
|
return this.exec `
|
|
53
|
-
SELECT * FROM ${this.sqlIdentifier(name)}
|
|
54
|
+
SELECT * FROM ${this.sqlIdentifier(collection.name)}
|
|
54
55
|
${query ? this.sqlClauses(query) : this.sql ``}
|
|
55
56
|
`;
|
|
56
57
|
}
|
|
57
|
-
getQuerySequence(
|
|
58
|
+
getQuerySequence(_collection, _query) {
|
|
58
59
|
throw new UnimplementedError(`SQLProvider does not support realtime subscriptions`);
|
|
59
60
|
}
|
|
60
|
-
async setQuery(
|
|
61
|
-
await this.exec `UPDATE ${this.sqlIdentifier(name)} SET ${this.sqlSetters(data)}${this.sqlClauses(query)}`;
|
|
61
|
+
async setQuery(collection, query, data) {
|
|
62
|
+
await this.exec `UPDATE ${this.sqlIdentifier(collection.name)} SET ${this.sqlSetters(data)}${this.sqlClauses(query)}`;
|
|
62
63
|
}
|
|
63
|
-
async updateQuery(
|
|
64
|
-
await this.exec `UPDATE ${this.sqlIdentifier(name)} SET ${this.sqlUpdates(updates)}${this.sqlClauses(query)}`;
|
|
64
|
+
async updateQuery(collection, query, updates) {
|
|
65
|
+
await this.exec `UPDATE ${this.sqlIdentifier(collection.name)} SET ${this.sqlUpdates(updates)}${this.sqlClauses(query)}`;
|
|
65
66
|
}
|
|
66
|
-
async deleteQuery(
|
|
67
|
-
await this.exec `DELETE FROM ${this.sqlIdentifier(name)}${this.sqlClauses(query)}`;
|
|
67
|
+
async deleteQuery(collection, query) {
|
|
68
|
+
await this.exec `DELETE FROM ${this.sqlIdentifier(collection.name)}${this.sqlClauses(query)}`;
|
|
68
69
|
}
|
|
69
70
|
/**
|
|
70
71
|
* Define an SQL fragment using Javascript template literal format.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import type { DataPath } from "../../util/data.js";
|
|
1
|
+
import type { Data, DataPath } from "../../util/data.js";
|
|
2
|
+
import type { Identifier } from "../../util/item.js";
|
|
2
3
|
import type { QueryFilter } from "../../util/query.js";
|
|
3
4
|
import type { Update } from "../../util/update.js";
|
|
5
|
+
import type { Collection } from "../collection/Collection.js";
|
|
4
6
|
import { type SQLFragment, SQLProvider } from "./SQLProvider.js";
|
|
5
7
|
/**
|
|
6
8
|
* Abstract SQLite provider with JSON1 function support for nested keys, array containment, and array mutations.
|
|
@@ -9,9 +11,11 @@ import { type SQLFragment, SQLProvider } from "./SQLProvider.js";
|
|
|
9
11
|
* - For `with` and `omit` updates this does not preserve ordering of the original array.
|
|
10
12
|
* - For `with` and `omit` updates this does not guarantee equality for de-duplication when working with nested objects or arrays.
|
|
11
13
|
*/
|
|
12
|
-
export declare abstract class SQLiteProvider extends SQLProvider {
|
|
14
|
+
export declare abstract class SQLiteProvider<I extends Identifier = Identifier, T extends Data = Data> extends SQLProvider<I, T> {
|
|
15
|
+
addItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, data: TT): Promise<II>;
|
|
13
16
|
/** Get the SQLite JSON path for the nested segments of a key (everything after the column name), e.g. `$.b.c` */
|
|
14
17
|
private sqlPath;
|
|
18
|
+
/** Get the SQLite JSON extract syntax, e.g. `json_extract("a", $.b.c)` */
|
|
15
19
|
sqlExtract(key: DataPath): SQLFragment;
|
|
16
20
|
sqlUpdate(update: Update): SQLFragment;
|
|
17
21
|
sqlFilter(filter: QueryFilter): SQLFragment;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { StringSchema } from "../../schema/StringSchema.js";
|
|
1
2
|
import { SQLProvider } from "./SQLProvider.js";
|
|
2
3
|
/**
|
|
3
4
|
* Abstract SQLite provider with JSON1 function support for nested keys, array containment, and array mutations.
|
|
@@ -7,11 +8,23 @@ import { SQLProvider } from "./SQLProvider.js";
|
|
|
7
8
|
* - For `with` and `omit` updates this does not guarantee equality for de-duplication when working with nested objects or arrays.
|
|
8
9
|
*/
|
|
9
10
|
export class SQLiteProvider extends SQLProvider {
|
|
11
|
+
// Override `addItem` to support string (UUID) IDs in SQLite.
|
|
12
|
+
// SQLite has no native UUID generation, so when the collection uses a `StringSchema` ID
|
|
13
|
+
// we generate the UUID client-side and delegate to `setItem`.
|
|
14
|
+
// Note: `as II` is required here because TypeScript cannot narrow the generic `II` from `instanceof StringSchema`.
|
|
15
|
+
async addItem(collection, data) {
|
|
16
|
+
if (collection.id instanceof StringSchema) {
|
|
17
|
+
const id = crypto.randomUUID(); // `as II` needed: TypeScript can't narrow II from instanceof check.
|
|
18
|
+
await this.setItem(collection, id, data);
|
|
19
|
+
return id;
|
|
20
|
+
}
|
|
21
|
+
return super.addItem(collection, data);
|
|
22
|
+
}
|
|
10
23
|
/** Get the SQLite JSON path for the nested segments of a key (everything after the column name), e.g. `$.b.c` */
|
|
11
24
|
sqlPath(key) {
|
|
12
25
|
return this.sqlConcat(key.slice(1).map(k => this.sqlIdentifier(k)), ".", "$.");
|
|
13
26
|
}
|
|
14
|
-
|
|
27
|
+
/** Get the SQLite JSON extract syntax, e.g. `json_extract("a", $.b.c)` */
|
|
15
28
|
sqlExtract(key) {
|
|
16
29
|
const column = this.sqlIdentifier(key[0]);
|
|
17
30
|
if (key.length > 1) {
|
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import type { Data } from "../../util/data.js";
|
|
2
2
|
import type { Identifier, Item, Items, OptionalItem } from "../../util/item.js";
|
|
3
|
-
import type {
|
|
3
|
+
import type { Query } from "../../util/query.js";
|
|
4
4
|
import type { Sourceable } from "../../util/source.js";
|
|
5
5
|
import type { Updates } from "../../util/update.js";
|
|
6
6
|
import type { Collection } from "../collection/Collection.js";
|
|
7
7
|
import type { DBProvider } from "./DBProvider.js";
|
|
8
8
|
/** A provider that passes through to an asynchronous source. */
|
|
9
|
-
export declare class ThroughDBProvider<I extends Identifier
|
|
10
|
-
readonly source: DBProvider<I>;
|
|
11
|
-
constructor(source: DBProvider<I>);
|
|
12
|
-
getItem<
|
|
13
|
-
requireItem<
|
|
14
|
-
getItemSequence<
|
|
15
|
-
addItem<
|
|
16
|
-
setItem<
|
|
17
|
-
updateItem<
|
|
18
|
-
deleteItem<
|
|
19
|
-
countQuery<
|
|
20
|
-
getQuery<
|
|
21
|
-
getQuerySequence<
|
|
22
|
-
setQuery<
|
|
23
|
-
updateQuery<
|
|
24
|
-
deleteQuery<
|
|
25
|
-
getFirst<
|
|
26
|
-
requireFirst<
|
|
9
|
+
export declare class ThroughDBProvider<I extends Identifier, T extends Data> implements DBProvider<I, T>, Sourceable<DBProvider<I, T>> {
|
|
10
|
+
readonly source: DBProvider<I, T>;
|
|
11
|
+
constructor(source: DBProvider<I, T>);
|
|
12
|
+
getItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): Promise<OptionalItem<II, TT>>;
|
|
13
|
+
requireItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): Promise<Item<II, TT>>;
|
|
14
|
+
getItemSequence<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): AsyncIterable<OptionalItem<II, TT>>;
|
|
15
|
+
addItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, data: TT): Promise<II>;
|
|
16
|
+
setItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II, data: TT): Promise<void>;
|
|
17
|
+
updateItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II, updates: Updates<Item<II, TT>>): Promise<void>;
|
|
18
|
+
deleteItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): Promise<void>;
|
|
19
|
+
countQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<number>;
|
|
20
|
+
getQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<Items<II, TT>>;
|
|
21
|
+
getQuerySequence<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): AsyncIterable<Items<II, TT>>;
|
|
22
|
+
setQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, data: TT): Promise<void>;
|
|
23
|
+
updateQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, updates: Updates<TT>): Promise<void>;
|
|
24
|
+
deleteQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<void>;
|
|
25
|
+
getFirst<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<OptionalItem<II, TT>>;
|
|
26
|
+
requireFirst<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<Item<II, TT>>;
|
|
27
27
|
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import type { Data } from "../../util/data.js";
|
|
2
|
-
import type { Identifier, Items, OptionalItem } from "../../util/item.js";
|
|
3
|
-
import type {
|
|
2
|
+
import type { Identifier, Item, Items, OptionalItem } from "../../util/item.js";
|
|
3
|
+
import type { Query } from "../../util/query.js";
|
|
4
4
|
import type { Updates } from "../../util/update.js";
|
|
5
5
|
import type { Collection } from "../collection/Collection.js";
|
|
6
6
|
import { ThroughDBProvider } from "./ThroughDBProvider.js";
|
|
7
7
|
/** Validate an asynchronous source provider (source can have any type because validation guarantees the type). */
|
|
8
|
-
export declare class ValidationDBProvider<I extends Identifier
|
|
9
|
-
getItem<
|
|
10
|
-
getItemSequence<
|
|
11
|
-
addItem<
|
|
12
|
-
setItem<
|
|
13
|
-
updateItem<
|
|
14
|
-
countQuery<
|
|
15
|
-
getQuery<
|
|
16
|
-
getQuerySequence<
|
|
17
|
-
setQuery<
|
|
18
|
-
updateQuery<
|
|
19
|
-
deleteQuery<
|
|
8
|
+
export declare class ValidationDBProvider<I extends Identifier, T extends Data> extends ThroughDBProvider<I, T> {
|
|
9
|
+
getItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): Promise<OptionalItem<II, TT>>;
|
|
10
|
+
getItemSequence<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II): AsyncIterable<OptionalItem<II, TT>>;
|
|
11
|
+
addItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, data: TT): Promise<II>;
|
|
12
|
+
setItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II, data: TT): Promise<void>;
|
|
13
|
+
updateItem<II extends I, TT extends T>(collection: Collection<string, II, TT>, id: II, updates: Updates<Item<II, TT>>): Promise<void>;
|
|
14
|
+
countQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<number>;
|
|
15
|
+
getQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): Promise<Items<II, TT>>;
|
|
16
|
+
getQuerySequence<II extends I, TT extends T>(collection: Collection<string, II, TT>, query?: Query<Item<II, TT>>): AsyncIterable<Items<II, TT>>;
|
|
17
|
+
setQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, data: TT): Promise<void>;
|
|
18
|
+
updateQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>, updates: Updates<TT>): Promise<void>;
|
|
19
|
+
deleteQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<void>;
|
|
20
20
|
}
|
|
@@ -53,8 +53,7 @@ function _validateIdentifier(collection, id, caller) {
|
|
|
53
53
|
throw new ValueError(`Invalid identifier for "${collection}"\n${thrown}`, { received: id, caller });
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
function _validateItem(collection,
|
|
57
|
-
item, caller) {
|
|
56
|
+
function _validateItem(collection, item, caller) {
|
|
58
57
|
if (!item)
|
|
59
58
|
return undefined;
|
|
60
59
|
try {
|
|
@@ -70,12 +69,10 @@ item, caller) {
|
|
|
70
69
|
* Validate a set of entities for this query reference.
|
|
71
70
|
* @throws `ValueError` if one or more items did not validate (conflict because the program is not in an expected state).
|
|
72
71
|
*/
|
|
73
|
-
function _validateItems(collection,
|
|
74
|
-
items, caller) {
|
|
72
|
+
function _validateItems(collection, items, caller) {
|
|
75
73
|
return Array.from(_yieldValidItems(collection, items, caller));
|
|
76
74
|
}
|
|
77
|
-
function* _yieldValidItems(collection,
|
|
78
|
-
items, caller) {
|
|
75
|
+
function* _yieldValidItems(collection, items, caller) {
|
|
79
76
|
const messages = [];
|
|
80
77
|
for (const item of items) {
|
|
81
78
|
try {
|
|
@@ -90,8 +87,7 @@ items, caller) {
|
|
|
90
87
|
if (messages.length)
|
|
91
88
|
throw new ValueError(`Invalid data for "${collection}"\n${messages.join("\n")}`, { received: items, caller });
|
|
92
89
|
}
|
|
93
|
-
function _validateUpdates(collection,
|
|
94
|
-
updates, caller) {
|
|
90
|
+
function _validateUpdates(collection, updates, caller) {
|
|
95
91
|
try {
|
|
96
92
|
return PARTIAL(collection).validate(updates);
|
|
97
93
|
}
|
package/db/store/QueryStore.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { ArrayStore } from "../../store/ArrayStore.js";
|
|
|
2
2
|
import { BooleanStore } from "../../store/BooleanStore.js";
|
|
3
3
|
import type { Data } from "../../util/data.js";
|
|
4
4
|
import type { Identifier, Item } from "../../util/item.js";
|
|
5
|
-
import type
|
|
5
|
+
import { type Query } from "../../util/query.js";
|
|
6
6
|
import type { StopCallback } from "../../util/start.js";
|
|
7
7
|
import type { Collection } from "../collection/Collection.js";
|
|
8
8
|
import type { DBProvider } from "../provider/DBProvider.js";
|
|
@@ -11,7 +11,7 @@ import type { MemoryDBProvider } from "../provider/MemoryDBProvider.js";
|
|
|
11
11
|
export declare class QueryStore<I extends Identifier, T extends Data> extends ArrayStore<Item<I, T>> {
|
|
12
12
|
readonly provider: DBProvider<I>;
|
|
13
13
|
readonly collection: Collection<string, I, T>;
|
|
14
|
-
readonly query:
|
|
14
|
+
readonly query: Query<Item<I, T>>;
|
|
15
15
|
readonly busy: BooleanStore;
|
|
16
16
|
readonly limit: number;
|
|
17
17
|
/** Can more items be loaded after the current result. */
|
|
@@ -21,7 +21,7 @@ export declare class QueryStore<I extends Identifier, T extends Data> extends Ar
|
|
|
21
21
|
get first(): Item<I, T>;
|
|
22
22
|
/** Get the last item in this store. */
|
|
23
23
|
get last(): Item<I, T>;
|
|
24
|
-
constructor(collection: Collection<string, I, T>, query:
|
|
24
|
+
constructor(collection: Collection<string, I, T>, query: Query<Item<I, T>>, provider: DBProvider<I>, memory?: MemoryDBProvider<I>);
|
|
25
25
|
/** Refresh this store from the source provider. */
|
|
26
26
|
refresh(provider?: DBProvider<I>): void;
|
|
27
27
|
private _refresh;
|
|
@@ -2,8 +2,8 @@ import type { Firestore } from "firebase/firestore";
|
|
|
2
2
|
import type { Collection } from "../../db/collection/Collection.js";
|
|
3
3
|
import { DBProvider } from "../../db/provider/DBProvider.js";
|
|
4
4
|
import type { Data } from "../../util/data.js";
|
|
5
|
-
import type { Items, OptionalItem } from "../../util/item.js";
|
|
6
|
-
import type
|
|
5
|
+
import type { Item, Items, OptionalItem } from "../../util/item.js";
|
|
6
|
+
import { type Query } from "../../util/query.js";
|
|
7
7
|
import type { Updates } from "../../util/update.js";
|
|
8
8
|
/**
|
|
9
9
|
* Firestore client database provider.
|
|
@@ -11,19 +11,25 @@ import type { Updates } from "../../util/update.js";
|
|
|
11
11
|
* - Supports offline mode.
|
|
12
12
|
* - Supports realtime subscriptions.
|
|
13
13
|
*/
|
|
14
|
-
export declare class FirestoreClientProvider extends DBProvider<
|
|
14
|
+
export declare class FirestoreClientProvider<I extends string = string, T extends Data = Data> extends DBProvider<I, T> {
|
|
15
15
|
private readonly _firestore;
|
|
16
16
|
constructor(firestore: Firestore);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
/** Get a Firestore CollectionReference for a given collection. */
|
|
18
|
+
private _collection;
|
|
19
|
+
/** Get a Firestore DocumentReference for a given document. */
|
|
20
|
+
private _doc;
|
|
21
|
+
/** Get a Firestore QueryReference for a given query. */
|
|
22
|
+
private _query;
|
|
23
|
+
getItem<II extends I, TT extends T>(c: Collection<string, II, TT>, id: II): Promise<OptionalItem<II, TT>>;
|
|
24
|
+
getItemSequence<II extends I, TT extends T>(c: Collection<string, II, TT>, id: II): AsyncIterable<OptionalItem<II, TT>>;
|
|
25
|
+
addItem<II extends I, TT extends T>(c: Collection<string, II, TT>, data: TT): Promise<II>;
|
|
26
|
+
setItem<II extends I, TT extends T>(c: Collection<string, II, TT>, id: II, data: TT): Promise<void>;
|
|
27
|
+
updateItem<II extends I, TT extends T>(c: Collection<string, II, TT>, id: II, updates: Updates<Item<II, TT>>): Promise<void>;
|
|
28
|
+
deleteItem<II extends I, TT extends T>(c: Collection<string, II, TT>, id: II): Promise<void>;
|
|
29
|
+
countQuery<II extends I, TT extends T>(c: Collection<string, II, TT>, q?: Query<Item<II, TT>>): Promise<number>;
|
|
30
|
+
getQuery<II extends I, TT extends T>(c: Collection<string, II, TT>, q?: Query<Item<II, TT>>): Promise<Items<II, TT>>;
|
|
31
|
+
getQuerySequence<II extends I, TT extends T>(c: Collection<string, II, TT>, q?: Query<Item<II, TT>>): AsyncIterable<Items<II, TT>>;
|
|
32
|
+
setQuery<II extends I, TT extends T>(c: Collection<string, II, TT>, q: Query<Item<II, TT>>, data: TT): Promise<void>;
|
|
33
|
+
updateQuery<II extends I, TT extends T>(c: Collection<string, II, TT>, q: Query<Item<II, TT>>, updates: Updates<TT>): Promise<void>;
|
|
34
|
+
deleteQuery<II extends I, TT extends T>(c: Collection<string, II, TT>, q: Query<Item<II, TT>>): Promise<void>;
|
|
29
35
|
}
|
|
@@ -21,10 +21,6 @@ const OPERATORS = {
|
|
|
21
21
|
lt: "<",
|
|
22
22
|
lte: "<=",
|
|
23
23
|
};
|
|
24
|
-
/** Get a Firestore QueryReference for a given query. */
|
|
25
|
-
function _getQuery(firestore, c, q) {
|
|
26
|
-
return q ? query(collection(firestore, c), ..._getConstraints(q)) : collection(firestore, c);
|
|
27
|
-
}
|
|
28
24
|
function* _getConstraints(q) {
|
|
29
25
|
for (const { key, direction } of getQueryOrders(q)) {
|
|
30
26
|
const k = joinDataKey(key);
|
|
@@ -39,16 +35,15 @@ function* _getConstraints(q) {
|
|
|
39
35
|
yield limit(l);
|
|
40
36
|
}
|
|
41
37
|
function _getItems(snapshot) {
|
|
42
|
-
return snapshot.docs.map(_getItem);
|
|
38
|
+
return snapshot.docs.map(s => _getItem(s));
|
|
43
39
|
}
|
|
44
40
|
function _getItem(snapshot) {
|
|
45
|
-
|
|
46
|
-
return getItem(snapshot.id, data);
|
|
41
|
+
return getItem(snapshot.id, snapshot.data()); // `as II` needed: Firestore snapshot.id is always string, not II.
|
|
47
42
|
}
|
|
48
43
|
function _getOptionalItem(snapshot) {
|
|
49
44
|
const data = snapshot.data();
|
|
50
45
|
if (data)
|
|
51
|
-
return getItem(snapshot.id, data);
|
|
46
|
+
return getItem(snapshot.id, data); // `as II` needed: Firestore snapshot.id is always string, not II.
|
|
52
47
|
}
|
|
53
48
|
/** Convert `Updates` object into corresponding Firestore `FieldValue` instances. */
|
|
54
49
|
function _getFieldValues(updates) {
|
|
@@ -78,51 +73,59 @@ export class FirestoreClientProvider extends DBProvider {
|
|
|
78
73
|
super();
|
|
79
74
|
this._firestore = firestore;
|
|
80
75
|
}
|
|
81
|
-
|
|
82
|
-
|
|
76
|
+
/** Get a Firestore CollectionReference for a given collection. */
|
|
77
|
+
_collection({ name }) {
|
|
78
|
+
return collection(this._firestore, name);
|
|
79
|
+
}
|
|
80
|
+
/** Get a Firestore DocumentReference for a given document. */
|
|
81
|
+
_doc(c, id) {
|
|
82
|
+
return doc(this._collection(c), id);
|
|
83
|
+
}
|
|
84
|
+
/** Get a Firestore QueryReference for a given query. */
|
|
85
|
+
_query(c, q) {
|
|
86
|
+
return q ? query(this._collection(c), ..._getConstraints(q)) : this._collection(c);
|
|
87
|
+
}
|
|
88
|
+
async getItem(c, id) {
|
|
89
|
+
const snapshot = await getDoc(this._doc(c, id));
|
|
83
90
|
return _getOptionalItem(snapshot);
|
|
84
91
|
}
|
|
85
|
-
getItemSequence(
|
|
86
|
-
return new LazyDeferredSequence(sequence => onSnapshot(
|
|
87
|
-
//
|
|
88
|
-
snapshot => sequence.resolve(_getOptionalItem(snapshot)), reason => sequence.reject(reason)));
|
|
92
|
+
getItemSequence(c, id) {
|
|
93
|
+
return new LazyDeferredSequence(sequence => onSnapshot(this._doc(c, id), snapshot => sequence.resolve(_getOptionalItem(snapshot)), reason => sequence.reject(reason)));
|
|
89
94
|
}
|
|
90
|
-
async addItem(
|
|
91
|
-
const reference = await addDoc(
|
|
92
|
-
return reference.id;
|
|
95
|
+
async addItem(c, data) {
|
|
96
|
+
const reference = await addDoc(this._collection(c), data);
|
|
97
|
+
return reference.id; // `as II` needed: Firestore returns string, not II.
|
|
93
98
|
}
|
|
94
|
-
async setItem(
|
|
95
|
-
await setDoc(
|
|
99
|
+
async setItem(c, id, data) {
|
|
100
|
+
await setDoc(this._doc(c, id), data);
|
|
96
101
|
}
|
|
97
|
-
async updateItem(
|
|
98
|
-
await updateDoc(
|
|
102
|
+
async updateItem(c, id, updates) {
|
|
103
|
+
await updateDoc(this._doc(c, id), _getFieldValues(updates));
|
|
99
104
|
}
|
|
100
|
-
async deleteItem(
|
|
101
|
-
await deleteDoc(
|
|
105
|
+
async deleteItem(c, id) {
|
|
106
|
+
await deleteDoc(this._doc(c, id));
|
|
102
107
|
}
|
|
103
|
-
async countQuery(
|
|
104
|
-
const snapshot = await getCountFromServer(
|
|
108
|
+
async countQuery(c, q) {
|
|
109
|
+
const snapshot = await getCountFromServer(this._query(c, q));
|
|
105
110
|
return snapshot.data().count;
|
|
106
111
|
}
|
|
107
|
-
async getQuery(
|
|
108
|
-
return _getItems(await getDocs(
|
|
112
|
+
async getQuery(c, q) {
|
|
113
|
+
return _getItems(await getDocs(this._query(c, q)));
|
|
109
114
|
}
|
|
110
|
-
getQuerySequence(
|
|
111
|
-
return new LazyDeferredSequence(sequence => onSnapshot(
|
|
112
|
-
//
|
|
113
|
-
snapshot => sequence.resolve(_getItems(snapshot)), reason => sequence.reject(reason)));
|
|
115
|
+
getQuerySequence(c, q) {
|
|
116
|
+
return new LazyDeferredSequence(sequence => onSnapshot(this._query(c, q), snapshot => sequence.resolve(_getItems(snapshot)), reason => sequence.reject(reason)));
|
|
114
117
|
}
|
|
115
|
-
async setQuery(
|
|
116
|
-
const snapshot = await getDocs(
|
|
118
|
+
async setQuery(c, q, data) {
|
|
119
|
+
const snapshot = await getDocs(this._query(c, q));
|
|
117
120
|
await Promise.all(snapshot.docs.map(s => setDoc(s.ref, data)));
|
|
118
121
|
}
|
|
119
|
-
async updateQuery(
|
|
120
|
-
const snapshot = await getDocs(
|
|
122
|
+
async updateQuery(c, q, updates) {
|
|
123
|
+
const snapshot = await getDocs(this._query(c, q));
|
|
121
124
|
const fieldValues = _getFieldValues(updates);
|
|
122
125
|
await Promise.all(snapshot.docs.map(s => updateDoc(s.ref, fieldValues)));
|
|
123
126
|
}
|
|
124
|
-
async deleteQuery(
|
|
125
|
-
const snapshot = await getDocs(
|
|
127
|
+
async deleteQuery(c, q) {
|
|
128
|
+
const snapshot = await getDocs(this._query(c, q));
|
|
126
129
|
await Promise.all(snapshot.docs.map(s => deleteDoc(s.ref)));
|
|
127
130
|
}
|
|
128
131
|
}
|