@procedures/collections 0.4.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @procedures/collections
2
2
 
3
+ ## 0.6.0
4
+
5
+ No changes in this release.
6
+
7
+ ## 0.5.0
8
+
9
+ ### Minor Changes
10
+
11
+ - bc74091: The storages agree on the contract of `CollectionStorage`. `InMemoryStorage` changes in three points:
12
+
13
+ - It keeps copies. Each write takes a copy of the value (`structuredClone`), and each read gives a new copy. Before, it kept the values themselves. The option `copies: false` gives the old behavior for a caller that changes no value.
14
+ - A delta of `getChangesSince` follows the order of the last change of each id. Before, it followed the first write of each id.
15
+ - After `close()`, each operation rejects with an error that says "closed", and a second `close()` does nothing. Before, `close()` emptied the storage, and the storage kept working.
16
+
17
+ The contract suite of `@procedures/collections/testing` has no options `copies` and `nullValues` now. It tests the copies, a stored `null`, the order of each delta and the operations after `close()` for each storage. The helper `byId` is removed.
18
+ - 89481b0: The contract of `CollectionStorage` has the optional method `getIds()`. It gives the ids in the order of the entries, and it does not read or copy the values.
19
+
20
+ - `InMemoryStorage.getIds()` gives the keys of its map, without a `structuredClone` of the values.
21
+ - The contract suite of `@procedures/collections/testing` tests `getIds()`: the order, a new array for each call, the ids of `getEntries()`, and the rejection after `close()`. A storage without `getIds()` skips the case.
22
+
3
23
  ## 0.4.0
4
24
 
5
25
  ### Minor Changes
package/README.md CHANGED
@@ -85,6 +85,9 @@ interface CollectionStorage<T> {
85
85
  // Read Operations
86
86
  get(id: string): Promise<T | undefined>;
87
87
  getAll(): Promise<T[]>;
88
+ getEntries?(): Promise<Array<[string, T]>>;
89
+ getIds?(): Promise<string[]>;
90
+ getChangesSince?(since?: string): Promise<StorageChanges<T>>;
88
91
  find(predicate: (item: T) => boolean): Promise<T[]>;
89
92
  has(id: string): Promise<boolean>;
90
93
  size(): Promise<number>;
@@ -105,6 +108,15 @@ interface CollectionStorage<T> {
105
108
  }
106
109
  ```
107
110
 
111
+ Each storage agrees on these points:
112
+
113
+ - A storage keeps a copy of each value that a write takes, and each read gives a new copy. A change of a written or a read value does not change the stored item.
114
+ - A stored `null` reads back as `null`. A missing id reads back as `undefined`.
115
+ - The entries keep the order of the first set of each id, as a `Map` does.
116
+ - A delta of `getChangesSince` follows the order of the last change of each id. A full result follows the order of the entries.
117
+ - `getIds()` gives the ids in the order of the entries, and it does not read or copy the values. A storage that cannot give the ids cheaply does not have it. `HybridStorage` uses it for a full sync.
118
+ - After `close()`, each operation rejects with an error whose message says "closed". A second `close()` does nothing.
119
+
108
120
  ### The contract suite: `@procedures/collections/testing`
109
121
 
110
122
  The test-only entry `@procedures/collections/testing` holds one test suite for the contract of `CollectionStorage`. A storage runs it with a factory:
@@ -118,39 +130,45 @@ describeCollectionStorageContract(
118
130
  {
119
131
  create: () => new MyStorage<ContractItem>(),
120
132
  createWithMaxTombstones: (max) => new MyStorage<ContractItem>({ maxTombstones: max }),
121
- copies: true,
122
133
  },
123
134
  { describe, it },
124
135
  );
125
136
  ```
126
137
 
127
- - The suite tests the reads, the writes and the batches, the order of the entries, the results as new arrays, the JSON values, the writes at the same time, `getChangesSince` and its versions, `flush`, `getMetadata`, `close` and the errors.
138
+ - The suite tests the reads, the writes and the batches, the order of the entries, `getIds`, and the results as new arrays.
139
+ - It tests the copies, the JSON values with `null`, and the writes at the same time.
140
+ - It tests `getChangesSince` with its versions and its order, `flush`, `getMetadata`, `close` and the errors.
128
141
  - A case for an optional method skips a storage without the method.
129
- - A random sequence of writes runs against a `Map`, and each saved version must give the right changes.
142
+ - A random sequence of writes runs against a `Map`. Each saved version must give the right changes, in the order of the last change of each id.
130
143
  - The suite imports no test framework. The caller gives `describe` and `it`, and a failed check throws `ContractViolation`. The runtime entries do not import this entry.
131
- - `copies: true` declares that the storage keeps a copy of each value. `nullValues: false` declares that a stored `null` reads back as `undefined`.
144
+ - The suite has no option that hides a difference of a storage. Before, the options `copies` and `nullValues` declared the differences, and the suite sorted each delta.
132
145
 
133
146
  | Storage | Package | Notes |
134
147
  |---|---|---|
135
- | `InMemoryStorage` | `@procedures/collections` | It keeps the values, not copies. |
136
- | `SqliteStorage` (in memory and in a file) | `@procedures/sqlite` | It gives copies. |
137
- | `ApiStorage` | `@procedures/store` | A stored `null` reads back as `undefined`. |
148
+ | `InMemoryStorage` | `@procedures/collections` | It copies with `structuredClone`. The run with `copies: false` skips only the case of the copies. |
149
+ | `SqliteStorage` (each engine, in memory and in a file) | `@procedures/sqlite` | It keeps JSON text, so it gives copies. |
150
+ | `ApiStorage` | `@procedures/store` | It reads `{ found, value }` from the procedure `get`, so a stored `null` stays `null`. It has no `getIds`. |
138
151
  | `HybridStorage` (each mode, and a queue in IndexedDB) | `@procedures/store` | It has no `getChangesSince`. |
139
152
 
140
153
  ## Storage Backends
141
154
 
142
- ### MemoryStorage
155
+ ### InMemoryStorage
143
156
 
144
157
  Fast, in-memory storage using JavaScript Map:
145
158
 
146
159
  ```typescript
147
- import { MemoryStorage } from "@procedures/collections";
160
+ import { InMemoryStorage } from "@procedures/collections";
148
161
 
149
- const storage = new MemoryStorage<User>();
162
+ const storage = new InMemoryStorage<User>();
150
163
  await storage.set("user-1", { name: "Alice", age: 30 });
151
164
  const user = await storage.get("user-1");
152
165
  ```
153
166
 
167
+ - The storage keeps a copy of each written value, and each read gives a new copy (`structuredClone`). A `Date` stays a `Date`. A function in a value makes the write reject.
168
+ - The option `copies: false` turns the copies off: the storage keeps the values themselves, and the reads give them. It is faster. Give it only when no caller changes a value after a write or a read.
169
+ - `getIds()` gives the ids without a copy of the values.
170
+ - `close()` drops the items. After it, each operation rejects, and the methods of the `Map` interface (`keys()`, `values()`, `entries()`, `forEach()`) throw.
171
+
154
172
  ### ApiStorage
155
173
 
156
174
  Remote storage via RPC client: