@tanstack/db 0.3.2 → 0.4.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 (171) hide show
  1. package/dist/cjs/{change-events.cjs → collection/change-events.cjs} +13 -42
  2. package/dist/cjs/collection/change-events.cjs.map +1 -0
  3. package/dist/{esm/change-events.d.ts → cjs/collection/change-events.d.cts} +6 -6
  4. package/dist/cjs/collection/changes.cjs +108 -0
  5. package/dist/cjs/collection/changes.cjs.map +1 -0
  6. package/dist/cjs/collection/changes.d.cts +53 -0
  7. package/dist/cjs/{collection-events.cjs → collection/events.cjs} +7 -5
  8. package/dist/cjs/collection/events.cjs.map +1 -0
  9. package/dist/cjs/{collection-events.d.cts → collection/events.d.cts} +7 -4
  10. package/dist/cjs/collection/index.cjs +417 -0
  11. package/dist/cjs/collection/index.cjs.map +1 -0
  12. package/dist/{esm/collection.d.ts → cjs/collection/index.d.cts} +46 -184
  13. package/dist/cjs/collection/indexes.cjs +124 -0
  14. package/dist/cjs/collection/indexes.cjs.map +1 -0
  15. package/dist/cjs/collection/indexes.d.cts +47 -0
  16. package/dist/cjs/collection/lifecycle.cjs +196 -0
  17. package/dist/cjs/collection/lifecycle.cjs.map +1 -0
  18. package/dist/cjs/collection/lifecycle.d.cts +81 -0
  19. package/dist/cjs/collection/mutations.cjs +315 -0
  20. package/dist/cjs/collection/mutations.cjs.map +1 -0
  21. package/dist/cjs/collection/mutations.d.cts +33 -0
  22. package/dist/cjs/collection/state.cjs +597 -0
  23. package/dist/cjs/collection/state.cjs.map +1 -0
  24. package/dist/cjs/collection/state.d.cts +122 -0
  25. package/dist/cjs/collection/subscription.cjs +160 -0
  26. package/dist/cjs/collection/subscription.cjs.map +1 -0
  27. package/dist/cjs/collection/subscription.d.cts +57 -0
  28. package/dist/cjs/collection/sync.cjs +154 -0
  29. package/dist/cjs/collection/sync.cjs.map +1 -0
  30. package/dist/cjs/collection/sync.d.cts +34 -0
  31. package/dist/cjs/index.cjs +8 -8
  32. package/dist/cjs/index.d.cts +2 -2
  33. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  34. package/dist/cjs/indexes/auto-index.d.cts +1 -1
  35. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  36. package/dist/cjs/indexes/base-index.d.cts +2 -2
  37. package/dist/cjs/indexes/btree-index.cjs +2 -2
  38. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  39. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  40. package/dist/cjs/query/builder/index.cjs +2 -2
  41. package/dist/cjs/query/builder/index.cjs.map +1 -1
  42. package/dist/cjs/query/builder/types.d.cts +1 -1
  43. package/dist/cjs/query/compiler/index.cjs +5 -2
  44. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  45. package/dist/cjs/query/compiler/index.d.cts +3 -2
  46. package/dist/cjs/query/compiler/joins.cjs +22 -24
  47. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  48. package/dist/cjs/query/compiler/joins.d.cts +3 -2
  49. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  50. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  51. package/dist/cjs/query/ir.cjs.map +1 -1
  52. package/dist/cjs/query/ir.d.cts +1 -1
  53. package/dist/cjs/query/live/collection-config-builder.cjs +29 -12
  54. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  55. package/dist/cjs/query/live/collection-config-builder.d.cts +3 -0
  56. package/dist/cjs/query/live/collection-subscriber.cjs +43 -104
  57. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  58. package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
  59. package/dist/cjs/query/live-query-collection.cjs +2 -2
  60. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  61. package/dist/cjs/query/live-query-collection.d.cts +1 -1
  62. package/dist/cjs/transactions.cjs +3 -3
  63. package/dist/cjs/transactions.cjs.map +1 -1
  64. package/dist/cjs/types.d.cts +12 -10
  65. package/dist/cjs/utils/browser-polyfills.cjs +22 -0
  66. package/dist/cjs/utils/browser-polyfills.cjs.map +1 -0
  67. package/dist/cjs/utils/browser-polyfills.d.cts +9 -0
  68. package/dist/{cjs/change-events.d.cts → esm/collection/change-events.d.ts} +6 -6
  69. package/dist/esm/{change-events.js → collection/change-events.js} +13 -42
  70. package/dist/esm/collection/change-events.js.map +1 -0
  71. package/dist/esm/collection/changes.d.ts +53 -0
  72. package/dist/esm/collection/changes.js +108 -0
  73. package/dist/esm/collection/changes.js.map +1 -0
  74. package/dist/esm/{collection-events.d.ts → collection/events.d.ts} +7 -4
  75. package/dist/esm/{collection-events.js → collection/events.js} +7 -5
  76. package/dist/esm/collection/events.js.map +1 -0
  77. package/dist/{cjs/collection.d.cts → esm/collection/index.d.ts} +46 -184
  78. package/dist/esm/collection/index.js +417 -0
  79. package/dist/esm/collection/index.js.map +1 -0
  80. package/dist/esm/collection/indexes.d.ts +47 -0
  81. package/dist/esm/collection/indexes.js +124 -0
  82. package/dist/esm/collection/indexes.js.map +1 -0
  83. package/dist/esm/collection/lifecycle.d.ts +81 -0
  84. package/dist/esm/collection/lifecycle.js +196 -0
  85. package/dist/esm/collection/lifecycle.js.map +1 -0
  86. package/dist/esm/collection/mutations.d.ts +33 -0
  87. package/dist/esm/collection/mutations.js +315 -0
  88. package/dist/esm/collection/mutations.js.map +1 -0
  89. package/dist/esm/collection/state.d.ts +122 -0
  90. package/dist/esm/collection/state.js +597 -0
  91. package/dist/esm/collection/state.js.map +1 -0
  92. package/dist/esm/collection/subscription.d.ts +57 -0
  93. package/dist/esm/collection/subscription.js +160 -0
  94. package/dist/esm/collection/subscription.js.map +1 -0
  95. package/dist/esm/collection/sync.d.ts +34 -0
  96. package/dist/esm/collection/sync.js +154 -0
  97. package/dist/esm/collection/sync.js.map +1 -0
  98. package/dist/esm/index.d.ts +2 -2
  99. package/dist/esm/index.js +1 -1
  100. package/dist/esm/indexes/auto-index.d.ts +1 -1
  101. package/dist/esm/indexes/auto-index.js.map +1 -1
  102. package/dist/esm/indexes/base-index.d.ts +2 -2
  103. package/dist/esm/indexes/base-index.js.map +1 -1
  104. package/dist/esm/indexes/btree-index.d.ts +1 -1
  105. package/dist/esm/indexes/btree-index.js +2 -2
  106. package/dist/esm/indexes/btree-index.js.map +1 -1
  107. package/dist/esm/proxy.js +1 -1
  108. package/dist/esm/query/builder/index.js +1 -1
  109. package/dist/esm/query/builder/index.js.map +1 -1
  110. package/dist/esm/query/builder/types.d.ts +1 -1
  111. package/dist/esm/query/compiler/index.d.ts +3 -2
  112. package/dist/esm/query/compiler/index.js +5 -2
  113. package/dist/esm/query/compiler/index.js.map +1 -1
  114. package/dist/esm/query/compiler/joins.d.ts +3 -2
  115. package/dist/esm/query/compiler/joins.js +22 -24
  116. package/dist/esm/query/compiler/joins.js.map +1 -1
  117. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  118. package/dist/esm/query/compiler/order-by.js.map +1 -1
  119. package/dist/esm/query/ir.d.ts +1 -1
  120. package/dist/esm/query/ir.js.map +1 -1
  121. package/dist/esm/query/live/collection-config-builder.d.ts +3 -0
  122. package/dist/esm/query/live/collection-config-builder.js +29 -12
  123. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  124. package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
  125. package/dist/esm/query/live/collection-subscriber.js +43 -104
  126. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  127. package/dist/esm/query/live-query-collection.d.ts +1 -1
  128. package/dist/esm/query/live-query-collection.js +1 -1
  129. package/dist/esm/query/live-query-collection.js.map +1 -1
  130. package/dist/esm/transactions.js +3 -3
  131. package/dist/esm/transactions.js.map +1 -1
  132. package/dist/esm/types.d.ts +12 -10
  133. package/dist/esm/utils/browser-polyfills.d.ts +9 -0
  134. package/dist/esm/utils/browser-polyfills.js +22 -0
  135. package/dist/esm/utils/browser-polyfills.js.map +1 -0
  136. package/package.json +2 -2
  137. package/src/{change-events.ts → collection/change-events.ts} +25 -39
  138. package/src/collection/changes.ts +163 -0
  139. package/src/{collection-events.ts → collection/events.ts} +8 -6
  140. package/src/collection/index.ts +808 -0
  141. package/src/collection/indexes.ts +172 -0
  142. package/src/collection/lifecycle.ts +289 -0
  143. package/src/collection/mutations.ts +535 -0
  144. package/src/collection/state.ts +866 -0
  145. package/src/collection/subscription.ts +239 -0
  146. package/src/collection/sync.ts +235 -0
  147. package/src/index.ts +2 -2
  148. package/src/indexes/auto-index.ts +1 -1
  149. package/src/indexes/base-index.ts +3 -3
  150. package/src/indexes/btree-index.ts +2 -2
  151. package/src/query/builder/index.ts +1 -1
  152. package/src/query/builder/types.ts +1 -1
  153. package/src/query/compiler/index.ts +7 -1
  154. package/src/query/compiler/joins.ts +28 -41
  155. package/src/query/compiler/order-by.ts +1 -1
  156. package/src/query/ir.ts +1 -1
  157. package/src/query/live/collection-config-builder.ts +48 -22
  158. package/src/query/live/collection-subscriber.ts +63 -168
  159. package/src/query/live-query-collection.ts +2 -2
  160. package/src/transactions.ts +3 -3
  161. package/src/types.ts +14 -15
  162. package/src/utils/browser-polyfills.ts +39 -0
  163. package/dist/cjs/change-events.cjs.map +0 -1
  164. package/dist/cjs/collection-events.cjs.map +0 -1
  165. package/dist/cjs/collection.cjs +0 -1625
  166. package/dist/cjs/collection.cjs.map +0 -1
  167. package/dist/esm/change-events.js.map +0 -1
  168. package/dist/esm/collection-events.js.map +0 -1
  169. package/dist/esm/collection.js +0 -1625
  170. package/dist/esm/collection.js.map +0 -1
  171. package/src/collection.ts +0 -2564
