diff --git a/.changeset/six-peas-grab.md b/.changeset/six-peas-grab.md
new file mode 100644
index 000000000..4d3327451
--- /dev/null
+++ b/.changeset/six-peas-grab.md
@@ -0,0 +1,5 @@
+---
+'@farfetched/core': minor
+---
+
+Add _Store_ `.$fiished` to _Remote Opration_
diff --git a/apps/website/docs/api/primitives/mutation.md b/apps/website/docs/api/primitives/mutation.md
index 7b7b85cc5..66bdf8dd9 100644
--- a/apps/website/docs/api/primitives/mutation.md
+++ b/apps/website/docs/api/primitives/mutation.md
@@ -28,6 +28,7 @@ For convenience, there are also the following [_Stores_](https://effector.dev/do
- `$pending` — `true` if the _Mutation_ is in the `"pending"` state, `false` otherwise.
- `$failed` — `true` if the _Mutation_ is in the `"fail"` state, `false` otherwise.
- `$succeeded` — `true` if the _Mutation_ is in the `"done"` state, `false` otherwise.
+- `$finished` — `true` if the _Mutation_ is in the `"done"` or `"fail"` state, `false` otherwise.
### `$enabled`
diff --git a/apps/website/docs/api/primitives/query.md b/apps/website/docs/api/primitives/query.md
index 9d501a9e1..4753b339a 100644
--- a/apps/website/docs/api/primitives/query.md
+++ b/apps/website/docs/api/primitives/query.md
@@ -44,6 +44,7 @@ For convenience, there are also the following [_Stores_](https://effector.dev/do
- `$pending` — `true` if the _Query_ is in the `"pending"` state, `false` otherwise.
- `$failed` — `true` if the _Query_ is in the `"fail"` state, `false` otherwise.
- `$succeeded` — `true` if the _Query_ is in the `"done"` state, `false` otherwise.
+- `$finished` — `true` if the _Query_ is in the `"done"` or `"fail"` state, `false` otherwise.
### `$enabled`
diff --git a/packages/core/src/mutation/__tests__/create_headless_mutation.test.ts b/packages/core/src/mutation/__tests__/create_headless_mutation.test.ts
index 7cbe41e70..7572d1f42 100644
--- a/packages/core/src/mutation/__tests__/create_headless_mutation.test.ts
+++ b/packages/core/src/mutation/__tests__/create_headless_mutation.test.ts
@@ -38,7 +38,7 @@ describe('createHeadlessMutation', () => {
expect(listeners.onSuccess).toHaveBeenCalledTimes(1);
expect(listeners.onSuccess).toHaveBeenCalledWith(
- expect.objectContaining({ params: 42, result: 42, status: 'done' })
+ expect.objectContaining({ params: 42, result: 42 })
);
expect(listeners.onSkip).not.toHaveBeenCalled();
diff --git a/packages/core/src/mutation/create_headless_mutation.ts b/packages/core/src/mutation/create_headless_mutation.ts
index 474e5b4d4..282fcae04 100644
--- a/packages/core/src/mutation/create_headless_mutation.ts
+++ b/packages/core/src/mutation/create_headless_mutation.ts
@@ -112,6 +112,7 @@ export function createHeadlessMutation<
$pending: readonly(operation.$pending),
$succeeded: readonly(operation.$succeeded),
$failed: readonly(operation.$failed),
+ $finished: readonly(operation.$finished),
$enabled: readonly(operation.$enabled),
finished: {
success: readonly(operation.finished.success),
diff --git a/packages/core/src/query/__tests__/create_headless_query.test.ts b/packages/core/src/query/__tests__/create_headless_query.test.ts
index 78067ecd2..433863456 100644
--- a/packages/core/src/query/__tests__/create_headless_query.test.ts
+++ b/packages/core/src/query/__tests__/create_headless_query.test.ts
@@ -35,7 +35,7 @@ describe('core/createHeadlessQuery without contract', () => {
expect(scope.getState(query.$error)).toBeNull();
expect(listeners.onSuccess).toHaveBeenCalledTimes(1);
expect(listeners.onSuccess).toHaveBeenCalledWith(
- expect.objectContaining({ params: 42, result: 42, status: 'done' })
+ expect.objectContaining({ params: 42, result: 42 })
);
expect(listeners.onSkip).not.toHaveBeenCalled();
diff --git a/packages/core/src/query/create_headless_query.ts b/packages/core/src/query/create_headless_query.ts
index 7c7ad6a84..e84af8bb7 100644
--- a/packages/core/src/query/create_headless_query.ts
+++ b/packages/core/src/query/create_headless_query.ts
@@ -247,6 +247,7 @@ export function createHeadlessQuery<
$pending: readonly(operation.$pending),
$succeeded: readonly(operation.$succeeded),
$failed: readonly(operation.$failed),
+ $finished: readonly(operation.$finished),
$enabled: readonly(operation.$enabled),
$stale,
aborted: readonly(aborted),
diff --git a/packages/core/src/remote_operation/__test__/create_remote_operation.test.ts b/packages/core/src/remote_operation/__test__/create_remote_operation.test.ts
index 6b48dadf5..99ee146ef 100644
--- a/packages/core/src/remote_operation/__test__/create_remote_operation.test.ts
+++ b/packages/core/src/remote_operation/__test__/create_remote_operation.test.ts
@@ -98,6 +98,7 @@ describe('createRemoteOperation, disable in-flight', () => {
expect(scope.getState(operation.$pending)).toBeFalsy();
expect(scope.getState(operation.$failed)).toBeFalsy();
expect(scope.getState(operation.$succeeded)).toBeFalsy();
+ expect(scope.getState(operation.$finished)).toBeFalsy();
// do not await
allSettled(operation.start, { scope, params: 42 });
@@ -107,6 +108,7 @@ describe('createRemoteOperation, disable in-flight', () => {
expect(scope.getState(operation.$pending)).toBeTruthy();
expect(scope.getState(operation.$failed)).toBeFalsy();
expect(scope.getState(operation.$succeeded)).toBeFalsy();
+ expect(scope.getState(operation.$finished)).toBeFalsy();
executorFirstDefer.resolve('result');
await allSettled(scope);
@@ -116,6 +118,7 @@ describe('createRemoteOperation, disable in-flight', () => {
expect(scope.getState(operation.$pending)).toBeFalsy();
expect(scope.getState(operation.$failed)).toBeFalsy();
expect(scope.getState(operation.$succeeded)).toBeTruthy();
+ expect(scope.getState(operation.$finished)).toBeTruthy();
// do not await
allSettled(operation.start, { scope, params: 42 });
@@ -125,6 +128,7 @@ describe('createRemoteOperation, disable in-flight', () => {
expect(scope.getState(operation.$pending)).toBeTruthy();
expect(scope.getState(operation.$failed)).toBeFalsy();
expect(scope.getState(operation.$succeeded)).toBeFalsy();
+ expect(scope.getState(operation.$finished)).toBeFalsy();
executorSecondDefer.reject(new Error('error'));
await allSettled(scope);
@@ -134,5 +138,6 @@ describe('createRemoteOperation, disable in-flight', () => {
expect(scope.getState(operation.$pending)).toBeFalsy();
expect(scope.getState(operation.$failed)).toBeTruthy();
expect(scope.getState(operation.$succeeded)).toBeFalsy();
+ expect(scope.getState(operation.$finished)).toBeTruthy();
});
});
diff --git a/packages/core/src/remote_operation/create_remote_operation.ts b/packages/core/src/remote_operation/create_remote_operation.ts
index bd69286a6..aaaec873c 100644
--- a/packages/core/src/remote_operation/create_remote_operation.ts
+++ b/packages/core/src/remote_operation/create_remote_operation.ts
@@ -167,6 +167,7 @@ export function createRemoteOperation<
const $pending = $status.map((status) => status === 'pending');
const $failed = $status.map((status) => status === 'fail');
const $succeeded = $status.map((status) => status === 'done');
+ const $finished = $status.map((status) => ['fail', 'done'].includes(status));
// -- Indicate status --
sample({
@@ -379,6 +380,7 @@ export function createRemoteOperation<
$pending,
$failed,
$succeeded,
+ $finished,
$enabled,
__: {
executeFx,
diff --git a/packages/core/src/remote_operation/type.ts b/packages/core/src/remote_operation/type.ts
index 925accb5e..6d2eb757f 100644
--- a/packages/core/src/remote_operation/type.ts
+++ b/packages/core/src/remote_operation/type.ts
@@ -24,6 +24,8 @@ export interface RemoteOperation {
$failed: Store;
/** Is fetching succeeded? */
$succeeded: Store;
+ /** Is fetching finished? */
+ $finished: Store;
/**
* Is operation enabled?
*