Skip to content

Conversation

@KyleAMathews
Copy link
Collaborator

🎯 Changes

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm test:pr.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

@changeset-bot
Copy link

changeset-bot bot commented Nov 17, 2025

🦋 Changeset detected

Latest commit: 7b1aa37

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@tanstack/db Minor
@tanstack/electric-db-collection Minor
@tanstack/query-db-collection Minor
@tanstack/angular-db Patch
@tanstack/offline-transactions Patch
@tanstack/powersync-db-collection Patch
@tanstack/react-db Patch
@tanstack/rxdb-db-collection Patch
@tanstack/solid-db Patch
@tanstack/svelte-db Patch
@tanstack/trailbase-db-collection Patch
@tanstack/vue-db Patch
todos Patch
@tanstack/db-example-paced-mutations-demo Patch
@tanstack/db-example-react-todo Patch
@tanstack/db-example-solid-todo Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@KyleAMathews KyleAMathews marked this pull request as draft November 17, 2025 23:12
@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 17, 2025

More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@843

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@843

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@843

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@843

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@843

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@843

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@843

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@843

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@843

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@843

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@843

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@843

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@843

commit: 7b1aa37

@github-actions
Copy link
Contributor

github-actions bot commented Nov 17, 2025

Size Change: +104 B (+0.11%)

Total Size: 90.9 kB

Filename Size Change
./packages/db/dist/esm/index.js 2.73 kB +35 B (+1.3%)
./packages/db/dist/esm/utils.js 993 B +69 B (+7.47%) 🔍
ℹ️ View Unchanged
Filename Size
./packages/db/dist/esm/collection/change-events.js 1.39 kB
./packages/db/dist/esm/collection/changes.js 1.19 kB
./packages/db/dist/esm/collection/events.js 388 B
./packages/db/dist/esm/collection/index.js 3.32 kB
./packages/db/dist/esm/collection/indexes.js 1.1 kB
./packages/db/dist/esm/collection/lifecycle.js 1.68 kB
./packages/db/dist/esm/collection/mutations.js 2.34 kB
./packages/db/dist/esm/collection/state.js 3.49 kB
./packages/db/dist/esm/collection/subscription.js 3.62 kB
./packages/db/dist/esm/collection/sync.js 2.41 kB
./packages/db/dist/esm/deferred.js 207 B
./packages/db/dist/esm/errors.js 4.7 kB
./packages/db/dist/esm/event-emitter.js 748 B
./packages/db/dist/esm/indexes/auto-index.js 742 B
./packages/db/dist/esm/indexes/base-index.js 766 B
./packages/db/dist/esm/indexes/btree-index.js 1.93 kB
./packages/db/dist/esm/indexes/lazy-index.js 1.1 kB
./packages/db/dist/esm/indexes/reverse-index.js 513 B
./packages/db/dist/esm/local-only.js 837 B
./packages/db/dist/esm/local-storage.js 2.1 kB
./packages/db/dist/esm/optimistic-action.js 359 B
./packages/db/dist/esm/paced-mutations.js 496 B
./packages/db/dist/esm/proxy.js 3.75 kB
./packages/db/dist/esm/query/builder/functions.js 733 B
./packages/db/dist/esm/query/builder/index.js 4.08 kB
./packages/db/dist/esm/query/builder/ref-proxy.js 1.05 kB
./packages/db/dist/esm/query/compiler/evaluators.js 1.42 kB
./packages/db/dist/esm/query/compiler/expressions.js 430 B
./packages/db/dist/esm/query/compiler/group-by.js 1.81 kB
./packages/db/dist/esm/query/compiler/index.js 1.96 kB
./packages/db/dist/esm/query/compiler/joins.js 2 kB
./packages/db/dist/esm/query/compiler/order-by.js 1.45 kB
./packages/db/dist/esm/query/compiler/select.js 1.06 kB
./packages/db/dist/esm/query/expression-helpers.js 1.43 kB
./packages/db/dist/esm/query/ir.js 673 B
./packages/db/dist/esm/query/live-query-collection.js 360 B
./packages/db/dist/esm/query/live/collection-config-builder.js 5.42 kB
./packages/db/dist/esm/query/live/collection-registry.js 264 B
./packages/db/dist/esm/query/live/collection-subscriber.js 1.93 kB
./packages/db/dist/esm/query/live/internal.js 145 B
./packages/db/dist/esm/query/optimizer.js 2.56 kB
./packages/db/dist/esm/query/predicate-utils.js 2.97 kB
./packages/db/dist/esm/query/subset-dedupe.js 921 B
./packages/db/dist/esm/scheduler.js 1.3 kB
./packages/db/dist/esm/SortedMap.js 1.3 kB
./packages/db/dist/esm/strategies/debounceStrategy.js 247 B
./packages/db/dist/esm/strategies/queueStrategy.js 428 B
./packages/db/dist/esm/strategies/throttleStrategy.js 246 B
./packages/db/dist/esm/transactions.js 2.9 kB
./packages/db/dist/esm/utils/browser-polyfills.js 304 B
./packages/db/dist/esm/utils/btree.js 5.61 kB
./packages/db/dist/esm/utils/comparison.js 852 B
./packages/db/dist/esm/utils/cursor.js 457 B
./packages/db/dist/esm/utils/index-optimization.js 1.51 kB
./packages/db/dist/esm/utils/type-guards.js 157 B