@@ -1,19 +1,13 @@
1
- import { SortedMap } from './SortedMap.js';
2
- import { BTreeIndex } from './indexes/btree-index.js';
3
- import { IndexProxy } from './indexes/lazy-index.js';
4
- import { AllCollectionEvents, CollectionEventHandler } from './collection-events.js';
5
- import { Transaction } from './transactions.js';
1
+ import { CollectionStateManager } from './state.cjs';
2
+ import { CollectionSubscription } from './subscription.cjs';
3
+ import { AllCollectionEvents, CollectionEventHandler } from './events.js';
4
+ import { BaseIndex, IndexResolver } from '../indexes/base-index.js';
5
+ import { IndexOptions } from '../indexes/index-options.js';
6
+ import { ChangeMessage, CollectionConfig, CollectionStatus, CurrentStateAsChangesOptions, Fn, InferSchemaInput, InferSchemaOutput, InsertConfig, OperationConfig, SubscribeChangesOptions, Transaction as TransactionType, UtilsRecord, WritableDeep } from '../types.cjs';
7
+ import { SingleRowRefProxy } from '../query/builder/ref-proxy.cjs';
6
8
  import { StandardSchemaV1 } from '@standard-schema/spec';
7
- import { SingleRowRefProxy } from './query/builder/ref-proxy.js';
8
- import { ChangeListener, ChangeMessage, CollectionConfig, CollectionStatus, CurrentStateAsChangesOptions, Fn, InferSchemaInput, InferSchemaOutput, InsertConfig, OperationConfig, OptimisticChangeMessage, SubscribeChangesOptions, Transaction as TransactionType, UtilsRecord, WritableDeep } from './types.js';
9
- import { IndexOptions } from './indexes/index-options.js';
10
- import { BaseIndex, IndexResolver } from './indexes/base-index.js';
11
- interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
12
- committed: boolean;
13
- operations: Array<OptimisticChangeMessage<T>>;
14
- truncate?: boolean;
15
- deletedKeys: Set<string | number>;
16
- }
9
+ import { BTreeIndex } from '../indexes/btree-index.js';
10
+ import { IndexProxy } from '../indexes/lazy-index.js';
17
11
  /**
18
12
  * Enhanced Collection interface that includes both data type T and utilities TUtils
19
13
  * @template T - The type of items in the collection
@@ -103,36 +97,31 @@ export declare function createCollection<T extends object, TKey extends string |
103
97
  utils?: TUtils;
104
98
  }): Collection<T, TKey, TUtils, never, T>;
105
99
  export declare class CollectionImpl<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
100
+ id: string;
106
101
  config: CollectionConfig<TOutput, TKey, TSchema>;
107
- transactions: SortedMap<string, Transaction<any>>;
108
- pendingSyncedTransactions: Array<PendingSyncedTransaction<TOutput>>;
109
- syncedData: Map<TKey, TOutput> | SortedMap<TKey, TOutput>;
110
- syncedMetadata: Map<TKey, unknown>;
111
- optimisticUpserts: Map<TKey, TOutput>;
112
- optimisticDeletes: Set<TKey>;
113
- private _size;
114
- private lazyIndexes;
115
- private resolvedIndexes;
116
- private isIndexesResolved;
117
- private indexCounter;
118
- private changeListeners;
119
- private changeKeyListeners;
120
102
  utils: Record<string, Fn>;
121
- private syncedKeys;
122
- private preSyncVisibleState;
123
- private recentlySyncedKeys;
124
- private hasReceivedFirstCommit;
125
- private isCommittingSyncTransactions;
126
- private onFirstReadyCallbacks;
127
- private hasBeenReady;
128
- private batchedEvents;
129
- private shouldBatchEvents;
130
- private _status;
131
- private activeSubscribersCount;
132
- private gcTimeoutId;
133
- private preloadPromise;
134
- private syncCleanupFn;
135
- private events;
103
+ private _events;
104
+ private _changes;
105
+ private _lifecycle;
106
+ private _sync;
107
+ private _indexes;
108
+ private _mutations;
109
+ _state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
110
+ /**
111
+ * Creates a new Collection instance
112
+ *
113
+ * @param config - Configuration object for the collection
114
+ * @throws Error if sync config is missing
115
+ */
116
+ constructor(config: CollectionConfig<TOutput, TKey, TSchema>);
117
+ /**
118
+ * Gets the current status of the collection
119
+ */
120
+ get status(): CollectionStatus;
121
+ /**
122
+ * Get the number of subscribers to the collection
123
+ */
124
+ get subscriberCount(): number;
136
125
  /**
137
126
  * Register a callback to be executed when the collection first becomes ready
138
127
  * Useful for preloading collections
@@ -157,107 +146,16 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
157
146
  * }
158
147
  */
