@cplieger/actions

中文文档
4.0.0发布于 2026/11/02Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 4768

Declarative async UI-action framework for TypeScript

此软件包已被标记为弃用

Moved into @cplieger/ui: import @cplieger/ui/actions (and @cplieger/ui/actions/testing in tests). Guide: https://github.com/cplieger/ui/blob/main/docs/migrating.md

原始使用文档
文档目录10 节
  1. Why use it
  2. Install
  3. Usage
  4. API
  5. Server text and arguments need care
  6. Unsupported by design
  7. Documentation
  8. Contributing
  9. Disclaimer
  10. License

actions

npm JSR Mutation (TS)

actions gives every server call in your TypeScript UI the same pending state, retry, rollback and messages, from one declaration per action.

It replaces the loading flag, error message and undo code you would otherwise write around each call that changes server data. Optimistic updates and retry are opt-in, and no UI framework is needed. It needs TypeScript 5.0 or later and an ESM bundler. It depends on @cplieger/reactive and @cplieger/fetch and is licensed under Apache-2.0.

Why use it

actions is built for vanilla TypeScript front ends that want the same behavior on every server call.

  • An optimistic update changes the UI before the request, and a rollback undoes it on failure or cancellation.
  • Retry backs off exponentially, retries only failures you mark as transient, and pauses while the browser is offline.
  • scope runs same-key dispatches in order, dedupe joins them into one request, and abort() or an AbortSignal passed to dispatch() cancels a single dispatch.
  • isPending and pendingCount are reactive through @cplieger/reactive, so a button can disable itself while its action runs.
  • A failure or a cancellation resolves the dispatch to null instead of rejecting, with two exceptions, and handle.outcome tells the three endings apart.
  • onSuccess, onError and onSettled follow TanStack Query's mutation callbacks, and configureApi follows RTK Query's fetchBaseQuery.
  • Only bindLoadingState and withAsyncFeedback need a DOM. The rest also runs in Node.

Consider TanStack Query if you also need a server-state cache, with cache invalidation after a mutation and framework hooks such as React's useMutation.

Install

npx jsr add @cplieger/actions
# or
npm i @cplieger/actions

Usage

Connect your notification display once at startup, define each action at module scope, then dispatch it from your event handlers.

import { configure, apiAction, retryNetwork } from "@cplieger/actions";

configure({
  success: (msg) => showToast("success", msg),
  error: (msg, retry) => showToast("error", msg, retry?.onClick),
});

const deleteItem = apiAction<string>({
  name: "items.delete",
  request: (id) => ({ method: "DELETE", path: `/api/items/${id}` }),
  error: "Could not delete the item",
  retryable: retryNetwork,
  retry: { count: 2, delay: 300 },
});

await deleteItem.dispatch(itemId);

dispatch() resolves to the result, or to null after a failure or a cancellation. After a failure, the error message has already been shown. Retry needs both settings. retryable decides which failures are transient, and retry sets the number of attempts and the wait. With retry alone, nothing is retried.

An optimistic update returns what the rollback needs to restore:

const toggleDone = apiAction<{ id: string; done: boolean }, Todo, Todo>({
  name: "todos.toggle",
  request: ({ id, done }) => ({ method: "PATCH", path: `/api/todos/${id}`, body: { done } }),
  optimistic: ({ id, done }) => {
    const previous = store.get(id);
    store.setDone(id, done);
    return previous;
  },
  rollback: ({ id }, previous) => {
    if (previous) store.set(id, previous);
  },
  scope: ({ id }) => `todo:${id}`,
});

rollback runs on an error or a cancellation, never on success. scope makes two toggles of the same item run one after the other.

To disable a button while its action runs, and to read the result with its status:

bindLoadingState("todos.toggle", toggleButton);

const outcome = await toggleDone.dispatch({ id, done: true }).outcome;
if (outcome.status === "error") console.warn(outcome.error.message);

