# @doweljs/core 中文文档

规范地址：https://npmcn.com/package/%40doweljs/core/

版本：0\.5\.0
npm 元数据更新时间：2026-10-27T05:31:49.935Z
中文译文更新时间：2026-11-13T05:14:14.916Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40doweljs/core
原文对照：https://npmcn.com/package/%40doweljs/core/?lang=original
项目主页：https://github.com/doweljs/dowel/tree/main/packages/dowel#readme
源码仓库：https://github.com/doweljs/dowel

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

- 包体积（解压后）：473 kB，173 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=20
- 运行依赖：0 个
- 首次发布：2026-10-21T19:15:32.405Z
- 最新版本发布：2026-10-27T05:31:49.562Z
- 近 30 天下载量：2230（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：6（近 12 个月 6 个）

## 中文简介

TypeScript 的完全依赖性注射.

## 安装

```sh
npm install @doweljs/core
```

也可以使用：`pnpm add @doweljs/core`、`yarn add @doweljs/core`、`bun add @doweljs/core`

## README

# @ doweljs/ 分数

TypeScript 的完全依赖性注射.

-   **编译器检查完整性 .** 一个容器不能用一个不捆绑的插槽来建造,打开时没有其范围值,或者要求一个它看不到的节点. 类型错误 :.
-   **只在边上发作.** `build()` 财务报告和审定财务报表 `open()` 返回承诺; `get()` 从来没有。 独立自动同步服务同时启动.
-   **范围是推定的.** 取决于请求范围槽和每个请求的服务寿命. 在更长寿的服务中捕捉更短寿值是一个编译错误.
-   **所有权是明确的.** 服务处理它所采纳的内容,资源有一个所有者.
-   **童工是可以注射的.** 一个 _执行_ 操作一个打字的跑道;每跑道得到自己的短寿命容器.
-   **没有装饰,反射或代理.** 大约9个KB被精密和被克齐克; `get()` 大约需要40纳秒.

```ts
import { container, service, slot } from '@doweljs/core'

const Logger = slot('Logger')<{ info(message: string): void }>()

const Config = service('Config', () => ({ dbUrl: process.env.DATABASE_URL! }))
const Db = service('Db', { config: Config }, async ({ config }, ctx) =>
    ctx.adopt(await connect(config.dbUrl), (client) => client.close()),
)
const Users = service('Users', { db: Db, log: Logger }, ({ db, log }) => ({
    list: () => {
        log.info('listing users')
        return db.query('select * from users')
    },
}))

const app = await container().provide(Users).bind(Logger, { info: console.log }).build()

app.get(Users).list()
await app.dispose()
```

删除 `.bind(Logger, …)` 财务报告和审定财务报表 `build()` 停止调用: 类型变为 `MissingBindings<{ Logger: "Users" }>`。 。 。 .

## 槽数

插槽是容器所填充的已命名并打入的空隙. 槽是图中唯一的开出点;其他的都是自动接线.

```ts
// Bound once per container.
const Logger = slot('Logger')<LoggerApi>()

// Supplied each time a container for PerRequest opens.
const PerRequest = scope('request')
const Request = slot('Request', { scope: PerRequest })<IncomingRequest>()

// Collects contributions into a readonly array.
const Middleware = slot('Middleware', { many: true })<Middleware>()
```

名称在一个容器内是独一无二的:类型系统用名称来识别节点.

## 服务

一个服务将其依赖性列为值 。 其要求、范围和是否同步都来自这些要求.

```ts
const Users = service(
    'Users',
    { db: Db, log: Logger, metrics: optional(Metrics) },
    ({ db, log, metrics }, ctx) => {
        ctx.defer(() => log.info('Users disposed'))
        return {
            list() {
                metrics?.increment('users.list')
                return db.query('select * from users')
            },
        }
    },
)
```

缓冲 `Users` 来查看图中的编译器理由 :

```ts
Service<UsersApi, {
    name: 'Users'; scope: 'root'; above: never
    graph: {
        Users:   { kind: 'service'; scope: 'root'; deps: 'Db' | 'Logger' | 'Metrics'; optional: 'Metrics'; … }
        Db:      { kind: 'service'; scope: 'root'; deps: 'Config'; … }
        Config:  { kind: 'service'; scope: 'root'; deps: never; … }
        Logger:  { kind: 'slot';     scope: 'root'; … }
        Metrics: { kind: 'slot';     scope: 'root'; … }
    }
}>
```