159
148
  isReady(): boolean;
160
- /**
161
- * Mark the collection as ready for use
162
- * This is called by sync implementations to explicitly signal that the collection is ready,
163
- * providing a more intuitive alternative to using commits for readiness signaling
164
- * @private - Should only be called by sync implementations
165
- */
166
- private markReady;
167
- id: string;
168
- /**
169
- * Gets the current status of the collection
170
- */
171
- get status(): CollectionStatus;
172
- /**
173
- * Get the number of subscribers to the collection
174
- */
175
- get subscriberCount(): number;
176
- /**
177
- * Validates that the collection is in a usable state for data operations
178
- * @private
179
- */
180
- private validateCollectionUsable;
181
- /**
182
- * Validates state transitions to prevent invalid status changes
183
- * @private
184
- */
185
- private validateStatusTransition;
186
- /**
187
- * Safely update the collection status with validation
188
- * @private
189
- */
190
- private setStatus;
191
- /**
192
- * Creates a new Collection instance
193
- *
194
- * @param config - Configuration object for the collection
195
- * @throws Error if sync config is missing
196
- */
197
- constructor(config: CollectionConfig<TOutput, TKey, TSchema>);
198
149
  /**
199
150
  * Start sync immediately - internal method for compiled queries
200
151
  * This bypasses lazy loading for special cases like live query results
201
152
  */
