@verdant-web/store 3.6.4 → 3.7.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/dist/bundle/index.js +12 -12
- package/dist/bundle/index.js.map +3 -3
- package/dist/esm/__tests__/batching.test.js +67 -1
- package/dist/esm/__tests__/batching.test.js.map +1 -1
- package/dist/esm/__tests__/fixtures/testStorage.d.ts +3 -1
- package/dist/esm/__tests__/fixtures/testStorage.js +3 -2
- package/dist/esm/__tests__/fixtures/testStorage.js.map +1 -1
- package/dist/esm/client/Client.d.ts +22 -1
- package/dist/esm/client/Client.js.map +1 -1
- package/dist/esm/client/ClientDescriptor.d.ts +7 -1
- package/dist/esm/client/ClientDescriptor.js +1 -0
- package/dist/esm/client/ClientDescriptor.js.map +1 -1
- package/dist/esm/entities/Entity.d.ts +16 -2
- package/dist/esm/entities/Entity.js +21 -4
- package/dist/esm/entities/Entity.js.map +1 -1
- package/dist/esm/entities/Entity.test.js +23 -2
- package/dist/esm/entities/Entity.test.js.map +1 -1
- package/dist/esm/entities/EntityMetadata.d.ts +2 -0
- package/dist/esm/entities/EntityMetadata.js +9 -1
- package/dist/esm/entities/EntityMetadata.js.map +1 -1
- package/dist/esm/entities/EntityStore.d.ts +1 -2
- package/dist/esm/entities/EntityStore.js +9 -7
- package/dist/esm/entities/EntityStore.js.map +1 -1
- package/dist/esm/entities/OperationBatcher.d.ts +25 -0
- package/dist/esm/entities/OperationBatcher.js +31 -3
- package/dist/esm/entities/OperationBatcher.js.map +1 -1
- package/dist/esm/entities/types.d.ts +7 -1
- package/dist/esm/metadata/Metadata.d.ts +3 -1
- package/dist/esm/metadata/Metadata.js +8 -1
- package/dist/esm/metadata/Metadata.js.map +1 -1
- package/dist/esm/queries/BaseQuery.d.ts +3 -0
- package/dist/esm/queries/BaseQuery.js +45 -11
- package/dist/esm/queries/BaseQuery.js.map +1 -1
- package/dist/esm/sync/Sync.d.ts +8 -2
- package/dist/esm/sync/Sync.js +6 -3
- package/dist/esm/sync/Sync.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/batching.test.ts +78 -0
- package/src/__tests__/fixtures/testStorage.ts +10 -1
- package/src/client/Client.ts +14 -1
- package/src/client/ClientDescriptor.ts +9 -0
- package/src/entities/Entity.test.ts +30 -2
- package/src/entities/Entity.ts +57 -10
- package/src/entities/EntityMetadata.ts +13 -3
- package/src/entities/EntityStore.ts +9 -9
- package/src/entities/OperationBatcher.ts +69 -2
- package/src/entities/types.ts +18 -1
- package/src/metadata/Metadata.ts +13 -0
- package/src/queries/BaseQuery.ts +50 -13
- package/src/sync/Sync.ts +12 -2
package/src/metadata/Metadata.ts
CHANGED
|
@@ -60,12 +60,16 @@ export class Metadata extends EventSubscriber<{
|
|
|
60
60
|
|
|
61
61
|
private context: Omit<Context, 'documentDb' | 'getNow'>;
|
|
62
62
|
|
|
63
|
+
private onOperation?: (operation: Operation) => void;
|
|
64
|
+
|
|
63
65
|
constructor({
|
|
64
66
|
disableRebasing,
|
|
65
67
|
context,
|
|
68
|
+
onOperation,
|
|
66
69
|
}: {
|
|
67
70
|
disableRebasing?: boolean;
|
|
68
71
|
context: Omit<Context, 'documentDb' | 'getNow'>;
|
|
72
|
+
onOperation?: (operation: Operation) => void;
|
|
69
73
|
}) {
|
|
70
74
|
super();
|
|
71
75
|
this.context = context;
|
|
@@ -76,6 +80,7 @@ export class Metadata extends EventSubscriber<{
|
|
|
76
80
|
this.ackInfo = new AckInfoStore(this.db);
|
|
77
81
|
this.messageCreator = new MessageCreator(this);
|
|
78
82
|
this.patchCreator = new PatchCreator(() => this.now);
|
|
83
|
+
this.onOperation = onOperation;
|
|
79
84
|
if (disableRebasing) this.disableRebasing = disableRebasing;
|
|
80
85
|
}
|
|
81
86
|
|
|
@@ -312,6 +317,10 @@ export class Metadata extends EventSubscriber<{
|
|
|
312
317
|
|
|
313
318
|
// we can now enqueue and check for rebase opportunities
|
|
314
319
|
this.tryAutonomousRebase();
|
|
320
|
+
|
|
321
|
+
if (this.onOperation) {
|
|
322
|
+
operations.forEach((o) => this.onOperation!(o));
|
|
323
|
+
}
|
|
315
324
|
};
|
|
316
325
|
|
|
317
326
|
/**
|
|
@@ -340,6 +349,10 @@ export class Metadata extends EventSubscriber<{
|
|
|
340
349
|
|
|
341
350
|
this.ack(operations[operations.length - 1].timestamp);
|
|
342
351
|
|
|
352
|
+
if (this.onOperation) {
|
|
353
|
+
operations.forEach((o) => this.onOperation!(o));
|
|
354
|
+
}
|
|
355
|
+
|
|
343
356
|
return affectedDocumentOids;
|
|
344
357
|
};
|
|
345
358
|
|
package/src/queries/BaseQuery.ts
CHANGED
|
@@ -42,6 +42,7 @@ export abstract class BaseQuery<T> extends Disposable {
|
|
|
42
42
|
|
|
43
43
|
readonly collection;
|
|
44
44
|
readonly key;
|
|
45
|
+
readonly isListQuery;
|
|
45
46
|
|
|
46
47
|
constructor({
|
|
47
48
|
initial,
|
|
@@ -53,6 +54,7 @@ export abstract class BaseQuery<T> extends Disposable {
|
|
|
53
54
|
super();
|
|
54
55
|
this._rawValue = initial;
|
|
55
56
|
this._value = initial;
|
|
57
|
+
this.isListQuery = Array.isArray(initial);
|
|
56
58
|
this._events = new EventSubscriber<BaseQueryEvents>(
|
|
57
59
|
(event: keyof BaseQueryEvents) => {
|
|
58
60
|
if (event === 'change') this._allUnsubscribedHandler?.(this);
|
|
@@ -98,6 +100,19 @@ export abstract class BaseQuery<T> extends Disposable {
|
|
|
98
100
|
return this._status;
|
|
99
101
|
}
|
|
100
102
|
|
|
103
|
+
private set status(v: QueryStatus) {
|
|
104
|
+
if (this._status === v) return;
|
|
105
|
+
this._status = v;
|
|
106
|
+
this._events.emit('statusChange', this._status);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
get hasDeleted() {
|
|
110
|
+
if (this.isListQuery) {
|
|
111
|
+
return (this._rawValue as any[]).length !== (this._value as any[]).length;
|
|
112
|
+
}
|
|
113
|
+
return !!this._rawValue && !this._value;
|
|
114
|
+
}
|
|
115
|
+
|
|
101
116
|
/**
|
|
102
117
|
* Subscribe to changes in the query value.
|
|
103
118
|
*
|
|
@@ -138,16 +153,38 @@ export abstract class BaseQuery<T> extends Disposable {
|
|
|
138
153
|
protected setValue = (value: T) => {
|
|
139
154
|
this._rawValue = value;
|
|
140
155
|
this.subscribeToDeleteAndRestore(this._rawValue);
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
156
|
+
const filtered = filterResultSet(value);
|
|
157
|
+
|
|
158
|
+
// prevent excess change notifications by diffing
|
|
159
|
+
// value by identity for single-value queries,
|
|
160
|
+
// and by item identity for multi-value
|
|
161
|
+
let changed = true;
|
|
162
|
+
// always fire change when going from initial to ready
|
|
163
|
+
if (this.status === 'initializing' || this.status === 'initial') {
|
|
164
|
+
changed = true;
|
|
165
|
+
} else {
|
|
166
|
+
// compare values by identity, after filtering.
|
|
167
|
+
if (this.isListQuery) {
|
|
168
|
+
if (
|
|
169
|
+
(this._value as any[]).length === (filtered as any[]).length &&
|
|
170
|
+
(this._value as any[]).every((v, i) => v === (filtered as any[])[i])
|
|
171
|
+
) {
|
|
172
|
+
changed = false;
|
|
173
|
+
}
|
|
174
|
+
} else {
|
|
175
|
+
if (this._value === filtered) {
|
|
176
|
+
changed = false;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this._value = filtered;
|
|
182
|
+
|
|
183
|
+
if (changed) {
|
|
184
|
+
this.context.log('debug', 'Query value changed', this.key);
|
|
185
|
+
this._events.emit('change', this._value);
|
|
148
186
|
}
|
|
149
|
-
this.
|
|
150
|
-
this._events.emit('change', this._value);
|
|
187
|
+
this.status = 'ready';
|
|
151
188
|
};
|
|
152
189
|
|
|
153
190
|
// re-applies filtering if results have changed
|
|
@@ -186,10 +223,10 @@ export abstract class BaseQuery<T> extends Disposable {
|
|
|
186
223
|
execute = () => {
|
|
187
224
|
this.context.log('debug', 'Executing query', this.key);
|
|
188
225
|
|
|
189
|
-
if (this.
|
|
190
|
-
this.
|
|
191
|
-
} else if (this.
|
|
192
|
-
this.
|
|
226
|
+
if (this.status === 'initial') {
|
|
227
|
+
this.status = 'initializing';
|
|
228
|
+
} else if (this.status === 'ready') {
|
|
229
|
+
this.status = 'revalidating';
|
|
193
230
|
}
|
|
194
231
|
// no status change needed if already in a 'running' status.
|
|
195
232
|
|
package/src/sync/Sync.ts
CHANGED
|
@@ -173,6 +173,11 @@ export interface ServerSyncOptions<Profile = any, Presence = any>
|
|
|
173
173
|
* but is not yet thoroughly vetted.
|
|
174
174
|
*/
|
|
175
175
|
useBroadcastChannel?: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Listen for outgoing messages from the client to the server.
|
|
178
|
+
* Not sure why you want to do this, but be careful.
|
|
179
|
+
*/
|
|
180
|
+
onOutgoingMessage?: (message: ClientMessage) => void;
|
|
176
181
|
}
|
|
177
182
|
|
|
178
183
|
export class ServerSync<Presence = any, Profile = any>
|
|
@@ -196,6 +201,8 @@ export class ServerSync<Presence = any, Profile = any>
|
|
|
196
201
|
|
|
197
202
|
readonly presence: PresenceManager<Profile, Presence>;
|
|
198
203
|
|
|
204
|
+
private onOutgoingMessage?: (message: ClientMessage) => void;
|
|
205
|
+
|
|
199
206
|
private log;
|
|
200
207
|
|
|
201
208
|
constructor(
|
|
@@ -211,6 +218,7 @@ export class ServerSync<Presence = any, Profile = any>
|
|
|
211
218
|
presenceUpdateBatchTimeout,
|
|
212
219
|
defaultProfile,
|
|
213
220
|
useBroadcastChannel,
|
|
221
|
+
onOutgoingMessage,
|
|
214
222
|
}: ServerSyncOptions<Profile, Presence>,
|
|
215
223
|
{
|
|
216
224
|
meta,
|
|
@@ -230,6 +238,7 @@ export class ServerSync<Presence = any, Profile = any>
|
|
|
230
238
|
this.meta = meta;
|
|
231
239
|
this.onData = onData;
|
|
232
240
|
this.log = ctx.log;
|
|
241
|
+
this.onOutgoingMessage = onOutgoingMessage;
|
|
233
242
|
this.presence = new PresenceManager({
|
|
234
243
|
initialPresence,
|
|
235
244
|
defaultProfile,
|
|
@@ -444,9 +453,10 @@ export class ServerSync<Presence = any, Profile = any>
|
|
|
444
453
|
return this.pushPullSync.interval;
|
|
445
454
|
}
|
|
446
455
|
|
|
447
|
-
send = (message: ClientMessage) => {
|
|
456
|
+
send = async (message: ClientMessage) => {
|
|
448
457
|
if (this.activeSync.status === 'active') {
|
|
449
|
-
|
|
458
|
+
await this.activeSync.send(message);
|
|
459
|
+
this.onOutgoingMessage?.(message);
|
|
450
460
|
}
|
|
451
461
|
};
|
|
452
462
|
|