@tanstack/db 0.1.3 → 0.1.5

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 (88) hide show
  1. package/dist/cjs/collection.cjs +112 -6
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +3 -2
  4. package/dist/cjs/errors.cjs +6 -0
  5. package/dist/cjs/errors.cjs.map +1 -1
  6. package/dist/cjs/errors.d.cts +3 -0
  7. package/dist/cjs/index.cjs +1 -0
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/indexes/auto-index.cjs +30 -19
  10. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  11. package/dist/cjs/indexes/auto-index.d.cts +1 -0
  12. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  13. package/dist/cjs/indexes/base-index.d.cts +2 -1
  14. package/dist/cjs/indexes/btree-index.cjs +26 -0
  15. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  16. package/dist/cjs/indexes/btree-index.d.cts +7 -0
  17. package/dist/cjs/indexes/index-options.d.cts +1 -1
  18. package/dist/cjs/query/compiler/evaluators.cjs +2 -2
  19. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  20. package/dist/cjs/query/compiler/evaluators.d.cts +1 -1
  21. package/dist/cjs/query/compiler/group-by.cjs +3 -1
  22. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  23. package/dist/cjs/query/compiler/index.cjs +72 -6
  24. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  25. package/dist/cjs/query/compiler/index.d.cts +16 -2
  26. package/dist/cjs/query/compiler/joins.cjs +111 -12
  27. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  28. package/dist/cjs/query/compiler/joins.d.cts +9 -2
  29. package/dist/cjs/query/compiler/order-by.cjs +62 -3
  30. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  31. package/dist/cjs/query/compiler/order-by.d.cts +12 -2
  32. package/dist/cjs/query/live-query-collection.cjs +200 -23
  33. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  34. package/dist/cjs/types.d.cts +1 -0
  35. package/dist/cjs/utils/btree.cjs +15 -0
  36. package/dist/cjs/utils/btree.cjs.map +1 -1
  37. package/dist/cjs/utils/btree.d.cts +8 -0
  38. package/dist/esm/collection.d.ts +3 -2
  39. package/dist/esm/collection.js +113 -7
  40. package/dist/esm/collection.js.map +1 -1
  41. package/dist/esm/errors.d.ts +3 -0
  42. package/dist/esm/errors.js +6 -0
  43. package/dist/esm/errors.js.map +1 -1
  44. package/dist/esm/index.js +2 -1
  45. package/dist/esm/indexes/auto-index.d.ts +1 -0
  46. package/dist/esm/indexes/auto-index.js +31 -20
  47. package/dist/esm/indexes/auto-index.js.map +1 -1
  48. package/dist/esm/indexes/base-index.d.ts +2 -1
  49. package/dist/esm/indexes/base-index.js.map +1 -1
  50. package/dist/esm/indexes/btree-index.d.ts +7 -0
  51. package/dist/esm/indexes/btree-index.js +26 -0
  52. package/dist/esm/indexes/btree-index.js.map +1 -1
  53. package/dist/esm/indexes/index-options.d.ts +1 -1
  54. package/dist/esm/query/compiler/evaluators.d.ts +1 -1
  55. package/dist/esm/query/compiler/evaluators.js +2 -2
  56. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  57. package/dist/esm/query/compiler/group-by.js +3 -1
  58. package/dist/esm/query/compiler/group-by.js.map +1 -1
  59. package/dist/esm/query/compiler/index.d.ts +16 -2
  60. package/dist/esm/query/compiler/index.js +73 -7
  61. package/dist/esm/query/compiler/index.js.map +1 -1
  62. package/dist/esm/query/compiler/joins.d.ts +9 -2
  63. package/dist/esm/query/compiler/joins.js +114 -15
  64. package/dist/esm/query/compiler/joins.js.map +1 -1
  65. package/dist/esm/query/compiler/order-by.d.ts +12 -2
  66. package/dist/esm/query/compiler/order-by.js +62 -3
  67. package/dist/esm/query/compiler/order-by.js.map +1 -1
  68. package/dist/esm/query/live-query-collection.js +200 -23
  69. package/dist/esm/query/live-query-collection.js.map +1 -1
  70. package/dist/esm/types.d.ts +1 -0
  71. package/dist/esm/utils/btree.d.ts +8 -0
  72. package/dist/esm/utils/btree.js +15 -0
  73. package/dist/esm/utils/btree.js.map +1 -1
  74. package/package.json +2 -2
  75. package/src/collection.ts +163 -10
  76. package/src/errors.ts +6 -0
  77. package/src/indexes/auto-index.ts +53 -31
  78. package/src/indexes/base-index.ts +6 -1
  79. package/src/indexes/btree-index.ts +29 -0
  80. package/src/indexes/index-options.ts +2 -2
  81. package/src/query/compiler/evaluators.ts +6 -3
  82. package/src/query/compiler/group-by.ts +3 -1
  83. package/src/query/compiler/index.ts +112 -5
  84. package/src/query/compiler/joins.ts +216 -20
  85. package/src/query/compiler/order-by.ts +98 -3
  86. package/src/query/live-query-collection.ts +359 -24
  87. package/src/types.ts +1 -0
  88. package/src/utils/btree.ts +17 -0