202
153
  startSyncImmediate(): void;
203
- /**
204
- * Start the sync process for this collection
205
- * This is called when the collection is first accessed or preloaded
206
- */
207
- private startSync;
208
154
  /**
209
155
  * Preload the collection data by starting sync if not already started
210
156
  * Multiple concurrent calls will share the same promise
211
157
  */
212
158
  preload(): Promise<void>;
213
- /**
214
- * Clean up the collection by stopping sync and clearing data
215
- * This can be called manually or automatically by garbage collection
216
- */
217
- cleanup(): Promise<void>;
218
- /**
219
- * Start the garbage collection timer
220
- * Called when the collection becomes inactive (no subscribers)
221
- */
222
- private startGCTimer;
223
- /**
224
- * Cancel the garbage collection timer
225
- * Called when the collection becomes active again
226
- */
227
- private cancelGCTimer;
228
- /**
229
- * Increment the active subscribers count and start sync if needed
230
- */
231
- private addSubscriber;
232
- /**
233
- * Decrement the active subscribers count and start GC timer if needed
234
- */
235
- private removeSubscriber;
236
- /**
237
- * Recompute optimistic state from active transactions
238
- */
239
- private recomputeOptimisticState;
240
- /**
241
- * Calculate the current size based on synced data and optimistic changes
242
- */
243
- private calculateSize;
244
- /**
245
- * Collect events for optimistic changes
246
- */
247
- private collectOptimisticChanges;
248
- /**
249
- * Get the previous value for a key given previous optimistic state
250
- */
251
- private getPreviousValue;
252
- /**
253
- * Emit an empty ready event to notify subscribers that the collection is ready
254
- * This bypasses the normal empty array check in emitEvents
255
- */
256
- private emitEmptyReadyEvent;
257
- /**
258
- * Emit events either immediately or batch them for later emission
259
- */
260
- private emitEvents;
261
159
  /**
262
160
  * Get the current value for a key (virtual derived state)
263
161
  */
@@ -294,19 +192,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
294
192
  * Create a new array with the results of calling a function for each entry in the collection
295
193
  */
