liwi-subscribe-store 13.4.0 → 14.0.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/README.md +29 -0
- package/dist/definitions/SubscribeStore.d.ts +26 -4
- package/dist/definitions/SubscribeStore.d.ts.map +1 -1
- package/dist/index-browser.es.js +175 -40
- package/dist/index-browser.es.js.map +1 -1
- package/dist/index-node.mjs +175 -40
- package/dist/index-node.mjs.map +1 -1
- package/package.json +3 -3
- package/src/SubscribeStore.test.ts +522 -9
- package/src/SubscribeStore.ts +198 -37
package/README.md
CHANGED
|
@@ -81,6 +81,35 @@ type Actions<Model> =
|
|
|
81
81
|
| { type: "deleted"; prev: Model[] };
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
`prev` is always the **stored** document, never the object passed by the caller: queries decide whether a document entered or left their result by testing `prev` and `next` against their criteria, so a stale copy would leave rows in lists they no longer belong to.
|
|
85
|
+
|
|
86
|
+
| Write | Where `prev` comes from |
|
|
87
|
+
| ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
88
|
+
| `partialUpdateByKey` / `partialUpdateOne` | `partialUpdateByKeyWithInfo` of the wrapped store (atomic with the write), else `findOne` just before the write |
|
|
89
|
+
| `replaceOne` / `replaceSeveral` | `replaceOneWithInfo` (atomic), else `findByKey` just before the write |
|
|
90
|
+
| `deleteByKey` / `deleteOne` | `deleteByKeyWithInfo` (atomic), else `findByKey` just before the write |
|
|
91
|
+
| `upsertOne` / `upsertOneWithInfo` | `upsertOneWithInfo` of the wrapped store |
|
|
92
|
+
| `partialUpdateMany` | the matching keys are read first, then each document is updated as by `partialUpdateByKey`, re-checking `criteria` |
|
|
93
|
+
| `deleteMany` | the matching keys are read first, then each document is deleted with `deleteByKeyWithInfo`, re-checking `criteria`; without it, a cursor read before one `deleteMany` (not atomic) |
|
|
94
|
+
|
|
95
|
+
See [Writes with info](../liwi-store#writes-with-info) for the `…WithInfo` contract.
|
|
96
|
+
|
|
97
|
+
For `partialUpdateMany` and `deleteMany`, the emitted documents are exactly the ones written: a document that stopped matching `criteria` after the keys were read is left alone and not emitted, and a document inserted after the keys were read is not written. This costs one round trip per document instead of a single command.
|
|
98
|
+
|
|
99
|
+
### Without listeners
|
|
100
|
+
|
|
101
|
+
When nothing is subscribed (no `subscribe` listener and no open query subscription), nobody needs `prev`, so writes go straight to the wrapped store: `partialUpdateMany` and `deleteMany` are a single command again, and `partialUpdateByKey`, `replaceOne` and `deleteByKey` use the plain methods (one round trip for a partial update by key instead of two). Behaviour is otherwise the same, including `NotFoundError` on a missing key.
|
|
102
|
+
|
|
103
|
+
### Missing keys and failures
|
|
104
|
+
|
|
105
|
+
- A write on a key that does not exist emits nothing. `partialUpdateByKey`, `partialUpdateOne`, `replaceOne` and `replaceSeveral` throw [`NotFoundError`](../liwi-store#errors); `deleteByKey` and `deleteOne` resolve.
|
|
106
|
+
- When a concurrent delete lands between a partial update and its read-back, the update was applied but there is no `next`: nothing is emitted (the delete emits its own `deleted` action) and `partialUpdateByKey` / `partialUpdateOne` throw `DeletedAfterUpdateError`, a subclass of `NotFoundError`. `partialUpdateMany` skips that document.
|
|
107
|
+
- `replaceSeveral`, `partialUpdateMany` and `deleteMany` emit the writes that succeeded before rethrowing the first failure, so listeners stay in sync with what was written.
|
|
108
|
+
|
|
109
|
+
### `updated`
|
|
110
|
+
|
|
111
|
+
Stamping `updated` and rejecting protected field updates is done by the wrapped store (see [`created` and `updated`](../liwi-store#created-and-updated)); partial update `options` such as `touchUpdated` are passed through. Because `partialUpdateMany` writes document by document here, each document gets its own write time, where a plain `MongoStore` gives the whole command one.
|
|
112
|
+
|
|
84
113
|
### `AbstractSubscribableStoreQuery`
|
|
85
114
|
|
|
86
115
|
Base class for store implementations providing subscribable queries. It holds the subscribe store (`setSubscribeStore` / `getSubscribeStore`) and implements `fetchAndSubscribe` / `subscribe` on top of an abstract `_subscribe`; implementations turn store actions into query `Changes` by testing each affected model against the query criteria. `liwi-mongo` does this in memory with [mingo](https://github.com/kofrasa/mingo).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbstractConnection, AbstractStoreCursor, AllowedKeyValue, BaseModel, CreateQueryOptions, Criteria, InsertType, OptionalBaseModelKeysForInsert, QueryParams, Sort, Store as StoreInterface, SubscribableStore, SubscribableStoreQuery, Update, UpsertPartialObject, UpsertResult } from "liwi-store";
|
|
1
|
+
import type { AbstractConnection, AbstractStoreCursor, AllowedKeyValue, BaseModel, CreateQueryOptions, Criteria, InsertType, OptionalBaseModelKeysForInsert, PartialUpdateOptions, QueryParams, Sort, Store as StoreInterface, SubscribableStore, SubscribableStoreQuery, Update, UpsertPartialObject, UpsertResult } from "liwi-store";
|
|
2
2
|
export type Actions<Model> = {
|
|
3
3
|
type: "deleted";
|
|
4
4
|
prev: Model[];
|
|
@@ -18,6 +18,15 @@ export default class SubscribeStore<KeyPath extends keyof Model, KeyValue extend
|
|
|
18
18
|
get connection(): Connection;
|
|
19
19
|
subscribe(callback: Listener<Model>): () => void;
|
|
20
20
|
callSubscribed(action: Actions<Model>): void;
|
|
21
|
+
/**
|
|
22
|
+
* Nobody needs `prev` without listeners: writes then go straight to the wrapped
|
|
23
|
+
* store, skipping the per-document reads and loops.
|
|
24
|
+
*/
|
|
25
|
+
private hasListeners;
|
|
26
|
+
private findKeys;
|
|
27
|
+
private replaceOneAndGetChange;
|
|
28
|
+
private partialUpdateByKeyAndGetResult;
|
|
29
|
+
private deleteByKeyAndGetPrev;
|
|
21
30
|
createQuerySingleItem<Result extends Record<KeyPath, KeyValue>, Params extends QueryParams<Params>>(options: CreateQueryOptions<Model, Result>): SubscribableStoreQuery<KeyPath, KeyValue, Model, SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>, Result, Params>;
|
|
22
31
|
createQueryCollection<Item extends Record<KeyPath, KeyValue>, Params extends QueryParams<Params>>(options: CreateQueryOptions<Model, Item>): SubscribableStoreQuery<KeyPath, KeyValue, Model, SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>, Item[], Params>;
|
|
23
32
|
findAll(criteria?: Criteria<Model>, sort?: Sort<Model>): Promise<Model[]>;
|
|
@@ -28,11 +37,24 @@ export default class SubscribeStore<KeyPath extends keyof Model, KeyValue extend
|
|
|
28
37
|
replaceSeveral(objects: Model[]): Promise<Model[]>;
|
|
29
38
|
upsertOne<K extends Exclude<keyof Model, KeyPath | OptionalBaseModelKeysForInsert>>(object: UpsertPartialObject<KeyPath, KeyValue, Model, K>, setOnInsertPartialObject?: Update<Model>["$setOnInsert"]): Promise<Model>;
|
|
30
39
|
upsertOneWithInfo<K extends Exclude<keyof Model, KeyPath | OptionalBaseModelKeysForInsert>>(object: UpsertPartialObject<KeyPath, KeyValue, Model, K>, setOnInsertPartialObject?: Update<Model>["$setOnInsert"]): Promise<UpsertResult<Model>>;
|
|
31
|
-
partialUpdateByKey(key: KeyValue, partialUpdate: Update<Model>, criteria?: Criteria<Model
|
|
32
|
-
partialUpdateOne(object: Model, partialUpdate: Update<Model
|
|
33
|
-
|
|
40
|
+
partialUpdateByKey(key: KeyValue, partialUpdate: Update<Model>, criteria?: Criteria<Model>, options?: PartialUpdateOptions): Promise<Model>;
|
|
41
|
+
partialUpdateOne(object: Model, partialUpdate: Update<Model>, options?: PartialUpdateOptions): Promise<Model>;
|
|
42
|
+
/**
|
|
43
|
+
* Without listeners, one command on the wrapped store. With listeners, the keys
|
|
44
|
+
* matching `criteria` are read first, then each document is updated on its own
|
|
45
|
+
* (re-checking `criteria`) so that every change carries its true `prev`: N + 1
|
|
46
|
+
* round trips. A document inserted after the keys were read is not updated.
|
|
47
|
+
*/
|
|
48
|
+
partialUpdateMany(criteria: Criteria<Model>, partialUpdate: Update<Model>, options?: PartialUpdateOptions): Promise<void>;
|
|
34
49
|
deleteByKey(key: KeyValue, criteria?: Criteria<Model>): Promise<void>;
|
|
35
50
|
deleteOne(object: Model): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Without listeners, one command on the wrapped store. With listeners and a store
|
|
53
|
+
* implementing `deleteByKeyWithInfo`, the keys matching `criteria` are read first,
|
|
54
|
+
* then each document is deleted on its own (re-checking `criteria`), so that the
|
|
55
|
+
* emitted documents are exactly the deleted ones: N + 1 round trips. A document
|
|
56
|
+
* inserted after the keys were read is not deleted.
|
|
57
|
+
*/
|
|
36
58
|
deleteMany(criteria: Criteria<Model>): Promise<void>;
|
|
37
59
|
count(criteria?: Criteria<Model>): Promise<number>;
|
|
38
60
|
cursor<Result extends Partial<Model> = Model>(criteria?: Criteria<Model>, sort?: Sort<Model>): Promise<AbstractStoreCursor<any, KeyValue, Model, Result>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscribeStore.d.ts","sourceRoot":"","sources":["../../src/SubscribeStore.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SubscribeStore.d.ts","sourceRoot":"","sources":["../../src/SubscribeStore.ts"],"names":[],"mappings":"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"}
|
package/dist/index-browser.es.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { NotFoundError, DeletedAfterUpdateError } from 'liwi-store';
|
|
2
|
+
|
|
1
3
|
class SubscribeStore {
|
|
2
4
|
store;
|
|
3
5
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -16,6 +18,68 @@ class SubscribeStore {
|
|
|
16
18
|
callSubscribed(action) {
|
|
17
19
|
this.listeners.forEach((listener) => listener(action));
|
|
18
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Nobody needs `prev` without listeners: writes then go straight to the wrapped
|
|
23
|
+
* store, skipping the per-document reads and loops.
|
|
24
|
+
*/
|
|
25
|
+
hasListeners() {
|
|
26
|
+
return this.listeners.size > 0;
|
|
27
|
+
}
|
|
28
|
+
async findKeys(criteria) {
|
|
29
|
+
const cursor = await this.store.cursor(criteria);
|
|
30
|
+
const keys = [];
|
|
31
|
+
await cursor.forEach((model) => {
|
|
32
|
+
keys.push(model[this.keyPath]);
|
|
33
|
+
});
|
|
34
|
+
return keys;
|
|
35
|
+
}
|
|
36
|
+
async replaceOneAndGetChange(object) {
|
|
37
|
+
const key = object[this.keyPath];
|
|
38
|
+
if (this.store.replaceOneWithInfo) {
|
|
39
|
+
const result = await this.store.replaceOneWithInfo(object);
|
|
40
|
+
if (!result) {
|
|
41
|
+
throw new NotFoundError(`Document not found: ${String(key)}`);
|
|
42
|
+
}
|
|
43
|
+
return [result.prev, result.next];
|
|
44
|
+
}
|
|
45
|
+
const prev = await this.store.findByKey(key);
|
|
46
|
+
if (!prev) throw new NotFoundError(`Document not found: ${String(key)}`);
|
|
47
|
+
return [prev, await this.store.replaceOne(object)];
|
|
48
|
+
}
|
|
49
|
+
async partialUpdateByKeyAndGetResult(key, partialUpdate, criteria, options) {
|
|
50
|
+
if (this.store.partialUpdateByKeyWithInfo) {
|
|
51
|
+
return this.store.partialUpdateByKeyWithInfo(
|
|
52
|
+
key,
|
|
53
|
+
partialUpdate,
|
|
54
|
+
criteria,
|
|
55
|
+
options
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const prev = await this.store.findOne({
|
|
59
|
+
[this.keyPath]: key,
|
|
60
|
+
...criteria
|
|
61
|
+
});
|
|
62
|
+
if (!prev) return void 0;
|
|
63
|
+
return {
|
|
64
|
+
prev,
|
|
65
|
+
next: await this.store.partialUpdateByKey(
|
|
66
|
+
key,
|
|
67
|
+
partialUpdate,
|
|
68
|
+
criteria,
|
|
69
|
+
options
|
|
70
|
+
)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
async deleteByKeyAndGetPrev(key, criteria) {
|
|
74
|
+
if (this.store.deleteByKeyWithInfo) {
|
|
75
|
+
const result = await this.store.deleteByKeyWithInfo(key, criteria);
|
|
76
|
+
return result?.prev;
|
|
77
|
+
}
|
|
78
|
+
const prev = await this.store.findByKey(key, criteria);
|
|
79
|
+
if (!prev) return void 0;
|
|
80
|
+
await this.store.deleteByKey(key, criteria);
|
|
81
|
+
return prev;
|
|
82
|
+
}
|
|
19
83
|
createQuerySingleItem(options) {
|
|
20
84
|
const query = this.store.createQuerySingleItem(options);
|
|
21
85
|
query.setSubscribeStore(this);
|
|
@@ -41,17 +105,25 @@ class SubscribeStore {
|
|
|
41
105
|
return inserted;
|
|
42
106
|
}
|
|
43
107
|
async replaceOne(object) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
108
|
+
if (!this.hasListeners()) return this.store.replaceOne(object);
|
|
109
|
+
const change = await this.replaceOneAndGetChange(object);
|
|
110
|
+
this.callSubscribed({ type: "updated", changes: [change] });
|
|
111
|
+
return change[1];
|
|
47
112
|
}
|
|
48
113
|
async replaceSeveral(objects) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
114
|
+
if (!this.hasListeners()) return this.store.replaceSeveral(objects);
|
|
115
|
+
const results = await Promise.allSettled(
|
|
116
|
+
objects.map((object) => this.replaceOneAndGetChange(object))
|
|
117
|
+
);
|
|
118
|
+
const changes = results.flatMap(
|
|
119
|
+
(result) => result.status === "fulfilled" ? [result.value] : []
|
|
120
|
+
);
|
|
121
|
+
if (changes.length > 0) this.callSubscribed({ type: "updated", changes });
|
|
122
|
+
const failure = results.find(
|
|
123
|
+
(result) => result.status === "rejected"
|
|
124
|
+
);
|
|
125
|
+
if (failure) throw failure.reason;
|
|
126
|
+
return changes.map(([, next]) => next);
|
|
55
127
|
}
|
|
56
128
|
async upsertOne(object, setOnInsertPartialObject) {
|
|
57
129
|
const result = await this.upsertOneWithInfo(
|
|
@@ -78,46 +150,109 @@ class SubscribeStore {
|
|
|
78
150
|
}
|
|
79
151
|
return upsertedWithInfo;
|
|
80
152
|
}
|
|
81
|
-
async partialUpdateByKey(key, partialUpdate, criteria) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
[this.store.keyPath]: key,
|
|
85
|
-
...criteria
|
|
86
|
-
}),
|
|
87
|
-
partialUpdate
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
async partialUpdateOne(object, partialUpdate) {
|
|
91
|
-
const updated = await this.store.partialUpdateOne(object, partialUpdate);
|
|
92
|
-
this.callSubscribed({ type: "updated", changes: [[object, updated]] });
|
|
93
|
-
return updated;
|
|
94
|
-
}
|
|
95
|
-
async partialUpdateMany(criteria, partialUpdate) {
|
|
96
|
-
const cursor = await this.store.cursor(criteria);
|
|
97
|
-
const changes = [];
|
|
98
|
-
await cursor.forEach(async (model) => {
|
|
99
|
-
const key = model[this.store.keyPath];
|
|
100
|
-
const updated = await this.store.partialUpdateByKey(
|
|
153
|
+
async partialUpdateByKey(key, partialUpdate, criteria, options) {
|
|
154
|
+
if (!this.hasListeners()) {
|
|
155
|
+
return this.store.partialUpdateByKey(
|
|
101
156
|
key,
|
|
102
157
|
partialUpdate,
|
|
103
|
-
criteria
|
|
158
|
+
criteria,
|
|
159
|
+
options
|
|
104
160
|
);
|
|
105
|
-
|
|
161
|
+
}
|
|
162
|
+
const result = await this.partialUpdateByKeyAndGetResult(
|
|
163
|
+
key,
|
|
164
|
+
partialUpdate,
|
|
165
|
+
criteria,
|
|
166
|
+
options
|
|
167
|
+
);
|
|
168
|
+
if (!result) throw new NotFoundError(`Document not found: ${String(key)}`);
|
|
169
|
+
if (!result.next) {
|
|
170
|
+
throw new DeletedAfterUpdateError(
|
|
171
|
+
`Document deleted after update: ${String(key)}`
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
this.callSubscribed({
|
|
175
|
+
type: "updated",
|
|
176
|
+
changes: [[result.prev, result.next]]
|
|
106
177
|
});
|
|
107
|
-
|
|
178
|
+
return result.next;
|
|
179
|
+
}
|
|
180
|
+
partialUpdateOne(object, partialUpdate, options) {
|
|
181
|
+
return this.partialUpdateByKey(
|
|
182
|
+
object[this.keyPath],
|
|
183
|
+
partialUpdate,
|
|
184
|
+
void 0,
|
|
185
|
+
options
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Without listeners, one command on the wrapped store. With listeners, the keys
|
|
190
|
+
* matching `criteria` are read first, then each document is updated on its own
|
|
191
|
+
* (re-checking `criteria`) so that every change carries its true `prev`: N + 1
|
|
192
|
+
* round trips. A document inserted after the keys were read is not updated.
|
|
193
|
+
*/
|
|
194
|
+
async partialUpdateMany(criteria, partialUpdate, options) {
|
|
195
|
+
if (!this.hasListeners()) {
|
|
196
|
+
await this.store.partialUpdateMany(criteria, partialUpdate, options);
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
const keys = await this.findKeys(criteria);
|
|
200
|
+
const changes = [];
|
|
201
|
+
try {
|
|
202
|
+
for (const key of keys) {
|
|
203
|
+
const result = await this.partialUpdateByKeyAndGetResult(
|
|
204
|
+
key,
|
|
205
|
+
partialUpdate,
|
|
206
|
+
criteria,
|
|
207
|
+
options
|
|
208
|
+
);
|
|
209
|
+
if (result?.next) changes.push([result.prev, result.next]);
|
|
210
|
+
}
|
|
211
|
+
} finally {
|
|
212
|
+
if (changes.length > 0) this.callSubscribed({ type: "updated", changes });
|
|
213
|
+
}
|
|
108
214
|
}
|
|
109
215
|
async deleteByKey(key, criteria) {
|
|
110
|
-
|
|
216
|
+
if (!this.hasListeners()) {
|
|
217
|
+
await this.store.deleteByKey(key, criteria);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const prev = await this.deleteByKeyAndGetPrev(key, criteria);
|
|
221
|
+
if (!prev) return;
|
|
222
|
+
this.callSubscribed({ type: "deleted", prev: [prev] });
|
|
111
223
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
this.callSubscribed({ type: "deleted", prev: [object] });
|
|
224
|
+
deleteOne(object) {
|
|
225
|
+
return this.deleteByKey(object[this.keyPath]);
|
|
115
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Without listeners, one command on the wrapped store. With listeners and a store
|
|
229
|
+
* implementing `deleteByKeyWithInfo`, the keys matching `criteria` are read first,
|
|
230
|
+
* then each document is deleted on its own (re-checking `criteria`), so that the
|
|
231
|
+
* emitted documents are exactly the deleted ones: N + 1 round trips. A document
|
|
232
|
+
* inserted after the keys were read is not deleted.
|
|
233
|
+
*/
|
|
116
234
|
async deleteMany(criteria) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
235
|
+
if (!this.hasListeners()) {
|
|
236
|
+
await this.store.deleteMany(criteria);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (!this.store.deleteByKeyWithInfo) {
|
|
240
|
+
const cursor = await this.store.cursor(criteria);
|
|
241
|
+
const prev2 = await cursor.toArray();
|
|
242
|
+
await this.store.deleteMany(criteria);
|
|
243
|
+
this.callSubscribed({ type: "deleted", prev: prev2 });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const keys = await this.findKeys(criteria);
|
|
247
|
+
const prev = [];
|
|
248
|
+
try {
|
|
249
|
+
for (const key of keys) {
|
|
250
|
+
const result = await this.store.deleteByKeyWithInfo(key, criteria);
|
|
251
|
+
if (result) prev.push(result.prev);
|
|
252
|
+
}
|
|
253
|
+
} finally {
|
|
254
|
+
if (prev.length > 0) this.callSubscribed({ type: "deleted", prev });
|
|
255
|
+
}
|
|
121
256
|
}
|
|
122
257
|
async count(criteria) {
|
|
123
258
|
return this.store.count(criteria);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-browser.es.js","sources":["../src/SubscribeStore.ts","../src/AbstractSubscribableStoreQuery.ts"],"sourcesContent":["import type {\n AbstractConnection,\n AbstractStoreCursor,\n AllowedKeyValue,\n BaseModel,\n CreateQueryOptions,\n Criteria,\n InsertType,\n OptionalBaseModelKeysForInsert,\n QueryParams,\n Sort,\n Store as StoreInterface,\n SubscribableStore,\n SubscribableStoreQuery,\n Update,\n UpsertPartialObject,\n UpsertResult,\n} from \"liwi-store\";\n\nexport type Actions<Model> =\n | { type: \"deleted\"; prev: Model[] }\n | { type: \"inserted\"; next: Model[] }\n | { type: \"updated\"; changes: [Model, Model][] };\n\nexport type Listener<Model> = (action: Actions<Model>) => unknown;\n\nexport default class SubscribeStore<\n KeyPath extends keyof Model,\n KeyValue extends AllowedKeyValue,\n Model extends BaseModel & Record<KeyPath, KeyValue>,\n ModelInsertType extends InsertType<Model, KeyPath>,\n Connection extends AbstractConnection,\n Store extends SubscribableStore<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n Connection\n >,\n> implements StoreInterface<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n Connection\n> {\n private readonly store: Store;\n\n private readonly listeners = new Set<Listener<Model>>();\n\n readonly keyPath: KeyPath;\n\n constructor(store: Store) {\n this.store = store;\n this.keyPath = store.keyPath;\n }\n\n get connection(): Connection {\n return this.store.connection;\n }\n\n subscribe(callback: Listener<Model>): () => void {\n this.listeners.add(callback);\n return () => this.listeners.delete(callback);\n }\n\n callSubscribed(action: Actions<Model>): void {\n this.listeners.forEach((listener) => listener(action));\n }\n\n createQuerySingleItem<\n Result extends Record<KeyPath, KeyValue>,\n Params extends QueryParams<Params>,\n >(\n options: CreateQueryOptions<Model, Result>,\n ): SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Result,\n Params\n > {\n const query: SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Result,\n Params\n > = this.store.createQuerySingleItem<Result, Params>(options);\n query.setSubscribeStore(this);\n return query;\n }\n\n createQueryCollection<\n Item extends Record<KeyPath, KeyValue>,\n Params extends QueryParams<Params>,\n >(\n options: CreateQueryOptions<Model, Item>,\n ): SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Item[],\n Params\n > {\n const query: SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Item[],\n Params\n > = this.store.createQueryCollection<Item, Params>(options);\n query.setSubscribeStore(this);\n return query;\n }\n\n findAll(criteria?: Criteria<Model>, sort?: Sort<Model>): Promise<Model[]> {\n return this.store.findAll(criteria, sort);\n }\n\n findByKey(\n key: KeyValue,\n criteria?: Criteria<Model>,\n ): Promise<Model | undefined> {\n return this.store.findByKey(key, criteria);\n }\n\n findOne(\n criteria: Criteria<Model>,\n sort?: Sort<Model>,\n ): Promise<Model | undefined> {\n return this.store.findOne(criteria, sort);\n }\n\n async insertOne(object: ModelInsertType): Promise<Model> {\n const inserted = await this.store.insertOne(object);\n this.callSubscribed({ type: \"inserted\", next: [inserted] });\n return inserted;\n }\n\n async replaceOne(object: Model): Promise<Model> {\n const replaced = await this.store.replaceOne(object);\n this.callSubscribed({ type: \"updated\", changes: [[object, replaced]] });\n return replaced;\n }\n\n async replaceSeveral(objects: Model[]): Promise<Model[]> {\n const replacedObjects = await this.store.replaceSeveral(objects);\n this.callSubscribed({\n type: \"updated\",\n changes: objects.map((prev, index) => [prev, replacedObjects[index]!]),\n });\n return replacedObjects;\n }\n\n async upsertOne<\n K extends Exclude<keyof Model, KeyPath | OptionalBaseModelKeysForInsert>,\n >(\n object: UpsertPartialObject<KeyPath, KeyValue, Model, K>,\n setOnInsertPartialObject?: Update<Model>[\"$setOnInsert\"],\n ): Promise<Model> {\n const result = await this.upsertOneWithInfo(\n object,\n setOnInsertPartialObject,\n );\n return result.object;\n }\n\n async upsertOneWithInfo<\n K extends Exclude<keyof Model, KeyPath | OptionalBaseModelKeysForInsert>,\n >(\n object: UpsertPartialObject<KeyPath, KeyValue, Model, K>,\n setOnInsertPartialObject?: Update<Model>[\"$setOnInsert\"],\n ): Promise<UpsertResult<Model>> {\n const upsertedWithInfo = await this.store.upsertOneWithInfo(\n object,\n setOnInsertPartialObject,\n );\n if (upsertedWithInfo.resolvedAs === \"inserted\") {\n this.callSubscribed({\n type: \"inserted\",\n next: [upsertedWithInfo.object],\n });\n } else {\n this.callSubscribed({\n type: \"updated\",\n changes: [[upsertedWithInfo.prev, upsertedWithInfo.object]],\n });\n }\n return upsertedWithInfo;\n }\n\n async partialUpdateByKey(\n key: KeyValue,\n partialUpdate: Update<Model>,\n criteria?: Criteria<Model>,\n ): Promise<Model> {\n return this.partialUpdateOne(\n (await this.findOne({\n [this.store.keyPath]: key,\n ...criteria,\n }))!,\n partialUpdate,\n );\n }\n\n async partialUpdateOne(\n object: Model,\n partialUpdate: Update<Model>,\n ): Promise<Model> {\n const updated = await this.store.partialUpdateOne(object, partialUpdate);\n this.callSubscribed({ type: \"updated\", changes: [[object, updated]] });\n return updated;\n }\n\n async partialUpdateMany(\n criteria: Criteria<Model>,\n partialUpdate: Update<Model>,\n ): Promise<void> {\n const cursor = await this.store.cursor(criteria);\n const changes: [Model, Model][] = [];\n\n await cursor.forEach(async (model) => {\n const key = model[this.store.keyPath];\n const updated = await this.store.partialUpdateByKey(\n key,\n partialUpdate,\n criteria,\n );\n changes.push([model, updated]);\n });\n this.callSubscribed({ type: \"updated\", changes });\n }\n\n async deleteByKey(key: KeyValue, criteria?: Criteria<Model>): Promise<void> {\n return this.deleteOne((await this.findByKey(key, criteria))!);\n }\n\n async deleteOne(object: Model): Promise<void> {\n await this.store.deleteOne(object);\n this.callSubscribed({ type: \"deleted\", prev: [object] });\n }\n\n async deleteMany(criteria: Criteria<Model>): Promise<void> {\n const cursor = await this.store.cursor(criteria);\n const prev: Model[] = await cursor.toArray();\n await this.store.deleteMany(criteria);\n this.callSubscribed({ type: \"deleted\", prev });\n }\n\n async count(criteria?: Criteria<Model>): Promise<number> {\n return this.store.count(criteria);\n }\n\n async cursor<Result extends Partial<Model> = Model>(\n criteria?: Criteria<Model>,\n sort?: Sort<Model>,\n ): Promise<AbstractStoreCursor<any, KeyValue, Model, Result>> {\n const cursor = await this.store.cursor<Result>(criteria, sort);\n cursor.overrideStore(this);\n return cursor;\n }\n}\n","import type {\n AllowedKeyValue,\n BaseModel,\n InsertType,\n Query,\n QueryParams,\n QueryResult,\n QuerySubscription,\n SubscribeCallback,\n} from \"liwi-store\";\nimport type SubscribeStore from \"./SubscribeStore\";\n\nexport default abstract class AbstractSubscribableStoreQuery<\n KeyPath extends keyof Model,\n KeyValue extends AllowedKeyValue,\n Model extends BaseModel & Record<KeyPath, KeyValue>,\n ModelInsertType extends InsertType<Model, KeyPath>,\n Params extends QueryParams<Params> = never,\n Result = Model,\n> implements Query<Result, Params, KeyValue> {\n changeParams(params: Params): never {\n throw new Error(\"Method not supported. Please create a new query.\");\n }\n\n changePartialParams(\n params: Params extends undefined ? never : Partial<Params>,\n ): never {\n throw new Error(\"Method not supported. Please create a new query.\");\n }\n\n private _subscribeStore?: SubscribeStore<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n any,\n any\n >;\n\n setSubscribeStore(\n store: SubscribeStore<KeyPath, KeyValue, Model, ModelInsertType, any, any>,\n ): void {\n this._subscribeStore = store;\n }\n\n getSubscribeStore(): SubscribeStore<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n any,\n any\n > {\n if (!this._subscribeStore) {\n throw new Error(\"_subscribeStore is not initialized\");\n }\n return this._subscribeStore;\n }\n\n abstract fetch<T>(\n onFulfilled: (result: QueryResult<Result>) => T,\n ): Promise<T>;\n\n fetchAndSubscribe(\n callback: SubscribeCallback<KeyValue, Result>,\n ): QuerySubscription {\n return this._subscribe(callback, true);\n }\n\n subscribe(callback: SubscribeCallback<KeyValue, Result>): QuerySubscription {\n return this._subscribe(callback, false);\n }\n\n abstract _subscribe(\n callback: SubscribeCallback<KeyValue, Result>,\n _includeInitial: boolean,\n ): QuerySubscription;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index-browser.es.js","sources":["../src/SubscribeStore.ts","../src/AbstractSubscribableStoreQuery.ts"],"sourcesContent":["import { DeletedAfterUpdateError, NotFoundError } from \"liwi-store\";\nimport type {\n AbstractConnection,\n AbstractStoreCursor,\n AllowedKeyValue,\n BaseModel,\n CreateQueryOptions,\n Criteria,\n InsertType,\n OptionalBaseModelKeysForInsert,\n PartialUpdateOptions,\n PartialUpdateResult,\n QueryParams,\n Sort,\n Store as StoreInterface,\n SubscribableStore,\n SubscribableStoreQuery,\n Update,\n UpsertPartialObject,\n UpsertResult,\n} from \"liwi-store\";\n\nexport type Actions<Model> =\n | { type: \"deleted\"; prev: Model[] }\n | { type: \"inserted\"; next: Model[] }\n | { type: \"updated\"; changes: [Model, Model][] };\n\nexport type Listener<Model> = (action: Actions<Model>) => unknown;\n\nexport default class SubscribeStore<\n KeyPath extends keyof Model,\n KeyValue extends AllowedKeyValue,\n Model extends BaseModel & Record<KeyPath, KeyValue>,\n ModelInsertType extends InsertType<Model, KeyPath>,\n Connection extends AbstractConnection,\n Store extends SubscribableStore<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n Connection\n >,\n> implements StoreInterface<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n Connection\n> {\n private readonly store: Store;\n\n private readonly listeners = new Set<Listener<Model>>();\n\n readonly keyPath: KeyPath;\n\n constructor(store: Store) {\n this.store = store;\n this.keyPath = store.keyPath;\n }\n\n get connection(): Connection {\n return this.store.connection;\n }\n\n subscribe(callback: Listener<Model>): () => void {\n this.listeners.add(callback);\n return () => this.listeners.delete(callback);\n }\n\n callSubscribed(action: Actions<Model>): void {\n this.listeners.forEach((listener) => listener(action));\n }\n\n /**\n * Nobody needs `prev` without listeners: writes then go straight to the wrapped\n * store, skipping the per-document reads and loops.\n */\n private hasListeners(): boolean {\n return this.listeners.size > 0;\n }\n\n private async findKeys(criteria: Criteria<Model>): Promise<KeyValue[]> {\n const cursor = await this.store.cursor(criteria);\n const keys: KeyValue[] = [];\n await cursor.forEach((model) => {\n keys.push(model[this.keyPath]);\n });\n return keys;\n }\n\n private async replaceOneAndGetChange(object: Model): Promise<[Model, Model]> {\n const key = object[this.keyPath];\n if (this.store.replaceOneWithInfo) {\n const result = await this.store.replaceOneWithInfo(object);\n if (!result) {\n throw new NotFoundError(`Document not found: ${String(key)}`);\n }\n return [result.prev, result.next];\n }\n const prev = await this.store.findByKey(key);\n if (!prev) throw new NotFoundError(`Document not found: ${String(key)}`);\n return [prev, await this.store.replaceOne(object)];\n }\n\n private async partialUpdateByKeyAndGetResult(\n key: KeyValue,\n partialUpdate: Update<Model>,\n criteria?: Criteria<Model>,\n options?: PartialUpdateOptions,\n ): Promise<PartialUpdateResult<Model> | undefined> {\n if (this.store.partialUpdateByKeyWithInfo) {\n return this.store.partialUpdateByKeyWithInfo(\n key,\n partialUpdate,\n criteria,\n options,\n );\n }\n const prev = await this.store.findOne({\n [this.keyPath]: key,\n ...criteria,\n });\n if (!prev) return undefined;\n return {\n prev,\n next: await this.store.partialUpdateByKey(\n key,\n partialUpdate,\n criteria,\n options,\n ),\n };\n }\n\n private async deleteByKeyAndGetPrev(\n key: KeyValue,\n criteria?: Criteria<Model>,\n ): Promise<Model | undefined> {\n if (this.store.deleteByKeyWithInfo) {\n const result = await this.store.deleteByKeyWithInfo(key, criteria);\n return result?.prev;\n }\n const prev = await this.store.findByKey(key, criteria);\n if (!prev) return undefined;\n await this.store.deleteByKey(key, criteria);\n return prev;\n }\n\n createQuerySingleItem<\n Result extends Record<KeyPath, KeyValue>,\n Params extends QueryParams<Params>,\n >(\n options: CreateQueryOptions<Model, Result>,\n ): SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Result,\n Params\n > {\n const query: SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Result,\n Params\n > = this.store.createQuerySingleItem<Result, Params>(options);\n query.setSubscribeStore(this);\n return query;\n }\n\n createQueryCollection<\n Item extends Record<KeyPath, KeyValue>,\n Params extends QueryParams<Params>,\n >(\n options: CreateQueryOptions<Model, Item>,\n ): SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Item[],\n Params\n > {\n const query: SubscribableStoreQuery<\n KeyPath,\n KeyValue,\n Model,\n SubscribableStore<KeyPath, KeyValue, Model, ModelInsertType, Connection>,\n Item[],\n Params\n > = this.store.createQueryCollection<Item, Params>(options);\n query.setSubscribeStore(this);\n return query;\n }\n\n findAll(criteria?: Criteria<Model>, sort?: Sort<Model>): Promise<Model[]> {\n return this.store.findAll(criteria, sort);\n }\n\n findByKey(\n key: KeyValue,\n criteria?: Criteria<Model>,\n ): Promise<Model | undefined> {\n return this.store.findByKey(key, criteria);\n }\n\n findOne(\n criteria: Criteria<Model>,\n sort?: Sort<Model>,\n ): Promise<Model | undefined> {\n return this.store.findOne(criteria, sort);\n }\n\n async insertOne(object: ModelInsertType): Promise<Model> {\n const inserted = await this.store.insertOne(object);\n this.callSubscribed({ type: \"inserted\", next: [inserted] });\n return inserted;\n }\n\n async replaceOne(object: Model): Promise<Model> {\n if (!this.hasListeners()) return this.store.replaceOne(object);\n\n const change = await this.replaceOneAndGetChange(object);\n this.callSubscribed({ type: \"updated\", changes: [change] });\n return change[1];\n }\n\n async replaceSeveral(objects: Model[]): Promise<Model[]> {\n if (!this.hasListeners()) return this.store.replaceSeveral(objects);\n\n const results = await Promise.allSettled(\n objects.map((object) => this.replaceOneAndGetChange(object)),\n );\n const changes = results.flatMap((result) =>\n result.status === \"fulfilled\" ? [result.value] : [],\n );\n if (changes.length > 0) this.callSubscribed({ type: \"updated\", changes });\n\n const failure = results.find(\n (result): result is PromiseRejectedResult => result.status === \"rejected\",\n );\n if (failure) throw failure.reason;\n return changes.map(([, next]) => next);\n }\n\n async upsertOne<\n K extends Exclude<keyof Model, KeyPath | OptionalBaseModelKeysForInsert>,\n >(\n object: UpsertPartialObject<KeyPath, KeyValue, Model, K>,\n setOnInsertPartialObject?: Update<Model>[\"$setOnInsert\"],\n ): Promise<Model> {\n const result = await this.upsertOneWithInfo(\n object,\n setOnInsertPartialObject,\n );\n return result.object;\n }\n\n async upsertOneWithInfo<\n K extends Exclude<keyof Model, KeyPath | OptionalBaseModelKeysForInsert>,\n >(\n object: UpsertPartialObject<KeyPath, KeyValue, Model, K>,\n setOnInsertPartialObject?: Update<Model>[\"$setOnInsert\"],\n ): Promise<UpsertResult<Model>> {\n const upsertedWithInfo = await this.store.upsertOneWithInfo(\n object,\n setOnInsertPartialObject,\n );\n if (upsertedWithInfo.resolvedAs === \"inserted\") {\n this.callSubscribed({\n type: \"inserted\",\n next: [upsertedWithInfo.object],\n });\n } else {\n this.callSubscribed({\n type: \"updated\",\n changes: [[upsertedWithInfo.prev, upsertedWithInfo.object]],\n });\n }\n return upsertedWithInfo;\n }\n\n async partialUpdateByKey(\n key: KeyValue,\n partialUpdate: Update<Model>,\n criteria?: Criteria<Model>,\n options?: PartialUpdateOptions,\n ): Promise<Model> {\n if (!this.hasListeners()) {\n return this.store.partialUpdateByKey(\n key,\n partialUpdate,\n criteria,\n options,\n );\n }\n\n const result = await this.partialUpdateByKeyAndGetResult(\n key,\n partialUpdate,\n criteria,\n options,\n );\n if (!result) throw new NotFoundError(`Document not found: ${String(key)}`);\n if (!result.next) {\n throw new DeletedAfterUpdateError(\n `Document deleted after update: ${String(key)}`,\n );\n }\n this.callSubscribed({\n type: \"updated\",\n changes: [[result.prev, result.next]],\n });\n return result.next;\n }\n\n partialUpdateOne(\n object: Model,\n partialUpdate: Update<Model>,\n options?: PartialUpdateOptions,\n ): Promise<Model> {\n return this.partialUpdateByKey(\n object[this.keyPath],\n partialUpdate,\n undefined,\n options,\n );\n }\n\n /**\n * Without listeners, one command on the wrapped store. With listeners, the keys\n * matching `criteria` are read first, then each document is updated on its own\n * (re-checking `criteria`) so that every change carries its true `prev`: N + 1\n * round trips. A document inserted after the keys were read is not updated.\n */\n async partialUpdateMany(\n criteria: Criteria<Model>,\n partialUpdate: Update<Model>,\n options?: PartialUpdateOptions,\n ): Promise<void> {\n if (!this.hasListeners()) {\n await this.store.partialUpdateMany(criteria, partialUpdate, options);\n return;\n }\n\n const keys = await this.findKeys(criteria);\n const changes: [Model, Model][] = [];\n\n try {\n for (const key of keys) {\n const result = await this.partialUpdateByKeyAndGetResult(\n key,\n partialUpdate,\n criteria,\n options,\n );\n if (result?.next) changes.push([result.prev, result.next]);\n }\n } finally {\n if (changes.length > 0) this.callSubscribed({ type: \"updated\", changes });\n }\n }\n\n async deleteByKey(key: KeyValue, criteria?: Criteria<Model>): Promise<void> {\n if (!this.hasListeners()) {\n await this.store.deleteByKey(key, criteria);\n return;\n }\n\n const prev = await this.deleteByKeyAndGetPrev(key, criteria);\n if (!prev) return;\n this.callSubscribed({ type: \"deleted\", prev: [prev] });\n }\n\n deleteOne(object: Model): Promise<void> {\n return this.deleteByKey(object[this.keyPath]);\n }\n\n /**\n * Without listeners, one command on the wrapped store. With listeners and a store\n * implementing `deleteByKeyWithInfo`, the keys matching `criteria` are read first,\n * then each document is deleted on its own (re-checking `criteria`), so that the\n * emitted documents are exactly the deleted ones: N + 1 round trips. A document\n * inserted after the keys were read is not deleted.\n */\n async deleteMany(criteria: Criteria<Model>): Promise<void> {\n if (!this.hasListeners()) {\n await this.store.deleteMany(criteria);\n return;\n }\n\n if (!this.store.deleteByKeyWithInfo) {\n const cursor = await this.store.cursor(criteria);\n const prev: Model[] = await cursor.toArray();\n await this.store.deleteMany(criteria);\n this.callSubscribed({ type: \"deleted\", prev });\n return;\n }\n\n const keys = await this.findKeys(criteria);\n const prev: Model[] = [];\n\n try {\n for (const key of keys) {\n const result = await this.store.deleteByKeyWithInfo(key, criteria);\n if (result) prev.push(result.prev);\n }\n } finally {\n if (prev.length > 0) this.callSubscribed({ type: \"deleted\", prev });\n }\n }\n\n async count(criteria?: Criteria<Model>): Promise<number> {\n return this.store.count(criteria);\n }\n\n async cursor<Result extends Partial<Model> = Model>(\n criteria?: Criteria<Model>,\n sort?: Sort<Model>,\n ): Promise<AbstractStoreCursor<any, KeyValue, Model, Result>> {\n const cursor = await this.store.cursor<Result>(criteria, sort);\n cursor.overrideStore(this);\n return cursor;\n }\n}\n","import type {\n AllowedKeyValue,\n BaseModel,\n InsertType,\n Query,\n QueryParams,\n QueryResult,\n QuerySubscription,\n SubscribeCallback,\n} from \"liwi-store\";\nimport type SubscribeStore from \"./SubscribeStore\";\n\nexport default abstract class AbstractSubscribableStoreQuery<\n KeyPath extends keyof Model,\n KeyValue extends AllowedKeyValue,\n Model extends BaseModel & Record<KeyPath, KeyValue>,\n ModelInsertType extends InsertType<Model, KeyPath>,\n Params extends QueryParams<Params> = never,\n Result = Model,\n> implements Query<Result, Params, KeyValue> {\n changeParams(params: Params): never {\n throw new Error(\"Method not supported. Please create a new query.\");\n }\n\n changePartialParams(\n params: Params extends undefined ? never : Partial<Params>,\n ): never {\n throw new Error(\"Method not supported. Please create a new query.\");\n }\n\n private _subscribeStore?: SubscribeStore<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n any,\n any\n >;\n\n setSubscribeStore(\n store: SubscribeStore<KeyPath, KeyValue, Model, ModelInsertType, any, any>,\n ): void {\n this._subscribeStore = store;\n }\n\n getSubscribeStore(): SubscribeStore<\n KeyPath,\n KeyValue,\n Model,\n ModelInsertType,\n any,\n any\n > {\n if (!this._subscribeStore) {\n throw new Error(\"_subscribeStore is not initialized\");\n }\n return this._subscribeStore;\n }\n\n abstract fetch<T>(\n onFulfilled: (result: QueryResult<Result>) => T,\n ): Promise<T>;\n\n fetchAndSubscribe(\n callback: SubscribeCallback<KeyValue, Result>,\n ): QuerySubscription {\n return this._subscribe(callback, true);\n }\n\n subscribe(callback: SubscribeCallback<KeyValue, Result>): QuerySubscription {\n return this._subscribe(callback, false);\n }\n\n abstract _subscribe(\n callback: SubscribeCallback<KeyValue, Result>,\n _includeInitial: boolean,\n ): QuerySubscription;\n}\n"],"names":["prev"],"mappings":"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"}
|