@alxia/tenancy

中文文档
0.6.0发布于 2026/11/06MITTypeScriptES Module零依赖近 30 天下载 1908

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

中文使用文档
文档目录12 节
  1. 30秒内
  2. 请求的范围
  3. 一个模块的手柄
  4. 标题, ID 或 sult
  5. 每个租户,一个工作
  6. 提供、同步、迁移
  7. 一个数据库
  8. 测试数据库
  9. API 密码
  10. 拒绝代码
  11. 文档
  12. 许可证

@alxia/ 租户

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

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

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

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

30秒内

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: 仓库 复用并永不闭塞,主机有.

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

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

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

请求的范围

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) 用于未知、不活动或 非会员组织, 无论是什么组织.

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

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; 它从不命名数据库 :

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数字 :

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

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

每个租户,一个工作

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 }

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

提供、同步、迁移

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 });

一个数据库

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

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. 所以一个套房 设立数量有限的组织,尽管它规定许多组织:

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限定的租户缓存 Mongos,对到达数据库名称的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 TenancyErrors 商店扔出,供主机自己的商店或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。 。 。 .

文档

  • 指南:注册端口,一个请求的范围,工作,提供,迁移,单商店,测试.
  • 解决问题:一个错误消息,以及该如何应对.
  • 路线图:即将到来的,以及没有计划的.

许可证

麻省理工学院

版本与发布记录

@alxia/tenancy 共发布 11 个版本,首次发布于 2026/11/01;过去 12 个月发布了 11 个版本。

版本发布日期距今
0.6.0latest2026/11/062 天前
0.5.02026/11/017 天前
0.4.02026/11/017 天前
0.3.02026/11/017 天前
0.2.12026/11/017 天前
0.2.02026/11/017 天前

关于 @alxia/tenancy 的常见问题

@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 和提交记录判断。