@procedures/store 0.7.0 → 0.8.1

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 (51) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +98 -1
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/indexeddb/sync-state-store.d.ts +40 -0
  5. package/dist/indexeddb/sync-state-store.d.ts.map +1 -0
  6. package/dist/indexeddb/sync-state-store.js +94 -0
  7. package/dist/indexeddb/sync-state-store.js.map +1 -0
  8. package/dist/indexeddb.d.ts +7 -2
  9. package/dist/indexeddb.d.ts.map +1 -1
  10. package/dist/indexeddb.js +6 -2
  11. package/dist/indexeddb.js.map +1 -1
  12. package/dist/storage/api.d.ts +94 -11
  13. package/dist/storage/api.d.ts.map +1 -1
  14. package/dist/storage/api.js +177 -24
  15. package/dist/storage/api.js.map +1 -1
  16. package/dist/storage/feed.d.ts +133 -0
  17. package/dist/storage/feed.d.ts.map +1 -0
  18. package/dist/storage/feed.js +159 -0
  19. package/dist/storage/feed.js.map +1 -0
  20. package/dist/storage/file-queue.d.ts.map +1 -1
  21. package/dist/storage/file-queue.js +2 -1
  22. package/dist/storage/file-queue.js.map +1 -1
  23. package/dist/storage/hybrid.d.ts +415 -20
  24. package/dist/storage/hybrid.d.ts.map +1 -1
  25. package/dist/storage/hybrid.js +1405 -196
  26. package/dist/storage/hybrid.js.map +1 -1
  27. package/dist/storage/index.d.ts +6 -2
  28. package/dist/storage/index.d.ts.map +1 -1
  29. package/dist/storage/index.js +6 -1
  30. package/dist/storage/index.js.map +1 -1
  31. package/dist/storage/sync-state.d.ts +56 -0
  32. package/dist/storage/sync-state.d.ts.map +1 -0
  33. package/dist/storage/sync-state.js +58 -0
  34. package/dist/storage/sync-state.js.map +1 -0
  35. package/dist/storage/synced-registry.d.ts +22 -4
  36. package/dist/storage/synced-registry.d.ts.map +1 -1
  37. package/dist/storage/synced-registry.js +63 -31
  38. package/dist/storage/synced-registry.js.map +1 -1
  39. package/dist/storage/types.d.ts +22 -1
  40. package/dist/storage/types.d.ts.map +1 -1
  41. package/package.json +4 -3
  42. package/src/indexeddb/sync-state-store.ts +101 -0
  43. package/src/indexeddb.ts +7 -2
  44. package/src/storage/api.ts +240 -28
  45. package/src/storage/feed.ts +251 -0
  46. package/src/storage/file-queue.ts +2 -1
  47. package/src/storage/hybrid.ts +1526 -203
  48. package/src/storage/index.ts +17 -0
  49. package/src/storage/sync-state.ts +89 -0
  50. package/src/storage/synced-registry.ts +76 -31
  51. package/src/storage/types.ts +25 -1
