# @\_linked/server-utils 中文文档

规范地址：https://npmcn.com/package/%40_linked/server-utils/

版本：1\.17\.0
npm 元数据更新时间：2026-10-28T08:06:00.528Z
中文译文更新时间：2026-11-06T06:37:41.154Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40_linked/server-utils
原文对照：https://npmcn.com/package/%40_linked/server-utils/?lang=original
项目主页：https://github.com/linked-fw/server-utils#readme
源码仓库：https://github.com/linked-fw/server-utils

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：475 kB，114 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：3 个
- peer 依赖：react ^18\.2\.0 \|\| ^19\.0\.0
- 首次发布：2026-04-22T17:53:52.238Z
- 最新版本发布：2026-10-28T08:06:00.136Z
- 近 30 天下载量：4429（2026-10-05 至 2026-11-03，npm 官方下载统计）
- 已发布版本数：41（近 12 个月 41 个）

## 中文简介

为应用提供一套具有林克-服务器后端的公用事业.

## 安装

```sh
npm install @_linked/server-utils
```

也可以使用：`pnpm add @_linked/server-utils`、`yarn add @_linked/server-utils`、`bun add @_linked/server-utils`

## README

# 林克-服务器-实用

为应用提供一套具有林克-服务器后端的公用事业.

## 服务器

```tsx
import { Server } from 'lincd-server-utils/lib/utils/Server';
```

从前端呼叫服务器 .

### 调用失败

默认情况下,一个无法使用非-2xx响应的呼叫会记录一个警告并解决 `undefined`。 。 。 .

拒绝,通过一个 `CallConfig` 与 `rejectOnError: true` 作为方法 :

```tsx
import { Server, ServerCallError } from '@_linked/server-utils/utils/Server';

try {
  const result = await Server.call(this, { method: 'selectQuery', rejectOnError: true }, query);
} catch (err) {
  if (ServerCallError.is(err)) {
    err.status; // e.g. 500 (provider threw) or 501 (no provider for the call)
    err.message; // the server's {error} message, or the status text
  }
}
```

随着选择进入,一个成功的呼叫仍然解决 任何提供者返回,包括 `null` 或者说 `undefined`。 。 。 .

在后门上, `Server.call` 直接调用本地服务器并遵循相同的规则 :

-   **无供养.** 一个没有提供者处理的电话解决了 `undefined` 默认。 与 `rejectOnError` 它以501的地位拒绝.
-   **供养所掷.** 一个总是拒绝的提供者方法。 与 `rejectOnError` 拒绝是 `ServerCallError` 状态 500,原始错误保留为 `cause`。 。 。 .

### 认证钩

认证软件包通过注册让服务器呼叫周围的会话存在 `AuthHandler`. 服务器使用器对符号一无所知: 处理器更改默认信头(`Server.addDefaultHeaders` 编号 : `Server.removeDefaultHeaders`)),而每个请求头都是在钩子运行后所建.

```tsx
import { Server } from '@_linked/server-utils/utils/Server';

Server.setAuthHandler({
  // before every request: e.g. refresh a token that has already expired
  async beforeRequest(url, init) {
    if (tokenExpired()) await refresh(); // refresh() calls Server.addDefaultHeaders(...)
  },
  // after a 401: resolve true to send the request again, once, with the current default headers
  async onUnauthorized(url, response) {
    return refresh();
  },
});
```