296
194
  map<U>(callbackfn: (value: TOutput, key: TKey, index: number) => U): Array<U>;
297
- /**
298
- * Attempts to commit pending synced transactions if there are no active transactions
299
- * This method processes operations from pending transactions and applies them to the synced data
300
- */
301
- commitPendingTransactions: () => void;
302
- /**
303
- * Schedule cleanup of a transaction when it completes
304
- * @private
305
- */
306
- private scheduleTransactionCleanup;
307
- private ensureStandardSchema;
308
195
  getKeyFromItem(item: TOutput): TKey;
309
- generateGlobalKey(key: any, item: any): string;
310
196
  /**
311
197
  * Creates an index on a collection for faster queries.
312
198
  * Indexes significantly improve query performance by allowing constant time lookups
@@ -338,25 +224,13 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
338
224
  * })
339
225
  */
340
226
  createIndex<TResolver extends IndexResolver<TKey> = typeof BTreeIndex>(indexCallback: (row: SingleRowRefProxy<TOutput>) => any, config?: IndexOptions<TResolver>): IndexProxy<TKey>;
341
- /**
342
- * Resolve all lazy indexes (called when collection first syncs)
343
- * @private
344
- */
345
- private resolveAllIndexes;
346
- /**
347
- * Resolve a single index immediately
348
- * @private
349
- */
350
- private resolveSingleIndex;
351
227
  /**
352
228
  * Get resolved indexes for query optimization
353
229
  */
354
230
  get indexes(): Map<number, BaseIndex<TKey>>;
355
231
  /**
356
- * Updates all indexes when the collection changes
357
- * @private
232
+ * Validates the data against the schema
358
233
  */
359
- private updateIndexes;
360
234
  validateData(data: unknown, type: `insert` | `update`, key?: TKey): TOutput | never;
361
235
  /**
362
236
  * Inserts one or more items into the collection
@@ -394,7 +268,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
394
268
  * console.log('Insert failed:', error)
395
269
  * }
396
270
  */
397
- insert: (data: TInput | Array<TInput>, config?: InsertConfig) => Transaction<Record<string, unknown>> | Transaction<TOutput>;
271
+ insert: (data: TInput | Array<TInput>, config?: InsertConfig) => TransactionType<Record<string, unknown>> | TransactionType<TOutput>;
398
272
  /**
399
273
  * Updates one or more items in the collection using a callback function
400
274
  * @param keys - Single key or array of keys to update
@@ -524,7 +398,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
524
398
  * whereExpression: eq(row.status, 'active')
525
399
  * })
526
400
  */
527
- currentStateAsChanges(options?: CurrentStateAsChangesOptions<TOutput>): Array<ChangeMessage<TOutput>>;
401
+ currentStateAsChanges(options?: CurrentStateAsChangesOptions): Array<ChangeMessage<TOutput>> | void;
528
402
  /**
529
403
  * Subscribe to changes in the collection
530
404
  * @param callback - Function called when items change
@@ -532,23 +406,23 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
532
406
  * @returns Unsubscribe function - Call this to stop listening for changes
533
407
  * @example
534
408
  * // Basic subscription
535
- * const unsubscribe = collection.subscribeChanges((changes) => {
409
+ * const subscription = collection.subscribeChanges((changes) => {
536
410
  * changes.forEach(change => {
537
411
  * console.log(`${change.type}: ${change.key}`, change.value)
538
412
  * })
539
413
  * })
540
414
  *
541
- * // Later: unsubscribe()
415
+ * // Later: subscription.unsubscribe()
542
416
  *
543
417
  * @example
544
418
  * // Include current state immediately
545
- * const unsubscribe = collection.subscribeChanges((changes) => {
419
+ * const subscription = collection.subscribeChanges((changes) => {
546
420
  * updateUI(changes)
547
421
  * }, { includeInitialState: true })
548
422
  *
549
423
  * @example
550
424
  * // Subscribe only to changes matching a condition
551
- * const unsubscribe = collection.subscribeChanges((changes) => {
425
+ * const subscription = collection.subscribeChanges((changes) => {
552
426
  * updateUI(changes)
553
427
  * }, {
554
428
  * includeInitialState: true,
@@ -557,30 +431,14 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
557
431
  *
558
432
  * @example
559
433
  * // Subscribe using a pre-compiled expression
560
- * const unsubscribe = collection.subscribeChanges((changes) => {
434
+ * const subscription = collection.subscribeChanges((changes) => {
561
435
  * updateUI(changes)
562
436
  * }, {
563
437
  * includeInitialState: true,
564
438
  * whereExpression: eq(row.status, 'active')
565
439
  * })
566
440
  */