服务每个集装箱都有一次服务。 唯一的选择, `scope`,将一个服务置于比其依赖性更深处,例如每个请求一个工作单位,尽管它只需要数据库;更浅是编译错误.

### 资源和处置

工厂的背景镜 `AsyncDisposableStack`数字 :

-   `ctx.use(resource)` 通过a `Disposable` 或者说 `AsyncDisposable` 并归还它.
-   `ctx.adopt(value, dispose)` 随其处置者收养其他任何东西.
-   `ctx.defer(fn)` 运行回调,随时可以使用.
-   `ctx.signal` 当实例处理开始时中止, 即使它的工厂仍在运行。 通过它来慢化启动工作 : `connect(url, { signal: ctx.signal })`。 。 。 .

只有工厂采用的方法被处理掉;采用另一种服务方法是一个错误。 处置是最后的先发制人 内部瞄准镜在外边 等待工厂仍在运行.

## 集装箱

```ts
const app = await container()
    .provide(Users, Mailer) // services; their dependencies are discovered
    .bind(Logger, PinoLogger) // a slot to a service…
    .bind(Config, { port: 3000 }) // …or to a value
    .contribute(Middleware, Cors) // one item of a multi-binding
    .with(infrastructure) // apply a composition function
    .override(Db, fakeDb) // replace a node in the graph (tests)
    .build()
```