API

  • Setup, once at startup: configure connects your notification display, and configureApi sets the base URL, headers and credentials of HTTP actions. configureTransport connects the channel for streaming commands, such as an endpoint whose replies arrive over server-sent events.
  • Factories: defineAction for any async function, apiAction for HTTP requests, transportAction for streaming commands.
  • Helpers: debouncedDispatch, pollAction, pollUntil, bindLoadingState, withAsyncFeedback, registerCleanup.
  • State and events: isPending, pendingCount, subscribeToActions, subscribeByName, getActionLog.
  • Errors and presets: ActionError, retryNetwork, classifyFetchError, hasErrorString, RETRY_STANDARD, IDEMPOTENCY_HEADER, IDEMPOTENCY_COMMAND_FIELD.
  • Tests: resetActionFramework from @cplieger/actions/testing, for beforeEach.

The full reference, generated from the doc comments, is on JSR.

Server text and arguments need care

  • An error message can include text from the server's response. Your configure adapter renders it as text, never as HTML.
  • In decodeError, info.body is the parsed body of a failed response. Check its shape before you read a field.
  • With baseUrl set, a request path cannot change the origin. Without baseUrl, the path goes to fetch() unchanged, so never build a whole path from server text.
  • The action log, its listeners and the retry button keep each dispatch's arguments and result in memory. Set sensitive: true on an action whose arguments or result carry a secret, a token or personal data. Its log entries then carry no arguments, result or error, and it takes no retry. See Sensitive actions.

Unsupported by design

FeatureReason
Query caching, stale-while-revalidateThis is an action runner, not a data cache. Use TanStack Query alongside it.
Cache invalidation, revalidationA data-cache concern.
Framework adapters for React, Vue or SvelteVanilla TypeScript by design. Framework bindings belong in separate packages.
Visual DevTools panelA separate package. getActionLog and subscribeByName give it the data.
SSR and hydrationActions are imperative mutations, with no state to carry from server to client.
Debounce maxWaitA deliberate simplification. Call flush() when a dispatch must fire.
Throttle helperNot specific to actions. Throttle before you call dispatch().
condition or a pre-execution guardAn if in the caller does it. dedupe covers the main case.
onProgress callbackTransport-specific. Report progress from your run() function.
Batch dispatchA store concern, and this library does not own a store.
dispose() or action deregistrationAn idle action holds little memory, so a realistic app does not leak.

Documentation

Contributing

Issues and pull requests are welcome. See CONTRIBUTING.md.

Disclaimer

This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.

This project was built with AI-assisted tooling using Claude, GPT, and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.

License

Apache-2.0. See LICENSE.

版本与发布记录

@cplieger/actions 共发布 48 个版本,首次发布于 2026/06/03;过去 12 个月发布了 48 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
4.0.0latest2026/11/029 天前
4.0.0-dev.6dev2026/11/029 天前
4.0.0-dev.52026/10/2022 天前
4.0.0-dev.42026/10/1823 天前
4.0.0-dev.32026/10/1823 天前
4.0.0-dev.22026/10/1823 天前

关于 @cplieger/actions 的常见问题

@cplieger/actions 是什么?

Declarative async UI-action framework for TypeScript注意:该包已被作者标记为弃用——Moved into @cplieger/ui: import @cplieger/ui/actions (and @cplieger/ui/actions/testing in tests). Guide: https://github.com/cplieger/ui/blob/main/docs/migrating.md

如何安装 @cplieger/actions?

在项目目录运行 npm install @cplieger/actions,或使用 pnpm add / yarn add / bun add。

@cplieger/actions 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@cplieger/actions 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@cplieger/actions 可以商用吗?

@cplieger/actions 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@cplieger/actions 的下载量有多少?

根据 npm 官方下载统计,@cplieger/actions 在 2026/10/11 – 2026/11/09 这 30 天内被下载 4,768 次,日均约 159 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@cplieger/actions 还在维护吗?

最新版本 4.0.0 发布于 2026/11/02(9 天前);至今共发布 48 个版本,其中过去 12 个月发布了 48 个;首次发布于 2026/06/03。作者已将其标记为弃用,新项目建议寻找替代方案。