-   **打电话来.** HTTP的每个电话: `Server.call`, (中文(简体) ), `Server.customPost` 财务报告和审定财务报表 `Server.callCustomShapeMethod`。直接针对后端本地服务器的电话不使用。 有一个处理器;设置另一个替换它 `null` 将取去.
-   **`init` 是一个预览.** 要更改发送的内容, 请更改默认标题 .
-   **401号.** 403表示来电者是已知的且不允许的;刷新无济于事,所以不会传递给 `onUnauthorized`。 。 。 .
-   **每个电话最多一次重试.** 重试的回复是给打电话的人的: `onUnauthorized` 再来一次 如果在请求飞行时默认头部发生变化(同时刷新了另一通电话),则401在一次重审时无需询问.
-   **货币.** 重试状态属于每个呼叫. 做同时刷新 分享一次往返是处理者的工作.
-   **扔钩子** 被登录,并作为“无变化” /“不重试”.
-   **反应行动没有经过反复研究.** 一个回答的提供者 `200` 反应行动(例如: `@_linked/auth`是因为 `ENFORCE_SIGNIN`)已经运行了,并且可能在决定来电者不签名之前写过一些东西,所以再次发送请求并不安全. 反应行动将持续到 `Server.registerActionHandler`。 。 。 .

`callCustomShapeMethod` 发送默认信头, 但 `Content-Type` (身体经常是) `FormData`,它需要浏览器来设定边界。 信头传递给它胜利。 它的躯体又被重试送去,所以使用一个能成为躯体的躯体(一串, `FormData`页:1 `Blob`),不是溪流.

## 客户端可以调用哪些提供者的方法

提供者方法可以达到 `/call/...` 只有在它被宣布的时候。 类型脚本 `private` 财务报告和审定财务报表 `protected` 在运行时间不存在,所以宣言是决定的。 有三个宣言,全部在 `@_linked/server-utils/utils/callable` (没有服务器导入, 可以安全加载客户端):

-   `@callable('public' | 'signed-in')` 使用实例方法,或 `declareCallable(Cls, {method: level})` (原始内容存档于2018-09-21) (英语). In plain JS. `'user'` 是一个已贬值的别名,其名称是: `'signed-in'`,对待完全像它.
-   `@internal()` 使用实例方法,或 `declareInternal(Cls, ['method'])`。它也用于您从另一个软件包导入的类.
-   终不宣说.

当客户称这个方法时 每个人会做什么:

| 声明 | 超过 HTTP |
| --- | --- |
| `@callable('public')` | 任何人都可以打电话, 签名与否. |
| `@callable('signed-in')` | 没有会话, 服务器会回答 `401` 在方法体运行之前。 机体从调用上下文读取用户,从不读取其论据. |
| 未申报 | 取决于 `server.rpcExposure` 内 `@_linked/server`: 内 `warn` 模式(默认)它运行一次并记录一次 `enforce` 模式 `501`,似乎该方法不存在. |
| `@internal()` | 从未以任何方式通过 HTTP 发送( )`501`),即使它也被宣布为可调用. 后端代码仍然使用 `Server.call`。 。 。 . |

**为登录用户采取行动的方法必须宣布 `@callable('signed-in')` 并读取用户 `requireSessionUser()`。 。 。 .** `'public'` 允许任何人进入,而参数中的用户id是客户端发送的任意信息.

```ts
import { BackendProvider } from '@_linked/server-utils/utils/BackendProvider';
import { callable, internal } from '@_linked/server-utils/utils/callable';
import { requireSessionUser } from '@_linked/server-utils/utils/CallContext';

export default class NotesBackendProvider extends BackendProvider {
  @callable('public')
  getPublicNotes() {
    return loadPublicNotes();
  }

  @callable('signed-in')
  getMyNotes() {
    const user = requireSessionUser(); // the session's user account, not an argument
    return loadNotesOf(user);
  }

  @internal()
  purgeNotes(olderThan: Date) {
    // only reachable from backend code
  }
}
```

`requireSessionUser()` 丢出一个 `401` `ServerCallError` 在HTTP呼叫中,没有一个会话,所以它也是对一种方法的检查,有人忘记了它.

一些规则值得了解:

-   只能宣告实例方法;静态方法或错误的页码名称丢出一个 `TypeError`。 。 。 .
-   一个子类,在不重新宣布的情况下,将宣布的最严格级别保留在它的阶级链上(`'signed-in'` 结束 `'public'`) (中文(简体) ). `@internal()` 被覆盖所继承,并且胜过可调用的声明(带有警告).
-   后端到后端的呼叫( N)`Server.call` )跳过这些检查,包括内部方法.