`provide()` 将根起名来。 他们在行刑之外接触到的东西是在 `build()`;只有执行所达到的才由需要它的第一个运行所创造(参见 [处决](https://npmcn.com/package/%40doweljs/core/#readme-executions)) (中文(简体) ). 约束只有在能够达到的东西取决于它的槽时才生效.

如果工厂失败, `build()` 处置它创造和拒绝的东西 `AggregateError`。 。 。 .

构建者是坚持不懈的:每一次呼叫都会返回一个新的构建者. 写入状态下通用的可重复使用成分, 因此它们应用在一个链中的任何位置 :

```ts
const infrastructure = <S extends ContainerState>(b: ContainerBuilder<S>) =>
    b.bind(Logger, ConsoleLogger).bind(Database, SqlDatabase)

container().with(infrastructure).bind(Config, config).provide(Checkout).build()
```

`get()` 以容器所创造的东西打入:

```ts
app.get(Users) // UsersApi
app.get(Mailer) // error: NotProvided<"Mailer">
app.get(Current) // error: NotVisible<"Current", "request">
app.get(Smtp) // error: CreatedOnDemand<"Smtp">, reached only through an execution
```

覆盖精确: 覆盖 `Db` 假的,没有别的 `Db` 还需要更多的资金.

## 范围

一个范围是比应用程序的寿命短,例如一个请求. `open()` 为它创建一个子容器,其范围槽被绑定在值上;一个缺失的值是一个编译错误来命名槽和需要它的东西.

```ts
await using request = await app.open(PerRequest, bind(Request, req))
request.get(CurrentUser) // created once per opened container
request.get(Users) // root-scope services are shared
```

**总是处理什么 `open()` 返回时**,与 `await using` 或在一个 `finally`容器将打开的东西保存到被处理为止,所以一个被丢弃的容器将一直停留到其母体被处理为止.

当容器打开时,就会产生一个范围中最深处的根部,也就是保持 `get()` 同步. 保持放靶工厂的便宜.

范围巢:

```ts
export const Session = scope('session')
export const Request = scope('request', Session)

const Sess = slot('Sess', { scope: Session })<SessionData>()
const Req = slot('Req', { scope: Request })<IncomingRequest>()

const Cart = service('Cart', { sess: Sess }, …)                  // lives in 'session'
const Handler = service('Handler', { req: Req, cart: Cart }, …)  // lives in 'request'

await using session = await app.open(Session, bind(Sess, data))
await using request = await session.open(Request, bind(Req, req))
request.get(Handler)   // sees request, session and root
session.get(Handler)   // error: NotVisible<"Handler", "request">
app.open(Request, …)   // error: NotChildScope<"request", "root">
```

一项服务在其附属单位中处于最深的范畴;在申报时,对不同分支的依赖被拒绝。 宣布每个范围一次,并在使用地点导入: 共享一个名称的两个范围无论在哪里相会都会被拒绝.

## 处决

工作服务本身开始和完成,如工作或信息处理器,是一种执行:范围,该范围或以上的条目,每个输入都运行供应。 受抚养人得到一个跑者.

```ts
const Job = scope('job')
const JobInput = slot('JobInput', { scope: Job })<{ id: string }>()
const JobRunner = service('JobRunner', { input: JobInput, db: Db }, ({ input, db }) => ({
    run: () => db.process(input.id),
}))

const RunJob = execution('RunJob', { scope: Job, entry: JobRunner, inputs: { job: JobInput } })

const Scheduler = service('Scheduler', { runJob: RunJob }, ({ runJob }) => ({
    schedule: (id: string) => runJob({ job: { id } }, (runner) => runner.run()),
}))
```

每次运行都会用给定的输入物打开放入范围的一个容器,将输入物交给尸体,一旦尸体安顿下来,便处置容器. 没有投入的处决只取出身体. `build()` 如果输入不包含条目需要的内容,则不可调用: `IncompleteExecutions<{ RunJob: "Other" }>`。 。 。 .

该条目在运行范围上达到的,且未提供达到的,由需要它的第一个运行创建,与后一个运行共享,并处置其范围;一个失败的创建在下一个需求下进行再审. `get()` 拒绝提供此类服务 `CREATED_ON_DEMAND`;提供它与容器一起创建.

货箱的跑道被解决了 处理该容器中止 `signal` 尸体收到作为第二个论据,等待尸体解决,然后处理跑道。 在等待之前,每跑道都会被中止 所以等待对方的跑道不会陷入僵局 车主在尸体被丢弃前被丢弃 `DISPOSED`其身体成功但处置失败的奔跑者以 `DisposalError` (单位:千美元)`DISPOSAL_FAILED`:身体的结果在 `value` 和处置失败 `cause`,所以一个不得重复承诺的工作的呼叫者可以登录并继续进行.

呼叫者取消运行 `{ signal }` 尸体之后,就像 `runJob({ job }, body, { signal: AbortSignal.timeout(5_000) })`。在身体开始前中止,运行处理它所创造的东西,并以信号的理由拒绝;在身体运行时中止, 身体的运行 `signal` 以这个理由中止 运行会像尸体那样稳定 被取消的跑步从不运行它的身体.

## 多约束

```ts
const Middleware = slot('Middleware', { many: true })<Middleware>()
const Server = service('Server', { middleware: Middleware }, ({ middleware }) =>
    createServer(middleware),
)

container().provide(Server).contribute(Middleware, Auth).contribute(Middleware, Cors).build()
```

对只读数组的贡献决心, 依次; 无, 改为 `[]`。 。 。 . `override()` 以多节制取代整个集.

## 回顾

`inspect()` 描述每个节点:其范围、依赖性、约束性和实例状态.

## 错误

运行时间错误 `DowelError`有马厩的 `code`,例如 `UNBOUND_SLOT`, (中文(简体) ), `CAPTIVE_DEPENDENCY`, (中文(简体) ), `CYCLE` 或者说 `DISPOSED`,并命名依赖路径:

```text
Slot 'Logger' is not bound. Needed by Api → Users → Logger.
```

类型良好的代码很少到达它们;它们是安全网 `any` 和动态组成.

## 页:1

-   **周期数** 只能来自约束性、超载性或执行性,其条目到达运行该条目的服务。 全部在建造时都被拒绝;前两个也按类型拒绝.
-   **类型脚本 5.9 或以后** 需要时间.
-   **字形类型.** TypeScript 在推论下保留嵌入函数的字面返回类型 : `service('S', () => ({ kind: () => 'a' }))` 推断 `() => "a"`。当您想要更宽的返回类型时,请注明返回类型.

## 常见问题

### @doweljs/core 是什么？

TypeScript 的完全依赖性注射\.

### 如何安装 @doweljs/core？

在项目目录运行 npm install @doweljs/core，或使用 pnpm add / yarn add / bun add。

### @doweljs/core 支持 TypeScript 吗？

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

### @doweljs/core 支持 ESM 还是 CommonJS？

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

### @doweljs/core 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=20。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @doweljs/core 可以商用吗？

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

### @doweljs/core 的下载量有多少？

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

### @doweljs/core 还在维护吗？

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