compressed-size-action::db-package-size

@github-actions
Copy link
Contributor

github-actions bot commented Nov 17, 2025

Size Change: 0 B

Total Size: 3.7 kB

ℹ️ View Unchanged
Filename Size
./packages/react-db/dist/esm/index.js 225 B
./packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.17 kB
./packages/react-db/dist/esm/useLiveQuery.js 1.34 kB
./packages/react-db/dist/esm/useLiveSuspenseQuery.js 559 B
./packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

@samwillis
Copy link
Collaborator

I'm in favour of this change, it makes everything much more explicit. My only hesitation is on the query collection where it becomes a little more verbose with the need to refetch the majority of the time... but that explicitness does ensure that the user is aware of what is actually happening.

on the whole, I think it's worth making this change.

// Users handle sync coordination in their own handlers
onInsert: config.onInsert,
onUpdate: config.onUpdate,
onDelete: config.onDelete
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is proof that this is a cleaner approach. Well done.

@KyleAMathews KyleAMathews changed the title Remove magic return APIs from collection handlers Deprecate magic return APIs from collection handlers Nov 19, 2025
@KyleAMathews KyleAMathews moved this to In Progress in TanStack DB 1.0.0 release Jan 6, 2026
Copy link
Contributor

@kevin-dp kevin-dp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I left a comment about improving the @deprecated annotation in the function's docstring.

* @param params Object containing transaction and collection information
* @returns Promise resolving to any value
* @returns Promise that should resolve to void
* @deprecated Returning values from this handler is deprecated. Use collection utilities to refetch/sync.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that @deprecated is going to mark the onInsert function as deprecated which is not really the case. It's only a specific usage of it that will be deprecated.

To do it right i think we should have an overload for the old signature and an overload for the new signature. And then deprecate the old signature via docstring. Like in this artificial example:

// Deprecated overload (old usage)
 /** @deprecated This function no longer returns a value. */
export function configure(): ConfigObject;

// New overload (current usage)
export function configure(): void;