567
- subscribeChanges(callback: (changes: Array<ChangeMessage<TOutput>>) => void, options?: SubscribeChangesOptions<TOutput>): () => void;
568
- /**
569
- * Subscribe to changes for a specific key
570
- */
571
- subscribeChangesKey(key: TKey, listener: ChangeListener<TOutput, TKey>, { includeInitialState }?: {
572
- includeInitialState?: boolean;
573
- }): () => void;
574
- /**
575
- * Capture visible state for keys that will be affected by pending sync operations
576
- * This must be called BEFORE onTransactionStateChange clears optimistic state
577
- */
578
- private capturePreSyncVisibleState;
579
- /**
580
- * Trigger a recomputation when transactions change
581
- * This method should be called by the Transaction class when state changes
582
- */
583
- onTransactionStateChange(): void;
441
+ subscribeChanges(callback: (changes: Array<ChangeMessage<TOutput>>) => void, options?: SubscribeChangesOptions): CollectionSubscription;
584
442
  /**
585
443
  * Subscribe to a collection event
586
444
  */
@@ -597,5 +455,9 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
597
455
  * Wait for a collection event
598
456
  */
599
457
  waitFor<T extends keyof AllCollectionEvents>(event: T, timeout?: number): Promise<AllCollectionEvents[T]>;
458
+ /**
459
+ * Clean up the collection by stopping sync and clearing data
460
+ * This can be called manually or automatically by garbage collection
461
+ */
462
+ cleanup(): Promise<void>;
600
463
  }
