Skip to content

Commit 8881363

Browse files
committed
fix: local collection manual transactions
Signed-off-by: Marc MacLeod <marbemac+gh@gmail.com>
1 parent ac42951 commit 8881363

File tree

3 files changed

+48
-6
lines changed

3 files changed

+48
-6
lines changed

.changeset/metal-results-float.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@tanstack/db": patch
3+
---
4+
5+
Fixed local collection manual transactions

packages/db/src/local-only.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,12 @@ export function localOnlyCollectionOptions<
179179
): LocalOnlyCollectionOptionsResult<T, TKey, TSchema> & {
180180
schema?: StandardSchemaV1
181181
} {
182-
const { initialData, onInsert, onUpdate, onDelete, ...restConfig } = config
182+
const { initialData, onInsert, onUpdate, onDelete, id, ...restConfig } =
183+
config
184+
185+
// Generate a stable collection ID for filtering mutations
186+
// If not provided, generate a unique ID that will be used by the collection
187+
const collectionId = id ?? `local-only:${crypto.randomUUID()}`
183188

184189
// Create the sync configuration with transaction confirmation capability
185190
const syncResult = createLocalOnlySync<T, TKey>(initialData)
@@ -245,11 +250,9 @@ export function localOnlyCollectionOptions<
245250
const acceptMutations = (transaction: {
246251
mutations: Array<PendingMutation<Record<string, unknown>>>
247252
}) => {
248-
// Filter mutations that belong to this collection
253+
// Filter mutations that belong to this collection by collection ID
249254
const collectionMutations = transaction.mutations.filter(
250-
(m) =>
251-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
252-
m.collection === syncResult.collection
255+
(m) => m.collection.id === collectionId
253256
)
254257

255258
if (collectionMutations.length === 0) {
@@ -264,6 +267,7 @@ export function localOnlyCollectionOptions<
264267

265268
return {
266269
...restConfig,
270+
id: collectionId,
267271
sync: syncResult.sync,
268272
onInsert: wrappedOnInsert,
269273
onUpdate: wrappedOnUpdate,

packages/db/tests/local-only.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ describe(`LocalOnly Collection`, () => {
481481
})
482482

483483
describe(`Manual transactions with acceptMutations`, () => {
484-
it(`should accept and persist mutations from manual transactions`, () => {
484+
it(`should accept and persist mutations from manual transactions`, async () => {
485485
const tx = createTransaction({
486486
mutationFn: async ({ transaction }: any) => {
487487
// Simulate API call success
@@ -510,6 +510,39 @@ describe(`LocalOnly Collection`, () => {
510510
id: 101,
511511
name: `Manual Tx Insert 2`,
512512
})
513+
514+
// Verify that the item is still present after async operations complete
515+
await new Promise((resolve) => setTimeout(resolve, 1))
516+
expect(collection.get(100)).toEqual({ id: 100, name: `Manual Tx Insert` })
517+
})
518+
519+
it(`should work without explicit collection ID`, async () => {
520+
// Create a collection without an explicit ID
521+
const noIdCollection = createCollection<
522+
TestItem,
523+
number,
524+
LocalOnlyCollectionUtils
525+
>(
526+
localOnlyCollectionOptions({
527+
getKey: (item) => item.id,
528+
})
529+
)
530+
531+
const tx = createTransaction({
532+
mutationFn: async ({ transaction }: any) => {
533+
noIdCollection.utils.acceptMutations(transaction)
534+
},
535+
autoCommit: false,
536+
})
537+
538+
tx.mutate(() => {
539+
noIdCollection.insert({ id: 999, name: `No ID Test` })
540+
})
541+
542+
await tx.commit()
543+
544+
// Data should persist even without explicit ID
545+
expect(noIdCollection.get(999)).toEqual({ id: 999, name: `No ID Test` })
513546
})
514547

515548
it(`should only accept mutations for the specific collection`, () => {

0 commit comments

Comments
 (0)