### 无用户工作 : `runAsSystem`

每个HTTP呼叫都运行在带有其请求的呼叫上下文中,因此一个呼叫的用户遵循它所期待的或计划的一切. 未为该用户完成的工作,如从请求内开始的工作、计时器或队列消费者,必须包入 `runAsSystem`,或以该请求的用户身份运行 :

```ts
import { runAsSystem } from '@_linked/server-utils/utils/CallContext';

runAsSystem(() => startNightlyReindex(), 'nightly reindex');
```

在系统内 `requireSessionUser()` 抛出平原 `Error` (没有用户可以接受);在那里,一个用户的行为得到该用户的明确通过. `getCallContext()` 返回时 `{kind: 'http', request, response}` 或者说 `{kind: 'system', reason}`。 。 。 .

## 链接电子邮件

从后端的任何地方发送邮件 .

确保先安装电子邮件客户端, 如 `lincd-zeptomail`。 。 。 . 通过添加 `lincd-zeptomail` 到您的项目( in package.json), 它会自动用作默认的电子邮件客户端 .

在此之后, 您可以从后端的任何地方发送邮件 .

```tsx
LinkedEmail.sendEmail({
  ...
});
```

### 设置默认提供者

如果您想要执行电子邮件客户端, 您可以使用 `LinkedEmail.setDefaultProvider` 设置默认的电子邮件客户端. 您可以在您的后端提供者的构造器中做到这一点 。 以下是一个例子:

```tsx
import { LinkedEmail } from 'lincd-server-utils/lib/utils/LinkedEmail';
// import your email client from this package
import { MyMailClient } from './utils/MyMailClient';
import { BackendProvider } from 'lincd-server-utils/lib/utils/BackendProvider';
export class MyBackendProvider extends BackendProvider {
  constructor(s) {
    super(s);
    LinkedEmail.setDefaultProvider(new MyMailClient());
  }
}
```

## 共享状态和错误品牌

此软件包的一个模块可以在一次运行中两次得到评价(Vite's SSR srunder and Node under `linked start`,或两个浏览器捆绑。 呼叫者设置的州 -- `Server.setLocalServer`, (中文(简体) ), `addDefaultHeaders`, (中文(简体) ), `setAuthHandler`, (中文(简体) ), `registerActionHandler`, (中文(简体) ), `LinkedEmail.setDefaultProvider`, (中文(简体) ), 实时更新日志,请求数据——保存在 `globalThis` 与 `sharedState` 从 `@_linked/core`,所以每个副本都能看到它. `ServerCallError` 标记为: `isLinkedError(err, ServerCallError)` 从 `@_linked/core` (或 `ServerCallError.is(err)`(没有) `instanceof`。新的代码遵循相同的规则;参见 [共享状态和错误品牌](https://github.com/linked-fw/cli/blob/main/docs/architecture/shared-state-and-error-brands.md)。 。 。 .

## 常见问题

### @\_linked/server-utils 是什么？

为应用提供一套具有林克-服务器后端的公用事业\.

### 如何安装 @\_linked/server-utils？

在项目目录运行 npm install @\_linked/server-utils，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：react（^18\.2\.0 \|\| ^19\.0\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @\_linked/server-utils 支持 TypeScript 吗？

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

### @\_linked/server-utils 支持 ESM 还是 CommonJS？

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

### @\_linked/server-utils 可以商用吗？

@\_linked/server-utils 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @\_linked/server-utils 的下载量有多少？

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

### @\_linked/server-utils 还在维护吗？

最新版本 1\.17\.0 发布于 2026/10/28（10 天前）；至今共发布 41 个版本，其中过去 12 个月发布了 41 个；首次发布于 2026/04/23。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