601
- export {};
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const lazyIndex = require("../indexes/lazy-index.cjs");
4
+ const refProxy = require("../query/builder/ref-proxy.cjs");
5
+ const btreeIndex = require("../indexes/btree-index.cjs");
6
+ class CollectionIndexesManager {
7
+ constructor() {
8
+ this.lazyIndexes = /* @__PURE__ */ new Map();
9
+ this.resolvedIndexes = /* @__PURE__ */ new Map();
10
+ this.isIndexesResolved = false;
11
+ this.indexCounter = 0;
12
+ }
13
+ setDeps(deps) {
14
+ this.state = deps.state;
15
+ this.lifecycle = deps.lifecycle;
16
+ }
17
+ /**
18
+ * Creates an index on a collection for faster queries.
19
+ */
20
+ createIndex(indexCallback, config = {}) {
21
+ this.lifecycle.validateCollectionUsable(`createIndex`);
22
+ const indexId = ++this.indexCounter;
23
+ const singleRowRefProxy = refProxy.createSingleRowRefProxy();
24
+ const indexExpression = indexCallback(singleRowRefProxy);
25
+ const expression = refProxy.toExpression(indexExpression);
26
+ const resolver = config.indexType ?? btreeIndex.BTreeIndex;
27
+ const lazyIndex$1 = new lazyIndex.LazyIndexWrapper(
28
+ indexId,
29
+ expression,
30
+ config.name,
31
+ resolver,
32
+ config.options,
33
+ this.state.entries()
34
+ );
35
+ this.lazyIndexes.set(indexId, lazyIndex$1);
36
+ if (resolver === btreeIndex.BTreeIndex) {
37
+ try {
38
+ const resolvedIndex = lazyIndex$1.getResolved();
39
+ this.resolvedIndexes.set(indexId, resolvedIndex);
40
+ } catch (error) {
41
+ console.warn(`Failed to resolve BTreeIndex:`, error);
42
+ }
43
+ } else if (typeof resolver === `function` && resolver.prototype) {
44
+ try {
45
+ const resolvedIndex = lazyIndex$1.getResolved();
46
+ this.resolvedIndexes.set(indexId, resolvedIndex);
47
+ } catch {
48
+ this.resolveSingleIndex(indexId, lazyIndex$1).catch((error) => {
49
+ console.warn(`Failed to resolve single index:`, error);
50
+ });
51
+ }
52
+ } else if (this.isIndexesResolved) {
53
+ this.resolveSingleIndex(indexId, lazyIndex$1).catch((error) => {
54
+ console.warn(`Failed to resolve single index:`, error);
55
+ });
56
+ }
57
+ return new lazyIndex.IndexProxy(indexId, lazyIndex$1);
58
+ }
59
+ /**
60
+ * Resolve all lazy indexes (called when collection first syncs)
61
+ */
62
+ async resolveAllIndexes() {
63
+ if (this.isIndexesResolved) return;
64
+ const resolutionPromises = Array.from(this.lazyIndexes.entries()).map(
65
+ async ([indexId, lazyIndex2]) => {
66
+ const resolvedIndex = await lazyIndex2.resolve();
67
+ resolvedIndex.build(this.state.entries());
68
+ this.resolvedIndexes.set(indexId, resolvedIndex);
69
+ return { indexId, resolvedIndex };
70
+ }
71
+ );
72
+ await Promise.all(resolutionPromises);
73
+ this.isIndexesResolved = true;
74
+ }
75
+ /**
76
+ * Resolve a single index immediately
77
+ */
78
+ async resolveSingleIndex(indexId, lazyIndex2) {
79
+ const resolvedIndex = await lazyIndex2.resolve();
80
+ resolvedIndex.build(this.state.entries());
81
+ this.resolvedIndexes.set(indexId, resolvedIndex);
82
+ return resolvedIndex;
83
+ }
84
+ /**
85
+ * Get resolved indexes for query optimization
86
+ */
87
+ get indexes() {
88
+ return this.resolvedIndexes;
89
+ }
90
+ /**
91
+ * Updates all indexes when the collection changes
92
+ */
93
+ updateIndexes(changes) {
94
+ for (const index of this.resolvedIndexes.values()) {
95
+ for (const change of changes) {
96
+ switch (change.type) {
97
+ case `insert`:
98
+ index.add(change.key, change.value);
99
+ break;
100
+ case `update`:
101
+ if (change.previousValue) {
102
+ index.update(change.key, change.previousValue, change.value);
103
+ } else {
104
+ index.add(change.key, change.value);
105
+ }
106
+ break;
107
+ case `delete`:
108
+ index.remove(change.key, change.value);
109
+ break;
110
+ }
111
+ }
112
+ }
113
+ }
114
+ /**
115
+ * Clean up the collection by stopping sync and clearing data
116
+ * This can be called manually or automatically by garbage collection
117
+ */
118
+ cleanup() {
119
+ this.lazyIndexes.clear();
120
+ this.resolvedIndexes.clear();
121
+ }
122
+ }
123
+ exports.CollectionIndexesManager = CollectionIndexesManager;
124
+ //# sourceMappingURL=indexes.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"indexes.cjs","sources":["../../../src/collection/indexes.ts"],"sourcesContent":["import { IndexProxy, LazyIndexWrapper } from \"../indexes/lazy-index\"\nimport {\n createSingleRowRefProxy,\n toExpression,\n} from \"../query/builder/ref-proxy\"\nimport { BTreeIndex } from \"../indexes/btree-index\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\nimport type { BaseIndex, IndexResolver } from \"../indexes/base-index\"\nimport type { ChangeMessage } from \"../types\"\nimport type { IndexOptions } from \"../indexes/index-options\"\nimport type { SingleRowRefProxy } from \"../query/builder/ref-proxy\"\nimport type { CollectionLifecycleManager } from \"./lifecycle\"\nimport type { CollectionStateManager } from \"./state\"\n\nexport class CollectionIndexesManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private state!: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n\n public lazyIndexes = new Map<number, LazyIndexWrapper<TKey>>()\n public resolvedIndexes = new Map<number, BaseIndex<TKey>>()\n public isIndexesResolved = false\n public indexCounter = 0\n\n constructor() {}\n\n setDeps(deps: {\n state: CollectionStateManager<TOutput, TKey, TSchema, TInput>\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n }) {\n this.state = deps.state\n this.lifecycle = deps.lifecycle\n }\n\n /**\n * Creates an index on a collection for faster queries.\n */\n public createIndex<TResolver extends IndexResolver<TKey> = typeof BTreeIndex>(\n indexCallback: (row: SingleRowRefProxy<TOutput>) => any,\n config: IndexOptions<TResolver> = {}\n ): IndexProxy<TKey> {\n this.lifecycle.validateCollectionUsable(`createIndex`)\n\n const indexId = ++this.indexCounter\n const singleRowRefProxy = createSingleRowRefProxy<TOutput>()\n const indexExpression = indexCallback(singleRowRefProxy)\n const expression = toExpression(indexExpression)\n\n // Default to BTreeIndex if no type specified\n const resolver = config.indexType ?? (BTreeIndex as unknown as TResolver)\n\n // Create lazy wrapper\n const lazyIndex = new LazyIndexWrapper<TKey>(\n indexId,\n expression,\n config.name,\n resolver,\n config.options,\n this.state.entries()\n )\n\n this.lazyIndexes.set(indexId, lazyIndex)\n\n // For BTreeIndex, resolve immediately and synchronously\n if ((resolver as unknown) === BTreeIndex) {\n try {\n const resolvedIndex = lazyIndex.getResolved()\n this.resolvedIndexes.set(indexId, resolvedIndex)\n } catch (error) {\n console.warn(`Failed to resolve BTreeIndex:`, error)\n }\n } else if (typeof resolver === `function` && resolver.prototype) {\n // Other synchronous constructors - resolve immediately\n try {\n const resolvedIndex = lazyIndex.getResolved()\n this.resolvedIndexes.set(indexId, resolvedIndex)\n } catch {\n // Fallback to async resolution\n this.resolveSingleIndex(indexId, lazyIndex).catch((error) => {\n console.warn(`Failed to resolve single index:`, error)\n })\n }\n } else if (this.isIndexesResolved) {\n // Async loader but indexes are already resolved - resolve this one\n this.resolveSingleIndex(indexId, lazyIndex).catch((error) => {\n console.warn(`Failed to resolve single index:`, error)\n })\n }\n\n return new IndexProxy(indexId, lazyIndex)\n }\n\n /**\n * Resolve all lazy indexes (called when collection first syncs)\n */\n public async resolveAllIndexes(): Promise<void> {\n if (this.isIndexesResolved) return\n\n const resolutionPromises = Array.from(this.lazyIndexes.entries()).map(\n async ([indexId, lazyIndex]) => {\n const resolvedIndex = await lazyIndex.resolve()\n\n // Build index with current data\n resolvedIndex.build(this.state.entries())\n\n this.resolvedIndexes.set(indexId, resolvedIndex)\n return { indexId, resolvedIndex }\n }\n )\n\n await Promise.all(resolutionPromises)\n this.isIndexesResolved = true\n }\n\n /**\n * Resolve a single index immediately\n */\n private async resolveSingleIndex(\n indexId: number,\n lazyIndex: LazyIndexWrapper<TKey>\n ): Promise<BaseIndex<TKey>> {\n const resolvedIndex = await lazyIndex.resolve()\n resolvedIndex.build(this.state.entries())\n this.resolvedIndexes.set(indexId, resolvedIndex)\n return resolvedIndex\n }\n\n /**\n * Get resolved indexes for query optimization\n */\n get indexes(): Map<number, BaseIndex<TKey>> {\n return this.resolvedIndexes\n }\n\n /**\n * Updates all indexes when the collection changes\n */\n public updateIndexes(changes: Array<ChangeMessage<TOutput, TKey>>): void {\n for (const index of this.resolvedIndexes.values()) {\n for (const change of changes) {\n switch (change.type) {\n case `insert`:\n index.add(change.key, change.value)\n break\n case `update`:\n if (change.previousValue) {\n index.update(change.key, change.previousValue, change.value)\n } else {\n index.add(change.key, change.value)\n }\n break\n case `delete`:\n index.remove(change.key, change.value)\n break\n }\n }\n }\n }\n\n /**\n * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public cleanup(): void {\n this.lazyIndexes.clear()\n this.resolvedIndexes.clear()\n }\n}\n"],"names":["createSingleRowRefProxy","toExpression","BTreeIndex","lazyIndex","LazyIndexWrapper","IndexProxy"],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { IndexProxy, LazyIndexWrapper } from '../indexes/lazy-index.cjs';
2
+ import { BTreeIndex } from '../indexes/btree-index.cjs';
3
+ import { StandardSchemaV1 } from '@standard-schema/spec';
4
+ import { BaseIndex, IndexResolver } from '../indexes/base-index.cjs';
5
+ import { ChangeMessage } from '../types.cjs';
6
+ import { IndexOptions } from '../indexes/index-options.cjs';
7
+ import { SingleRowRefProxy } from '../query/builder/ref-proxy.cjs';
8
+ import { CollectionLifecycleManager } from './lifecycle.cjs';
9
+ import { CollectionStateManager } from './state.cjs';
10
+ export declare class CollectionIndexesManager<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
11
+ private lifecycle;
12
+ private state;
13
+ lazyIndexes: Map<number, LazyIndexWrapper<TKey>>;
14
+ resolvedIndexes: Map<number, BaseIndex<TKey>>;
15
+ isIndexesResolved: boolean;
16
+ indexCounter: number;
17
+ constructor();
18
+ setDeps(deps: {
19
+ state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
20
+ lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>;
21
+ }): void;
22
+ /**
23
+ * Creates an index on a collection for faster queries.
24
+ */
25
+ createIndex<TResolver extends IndexResolver<TKey> = typeof BTreeIndex>(indexCallback: (row: SingleRowRefProxy<TOutput>) => any, config?: IndexOptions<TResolver>): IndexProxy<TKey>;
26
+ /**
27
+ * Resolve all lazy indexes (called when collection first syncs)
28
+ */
29
+ resolveAllIndexes(): Promise<void>;
30
+ /**
31
+ * Resolve a single index immediately
32
+ */
33
+ private resolveSingleIndex;
34
+ /**
35
+ * Get resolved indexes for query optimization
36
+ */
37
+ get indexes(): Map<number, BaseIndex<TKey>>;
38
+ /**
39
+ * Updates all indexes when the collection changes
40
+ */
41
+ updateIndexes(changes: Array<ChangeMessage<TOutput, TKey>>): void;
42
+ /**
43
+ * Clean up the collection by stopping sync and clearing data
44
+ * This can be called manually or automatically by garbage collection
45
+ */
46
+ cleanup(): void;
47
+ }