@@ -5,7 +5,7 @@ import { BTreeIndex } from "./indexes/btree-index.js";
5
5
  import { LazyIndexWrapper, IndexProxy } from "./indexes/lazy-index.js";
6
6
  import { ensureIndexForExpression } from "./indexes/auto-index.js";
7
7
  import { getActiveTransaction, createTransaction } from "./transactions.js";
8
- import { MissingInsertHandlerError, DuplicateKeyError, MissingDeleteHandlerError, NoKeysPassedToDeleteError, DeleteKeyNotFoundError, CollectionRequiresConfigError, CollectionRequiresSyncConfigError, CollectionInErrorStateError, InvalidCollectionStatusTransitionError, NoPendingSyncTransactionCommitError, SyncTransactionAlreadyCommittedError, NoPendingSyncTransactionWriteError, SyncTransactionAlreadyCommittedWriteError, DuplicateKeySyncError, CollectionIsInErrorStateError, SyncCleanupError, NegativeActiveSubscribersError, InvalidSchemaError, UndefinedKeyError, SchemaMustBeSynchronousError, SchemaValidationError, MissingUpdateArgumentError, MissingUpdateHandlerError, NoKeysPassedToUpdateError, UpdateKeyNotFoundError, KeyUpdateNotAllowedError } from "./errors.js";
8
+ 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";
9
9
  import { currentStateAsChanges, createFilteredCallback } from "./change-events.js";
10
10
  function createCollection(options) {
11
11
  const collection = new CollectionImpl(options);
@@ -59,7 +59,10 @@ class CollectionImpl {
59
59
  break;
60
60
  }
61
61
  }
