Skip to content

feat(vue): add select option to MutationOptionsBase and per-call options to MutationExtWithInput#705

Merged
patroza merged 10 commits intomainfrom
copilot/implement-delayed-cache-invalidation
May 1, 2026
Merged

feat(vue): add select option to MutationOptionsBase and per-call options to MutationExtWithInput#705
patroza merged 10 commits intomainfrom
copilot/implement-delayed-cache-invalidation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 1, 2026

Cache invalidation fired only once — immediately when the mutation RPC returned — even for long-running operations where meaningful state change happens later (e.g. after a background job finishes).

Changes

  • MutationOptionsBase<A = unknown, B = A> — made generic over both the mutation result (A) and the select output (B); adds optional select: (result: A) => Effect<B, unknown, never> field
  • Double invalidation when select is provided:
    1. On mutation exit (unchanged, always fires)
    2. After select completes — allowing a polling/awaiting effect to run before the second refresh
  • When select is provided, the caller receives the select output (B) as the final result instead of the original mutation result (A); when select is omitted, B defaults to A so the return type is unchanged
  • invalidateQueries now accepts the full MutationOptionsBase instead of just queryInvalidation
  • Overload signatures in makeMutation, useMakeMutation, useMutation, useMutationInt updated to MutationOptionsBase<A, B>, returning Effect<B, E, R>
  • MutationExtWithInput call signature updated to accept optional per-call MutationOptionsBase as a second argument: <B = A, E2 = never, R2 = never>(i: I, options?: MutationOptionsBase<A, B, E2, R2>): Effect.Effect<B, E | E2, R | R2> — per-call select/queryInvalidation override construction-time options at invocation time

Usage

// Construction-time select (applies to all invocations)
useMutation(startExportCommand, {
  select: (result) => pollUntilDone(result.jobId)
  // cache invalidates immediately on mutation exit,
  // then again once the job is confirmed done;
  // the caller receives the output of pollUntilDone (typed as B), not the original startExport result
})

// Per-call select via MutationExtWithInput (overrides construction-time options for that call)
const { startExportMutation } = useClient()
startExportMutation(input, {
  select: (result) => pollUntilDone(result.jobId)
})

@patroza
Copy link
Copy Markdown
Member

patroza commented May 1, 2026

@copilot check, lint and fix if needed

@patroza
Copy link
Copy Markdown
Member

patroza commented May 1, 2026

@copilot integrate latest main changes

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot check, lint and fix if needed

Ran pnpm lint-fix and pnpm check — both pass with no errors (only pre-existing no-underscore-dangle warnings unrelated to this change). No fixes needed.

…ayed-cache-invalidation

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

Co-authored-by: patroza <42661+patroza@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot integrate latest main changes

Merged latest main (commit 37089ead — predicate-based invalidateQueries consolidation) into the branch. No conflicts; lint and type-check still pass. (665c1ec)

Copilot finished work on behalf of patroza May 1, 2026 13:59
@patroza
Copy link
Copy Markdown
Member

patroza commented May 1, 2026

@copilot when there is a select provided, the final result the caller receives should be the select output, not the original

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot when there is a select provided, the final result the caller receives should be the select output, not the original

Done — handle now return yield*s the select result, so the caller receives the select output instead of the original mutation result. (0b25308)

Copilot finished work on behalf of patroza May 1, 2026 14:54
@patroza
Copy link
Copy Markdown
Member

patroza commented May 1, 2026

@copilot how is that supposed to work type wise? You would need to conditionally return the select effect A type only when specified, otherwise the mutation A type

@patroza
Copy link
Copy Markdown
Member

patroza commented May 1, 2026

@copilot check, lint and fix if needed

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot how is that supposed to work type wise? You would need to conditionally return the select effect A type only when specified, otherwise the mutation A type

Added a second type param B = A to MutationOptionsBase<A, B> and changed select to (result: A) => Effect.Effect<B, unknown, never>. All overload signatures in makeMutation, useMakeMutation, useMutation, and useMutationInt now return Effect<B, E, R>. TypeScript infers B = A when no select is provided (unchanged return type), and infers B from the select function's return when it is provided. (24231fa)

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot check, lint and fix if needed

pnpm lint-fix and pnpm check both pass with no errors. No fixes needed (only pre-existing no-underscore-dangle warnings unrelated to this change).

Copilot finished work on behalf of patroza May 1, 2026 15:16
@patroza
Copy link
Copy Markdown
Member

patroza commented May 1, 2026

@copilot on MutationExtWithInput instead of
(i: I): Effect.Effect<A, E, R>
I would like to pass the mutation options as an optional second argument:
<B, E2, R2>(input: I, options?: MutationOptionsBase<A, B, E2, R2>): Effect.Effect<B, E | E2, R | R2>

similarly for MutationExt, I no longer want it extending Effect.Effect<A, E, R>
but instead be like MutationExtWithInput; a function <B, E2, R2>(options?: MutationOptionsBase<A, B, E2, R2>): Effect.Effect<B, E | E2, R | R2>

that means we need to update all the useMutation helpers as well as they expect (handler, options) and return either Effect, or (I) => Effect - that's no longer useful

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 1, 2026

@copilot on MutationExtWithInput instead of
(i: I): Effect.Effect<A, E, R>
I would like to pass the mutation options as an optional second argument:
...

Done in 874170b. MutationExtWithInput's call signature is now:

<B = A, E2 = never, R2 = never>(i: I, options?: MutationOptionsBase<A, B, E2, R2>): Effect.Effect<B, E | E2, R | R2>

When no options is given, B = A so the return type is unchanged. When options.select is provided, TypeScript infers B from the select return type.

The implementation in makeMutation/useMakeMutation merges per-call options with construction-time options at invocation time (per-call select/queryInvalidation take precedence). The span wrapper in useMutation/useMutationInt was also updated to forward the per-call options argument.

Copilot AI changed the title feat(vue): add select option to MutationOptionsBase for post-mutation polling feat(vue): add select option to MutationOptionsBase and per-call options to MutationExtWithInput May 1, 2026
Copilot finished work on behalf of patroza May 1, 2026 16:08
@patroza patroza force-pushed the copilot/implement-delayed-cache-invalidation branch from 874170b to d279faa Compare May 1, 2026 16:17
@patroza patroza merged commit 9992e70 into main May 1, 2026
2 of 3 checks passed
@patroza patroza deleted the copilot/implement-delayed-cache-invalidation branch May 1, 2026 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants