# @alxia/tenancy 中文文档

规范地址：https://npmcn.com/package/%40alxia/tenancy/

版本：0\.6\.0
npm 元数据更新时间：2026-11-05T17:50:27.283Z
中文译文更新时间：2026-11-08T03:31:45.504Z
软件包许可：MIT

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

原始 npm 页面：https://www.npmjs.com/package/%40alxia/tenancy
原文对照：https://npmcn.com/package/%40alxia/tenancy/?lang=original
源码仓库：https://github.com/softistx/alxia

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

- 包体积（解压后）：147 kB，35 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：0 个
- peer 依赖：mongodb ^7\.7\.0，typescript ^6\.0\.3 \|\| ^7\.0\.0，@alxia/modules ^0\.4\.0，@softistx/mongo ^0\.27\.0
- 首次发布：2026-10-31T16:35:20.724Z
- 最新版本发布：2026-11-05T17:50:27.064Z
- 近 30 天下载量：1908（2026-10-06 至 2026-11-04，npm 官方下载统计）
- 已发布版本数：11（近 12 个月 11 个）

## 中文简介

在@softistx/mongo上申请的租赁:一个合同、两个商店(一个核心数据库旁边的每个组织一个数据库,或一个数据库)、一个标题解决了请求范围、提供和每个租户的循环

## 安装

```sh
npm install @alxia/tenancy
```

也可以使用：`pnpm add @alxia/tenancy`、`yarn add @alxia/tenancy`、`bun add @alxia/tenancy`

## README

# @alxia/ 租户

申请的租赁 `@softistx/mongo`数字 : 一份合同, `TenantStore`,以及背后的两个策略. `perOrganization` 维持一个核心数据库(账户、组织登记册、跨组织) 每个组织有一个数据库; `single` 保留一个数据库和一个 固定租户。 请求在标题中标明其组织及其范围 得到的是组织的数据库, 所以解析器从来没有建立数据库 名称和查询无法到达另一个组织的数据.

```sh
bun add @alxia/tenancy @alxia/modules @softistx/mongo mongodb
bun add -d typescript
```

`@softistx/mongo` 是私人的,在 GitHub 包上。 绘制范围 项目的 `bunfig.toml` 和导出 `GITHUB_TOKEN`,与 `read:packages`数字 :

```toml
[install.scopes]
"@softistx" = { url = "https://npm.pkg.github.com/", token = "$GITHUB_TOKEN" }
```

## 30秒内

```ts
import { connectMongo, defineMongo, openMongo } from '@softistx/mongo';
import { perOrganization } from '@alxia/tenancy';

const { client } = await connectMongo(process.env.MONGO_URI!);

// The host's core database: the registry of organisations, accounts...
const core = await openMongo(
    defineMongo({ client, database: 'app_core', collections: coreCollections }),
);

export const tenancy = perOrganization({
    core,
    registry, // the port over your `organizations` collection, see the guide
    database: 'app_org_', // databases are named `app_org_<id>`
    configure: (database) =>
        defineMongo({ client, database, collections: tenantCollections }),
    membership: async (organizationId, accountId) =>
        (await roleOf(organizationId, accountId)) ?? null, // null: not a member
});
```

已经有一个缓存从 `createTenantDatabases` (与工作和 测试)? 过 `databases` 改为 `database` 财务报告和审定财务报表 `configure`: 仓库 复用并永不闭塞,主机有.

```ts
import { createTenantDatabases, perOrganization } from '@alxia/tenancy';

const databases = createTenantDatabases({ database: 'app_org_', configure });

perOrganization({ core, registry, databases, membership });
```

## 请求的范围

```ts
const scope = await tenancy.scope(request, viewer); // viewer: { id } or null

scope.tenant; // the organisation record
scope.mongo; // ITS database, writes stamped with the viewer
scope.core; // the host's core database
scope.member; // what `membership` answered, a role for instance
```

它扔一个 `TenancyError` 带有 `code`页:1 `status` 财务报告和审定财务报表 `params` 映射到 错误: `tenancy.errors.organization-required` (400,标题缺失), `tenancy.errors.unknown-organization` (404) 用于未知、不活动或 非会员组织, 无论是什么组织.

每个请求一次解决范围问题,只有在有东西问:

```ts
import { lazyScope } from '@alxia/tenancy';

const getScope = lazyScope(tenancy, request, viewer);
const { mongo } = await getScope(); // the same promise every time
```

## 一个模块的手柄

`@alxia/modules` 是需要的对等点。 这个 `modules` 选项是可选的 : 没有它, `forModule` 掷出 `module "x" is not registered`。 。 。 .

给存储您的注册和插件代码只触及其控件 模块,通过 `Register`; 它从不命名数据库 :

```ts
import { resolveModules } from '@alxia/modules';

const modules = resolveModules(registered);
const tenancy = perOrganization({ /* … */ modules });

const scope = await tenancy.scope(request, viewer);
const { mongo, core } = tenancy.forModule(scope, 'notes');
mongo.db.notes; // the module's tenant collections and its `dependsOn` ones
core.db.accounts; // its `coreCollections`, on the core database
```

`collections` 租户数据库中的土地, `coreCollections` 输入核心 一个; 在 `single` 存储两者都是相同的数据库. `forModule` 还要 `{ mongo, core: tenancy.core }` 租户 `eachTenant` 把手举起来 它会扔 (单位:千美元) `ModuleWiringError` 对于一个缺少的模块,或者您的收藏 `defineMongo` 接线不接 (用 `collectionsOf(modules)` 财务报告和审定财务报表 `coreCollectionsOf(modules)`规定, `sync` 财务报告和审定财务报表 `migrate` 拿着 从登记册移出`migrationsOf`, (中文(简体) ), `coreMigrationsOf`),在 联合国 `migrations` 财务报告和审定财务报表 `coreMigrations`。 。 。 .

## 标题, ID 或 sult

标题是 `x-organization-id` 以 id 命名组织 或者用弹子,无论如何。 换一个 `header`数字 :

```ts
perOrganization({ /* … */ header: 'x-tenant-id' });
```

默认导出为 `DEFAULT_HEADER`。只显示 `active` 组织决定。 除了请求之外, `tenancy.resolve(ref)` 进行同样的检查.

## 每个租户,一个工作

```ts
await tenancy.eachTenant(async (tenant, mongo) => {
    await mongo.db.outbox.deleteMany({ sentAt: { $lt: cutoff } });
}); // the active organisations, one after the other

await tenancy.eachTenant(visit, { withDeleted: true }); // every registered one

// keep going when a tenant fails (database unreachable, `fn` throws)
const results = await tenancy.eachTenant(visit, { onError: 'collect' });
const failed = results.filter((r) => !r.ok); // { tenant, ok: false, error }
// the others are { tenant, ok: true, value }
```

默认第一个失败者拒绝,而后来的租户则不被探视.

## 提供、同步、迁移

```ts
const tenant = await tenancy.provision({ slug: 'north', name: 'North' }, actorId);
// registered as `provisioning`, database synced and migrated, then `active`

// throws a TenancyError `tenancy.errors.slug-taken` (409) when the slug is taken

// a failed `provision` leaves the tenant `provisioning`: fix the cause, then
await tenancy.prepare(tenant.id);
await tenancy.activate(tenant.id, actorId); // `perOrganization`; active tenants are returned as is

await tenancy.sync(); // `db:sync`: the core, then every organisation
await tenancy.migrate({ kind: 'apply', dryRun: false }); // `db:migrate`
await tenancy.migrate({ kind: 'rollback', to: '2026-10-27-marker', dryRun: true });
```

## 一个数据库

对于服务于一个组织的东道方,同样的合同没有标题 不检查会员:

```ts
import { single } from '@alxia/tenancy';

export const tenancy = single({
    mongo, // the one database
    tenant: { id: 'main', slug: 'main', name: 'Main' },
    migrations,
});
```

代码写入反对 `TenantStore` 两家店都一样.

## 测试数据库

`slotNaming` 名称数据库 `<prefix>_slot1`, (中文(简体) ), `<prefix>_slot2`. 所以一个套房 设立数量有限的组织,尽管它规定许多组织:

```ts
import { slotNaming } from '@alxia/tenancy';

const database = slotNaming('app_test', 4);
perOrganization({ /* … */ database });
database.reset(); // between tests
```

## API 密码

