@tanstack/db 0.3.1 → 0.3.2
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/cjs/collection-events.cjs +88 -0
- package/dist/cjs/collection-events.cjs.map +1 -0
- package/dist/cjs/collection-events.d.cts +50 -0
- package/dist/cjs/collection.cjs +45 -0
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +22 -0
- package/dist/esm/collection-events.d.ts +50 -0
- package/dist/esm/collection-events.js +88 -0
- package/dist/esm/collection-events.js.map +1 -0
- package/dist/esm/collection.d.ts +22 -0
- package/dist/esm/collection.js +45 -0
- package/dist/esm/collection.js.map +1 -1
- package/package.json +1 -1
- package/src/collection-events.ts +169 -0
- package/src/collection.ts +76 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SortedMap } from './SortedMap.cjs';
|
|
2
2
|
import { BTreeIndex } from './indexes/btree-index.js';
|
|
3
3
|
import { IndexProxy } from './indexes/lazy-index.js';
|
|
4
|
+
import { AllCollectionEvents, CollectionEventHandler } from './collection-events.js';
|
|
4
5
|
import { Transaction } from './transactions.cjs';
|
|
5
6
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
6
7
|
import { SingleRowRefProxy } from './query/builder/ref-proxy.cjs';
|
|
@@ -131,6 +132,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
|
|
|
131
132
|
private gcTimeoutId;
|
|
132
133
|
private preloadPromise;
|
|
133
134
|
private syncCleanupFn;
|
|
135
|
+
private events;
|
|
134
136
|
/**
|
|
135
137
|
* Register a callback to be executed when the collection first becomes ready
|
|
136
138
|
* Useful for preloading collections
|
|
@@ -167,6 +169,10 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
|
|
|
167
169
|
* Gets the current status of the collection
|
|
168
170
|
*/
|
|
169
171
|
get status(): CollectionStatus;
|
|
172
|
+
/**
|
|
173
|
+
* Get the number of subscribers to the collection
|
|
174
|
+
*/
|
|
175
|
+
get subscriberCount(): number;
|
|
170
176
|
/**
|
|
171
177
|
* Validates that the collection is in a usable state for data operations
|
|
172
178
|
* @private
|
|
@@ -575,5 +581,21 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
|
|
|
575
581
|
* This method should be called by the Transaction class when state changes
|
|
576
582
|
*/
|
|
577
583
|
onTransactionStateChange(): void;
|
|
584
|
+
/**
|
|
585
|
+
* Subscribe to a collection event
|
|
586
|
+
*/
|
|
587
|
+
on<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
588
|
+
/**
|
|
589
|
+
* Subscribe to a collection event once
|
|
590
|
+
*/
|
|
591
|
+
once<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
592
|
+
/**
|
|
593
|
+
* Unsubscribe from a collection event
|
|
594
|
+
*/
|
|
595
|
+
off<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): void;
|
|
596
|
+
/**
|
|
597
|
+
* Wait for a collection event
|
|
598
|
+
*/
|
|
599
|
+
waitFor<T extends keyof AllCollectionEvents>(event: T, timeout?: number): Promise<AllCollectionEvents[T]>;
|
|
578
600
|
}
|
|
579
601
|
export {};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { Collection } from './collection.js';
|
|
2
|
+
import { CollectionStatus } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Event emitted when the collection status changes
|
|
5
|
+
*/
|
|
6
|
+
export interface CollectionStatusChangeEvent {
|
|
7
|
+
type: `status:change`;
|
|
8
|
+
collection: Collection;
|
|
9
|
+
previousStatus: CollectionStatus;
|
|
10
|
+
status: CollectionStatus;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Event emitted when the collection status changes to a specific status
|
|
14
|
+
*/
|
|
15
|
+
export interface CollectionStatusEvent<T extends CollectionStatus> {
|
|
16
|
+
type: `status:${T}`;
|
|
17
|
+
collection: Collection;
|
|
18
|
+
previousStatus: CollectionStatus;
|
|
19
|
+
status: T;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Event emitted when the number of subscribers to the collection changes
|
|
23
|
+
*/
|
|
24
|
+
export interface CollectionSubscribersChangeEvent {
|
|
25
|
+
type: `subscribers:change`;
|
|
26
|
+
collection: Collection;
|
|
27
|
+
previousSubscriberCount: number;
|
|
28
|
+
subscriberCount: number;
|
|
29
|
+
}
|
|
30
|
+
export type AllCollectionEvents = {
|
|
31
|
+
"status:change": CollectionStatusChangeEvent;
|
|
32
|
+
"subscribers:change": CollectionSubscribersChangeEvent;
|
|
33
|
+
} & {
|
|
34
|
+
[K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>;
|
|
35
|
+
};
|
|
36
|
+
export type CollectionEvent = AllCollectionEvents[keyof AllCollectionEvents] | CollectionStatusChangeEvent | CollectionSubscribersChangeEvent;
|
|
37
|
+
export type CollectionEventHandler<T extends keyof AllCollectionEvents> = (event: AllCollectionEvents[T]) => void;
|
|
38
|
+
export declare class CollectionEvents {
|
|
39
|
+
private collection;
|
|
40
|
+
private listeners;
|
|
41
|
+
constructor(collection: Collection<any, any, any, any, any>);
|
|
42
|
+
on<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
43
|
+
once<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
44
|
+
off<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): void;
|
|
45
|
+
waitFor<T extends keyof AllCollectionEvents>(event: T, timeout?: number): Promise<AllCollectionEvents[T]>;
|
|
46
|
+
emit<T extends keyof AllCollectionEvents>(event: T, eventPayload: AllCollectionEvents[T]): void;
|
|
47
|
+
emitStatusChange<T extends CollectionStatus>(status: T, previousStatus: CollectionStatus): void;
|
|
48
|
+
emitSubscribersChange(subscriberCount: number, previousSubscriberCount: number): void;
|
|
49
|
+
cleanup(): void;
|
|
50
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
class CollectionEvents {
|
|
2
|
+
constructor(collection) {
|
|
3
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
4
|
+
this.collection = collection;
|
|
5
|
+
}
|
|
6
|
+
on(event, callback) {
|
|
7
|
+
if (!this.listeners.has(event)) {
|
|
8
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
9
|
+
}
|
|
10
|
+
this.listeners.get(event).add(callback);
|
|
11
|
+
return () => {
|
|
12
|
+
this.listeners.get(event).delete(callback);
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
once(event, callback) {
|
|
16
|
+
const unsubscribe = this.on(event, (eventPayload) => {
|
|
17
|
+
callback(eventPayload);
|
|
18
|
+
unsubscribe();
|
|
19
|
+
});
|
|
20
|
+
return unsubscribe;
|
|
21
|
+
}
|
|
22
|
+
off(event, callback) {
|
|
23
|
+
var _a;
|
|
24
|
+
(_a = this.listeners.get(event)) == null ? void 0 : _a.delete(callback);
|
|
25
|
+
}
|
|
26
|
+
waitFor(event, timeout) {
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
let timeoutId;
|
|
29
|
+
const unsubscribe = this.on(event, (eventPayload) => {
|
|
30
|
+
if (timeoutId) {
|
|
31
|
+
clearTimeout(timeoutId);
|
|
32
|
+
timeoutId = void 0;
|
|
33
|
+
}
|
|
34
|
+
resolve(eventPayload);
|
|
35
|
+
unsubscribe();
|
|
36
|
+
});
|
|
37
|
+
if (timeout) {
|
|
38
|
+
timeoutId = setTimeout(() => {
|
|
39
|
+
timeoutId = void 0;
|
|
40
|
+
unsubscribe();
|
|
41
|
+
reject(new Error(`Timeout waiting for event ${event}`));
|
|
42
|
+
}, timeout);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
emit(event, eventPayload) {
|
|
47
|
+
var _a;
|
|
48
|
+
(_a = this.listeners.get(event)) == null ? void 0 : _a.forEach((listener) => {
|
|
49
|
+
try {
|
|
50
|
+
listener(eventPayload);
|
|
51
|
+
} catch (error) {
|
|
52
|
+
queueMicrotask(() => {
|
|
53
|
+
throw error;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
emitStatusChange(status, previousStatus) {
|
|
59
|
+
this.emit(`status:change`, {
|
|
60
|
+
type: `status:change`,
|
|
61
|
+
collection: this.collection,
|
|
62
|
+
previousStatus,
|
|
63
|
+
status
|
|
64
|
+
});
|
|
65
|
+
const eventKey = `status:${status}`;
|
|
66
|
+
this.emit(eventKey, {
|
|
67
|
+
type: eventKey,
|
|
68
|
+
collection: this.collection,
|
|
69
|
+
previousStatus,
|
|
70
|
+
status
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
emitSubscribersChange(subscriberCount, previousSubscriberCount) {
|
|
74
|
+
this.emit(`subscribers:change`, {
|
|
75
|
+
type: `subscribers:change`,
|
|
76
|
+
collection: this.collection,
|
|
77
|
+
previousSubscriberCount,
|
|
78
|
+
subscriberCount
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
cleanup() {
|
|
82
|
+
this.listeners.clear();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export {
|
|
86
|
+
CollectionEvents
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=collection-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection-events.js","sources":["../../src/collection-events.ts"],"sourcesContent":["import type { Collection } from \"./collection\"\nimport type { CollectionStatus } from \"./types\"\n\n/**\n * Event emitted when the collection status changes\n */\nexport interface CollectionStatusChangeEvent {\n type: `status:change`\n collection: Collection\n previousStatus: CollectionStatus\n status: CollectionStatus\n}\n\n/**\n * Event emitted when the collection status changes to a specific status\n */\nexport interface CollectionStatusEvent<T extends CollectionStatus> {\n type: `status:${T}`\n collection: Collection\n previousStatus: CollectionStatus\n status: T\n}\n\n/**\n * Event emitted when the number of subscribers to the collection changes\n */\nexport interface CollectionSubscribersChangeEvent {\n type: `subscribers:change`\n collection: Collection\n previousSubscriberCount: number\n subscriberCount: number\n}\n\nexport type AllCollectionEvents = {\n \"status:change\": CollectionStatusChangeEvent\n \"subscribers:change\": CollectionSubscribersChangeEvent\n} & {\n [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>\n}\n\nexport type CollectionEvent =\n | AllCollectionEvents[keyof AllCollectionEvents]\n | CollectionStatusChangeEvent\n | CollectionSubscribersChangeEvent\n\nexport type CollectionEventHandler<T extends keyof AllCollectionEvents> = (\n event: AllCollectionEvents[T]\n) => void\n\nexport class CollectionEvents {\n private collection: Collection<any, any, any, any, any>\n private listeners = new Map<\n keyof AllCollectionEvents,\n Set<CollectionEventHandler<any>>\n >()\n\n constructor(collection: Collection<any, any, any, any, any>) {\n this.collection = collection\n }\n\n on<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set())\n }\n this.listeners.get(event)!.add(callback)\n\n return () => {\n this.listeners.get(event)!.delete(callback)\n }\n }\n\n once<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n const unsubscribe = this.on(event, (eventPayload) => {\n callback(eventPayload)\n unsubscribe()\n })\n return unsubscribe\n }\n\n off<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n this.listeners.get(event)?.delete(callback)\n }\n\n waitFor<T extends keyof AllCollectionEvents>(\n event: T,\n timeout?: number\n ): Promise<AllCollectionEvents[T]> {\n return new Promise((resolve, reject) => {\n let timeoutId: NodeJS.Timeout | undefined\n const unsubscribe = this.on(event, (eventPayload) => {\n if (timeoutId) {\n clearTimeout(timeoutId)\n timeoutId = undefined\n }\n resolve(eventPayload)\n unsubscribe()\n })\n if (timeout) {\n timeoutId = setTimeout(() => {\n timeoutId = undefined\n unsubscribe()\n reject(new Error(`Timeout waiting for event ${event}`))\n }, timeout)\n }\n })\n }\n\n emit<T extends keyof AllCollectionEvents>(\n event: T,\n eventPayload: AllCollectionEvents[T]\n ) {\n this.listeners.get(event)?.forEach((listener) => {\n try {\n listener(eventPayload)\n } catch (error) {\n // Re-throw in a microtask to surface the error\n queueMicrotask(() => {\n throw error\n })\n }\n })\n }\n\n emitStatusChange<T extends CollectionStatus>(\n status: T,\n previousStatus: CollectionStatus\n ) {\n this.emit(`status:change`, {\n type: `status:change`,\n collection: this.collection,\n previousStatus,\n status,\n })\n\n // Emit specific status event using type assertion\n const eventKey: `status:${T}` = `status:${status}`\n this.emit(eventKey, {\n type: eventKey,\n collection: this.collection,\n previousStatus,\n status,\n } as AllCollectionEvents[`status:${T}`])\n }\n\n emitSubscribersChange(\n subscriberCount: number,\n previousSubscriberCount: number\n ) {\n this.emit(`subscribers:change`, {\n type: `subscribers:change`,\n collection: this.collection,\n previousSubscriberCount,\n subscriberCount,\n })\n }\n\n cleanup() {\n this.listeners.clear()\n }\n}\n"],"names":[],"mappings":"AAiDO,MAAM,iBAAiB;AAAA,EAO5B,YAAY,YAAiD;AAL7D,SAAQ,gCAAgB,IAAA;AAMtB,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,GACE,OACA,UACA;AACA,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,KAAK;AAAA,IACrC;AACA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,QAAQ;AAEvC,WAAO,MAAM;AACX,WAAK,UAAU,IAAI,KAAK,EAAG,OAAO,QAAQ;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,KACE,OACA,UACA;AACA,UAAM,cAAc,KAAK,GAAG,OAAO,CAAC,iBAAiB;AACnD,eAAS,YAAY;AACrB,kBAAA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,IACE,OACA,UACA;AAvCG;AAwCH,eAAK,UAAU,IAAI,KAAK,MAAxB,mBAA2B,OAAO;AAAA,EACpC;AAAA,EAEA,QACE,OACA,SACiC;AACjC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACJ,YAAM,cAAc,KAAK,GAAG,OAAO,CAAC,iBAAiB;AACnD,YAAI,WAAW;AACb,uBAAa,SAAS;AACtB,sBAAY;AAAA,QACd;AACA,gBAAQ,YAAY;AACpB,oBAAA;AAAA,MACF,CAAC;AACD,UAAI,SAAS;AACX,oBAAY,WAAW,MAAM;AAC3B,sBAAY;AACZ,sBAAA;AACA,iBAAO,IAAI,MAAM,6BAA6B,KAAK,EAAE,CAAC;AAAA,QACxD,GAAG,OAAO;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,KACE,OACA,cACA;AAtEG;AAuEH,eAAK,UAAU,IAAI,KAAK,MAAxB,mBAA2B,QAAQ,CAAC,aAAa;AAC/C,UAAI;AACF,iBAAS,YAAY;AAAA,MACvB,SAAS,OAAO;AAEd,uBAAe,MAAM;AACnB,gBAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBACE,QACA,gBACA;AACA,SAAK,KAAK,iBAAiB;AAAA,MACzB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAGD,UAAM,WAA0B,UAAU,MAAM;AAChD,SAAK,KAAK,UAAU;AAAA,MAClB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACqC;AAAA,EACzC;AAAA,EAEA,sBACE,iBACA,yBACA;AACA,SAAK,KAAK,sBAAsB;AAAA,MAC9B,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEA,UAAU;AACR,SAAK,UAAU,MAAA;AAAA,EACjB;AACF;"}
|
package/dist/esm/collection.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { SortedMap } from './SortedMap.js';
|
|
2
2
|
import { BTreeIndex } from './indexes/btree-index.js';
|
|
3
3
|
import { IndexProxy } from './indexes/lazy-index.js';
|
|
4
|
+
import { AllCollectionEvents, CollectionEventHandler } from './collection-events.js';
|
|
4
5
|
import { Transaction } from './transactions.js';
|
|
5
6
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
6
7
|
import { SingleRowRefProxy } from './query/builder/ref-proxy.js';
|
|
@@ -131,6 +132,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
|
|
|
131
132
|
private gcTimeoutId;
|
|
132
133
|
private preloadPromise;
|
|
133
134
|
private syncCleanupFn;
|
|
135
|
+
private events;
|
|
134
136
|
/**
|
|
135
137
|
* Register a callback to be executed when the collection first becomes ready
|
|
136
138
|
* Useful for preloading collections
|
|
@@ -167,6 +169,10 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
|
|
|
167
169
|
* Gets the current status of the collection
|
|
168
170
|
*/
|
|
169
171
|
get status(): CollectionStatus;
|
|
172
|
+
/**
|
|
173
|
+
* Get the number of subscribers to the collection
|
|
174
|
+
*/
|
|
175
|
+
get subscriberCount(): number;
|
|
170
176
|
/**
|
|
171
177
|
* Validates that the collection is in a usable state for data operations
|
|
172
178
|
* @private
|
|
@@ -575,5 +581,21 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
|
|
|
575
581
|
* This method should be called by the Transaction class when state changes
|
|
576
582
|
*/
|
|
577
583
|
onTransactionStateChange(): void;
|
|
584
|
+
/**
|
|
585
|
+
* Subscribe to a collection event
|
|
586
|
+
*/
|
|
587
|
+
on<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
588
|
+
/**
|
|
589
|
+
* Subscribe to a collection event once
|
|
590
|
+
*/
|
|
591
|
+
once<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
592
|
+
/**
|
|
593
|
+
* Unsubscribe from a collection event
|
|
594
|
+
*/
|
|
595
|
+
off<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): void;
|
|
596
|
+
/**
|
|
597
|
+
* Wait for a collection event
|
|
598
|
+
*/
|
|
599
|
+
waitFor<T extends keyof AllCollectionEvents>(event: T, timeout?: number): Promise<AllCollectionEvents[T]>;
|
|
578
600
|
}
|
|
579
601
|
export {};
|
package/dist/esm/collection.js
CHANGED
|
@@ -8,6 +8,7 @@ import { ensureIndexForExpression } from "./indexes/auto-index.js";
|
|
|
8
8
|
import { getActiveTransaction, createTransaction } from "./transactions.js";
|
|
9
9
|
import { MissingInsertHandlerError, DuplicateKeyError, MissingDeleteHandlerError, NoKeysPassedToDeleteError, DeleteKeyNotFoundError, CollectionRequiresConfigError, CollectionRequiresSyncConfigError, CollectionInErrorStateError, InvalidCollectionStatusTransitionError, NoPendingSyncTransactionWriteError, SyncTransactionAlreadyCommittedWriteError, NoPendingSyncTransactionCommitError, SyncTransactionAlreadyCommittedError, DuplicateKeySyncError, CollectionIsInErrorStateError, SyncCleanupError, NegativeActiveSubscribersError, InvalidSchemaError, UndefinedKeyError, SchemaMustBeSynchronousError, SchemaValidationError, MissingUpdateArgumentError, MissingUpdateHandlerError, NoKeysPassedToUpdateError, UpdateKeyNotFoundError, KeyUpdateNotAllowedError } from "./errors.js";
|
|
10
10
|
import { currentStateAsChanges, createFilteredCallback } from "./change-events.js";
|
|
11
|
+
import { CollectionEvents } from "./collection-events.js";
|
|
11
12
|
function createCollection(options) {
|
|
12
13
|
const collection = new CollectionImpl(
|
|
13
14
|
options
|
|
@@ -460,6 +461,7 @@ class CollectionImpl {
|
|
|
460
461
|
} else {
|
|
461
462
|
this.syncedData = /* @__PURE__ */ new Map();
|
|
462
463
|
}
|
|
464
|
+
this.events = new CollectionEvents(this);
|
|
463
465
|
if (config.startSync === true) {
|
|
464
466
|
this.startSync();
|
|
465
467
|
}
|
|
@@ -525,6 +527,12 @@ class CollectionImpl {
|
|
|
525
527
|
get status() {
|
|
526
528
|
return this._status;
|
|
527
529
|
}
|
|
530
|
+
/**
|
|
531
|
+
* Get the number of subscribers to the collection
|
|
532
|
+
*/
|
|
533
|
+
get subscriberCount() {
|
|
534
|
+
return this.activeSubscribersCount;
|
|
535
|
+
}
|
|
528
536
|
/**
|
|
529
537
|
* Validates that the collection is in a usable state for data operations
|
|
530
538
|
* @private
|
|
@@ -564,12 +572,14 @@ class CollectionImpl {
|
|
|
564
572
|
*/
|
|
565
573
|
setStatus(newStatus) {
|
|
566
574
|
this.validateStatusTransition(this._status, newStatus);
|
|
575
|
+
const previousStatus = this._status;
|
|
567
576
|
this._status = newStatus;
|
|
568
577
|
if (newStatus === `ready` && !this.isIndexesResolved) {
|
|
569
578
|
this.resolveAllIndexes().catch((error) => {
|
|
570
579
|
console.warn(`Failed to resolve indexes:`, error);
|
|
571
580
|
});
|
|
572
581
|
}
|
|
582
|
+
this.events.emitStatusChange(newStatus, previousStatus);
|
|
573
583
|
}
|
|
574
584
|
/**
|
|
575
585
|
* Start sync immediately - internal method for compiled queries
|
|
@@ -729,6 +739,7 @@ class CollectionImpl {
|
|
|
729
739
|
this.preloadPromise = null;
|
|
730
740
|
this.batchedEvents = [];
|
|
731
741
|
this.shouldBatchEvents = false;
|
|
742
|
+
this.events.cleanup();
|
|
732
743
|
this.setStatus(`cleaned-up`);
|
|
733
744
|
return Promise.resolve();
|
|
734
745
|
}
|
|
@@ -764,22 +775,32 @@ class CollectionImpl {
|
|
|
764
775
|
* Increment the active subscribers count and start sync if needed
|
|
765
776
|
*/
|
|
766
777
|
addSubscriber() {
|
|
778
|
+
const previousSubscriberCount = this.activeSubscribersCount;
|
|
767
779
|
this.activeSubscribersCount++;
|
|
768
780
|
this.cancelGCTimer();
|
|
769
781
|
if (this._status === `cleaned-up` || this._status === `idle`) {
|
|
770
782
|
this.startSync();
|
|
771
783
|
}
|
|
784
|
+
this.events.emitSubscribersChange(
|
|
785
|
+
this.activeSubscribersCount,
|
|
786
|
+
previousSubscriberCount
|
|
787
|
+
);
|
|
772
788
|
}
|
|
773
789
|
/**
|
|
774
790
|
* Decrement the active subscribers count and start GC timer if needed
|
|
775
791
|
*/
|
|
776
792
|
removeSubscriber() {
|
|
793
|
+
const previousSubscriberCount = this.activeSubscribersCount;
|
|
777
794
|
this.activeSubscribersCount--;
|
|
778
795
|
if (this.activeSubscribersCount === 0) {
|
|
779
796
|
this.startGCTimer();
|
|
780
797
|
} else if (this.activeSubscribersCount < 0) {
|
|
781
798
|
throw new NegativeActiveSubscribersError();
|
|
782
799
|
}
|
|
800
|
+
this.events.emitSubscribersChange(
|
|
801
|
+
this.activeSubscribersCount,
|
|
802
|
+
previousSubscriberCount
|
|
803
|
+
);
|
|
783
804
|
}
|
|
784
805
|
/**
|
|
785
806
|
* Recompute optimistic state from active transactions
|
|
@@ -1572,6 +1593,30 @@ class CollectionImpl {
|
|
|
1572
1593
|
this.capturePreSyncVisibleState();
|
|
1573
1594
|
this.recomputeOptimisticState(false);
|
|
1574
1595
|
}
|
|
1596
|
+
/**
|
|
1597
|
+
* Subscribe to a collection event
|
|
1598
|
+
*/
|
|
1599
|
+
on(event, callback) {
|
|
1600
|
+
return this.events.on(event, callback);
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Subscribe to a collection event once
|
|
1604
|
+
*/
|
|
1605
|
+
once(event, callback) {
|
|
1606
|
+
return this.events.once(event, callback);
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* Unsubscribe from a collection event
|
|
1610
|
+
*/
|
|
1611
|
+
off(event, callback) {
|
|
1612
|
+
this.events.off(event, callback);
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Wait for a collection event
|
|
1616
|
+
*/
|
|
1617
|
+
waitFor(event, timeout) {
|
|
1618
|
+
return this.events.waitFor(event, timeout);
|
|
1619
|
+
}
|
|
1575
1620
|
}
|
|
1576
1621
|
export {
|
|
1577
1622
|
CollectionImpl,
|