62
- if (!hasPersistingTransaction) {
62
+ const hasTruncateSync = this.pendingSyncedTransactions.some(
63
+ (t) => t.truncate === true
64
+ );
65
+ if (!hasPersistingTransaction || hasTruncateSync) {
63
66
  this.isCommittingSyncTransactions = true;
64
67
  const changedKeys = /* @__PURE__ */ new Set();
65
68
  for (const transaction of this.pendingSyncedTransactions) {
@@ -80,6 +83,18 @@ class CollectionImpl {
80
83
  const events = [];
81
84
  const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`;
82
85
  for (const transaction of this.pendingSyncedTransactions) {
86
+ if (transaction.truncate) {
87
+ for (const key of this.syncedData.keys()) {
88
+ if (this.optimisticDeletes.has(key)) continue;
89
+ const previousValue = this.optimisticUpserts.get(key) || this.syncedData.get(key);
90
+ if (previousValue !== void 0) {
91
+ events.push({ type: `delete`, key, value: previousValue });
92
+ }
93
+ }
94
+ this.syncedData.clear();
95
+ this.syncedMetadata.clear();
96
+ this.syncedKeys.clear();
97
+ }
83
98
  for (const operation of transaction.operations) {
84
99
  const key = operation.key;
85
100
  this.syncedKeys.add(key);
@@ -124,6 +139,78 @@ class CollectionImpl {
124
139
  }
125
140
  }
126
141
  }
142
+ const hadTruncate = this.pendingSyncedTransactions.some(
143
+ (t) => t.truncate === true
144
+ );
145
+ if (hadTruncate) {
146
+ const syncedInsertedOrUpdatedKeys = /* @__PURE__ */ new Set();
147
+ for (const t of this.pendingSyncedTransactions) {
148
+ for (const op of t.operations) {
149
+ if (op.type === `insert` || op.type === `update`) {
150
+ syncedInsertedOrUpdatedKeys.add(op.key);
151
+ }
152
+ }
153
+ }
154
+ const reapplyUpserts = /* @__PURE__ */ new Map();
155
+ const reapplyDeletes = /* @__PURE__ */ new Set();
156
+ for (const tx of this.transactions.values()) {
157
+ if ([`completed`, `failed`].includes(tx.state)) continue;
158
+ for (const mutation of tx.mutations) {
159
+ if (mutation.collection !== this || !mutation.optimistic) continue;
160
+ const key = mutation.key;
161
+ switch (mutation.type) {
162
+ case `insert`:
163
+ reapplyUpserts.set(key, mutation.modified);
164
+ reapplyDeletes.delete(key);
165
+ break;
166
+ case `update`: {
167
+ const base = this.syncedData.get(key);
168
+ const next = base ? Object.assign({}, base, mutation.changes) : mutation.modified;
169
+ reapplyUpserts.set(key, next);
170
+ reapplyDeletes.delete(key);
171
+ break;
172
+ }
173
+ case `delete`:
174
+ reapplyUpserts.delete(key);
175
+ reapplyDeletes.add(key);
176
+ break;
177
+ }
178
+ }
179
+ }
180
+ for (const [key, value] of reapplyUpserts) {
181
+ if (reapplyDeletes.has(key)) continue;
182
+ if (syncedInsertedOrUpdatedKeys.has(key)) {
183
+ let foundInsert = false;
184
+ for (let i = events.length - 1; i >= 0; i--) {
185
+ const evt = events[i];
186
+ if (evt.key === key && evt.type === `insert`) {
187
+ evt.value = value;
188
+ foundInsert = true;
189
+ break;
190
+ }
191
+ }
192
+ if (!foundInsert) {
193
+ events.push({ type: `insert`, key, value });
194
+ }
195
+ } else {
196
+ events.push({ type: `insert`, key, value });
197
+ }
198
+ }
199
+ if (events.length > 0 && reapplyDeletes.size > 0) {
200
+ const filtered = [];
201
+ for (const evt of events) {
202
+ if (evt.type === `insert` && reapplyDeletes.has(evt.key)) {
203
+ continue;
204
+ }
205
+ filtered.push(evt);
206
+ }
207
+ events.length = 0;
208
+ events.push(...filtered);
209
+ }
210
+ if (!this.isReady()) {
211
+ this.setStatus(`ready`);
212
+ }
213
+ }
127
214
  this.optimisticUpserts.clear();
128
215
  this.optimisticDeletes.clear();
129
216
  this.isCommittingSyncTransactions = false;
@@ -404,7 +491,7 @@ class CollectionImpl {
404
491
  const callbacks = [...this.onFirstReadyCallbacks];
405
492
  this.onFirstReadyCallbacks = [];
406
493
  callbacks.forEach((callback) => callback());
407
- if (this.size === 0 && this.changeListeners.size > 0) {
494
+ if (this.changeListeners.size > 0) {
408
495
  this.emitEmptyReadyEvent();
409
496
  }
410
497
  }
@@ -497,9 +584,12 @@ class CollectionImpl {
497
584
  }
498
585
  const key = this.getKeyFromItem(messageWithoutKey.value);
499
586
  if (messageWithoutKey.type === `insert`) {
500
- if (this.syncedData.has(key) && !pendingTransaction.operations.some(
587
+ const insertingIntoExistingSynced = this.syncedData.has(key);
588
+ const hasPendingDeleteForKey = pendingTransaction.operations.some(
501
589
  (op) => op.key === key && op.type === `delete`
502
- )) {
590
+ );
591
+ const isTruncateTransaction = pendingTransaction.truncate === true;
592
+ if (insertingIntoExistingSynced && !hasPendingDeleteForKey && !isTruncateTransaction) {
503
593
  throw new DuplicateKeySyncError(key, this.id);
504
594
  }
505
595
  }
@@ -525,6 +615,17 @@ class CollectionImpl {
525
615
  },
526
616
  markReady: () => {
527
617
  this.markReady();
618
+ },
619
+ truncate: () => {
620
+ const pendingTransaction = this.pendingSyncedTransactions[this.pendingSyncedTransactions.length - 1];
621
+ if (!pendingTransaction) {
622
+ throw new NoPendingSyncTransactionWriteError();
623
+ }
624
+ if (pendingTransaction.committed) {
625
+ throw new SyncTransactionAlreadyCommittedWriteError();
626
+ }
627
+ pendingTransaction.operations = [];
628
+ pendingTransaction.truncate = true;
528
629
  }
529
630
  });
530
631
  this.syncCleanupFn = typeof cleanupFn === `function` ? cleanupFn : null;
@@ -794,6 +895,11 @@ class CollectionImpl {
794
895
  for (const listener of this.changeListeners) {
795
896
  listener([]);
796
897
  }
898
+ for (const [_key, keyListeners] of this.changeKeyListeners) {
899
+ for (const listener of keyListeners) {
900
+ listener([]);
901
+ }
902
+ }
797
903
  }
798
904
  /**
799
905
  * Emit events either immediately or batch them for later emission
@@ -957,8 +1063,8 @@ class CollectionImpl {
957
1063
  }
958
1064
  /**
959
1065
  * Creates an index on a collection for faster queries.
960
- * Indexes significantly improve query performance by allowing binary search
961
- * and range queries instead of full scans.
1066
+ * Indexes significantly improve query performance by allowing constant time lookups
1067
+ * and logarithmic time range queries instead of full scans.
962
1068
  *
963
1069
  * @template TResolver - The type of the index resolver (constructor or async loader)
964
1070
  * @param indexCallback - Function that extracts the indexed value from each item