// Implementation
export function configure() {
  // ...
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude Address this comment.

Copy link
Contributor

@claude claude bot Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @kevin-dp's task in 3m 49s —— View job


Addressed deprecation annotation

I've improved the @deprecated annotations as suggested by using union types to create overloads.

What I changed

Instead of marking the entire onInsert, onUpdate, and onDelete handler functions as deprecated, I've restructured the type definitions to use union types that provide two alternatives:

  1. The current, non-deprecated signature: InsertMutationFn<T, TKey, TUtils, void> (returns Promise<void>)
  2. The deprecated signature: InsertMutationFn<T, TKey, TUtils, TReturn> (returns Promise<TReturn>) with a @deprecated JSDoc comment

This approach ensures that:

  • Users who write handlers returning void (the new, recommended way) won't see deprecation warnings
  • Users who write handlers returning other values will see the deprecation warning specifically on that usage
  • The deprecation message clearly explains what's deprecated and how to migrate

Changes made

  • Read current types.ts implementation
  • Identify all handler types that need overload treatment
  • Create union type overloads for deprecated signatures
  • Update deprecation messages to be clearer
  • Commit and push changes

The changes have been pushed to the branch in commit 2716d59.
| Branch

claude and others added 7 commits January 12, 2026 15:19
BREAKING CHANGE: Mutation handler return values are now deprecated

**Problem:**
Users were confused about the difference between collection handlers
(onInsert, onUpdate, onDelete) and manual actions/transactions. The
magic of returning values like `{ refetch: false }` from handlers was
specific to Query Collections but users expected it to work everywhere,
leading to incorrect mental models.

**Solution:**
1. Deprecate returning values from mutation handlers in TypeScript
   - Changed `TReturn = any` to `TReturn = void` in base handler types
   - Added @deprecated JSDoc tags with migration guidance
   - Updated all handler examples to show manual refetch pattern

2. Update documentation to teach manual patterns:
   - Query Collections: Use `await collection.utils.refetch()`
   - Electric Collections: Return `{ txid }` or use `collection.utils.awaitMatch()`

3. Clarify Electric Collection's special pattern:
   - Electric handlers REQUIRE returning txid or calling awaitMatch
   - This is Electric-specific, not a general pattern
   - Updated JSDoc to emphasize this distinction

**Migration Guide:**

Before (deprecated):
```typescript
onInsert: async ({ transaction }) => {
  await api.create(data)
  return { refetch: false }  // ❌ Deprecated
}
```

After (recommended):
```typescript
onInsert: async ({ transaction, collection }) => {
  await api.create(data)
  await collection.utils.refetch()  // ✅ Explicit and clear
}
```

For Electric Collections (unchanged):
```typescript
onInsert: async ({ transaction }) => {
  const result = await api.create(data)
  return { txid: result.txid }  // ✅ Electric-specific pattern
}
```

**Files Changed:**
- packages/db/src/types.ts: Deprecated return types, updated JSDoc
- packages/electric-db-collection/src/electric.ts: Clarified Electric-specific pattern
- docs/collections/query-collection.md: Removed refetch control examples, added manual patterns
- docs/guides/mutations.md: Updated collection-specific handler patterns

This change reduces confusion and makes the API more explicit and easier to understand.
…alues

Update the guide for creating custom collection options creators to reflect
the deprecation of mutation handler return values.

**Changes:**

1. **Pattern A (User-Provided Handlers):**
   - Clarified that handler return values are deprecated
   - Users should manually trigger refetch/sync within their handlers
   - Added note about Electric-specific exception (txid returns)
   - Simplified example to just pass through handlers

2. **Pattern B (Built-in Handlers):**
   - Updated examples to show handlers completing after sync coordination
   - Removed misleading return statements
   - Added key principle: coordinate sync internally via `await`

3. **Strategy 5 (Query Collection):**
   - Renamed from "Full Refetch" to "Manual Refetch"
   - Updated to show user explicitly calling `collection.utils.refetch()`
   - Clarified that users manage refetch in their own handlers

4. **WebSocket Example:**
   - Added explicit `Promise<void>` return types to handlers
   - Added comments about handlers completing after server confirmation

5. **Electric Example:**
   - Clarified that txid returns are Electric-specific
   - Updated wrapper example to not return the result

6. **Best Practices:**
   - Added new guideline about deprecated handler return values
   - Emphasized Electric is the only exception

These changes align the guide with the new pattern where mutation handlers
don't return values, and sync coordination happens explicitly within handlers
via await or manual refetch calls.
…pattern

BREAKING CHANGE: Electric collection handlers no longer support returning { txid }

**Problem:**
The previous commit deprecated return values for most collections but kept
Electric's `return { txid }` pattern as an exception. This creates confusion
because it's still "magic" - users don't understand why Electric is different
and it maintains the same conceptual problem of implicit behavior.

**Solution:**
Deprecate ALL return value patterns, including Electric's. Electric handlers
should now use `await collection.utils.awaitTxId(txid)` explicitly.

**Changes:**

1. **Base Collection Types** (`packages/db/src/types.ts`):
   - Added Electric example showing manual `await collection.utils.awaitTxId()`
   - Removed mentions of Electric-specific return pattern
   - All handlers consistently show void return with manual sync

2. **Electric Collection** (`packages/electric-db-collection/src/electric.ts`):
   - Deprecated `return { txid }` pattern in all three handlers
   - Updated JSDoc to show manual `await collection.utils.awaitTxId(txid)`
   - Added `@deprecated` tags explaining the migration
   - Updated all examples to use manual await pattern

3. **Electric Documentation** (`docs/collections/electric-collection.md`):
   - Updated "Using Txid" section to show manual awaitTxId
   - Changed "return txid to wait for sync" to "manually wait for txid"
   - All code examples now use `await collection.utils.awaitTxId()`

4. **Collection Options Creator Guide** (`docs/guides/collection-options-creator.md`):
   - Removed "Electric-Specific Exception" note
   - Updated best practices to mention Electric should use awaitTxId
   - No more special cases - all handlers work the same way

5. **Mutations Guide** (`docs/guides/mutations.md`):
   - Updated Electric example to show manual awaitTxId pattern
   - Changed from "return { txid }" to manual await pattern

**Migration Guide:**

Before (deprecated):
```typescript
// Electric Collection
onInsert: async ({ transaction }) => {
  const result = await api.create(data)
  return { txid: result.txid }  // ❌ Deprecated
}
```

After (recommended):
```typescript
// Electric Collection
onInsert: async ({ transaction, collection }) => {
  const result = await api.create(data)
  await collection.utils.awaitTxId(result.txid)  // ✅ Explicit and clear
}
```

**Benefits:**
1. **No more magic**: All collections follow the same explicit pattern
2. **Clearer mental model**: Users understand they're waiting for sync
3. **Consistent API**: No special cases to remember
4. **Better visibility**: `await` makes the async nature explicit
5. **Same control**: Users have same power, just more explicit

All collections now have a consistent pattern: handlers coordinate sync
explicitly via await, not implicitly via return values.
…entation

Cleaned up language throughout mutation handler docs and JSDoc to be more direct.
Changed phrases like "manually wait for" to "wait for" since there's only one way to do it.
Based on external code review feedback, this commit implements a soft deprecation
strategy for mutation handler return values:

Runtime changes:
- Add console warnings when QueryCollection handlers return { refetch }
- Add console warnings when Electric handlers return { txid }
- Keep runtime functionality intact for backward compatibility
- Runtime support will be removed in v1.0 RC

Type improvements:
- Mark TReturn generic as @internal and deprecated across all mutation types
- Clarify that it exists only for backward compatibility

Documentation improvements:
- Clarify Electric JSDoc: handlers return Promise<void> but must not RESOLVE
  until synchronization is complete (avoiding "void but not void" confusion)
- Add timeout error handling example showing policy choices (rollback vs
  eventual consistency)
- Update changeset to clearly communicate this is a soft deprecation

This aligns with the review recommendation for a gradual migration path with
clear runtime feedback to help users migrate to the new explicit patterns.
@kevin-dp kevin-dp force-pushed the claude/fix-mutation-callbacks-01HTYzMduGUSY1Dvt3B7YBEa branch from 58eb211 to 8d9f691 Compare January 12, 2026 14:24
@kevin-dp kevin-dp marked this pull request as ready for review January 13, 2026 15:12
@kevin-dp kevin-dp moved this from In Progress to Ready for review in TanStack DB 1.0.0 release Jan 20, 2026
@KyleAMathews
Copy link
Collaborator Author

/review

@@ -0,0 +1,51 @@
---
'@tanstack/db': major
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude make these minor — we're < 1.0

Copy link
Contributor

@claude claude bot Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @KyleAMathews's task in 1m 52s —— View job


Changeset updated to use minor versions

I've updated the changeset file locally to change version bumps from major to minor for all three packages since the project is < 1.0:

  • @tanstack/db: major → minor
  • @tanstack/electric-db-collection: major → minor
  • @tanstack/query-db-collection: minor → minor

Status: The change is committed locally but cannot be pushed automatically due to concurrent changes on the remote branch (likely from another Claude instance addressing Kevin's deprecation comment).

Next steps: You'll need to either:

  1. Grant me fetch/pull permissions so I can rebase and push, or
  2. Manually apply this simple change to .changeset/deprecate-handler-return-values.md (lines 2-4: change major to minor)

Local commit: ad9f319a - chore: change version bumps from major to minor (< 1.0)

@KyleAMathews
Copy link
Collaborator Author

@claude throughly review all docs for any other docs that are using this now deprecated pattern & rewrite as needed to the new pattern

Use union types to create overloads that deprecate only the non-void
return signatures, not the entire handler functions. This provides
more precise deprecation warnings to users.

Co-authored-by: Kevin <kevin-dp@users.noreply.github.com>
@claude
Copy link
Contributor

claude bot commented Jan 20, 2026

Claude finished @KyleAMathews's task in 3m 24s —— View job


Documentation review complete ✅

I've thoroughly reviewed all documentation files and updated any usage of the deprecated magic return API pattern from collection handlers.

Tasks

  • Find all documentation files in the repository
  • Review already-updated files to understand the pattern change
  • Search for handler usage in remaining documentation files
  • Update files using the deprecated pattern
  • Commit and push changes

Pattern Change Summary:
The deprecated pattern had handlers returning values (like { txid }) to signal sync completion. The new pattern requires handlers to explicitly await sync using collection utility methods:

  • QueryCollection: Call await collection.utils.refetch() after persisting
  • ElectricCollection: Call await collection.utils.awaitTxId(txid) after getting txid from backend
  • Other collections: Await sync explicitly in the handler

Files Updated:

  1. docs/reference/electric-db-collection/interfaces/ElectricCollectionConfig.md - Updated 5 handler examples to use await collection.utils.awaitTxId()
  2. docs/overview.md - Updated onInsert handler to await txid
  3. docs/guides/mutations.md - Updated onUpdate handler to show cleaner pattern for awaiting txids
  4. examples/react/projects/README.md - Updated onInsert handler to use await collection.utils.refetch() (appropriate for query collection)

Files Already Updated (in this PR):

  • docs/collections/electric-collection.md
  • docs/collections/query-collection.md
  • docs/guides/collection-options-creator.md
  • docs/guides/mutations.md (additional updates)

Note: CHANGELOG files contain historical information and were intentionally not modified.

All documentation now consistently uses the new explicit sync pattern instead of deprecated return values!

View commit |

@claude
Copy link
Contributor

claude bot commented Jan 20, 2026

🔍 Comprehensive PR Review

PR #843: Deprecate magic return APIs from collection handlers

This PR deprecates the "magic return value" pattern from mutation handlers (onInsert, onUpdate, onDelete), transitioning to explicit utility-based synchronization via collection.utils.refetch() and collection.utils.awaitTxId().


Summary

Category Issues Found
🔴 Critical 0
🟠 Important 4
🟡 Suggestions 6
✅ Strengths 8

🟠 Important Issues (Should Fix)

1. JSDoc/Type Declaration Mismatch in Electric Collection

Files: packages/electric-db-collection/src/electric.ts:168-178, 259-269, 305-317

The JSDoc comments state "This handler returns Promise<void>" but the actual function signature returns Promise<MatchingStrategy>. This is a direct contradiction that could confuse developers.

Recommendation: Update the JSDoc to accurately reflect: "This handler returns Promise<MatchingStrategy>, but returning values is deprecated. Use await collection.utils.awaitTxId(txid) instead."


2. Changeset Inaccurately Claims TypeScript Will Error

File: .changeset/deprecate-handler-return-values.md:11

The changeset states "TypeScript will error on return { refetch: false } or return { txid }" but the actual type TReturn defaults to void, not never. TypeScript will NOT produce an error for returning values when users explicitly specify TReturn - only runtime warnings are emitted.

Recommendation: Reword to: "Handlers now default to Promise<void> type, indicating the new pattern. Old return patterns still work at runtime with deprecation warnings, but v1.0 will enforce void returns."


3. Poor Error Sequence for { txid: undefined } Returns

File: packages/electric-db-collection/src/electric.ts:837-838

When a handler returns { txid: undefined } or { txid: null } (perhaps from an API that didn't return a txid), users see a deprecation warning followed by ExpectedNumberInAwaitTxIdError. The deprecation warning implies they're using a deprecated pattern when the real problem is a missing/invalid txid value.

Recommendation: Add explicit handling for falsy txid values to provide a clearer error message:

if (txidValue === null || txidValue === undefined) {
  console.warn(
    '[TanStack DB] Handler returned { txid } but txid value is null/undefined. ' +
    'If you intended to wait for synchronization, ensure your API returns a valid txid.'
  )
  return
}

4. Documentation Timeout Value Inconsistency

Files: docs/collections/electric-collection.md:314-316 vs packages/electric-db-collection/src/electric.ts:671

Documentation says "default is 30 seconds" but the actual code shows the default timeout is 5000ms (5 seconds).

Recommendation: Update documentation to match code: "// With custom timeout (default is 5 seconds)"


🟡 Suggestions (Nice to Have)

1. Extract Duplicated Deprecation Warning Logic

File: packages/query-db-collection/src/query.ts:1254-1265, 1282-1293, 1310-1321

The same deprecation warning logic is repeated verbatim three times. Extract to a helper function:

function warnIfReturnsValue(handlerResult: unknown): void {
  if (
    handlerResult &&
    typeof handlerResult === 'object' &&
    Object.keys(handlerResult).length > 0
  ) {
    console.warn(QUERY_DEPRECATION_WARNING)
  }
}

2. BaseCollectionConfig TReturn Default Inconsistency

File: packages/db/src/types.ts:517

BaseCollectionConfig keeps TReturn = any while the individual function types default to TReturn = void. This creates subtle inconsistency where inline handler definitions in config objects inherit any return type.

Recommendation: Consider aligning to TReturn = void for consistency.


3. More Specific @deprecated Tags

File: packages/db/src/types.ts:582, 636, 693

The @deprecated tags say "Use collection utilities to refetch/sync" which is generic. Consider being more specific: "For QueryCollection, use await collection.utils.refetch(). For ElectricCollection, use await collection.utils.awaitTxId(txid)."


4. Consider Warning for Primitive Returns

File: packages/query-db-collection/src/query.ts

Primitive returns (strings, numbers, booleans) are silently ignored. Consider adding a warning for unexpected return types to help users catch bugs.


5. Verify URL Anchors in Warning Messages

Files: electric.ts:840-843 and query.ts:1260-1263

The Electric warning uses #persistence-handlers--synchronization (double dash). Verify the URL anchors match actual documentation headings.


6. Use Single Quotes for Consistency

File: packages/electric-db-collection/src/electric.ts:838

// Current (unconventional):
if (result && `txid` in result)

// Suggested (consistent):
if (result && 'txid' in result)

✅ Strengths

  1. Gradual Migration Path: The approach is well-designed for backward compatibility. Existing code continues to work at runtime while TypeScript provides feedback about the deprecation.

  2. Explicit Over Implicit: The new pattern makes synchronization coordination explicit, reducing "magic" behavior.

  3. Consistent API Across Collection Types: Both Electric and Query collections now follow the same handler pattern.

  4. Comprehensive Documentation: The documentation updates across multiple files clearly explain the migration path with excellent examples.

  5. Runtime Deprecation Warnings: The warnings provide actionable guidance with links to migration documentation.

  6. Well-Written Changeset: The changeset clearly explains the breaking change, what's affected, and provides migration examples.

  7. Type Safety Improvement: Changing default from any to void makes the expected return type explicit.

  8. Proper Backward Compatibility: Old code continues to work with deprecation warnings, and runtime still processes legacy return values.


Type Design Assessment

Aspect Rating Notes
Encapsulation 7/10 Good, but TReturn remains overridable
Invariant Expression 7/10 Clear intent, minor JSDoc/type mismatches
Invariant Usefulness 9/10 Excellent improvement for API clarity
Invariant Enforcement 6/10 Runtime enforcement only for deprecated pattern

Recommended Action

  1. Fix the 4 important issues before merge (particularly the JSDoc/type mismatch and timeout documentation)
  2. Consider suggestions for code quality improvements
  3. The PR is fundamentally sound and ready for merge after addressing important issues

🤖 Generated with Claude Code

…recated return values

- Update ElectricCollectionConfig examples to use await collection.utils.awaitTxId()
- Update overview.md example to use explicit sync awaiting
- Update mutations.md to show cleaner pattern for awaiting txids
- Update examples/react/projects/README.md to use collection.utils.refetch()

Co-authored-by: Kyle Mathews <KyleAMathews@users.noreply.github.com>
KyleAMathews and others added 3 commits January 20, 2026 10:43
- Fix changeset to remove incorrect claim that TypeScript would error on
  old return patterns (types are generic/overridable, only runtime warns)
- Update awaitTxId default timeout from 5s to 15s for more reliable sync
- Fix documentation that incorrectly stated 30s default

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add warnOnce utility to prevent console spam (logs each warning once)
- QueryCollection: warn about deprecated auto-refetch behavior
- QueryCollection: clarify { refetch: false } is correct pattern for now
- ElectricCollection: use warnOnce for { txid } return deprecation
- Update docs to accurately describe current vs v1.0 behavior
- Update changeset with clearer migration guidance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@KyleAMathews KyleAMathews force-pushed the claude/fix-mutation-callbacks-01HTYzMduGUSY1Dvt3B7YBEa branch from 3e497ff to 5d564f7 Compare January 20, 2026 19:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready for review

Development

Successfully merging this pull request may close these issues.

6 participants