From aa6f07920e5fae874e536e69f5e725c571a40fba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 26 Aug 2026 00:21:06 +0200 Subject: [PATCH] Fix Mutation --- packages/effect-view/src/Mutation.test.ts | 23 +++++++++++++++++++++++ packages/effect-view/src/Mutation.ts | 20 +++++++++++--------- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/effect-view/src/Mutation.test.ts b/packages/effect-view/src/Mutation.test.ts index ad714e2..5e84e86 100644 --- a/packages/effect-view/src/Mutation.test.ts +++ b/packages/effect-view/src/Mutation.test.ts @@ -77,6 +77,29 @@ describe("Mutation", () => { expect(result.result.previousSuccess.value.value).toBe("saved") }) + it("runs a second mutation with its own key, not the previous one", async () => { + const result = await runMutationTest(Effect.gen(function*() { + const calls: Array = [] + const mutation = yield* Mutation.make({ + f: (key: string) => Effect.sync(() => { + calls.push(key) + return key + }), + }) + + const first = yield* mutation.mutate("a") + const second = yield* mutation.mutate("b") + + return { calls, first, second } + })) + + expect(result.calls).toEqual(["a", "b"]) + expect(result.first.key.value).toBe("a") + expect(expectSuccessValue(result.first)).toBe("a") + expect(result.second.key.value).toBe("b") + expect(expectSuccessValue(result.second)).toBe("b") + }) + it("mutateView returns a waiting state without waiting for completion", async () => { const result = await runMutationTest(Effect.gen(function*() { const deferred = yield* Deferred.make() diff --git a/packages/effect-view/src/Mutation.ts b/packages/effect-view/src/Mutation.ts index 073630d..18c8766 100644 --- a/packages/effect-view/src/Mutation.ts +++ b/packages/effect-view/src/Mutation.ts @@ -78,8 +78,10 @@ extends Pipeable.Class implements Mutation { Scope.Scope | R > { return Effect.gen({ self: this }, function*() { + const currentKey = Option.some(key) as Option.Some + const previous: MutationState = Option.getOrElse(yield* Lens.get(this.latestFinalState), () => ({ - key: Option.some(key) as Option.Some, + key: currentKey, result: AsyncResult.initial(), })) const state = yield* makeMutationStateLens(previous) @@ -89,17 +91,17 @@ extends Pipeable.Class implements Mutation { state, previous => AsyncResult.match(previous.result, { onInitial: () => ({ - key: previous.key, + key: currentKey, result: AsyncResult.initial(true), }), onSuccess: result => ({ - key: previous.key, + key: currentKey, result: AsyncResult.success(result.value, { waiting: true, }), }), onFailure: result => ({ - key: previous.key, + key: currentKey, result: AsyncResult.failure(result.cause, { waiting: true, previousSuccess: result.previousSuccess, @@ -108,7 +110,7 @@ extends Pipeable.Class implements Mutation { } )), - Effect.onExit(this.f(previous.key.value), exit => Effect.gen({ self: this }, function*() { + Effect.onExit(this.f(key), exit => Effect.gen({ self: this }, function*() { const fiberId = yield* Effect.fiberId const fiber = yield* Lens.get(this.fiber) @@ -119,24 +121,24 @@ extends Pipeable.Class implements Mutation { state, previous => Exit.match(exit, { onSuccess: v => ({ - key: previous.key, + key: currentKey, result: AsyncResult.success(v), }), onFailure: c => Cause.hasInterruptsOnly(c) ? previous : AsyncResult.match(previous.result, { onInitial: () => ({ - key: previous.key, + key: currentKey, result: AsyncResult.failure(c), }), onSuccess: v => ({ - key: previous.key, + key: currentKey, result: AsyncResult.failure(c, { previousSuccess: Option.some(v), }), }), onFailure: v => ({ - key: previous.key, + key: currentKey, result: AsyncResult.failure(c, { previousSuccess: v.previousSuccess, }),