Skip to content
Merged

Main #17

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions docs/core-concepts/action.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,10 @@ Every action has built-in reactive metadata:
store.action.fetch.loading; // ComputedRef<boolean>
store.action.fetch.status; // Readonly<Ref<ActionStatus>>
store.action.fetch.error; // Readonly<Ref<Error | null>>
store.action.fetch.data; // DeepReadonly<T> | null
store.action.fetch.reset(); // Reset to idle
```

> **Note:** `data`, `status`, and `error` persist after execution. Call `reset()` to clear them back to their initial values (`null`, `IDLE`, `null`).
> **Note:** `status` and `error` persist after execution. Call `reset()` to clear them back to their initial values (`IDLE`, `null`).

### Template Usage

Expand Down
7 changes: 1 addition & 6 deletions playground/pages/posts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,6 @@ function resetSortAction() {
<pre>{{ JSON.stringify(postStore.view.editor.value, null, 2) }}</pre>
</div>

<div v-if="postStore.action.sort.data" class="detail" data-testid="sort-data">
<h3>action.sort.data (last sort result)</h3>
<p>{{ (postStore.action.sort.data as any)?.length }} items sorted</p>
</div>

<!-- Action Status -->
<div class="monitor-status" data-testid="action-status">
<h3>Action Status</h3>
Expand Down Expand Up @@ -185,7 +180,7 @@ function resetSortAction() {
<code>action(&#123; commit: &#123; mode: ActionManyMode.ADD &#125; &#125;)</code> - Call-time
commit.mode override
</li>
<li><code>action.sort.data</code> - Last successful result from action</li>

<li><code>action.sort.reset()</code> - Reset action state</li>
<li><code>action(&#123; body &#125;)</code> - Call-time payload with body data</li>
<li><code>shape.defaults()</code> - Auto-generate zero-value form data from shape</li>
Expand Down
6 changes: 0 additions & 6 deletions playground/pages/users.vue
Original file line number Diff line number Diff line change
Expand Up @@ -143,11 +143,6 @@ function resetListAction() {
<pre>{{ JSON.stringify(userStore.view.summary.value, null, 2) }}</pre>
</div>

<div v-if="userStore.action.list.data" class="detail" data-testid="action-data">
<h3>action.list.data (last successful result)</h3>
<pre>{{ JSON.stringify(userStore.action.list.data, null, 2)?.substring(0, 200) }}...</pre>
</div>

<!-- Action Status -->
<div class="monitor-status" data-testid="action-status">
<h3>Action Status</h3>
Expand Down Expand Up @@ -218,7 +213,6 @@ function resetListAction() {
<li><code>commit("list", ActionManyMode.RESET)</code> - Standalone commit without api/handle</li>
<li><code>commit(..., { unique: true })</code> - Deduplicate on add</li>
<li><code>commit(..., { by: "email" })</code> - Custom identifier field for patch</li>
<li><code>action.list.data</code> - Last successful result</li>
<li><code>action.list.reset()</code> - Reset action state to idle</li>
<li><code>shape.defaults()</code> - Auto-generate zero-value form data from shape</li>
</ul>
Expand Down
5 changes: 2 additions & 3 deletions src/runtime/core/types/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,9 +181,8 @@ export interface ActionCallOptions {

export interface ActionCall<T = void> {
(options?: ActionCallOptions): Promise<T>;
readonly loading: ComputedRef<boolean>;
readonly status: Readonly<Ref<ActionStatus>>;
readonly error: Readonly<Ref<Error | null>>;
readonly data: DeepReadonly<T> | null;
readonly status: Readonly<Ref<ActionStatus>>;
readonly loading: ComputedRef<boolean>;
reset: () => void;
}
13 changes: 3 additions & 10 deletions src/runtime/core/utils/action.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defu } from "defu";
import { type DeepReadonly, type Ref, ref, computed, readonly, toValue, nextTick } from "vue";
import { type Ref, ref, computed, readonly, toValue, nextTick } from "vue";

import { type StoreModel, type ModelDefinitions, type ModelCall, ModelOneMode, ModelManyMode } from "../types/model";
import type { Shape } from "../types/shape";
Expand Down Expand Up @@ -341,8 +341,6 @@ export function createAction<MD extends ModelDefinitions, VD extends ViewDefinit
let currentController: Promise<R> | null = null;
let abortController: AbortController | null = null;

let globalData: R | null = null;

const globalError = ref<Error | null>(null);
const globalStatus = ref<ActionStatus>(ActionStatus.IDLE);

Expand Down Expand Up @@ -424,7 +422,6 @@ export function createAction<MD extends ModelDefinitions, VD extends ViewDefinit
data = await executeHandler(definition as ActionHandlerDefinition<MD, VD, R>, model, view);
}

globalData = data;
activeStatus.value = ActionStatus.SUCCESS;

definition.logger?.debug("Action success", {
Expand All @@ -447,22 +444,18 @@ export function createAction<MD extends ModelDefinitions, VD extends ViewDefinit
}

const action = Object.assign(execute, {
get loading() {
return loading;
},
get error() {
return readonly(globalError) as Readonly<Ref<Error | null>>;
},
get status() {
return readonly(globalStatus) as Readonly<Ref<ActionStatus>>;
},
get data() {
return globalData as DeepReadonly<R> | null;
get loading() {
return loading;
},
reset() {
globalError.value = null;
globalStatus.value = ActionStatus.IDLE;
globalData = null;
},
});

Expand Down
4 changes: 0 additions & 4 deletions test/__mocks__/setup.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { vi, beforeEach } from "vitest";

declare global {
var $fetch: ReturnType<typeof vi.fn>;
}

vi.mock("#build/harlemify.config", () => {
return {
default: {
Expand Down
4 changes: 1 addition & 3 deletions test/action.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,12 @@ describe("createAction", () => {
expect(action).toBeTypeOf("function");
});

it("has loading, status, error, data, reset", () => {
it("has loading, status, error, reset", () => {
const { action } = setup();

expect(action.loading).toBeDefined();
expect(action.status).toBeDefined();
expect(action.error).toBeDefined();
expect(action.data).toBeNull();
expect(action.reset).toBeTypeOf("function");
});

Expand Down Expand Up @@ -237,7 +236,6 @@ describe("createAction", () => {

expect(action.status.value).toBe(ActionStatus.IDLE);
expect(action.error.value).toBeNull();
expect(action.data).toBeNull();
});
});

Expand Down
2 changes: 1 addition & 1 deletion test/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ModelOneMode, ModelManyMode } from "../src/runtime/core/types/model";
import { ActionStatus } from "../src/runtime/core/types/action";
import type { ShapeInfer } from "../src/runtime/core/types/shape";

const mockFetch = globalThis.$fetch;
const mockFetch = (globalThis as any).$fetch;

const UserShape = shape((factory) => {
return {
Expand Down