| 导出 | 它会做什么 |
| --- | --- |
| `perOrganization(options)`, (中文(简体) ), `PerOrganizationOptions`, (中文(简体) ), `PerOrganizationStore` | 每个组织的核心数据库加一个数据库 |
| `single(options)`, (中文(简体) ), `SingleOptions`, (中文(简体) ), `SingleStore` | 一个数据库,一个固定租户 |
| `TenantStore` | 合同双方都回答: `resolve`, (中文(简体) ), `tenantOf`, (中文(简体) ), `databaseOf`, (中文(简体) ), `scope`, (中文(简体) ), `forModule`, (中文(简体) ), `eachTenant`, (中文(简体) ), `provision`, (中文(简体) ), `prepare`, (中文(简体) ), `sync`, (中文(简体) ), `migrate`, (中文(简体) ), `close` |
| `lazyScope(store, request, viewer)` | 请求的范围,在首次使用时解决一次 |
| `viewerMemberships(membership, viewer)`, (中文(简体) ), `ViewerMemberships` | 观众在任何租户中的成员资格,按要求读取一次 |
| `slotNaming(prefix, slots)`, (中文(简体) ), `SlotNaming`, (中文(简体) ), `DatabaseNaming` | `<prefix>_slot<n>` 数据库名称和类型 `database` 选项( 租户代号的前缀或函数) |
| `createTenantDatabases(options)`, (中文(简体) ), `TenantDatabases`, (中文(简体) ), `TenantDatabasesOptions`, (中文(简体) ), `isTenantId`, (中文(简体) ), `dbOf` | 限定的租户缓存 `Mongo`s,对到达数据库名称的id的检查,驱动程序 `Db` a 项下 `Mongo` |
| `ModuleScope`, (中文(简体) ), `ModuleSource` | 什麽 `forModule` 返回( r)`mongo`, (中文(简体) ), `core`并且具有:范围,或 `{ mongo, core: store.core }` 租户 `eachTenant` 访问 |
| `TenancyError`, (中文(简体) ), `TenancyErrorCode`, (中文(简体) ), `UnknownMigrationError`, (中文(简体) ), `ModuleWiringError` | 拒绝(`tenancy.errors.*`,见下文)和两个靴子出错,带有代码 `tenancy.unknown-migration` 财务报告和审定财务报表 `tenancy.module-wiring` |
| `organizationRequired(header)`, (中文(简体) ), `unknownOrganization()`, (中文(简体) ), `slugTaken(slug)` | 页:1 `TenancyError`s 商店扔出,供主机自己的商店或spec扔出相同的商店 |
| `DEFAULT_HEADER` | `'x-organization-id'`,在 `header` 未给出 |
| `membershipOf(source)` | (单位:千美元) `Membership` 对于一个 spec, 从一个纯函数( 同步或 sync) 或 a `{ [tenantId]: answer }` 地图; `undefined` (b) 使用一个功能来区分账户 |
| `noMembers` | (单位:千美元) `Membership` 承认没有人, 对于一个spec 或锁定主机 |
| `Tenant`, (中文(简体) ), `TenantStatus`, (中文(简体) ), `TenantRegistry`, (中文(简体) ), `TenantResult`, (中文(简体) ), `EachTenant`, (中文(简体) ), `Membership`, (中文(简体) ), `TenantScope`, (中文(简体) ), `Viewer`, (中文(简体) ), `Resolution`, (中文(简体) ), `EachTenantOptions`, (中文(简体) ), `MigrationLists`, (中文(简体) ), `MigrateMode`, (中文(简体) ), `MigrationOutcome` | 类型(`Viewer` 实值 `{ id }`· ; `@alxia/access` 拥有相同的形状) |

### 拒绝代码

无法满足的请求 `TenancyError` 谁 `code` 实值 `tenancy.errors.organization-required` (400个), `tenancy.errors.unknown-organization` (404)或(404) `tenancy.errors.slug-taken` (409) (英语). 这些代码保存着 `tenancy.errors.` 有意命名空间: 它们是已存在主机的 i18n 目录的密钥 翻译,它们符合 `x-organization-id` 页眉和 `perOrganization` 名称。 重新校正他们会无益地打破每一译本. 靴子出错( A)`tenancy.module-wiring`, (中文(简体) ), `tenancy.unknown-migration`是一个不同的 类型: 开发者, 从未向用户显示, 因此它们使用内核的后缀形状 (单位:千美元)`tenancy.<reason>` 和一条消息.

端点 : `@alxia/modules`, (中文(简体) ), `@softistx/mongo`, (中文(简体) ), `mongodb`, (中文(简体) ), `typescript`。 。 。 .

## 文档

-   [指南](https://github.com/softistx/alxia/blob/develop/packages/tenancy/docs/guide.md):注册端口,一个请求的范围,工作,提供,迁移,单商店,测试.
-   [解决问题](https://github.com/softistx/alxia/blob/develop/packages/tenancy/docs/troubleshooting.md):一个错误消息,以及该如何应对.
-   [路线图](https://github.com/softistx/alxia/blob/develop/packages/tenancy/docs/roadmap.md):即将到来的,以及没有计划的.

## 许可证

麻省理工学院

## 常见问题

### @alxia/tenancy 是什么？

在@softistx/mongo上申请的租赁:一个合同、两个商店\(一个核心数据库旁边的每个组织一个数据库,或一个数据库\)、一个标题解决了请求范围、提供和每个租户的循环

### 如何安装 @alxia/tenancy？

在项目目录运行 npm install @alxia/tenancy，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：mongodb（^7\.7\.0）、typescript（^6\.0\.3 \|\| ^7\.0\.0）、@alxia/modules（^0\.4\.0）、@softistx/mongo（^0\.27\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @alxia/tenancy 支持 TypeScript 吗？

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

### @alxia/tenancy 支持 ESM 还是 CommonJS？

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

### @alxia/tenancy 可以商用吗？

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

### @alxia/tenancy 的下载量有多少？

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

### @alxia/tenancy 还在维护吗？

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