@@ -0,0 +1,40 @@
1
+ /**
2
+ * A sync state store in IndexedDB, for the `HybridStorage` of a browser (roadmap CLI-21).
3
+ *
4
+ * The state is in the object store `state` of one database: one record for the cursor, and one
5
+ * record for the base version of each id. A change writes only the records that it names, in one
6
+ * transaction, so a reader never sees a part of it. Give each storage its own database.
7
+ */
8
+ import type { SyncState, SyncStateChange, SyncStateStore } from "../storage/sync-state.js";
9
+ import { type IndexedDbOptions } from "./idb.js";
10
+ /**
11
+ * A sync state store in IndexedDB.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { HybridStorage } from "@procedures/store";
16
+ * import { IndexedDbQueueStore, IndexedDbSyncStateStore } from "@procedures/store/indexeddb";
17
+ *
18
+ * const users = new HybridStorage(remote, {
19
+ * local: durableLocalCopy,
20
+ * queueStore: new IndexedDbQueueStore("users-queue"),
21
+ * syncState: new IndexedDbSyncStateStore("users-sync"),
22
+ * });
23
+ * ```
24
+ */
25
+ export declare class IndexedDbSyncStateStore implements SyncStateStore {
26
+ private readonly connection;
27
+ /**
28
+ * @param name - The name of the database: one database for each storage. The store makes it at the first operation.
29
+ * @param options - The IndexedDB factory. The default is the `indexedDB` of the global scope.
30
+ */
31
+ constructor(name: string, options?: IndexedDbOptions);
32
+ /** The name of the database. */
33
+ get name(): string;
34
+ load(): Promise<SyncState | undefined>;
35
+ write(change: SyncStateChange): Promise<void>;
36
+ clear(): Promise<void>;
37
+ /** This method closes the database. The state stays in it. */
38
+ close(): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=sync-state-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-state-store.d.ts","sourceRoot":"","sources":["../../src/indexeddb/sync-state-store.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC3F,OAAO,EAAsB,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AA2BrE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAwB,YAAW,cAAc;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAEhD;;;OAGG;IACH,YAAY,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,EAGvD;IAED,gCAAgC;IAChC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAKrC;IAEK,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAYlD;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAK3B;IAED,8DAA8D;IAC9D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAErB;CACF"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * A sync state store in IndexedDB, for the `HybridStorage` of a browser (roadmap CLI-21).
3
+ *
4
+ * The state is in the object store `state` of one database: one record for the cursor, and one
5
+ * record for the base version of each id. A change writes only the records that it names, in one
6
+ * transaction, so a reader never sees a part of it. Give each storage its own database.
7
+ */
8
+ import { IdbStoreConnection } from "./idb.js";
9
+ /** The key of the record of the cursor. The keys of the bases start with `BASE`. */
10
+ const CURSOR_KEY = "cursor";
11
+ const BASE = "base:";
12
+ /** The state of the records, or an error for a record of another form. */
13
+ function stateOf(records, name) {
14
+ if (records.length === 0)
15
+ return undefined;
16
+ let cursor;
17
+ const bases = [];
18
+ records.forEach((value, index) => {
19
+ const record = value;
20
+ if (record?.kind === "cursor" && (record.cursor === undefined || typeof record.cursor === "string")) {
21
+ cursor = record.cursor;
22
+ }
23
+ else if (record?.kind === "base" && typeof record.id === "string" && (record.version === null || typeof record.version === "string")) {
24
+ bases.push([record.id, record.version]);
25
+ }
26
+ else {
27
+ throw new Error(`The sync state database ${name} has a record that is not a sync state (record ${index + 1})`);
28
+ }
29
+ });
30
+ return { ...(cursor !== undefined && { cursor }), bases };
31
+ }
32
+ /**
33
+ * A sync state store in IndexedDB.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * import { HybridStorage } from "@procedures/store";
38
+ * import { IndexedDbQueueStore, IndexedDbSyncStateStore } from "@procedures/store/indexeddb";
39
+ *
40
+ * const users = new HybridStorage(remote, {
41
+ * local: durableLocalCopy,
42
+ * queueStore: new IndexedDbQueueStore("users-queue"),
43
+ * syncState: new IndexedDbSyncStateStore("users-sync"),
44
+ * });
45
+ * ```
46
+ */
47
+ export class IndexedDbSyncStateStore {
48
+ connection;
49
+ /**
50
+ * @param name - The name of the database: one database for each storage. The store makes it at the first operation.
51
+ * @param options - The IndexedDB factory. The default is the `indexedDB` of the global scope.
52
+ */
53
+ constructor(name, options = {}) {
54
+ if (typeof name !== "string" || name === "")
55
+ throw new TypeError("IndexedDbSyncStateStore needs the name of a database");
56
+ this.connection = new IdbStoreConnection(name, "state", {}, options.indexedDB);
57
+ }
58
+ /** The name of the database. */
59
+ get name() {
60
+ return this.connection.database;
61
+ }
62
+ load() {
63
+ return this.connection.run("readonly", (store) => {
64
+ const request = store.getAll();
65
+ return () => stateOf(request.result, this.name);
66
+ });
67
+ }
68
+ async write(change) {
69
+ await this.connection.run("readwrite", (store) => {
70
+ if (change.reset)
71
+ store.clear();
72
+ const cursor = change.cursor === undefined ? { kind: "cursor" } : { kind: "cursor", cursor: change.cursor };
73
+ store.put(cursor, CURSOR_KEY);
74
+ for (const [id, version] of change.bases) {
75
+ const record = { kind: "base", id, version };
76
+ store.put(record, BASE + id);
77
+ }
78
+ for (const id of change.forgotten)
79
+ store.delete(BASE + id);
80
+ return () => undefined;
81
+ });
82
+ }
83
+ async clear() {
84
+ await this.connection.run("readwrite", (store) => {
85
+ store.clear();
86
+ return () => undefined;
87
+ });
88
+ }
89
+ /** This method closes the database. The state stays in it. */
90
+ close() {
91
+ return this.connection.close();
92
+ }
93
+ }
94
+ //# sourceMappingURL=sync-state-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-state-store.js","sourceRoot":"","sources":["../../src/indexeddb/sync-state-store.ts"],"names":[],"mappings":"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"}
@@ -3,17 +3,22 @@
3
3
  *
4
4
  * - `IndexedDbProgramStore` keeps programs in IndexedDB.
5
5
  * - `IndexedDbQueueStore` keeps the queue of a `HybridStorage` in IndexedDB.
6
- * - `MemoryProgramStore` and `MemoryQueueStore` are the fallbacks for a browser without
7
- * IndexedDB. `hasIndexedDb()` tells which one to use.
6
+ * - `IndexedDbSyncStateStore` keeps the sync state of a `HybridStorage` in IndexedDB: the cursor
7
+ * and the base versions (roadmap CLI-21).
8
+ * - `MemoryProgramStore`, `MemoryQueueStore` and `MemorySyncStateStore` are the fallbacks for a
9
+ * browser without IndexedDB. `hasIndexedDb()` tells which one to use.
8
10
  *
9
11
  * The package root does not import this entry, and this entry does not import the client or zod:
10
12
  * a page that loads it gets only these stores.
11
13
  */
12
14
  export { IndexedDbProgramStore, DEFAULT_PROGRAM_DATABASE } from "./indexeddb/program-store.js";
13
15
  export { IndexedDbQueueStore } from "./indexeddb/queue-store.js";
16
+ export { IndexedDbSyncStateStore } from "./indexeddb/sync-state-store.js";
14
17
  export { hasIndexedDb, type IdbFactoryLike, type IndexedDbOptions } from "./indexeddb/idb.js";
15
18
  export { MemoryProgramStore } from "./program-store/memory.js";
16
19
  export { MemoryQueueStore } from "./storage/queue.js";
20
+ export { MemorySyncStateStore } from "./storage/sync-state.js";
21
+ export type { SyncState, SyncStateChange, SyncStateStore } from "./storage/sync-state.js";
17
22
  export type { ProgramStore, StoredProgram } from "./program-store/types.js";
18
23
  export type { QueuedOperation, QueueStore } from "./storage/queue.js";
19
24
  //# sourceMappingURL=indexeddb.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,KAAK,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC1F,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC5E,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/indexeddb.js CHANGED
@@ -3,15 +3,19 @@
3
3
  *
4
4
  * - `IndexedDbProgramStore` keeps programs in IndexedDB.
5
5
  * - `IndexedDbQueueStore` keeps the queue of a `HybridStorage` in IndexedDB.
6
- * - `MemoryProgramStore` and `MemoryQueueStore` are the fallbacks for a browser without
7
- * IndexedDB. `hasIndexedDb()` tells which one to use.
6
+ * - `IndexedDbSyncStateStore` keeps the sync state of a `HybridStorage` in IndexedDB: the cursor
7
+ * and the base versions (roadmap CLI-21).
8
+ * - `MemoryProgramStore`, `MemoryQueueStore` and `MemorySyncStateStore` are the fallbacks for a
9
+ * browser without IndexedDB. `hasIndexedDb()` tells which one to use.
8
10
  *
9
11
  * The package root does not import this entry, and this entry does not import the client or zod:
10
12
  * a page that loads it gets only these stores.
11
13
  */
12
14
  export { IndexedDbProgramStore, DEFAULT_PROGRAM_DATABASE } from "./indexeddb/program-store.js";
13
15
  export { IndexedDbQueueStore } from "./indexeddb/queue-store.js";
16
+ export { IndexedDbSyncStateStore } from "./indexeddb/sync-state-store.js";
14
17
  export { hasIndexedDb } from "./indexeddb/idb.js";
15
18
  export { MemoryProgramStore } from "./program-store/memory.js";
16
19
  export { MemoryQueueStore } from "./storage/queue.js";
20
+ export { MemorySyncStateStore } from "./storage/sync-state.js";
17
21
  //# sourceMappingURL=indexeddb.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexeddb.js","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,YAAY,EAA8C,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"indexeddb.js","sourceRoot":"","sources":["../src/indexeddb.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAC/F,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAA8C,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC"}
@@ -6,9 +6,10 @@
6
6
  *
7
7
  * This is the KEY integration between Collections and Universal Client!
8
8
  */
9
- import type { CollectionStorage, StorageChanges, StorageMetadata } from "@procedures/collections";
9
+ import type { CollectionStorage, StorageChanges, StorageMetadata, StorageWriteOptions } from "@procedures/collections";
10
+ import { type StoragePatch, type StorageQueryOptions, type StorageQueryPolicy, type StorageSelector } from "@procedures/client/query";
10
11
  import type { Client } from "@procedures/client";
11
- import { type Duration } from "@procedures/client";
12
+ import { type Clock, type Duration } from "@procedures/client";
12
13
  /**
13
14
  * API storage configuration options.
14
15
  */
@@ -31,14 +32,16 @@ export interface ApiStorageOptions {
31
32
  */
32
33
  version?: string;
33
34
  /**
34
- * Custom operation names (override defaults)
35
+ * Custom operation names (override defaults). `find` is the operation of `query()`.
35
36
  */
36
37
  operations?: {
37
38
  get?: string;
38
39
  getAll?: string;
39
40
  entries?: string;
41
+ ids?: string;
40
42
  changes?: string;
41
43
  find?: string;
44
+ patch?: string;
42
45
  has?: string;
43
46
  size?: string;
44
47
  set?: string;
@@ -55,15 +58,50 @@ export interface ApiStorageOptions {
55
58
  timeout?: Duration;
56
59
  /**
57
60
  * Retry a call that fails with a retryable error: true (3 attempts), false (1 attempt), or the
58
- * number of attempts.
61
+ * number of attempts. Each collection procedure is idempotent, so a retry is safe.
59
62
  * @default true
60
63
  */
61
64
  retry?: boolean | number;
62
65
  /**
63
- * The first delay between attempts. Each later delay is two times longer.
66
+ * The backoff of the first retry. Each later backoff is `retryFactor` times longer, at most
67
+ * `maxRetryDelay`. The wait is `backoffDelay` of `@mark1russell7/service/backoff`.
64
68
  * @default 100 milliseconds
65
69
  */
66
70
  retryDelay?: Duration;
71
+ /**
72
+ * The factor of the backoff from one retry to the next.
73
+ * @default 2
74
+ */
75
+ retryFactor?: number;
76
+ /**
77
+ * The longest backoff of a retry, before its random part.
78
+ * @default 30 seconds
79
+ */
80
+ maxRetryDelay?: Duration;
81
+ /**
82
+ * The random part of each wait, from 0 to 1. With 1 (full jitter), the wait is a random time from
83
+ * 0 to the backoff, so the clients that failed together do not retry together. 0 gives the
84
+ * backoff exactly. A server that asks for a wait (`retryAfterMs` of the error) gets at least it.
85
+ * @default 1
86
+ */
87
+ retryJitter?: number;
88
+ /**
89
+ * The clock of the timeout and of the waits between attempts. A test gives `fakeClock` of
90
+ * `@mark1russell7/service/testing`.
91
+ * @default the clock of the platform (`realClock` of `@procedures/client`)
92
+ */
93
+ clock?: Clock;
94
+ /**
95
+ * The random numbers of the jitter, from 0 up to 1. A test gives a fixed generator.
96
+ * @default Math.random
97
+ */
98
+ random?: () => number;
99
+ /**
100
+ * The query policy that `query()` applies before it sends a query, for a call without a policy.
101
+ * The server applies its own policy too. Without it, the storage examines only the form of a
102
+ * selector, and the server decides the rest.
103
+ */
104
+ queryPolicy?: StorageQueryPolicy;
67
105
  /**
68
106
  * AbortSignal for cancelling requests
69
107
  */
@@ -82,11 +120,17 @@ export interface ApiStorageOptions {
82
120
  * - get(id) → `collections.<collection>.get` with `{ id }`
83
121
  * - set(id, value) → `collections.<collection>.set` with `{ id, value }`
84
122
  * - setBatch(items) → `collections.<collection>.setBatch` with `{ items: [{ id, value }] }`
123
+ * - getIds() → `collections.<collection>.ids` with `{}`, or the entries of an older server
124
+ * - query(selector, options) → `collections.<collection>.find` with `{ selector, sort, skip, limit }`
125
+ * - patch(id, fields) → `collections.<collection>.patch` with `{ id, fields }`
85
126
  * - etc.
86
127
  *
87
128
  * Before, the class expected a `{ data }` envelope and called `<service>.get`: no core
88
129
  * procedure matched, and every read was undefined (deep dive CORE-9, DATA-9).
89
130
  *
131
+ * The retries wait with `backoffDelay` of `@mark1russell7/service/backoff` on the clock of the
132
+ * options, with full jitter by default. Before, the waits had no jitter, and they used the timers of the platform directly.
133
+ *
90
134
  * @example
91
135
  * ```typescript
92
136
  * // Create API storage
@@ -99,12 +143,17 @@ export interface ApiStorageOptions {
99
143
  * // Operations are automatically sent to server
100
144
  * await storage.set("123", { name: "John" });
101
145
  * const user = await storage.get("123"); // Fetched from server
146
+ * const admins = await storage.query({ role: "admin" }, { sort: { name: 1 }, limit: 50 }); // Runs on the server
102
147
  * ```
103
148
  */
104
149
  export declare class ApiStorage<T> implements CollectionStorage<T> {
105
150
  private client;
106
151
  private options;
107
152
  private closed;
153
+ /** True when the server does not know the procedure `ids`: `getIds()` reads the entries */
154
+ private idsUnsupported;
155
+ /** True when the server does not know the procedure `find`: `query()` reads the entries */
156
+ private findUnsupported;
108
157
  constructor(client: Client, options: ApiStorageOptions);
109
158
  /**
110
159
  * The item of an id. For the core collection procedures, the call asks for `{ found, value }`,
@@ -115,20 +164,50 @@ export declare class ApiStorage<T> implements CollectionStorage<T> {
115
164
  getAll(): Promise<T[]>;
116
165
  /** Every item with its id, from the `entries` procedure of the collection (roadmap R2). */
117
166
  getEntries(): Promise<Array<[string, T]>>;
167
+ /**
168
+ * The ids of the items in the order of the entries, from the `ids` procedure of the collection.
169
+ * The values stay on the server. An older server without the procedure answers `NOT_FOUND`.
170
+ * Then this call and each later call read the ids from `getEntries()`.
171
+ */
172
+ getIds(): Promise<string[]>;
118
173
  /**
119
174
  * The changes after a version, from the `changes` procedure of the collection (roadmap R28).
120
175
  * A server without the procedure answers `NOT_FOUND`, and `HybridStorage` then uses
121
176
  * `getEntries()`.
122
177
  */
123
178
  getChangesSince(since?: string): Promise<StorageChanges<T>>;
179
+ /**
180
+ * The items for which the predicate gives true. A function cannot go over the network, so this
181
+ * method reads all items and filters here. `query()` sends a JSON selector to the server.
182
+ */
124
183
  find(predicate: (item: T) => boolean): Promise<T[]>;
184
+ /**
185
+ * The entries whose items match the selector, from the `find` procedure of the collection: the
186
+ * server runs the selector, the order, the skip and the limit (roadmap CLI-14), under its own
187
+ * query policy. An older server without the procedure answers `NOT_FOUND`. Then this call and
188
+ * each later call read the entries and run the query here, with the same result.
189
+ *
190
+ * @param policy - The policy that the storage applies before it sends the query. The default is
191
+ * the option `queryPolicy`. The server applies its own policy too.
192
+ */
193
+ query(selector: StorageSelector, options?: StorageQueryOptions, policy?: StorageQueryPolicy): Promise<Array<[string, T]>>;
125
194
  has(id: string): Promise<boolean>;
126
195
  size(): Promise<number>;
127
- set(id: string, value: T): Promise<void>;
128
- delete(id: string): Promise<boolean>;
129
- clear(): Promise<void>;
130
- setBatch(items: Array<[string, T]>): Promise<void>;
131
- deleteBatch(ids: string[]): Promise<number>;
196
+ /**
197
+ * This method sets an item on the server. The origin of the options goes to the procedure `set`,
198
+ * so the storage of the server and the change event of the write have it (roadmap CLI-15).
199
+ */
200
+ set(id: string, value: T, options?: StorageWriteOptions): Promise<void>;
201
+ delete(id: string, options?: StorageWriteOptions): Promise<boolean>;
202
+ clear(options?: StorageWriteOptions): Promise<void>;
203
+ /**
204
+ * This method changes an item on the server in one step: the `patch` procedure of the collection
205
+ * (roadmap CLI-14). The fields are a JSON Merge Patch, or the operations `$set` and `$unset`.
206
+ * It gives the new item, or undefined for a missing id.
207
+ */
208
+ patch(id: string, fields: StoragePatch, options?: StorageWriteOptions): Promise<T | undefined>;
209
+ setBatch(items: Array<[string, T]>, options?: StorageWriteOptions): Promise<void>;
210
+ deleteBatch(ids: string[], options?: StorageWriteOptions): Promise<number>;
132
211
  getBatch(ids: string[]): Promise<Map<string, T>>;
133
212
  /**
134
213
  * This method closes the storage. After it, each operation rejects, and a second `close()` does
@@ -137,8 +216,12 @@ export declare class ApiStorage<T> implements CollectionStorage<T> {
137
216
  */
138
217
  close(): Promise<void>;
139
218
  getMetadata(): Promise<StorageMetadata>;
219
+ /** The field `origin` of a write input, or no field: a custom service gets no origin. */
220
+ private originOf;
140
221
  /**
141
- * Make an RPC call via the universal client, with the timeout, the signal and the retries.
222
+ * Make an RPC call via the universal client, with the timeout, the signal and the retries. The
223
+ * wait before retry `n` is `backoffDelay(backoff, n)`, or the wait that the error asks for when
224
+ * it is longer.
142
225
  */
143
226
  private call;
144
227
  /** One attempt: the call ends at the timeout, or when the signal of the options aborts. */
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/storage/api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/storage/api.ts"],"names":[],"mappings":"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"}