@classytic/repo-core

中文文档
0.38.0发布于 2026/11/04MITTypeScriptES Module零依赖近 30 天下载 3555

驱动-不可知寄存器原始:钩子,滤镜IR,操作,发卡,缓存合同. 蒙戈基特、斯克利特基特、克基特和棱镜基金会。 从设计上看——这里没有插件船;每个套件都有自己的.

中文使用文档
文档目录12 节
  1. 设计原则
  2. 子路径
  3. 示例:工具包的仓库扩展 RepositoryBase
  4. 示例: URL – 解析查询 – 任何工具包
  5. 方便插件的事件名称
  6. 能力——特征检测,而非运行时出乎意料
  7. 配置驱动验证+事件(0.6.0)
  8. 更改实时重播的种子
  9. 输入结果额外
  10. 状态
  11. 许可证
  12. 商标

@ 经典/ repo- core

Sponsor

驱动-不可知寄存器原始. 钩子 过滤 IR、 操作注册、 页码、 URL 查询解析、 缓存合同—— 共享基础 @classytic/mongokit, (中文(简体) ), @classytic/sqlitekit,以及未来 @classytic/pgkit 编号 : @classytic/prismakit。 。 。 .

复古核心为 工具包作者的基础设施. 终端用户安装一个工具包(mongokit / sqlitekit),并从该命名空间导入完整的API. Repo-core是每个套件运行时间所基于的——你通常不会直接在应用代码中导入.

设计原则

  • 仅限无害环境管理. .mjs + 键 .d.mts 输出。节点22+.
  • 无根桶. 每个公共地表都是它的子路径 exports。直接从符号所在位置导入;未使用的模块从不输入您解析图.
  • 零运行时间依赖关系. 没有驱动器导入到任何地方 src/。 。 。 .
  • 树能被建筑所震动. unbundle: true 在tsdown——1:1 src-dist,没有共享块.
  • 这里没有插件 . 每个套件都拥有自己的插件执行功能,这样它们就可以使用驱动本地特性(mongoose的内置时间戳, SQLite 触发器, Postgres) now()普里斯玛 @default) (中文(简体) ). Repo-core提供原始人包作曲: CacheAdapter, (中文(简体) ), stableStringify, (中文(简体) ), buildTenantScope, (中文(简体) ), HOOK_PRIORITY,钩状引擎,过滤器IR.

子路径

// Hook engine + priority constants + canonical event names. HOOK_EVENTS turns
// raw strings into compile-checked constants so typos fail at build time.
import { HOOK_EVENTS, HOOK_PRIORITY, HookEngine } from '@classytic/repo-core/hooks';

// Abstract repository base + MinimalRepo / StandardRepo contracts + plugin types.
import { RepositoryBase, type MinimalRepo, type Plugin } from '@classytic/repo-core/repository';

// Driver-agnostic filter AST — combinators (eq/and/or/in/like/between/...) + walk/match + scope helpers.
import { and, eq, gte, in_, like, buildTenantScope, matchFilter } from '@classytic/repo-core/filter';

// URL → ParsedQuery grammar. Backend frameworks (Express/Arc/Fastify) parse req.query here.
import { parseUrl } from '@classytic/repo-core/query-parser';

// Pagination primitives — cursor codec, keyset helpers, offset math, the canonical
// result types (`OffsetPaginationResult`, `KeysetPaginationResult`,
// `AggregatePaginationResult`, `PaginationResult`) and the wire helper
// `toCanonicalList()`. Single source of truth — primitives' duplicate dropped,
// mongokit/sqlitekit re-export from here.
import { encodeCursor, decodeCursor, validateKeysetSort, toCanonicalList } from '@classytic/repo-core/pagination';
import type { OffsetPaginationResult, KeysetPaginationResult, AggregatePaginationResult, PaginationResult } from '@classytic/repo-core/pagination';

// Tenant config — the canonical `TenantConfig`, `TenantStrategy`, `TenantFieldType`,
// `resolveTenantConfig`, `DEFAULT_TENANT_CONFIG`, `ResolvedTenantConfig`. Kits'
// `MultiTenantOptions extends Pick<TenantConfig, ...>`.
import { resolveTenantConfig, DEFAULT_TENANT_CONFIG } from '@classytic/repo-core/tenant';
import type { TenantConfig, ResolvedTenantConfig } from '@classytic/repo-core/tenant';

// Cache plumbing — the CacheAdapter interface every kit's cachePlugin writes against.
import { type CacheAdapter, stableStringify, createMemoryCacheAdapter } from '@classytic/repo-core/cache';

// Error contracts — `HttpError` throwable + `ErrorContract` wire shape +
// `ErrorDetail` + `ErrorCode` + `ERROR_CODES` + `toErrorContract()` +
// `statusToErrorCode()`. Single source of truth — primitives' errors module dropped,
// mongokit's local `HttpError` dropped, `ArcError implements HttpError`.
import { toErrorContract, statusToErrorCode, ERROR_CODES, createError, conservativeMongoIsDuplicateKey } from '@classytic/repo-core/errors';
import type { HttpError, ErrorContract, ErrorDetail, ErrorCode } from '@classytic/repo-core/errors';

// Error kinds: one coarse class per wire code (validation, unauthorized, forbidden,
// not_found, conflict, precondition_failed, rate_limited, unavailable, internal).
// `errorKind(code, status?)` reads the table first, then falls back to the status.
import { errorKind, ERROR_KIND_BY_CODE, type ErrorKind } from '@classytic/repo-core/errors';

// Change feed: the ChangeLogStore contract a sync engine or a replaying SSE stream
// reads, plus the in-memory reference store. See "Change feed for realtime replay".
import { MemoryChangeLogStore, CursorExpiredError, isCursorExpiredError } from '@classytic/repo-core/sync';
import type { ChangeLogStore, ChangeEntry, ChangesSinceOptions, ChangesPage } from '@classytic/repo-core/sync';

// Schema generator interface — kits ship `SchemaGenerator<TModel>` + the
// compile-time conformance assertion; arc adapters are typed against it.
import type { SchemaGenerator, SchemaGeneratorContext } from '@classytic/repo-core/schema';
import { isSchemaGenerator } from '@classytic/repo-core/schema';

// Operation registry (for arc-level policy dispatch + doc generation).
import { CORE_OP_REGISTRY, describe } from '@classytic/repo-core/operations';

// Repository hook context type (for plugin authors).
import type { RepositoryContext } from '@classytic/repo-core/context';

// Capabilities + resilience (0.6.0) — runtime feature detection, the unified
// retry/abort primitives, and the change-feed contract types.
import { withRetry, throwIfAborted } from '@classytic/repo-core/repository';
import type { RepoCapabilities, RetryPolicy, ChangeEvent, WatchOptions } from '@classytic/repo-core/repository';

// Domain events (0.6.0) — pass `events: { transport }` at construction and every
// mutating op publishes `<resource>.<verb>` through any arc-compatible transport.
import type { RepositoryEventPublisher, DomainEvent } from '@classytic/repo-core/events';

// Standard Schema validation (0.6.0) — `schema` / `updateSchema` construction
// options accept any Zod / Valibot / ArkType / Effect schema.
import { validateStandardSchema, type StandardSchemaV1 } from '@classytic/repo-core/schema';

没有 . / 根条目. 从确切的子路径导入——这就是保持树摇的诚实的合同.

示例:工具包的仓库扩展 RepositoryBase

import { RepositoryBase } from '@classytic/repo-core/repository';
import { compileFilter } from './my-kit-compiler.js';

export class MyKitRepository<T> extends RepositoryBase {
  // Kit-specific CRUD methods. Each routes through `_buildContext` → plugins
  // → native driver call → `_emitAfter` so every plugin (timestamp, cache,
  // multi-tenant, audit) composes identically across kits.
  async getAll(params: {...}) {
    const context = await this._buildContext('getAll', params);
    const cached = this._cachedValue(context);
    if (cached) return cached;
    const where = compileFilter(context.filters);
    const result = await this.driver.query(where);
    await this._emitAfter('getAll', context, result);
    return result;
  }
}

示例: URL – 解析查询 – 任何工具包

每个工具包的URL语法都是一样的. 前端释放出一个 URL; 交换后端 DB 不改变单个查询字符串.

import { parseUrl } from '@classytic/repo-core/query-parser';

app.get('/users', async (req, res) => {
  const parsed = parseUrl(req.query, {
    allowedFilterFields: ['email', 'role', 'active', 'createdAt'],
    fieldTypes: { active: 'boolean', createdAt: 'date' },
    maxLimit: 200,
  });
  // Works identically against any kit's repository:
  //   const page = await sqliteUserRepo.getAll(parsed);
  //   const page = await mongoUserRepo.getAll(parsed);
  res.json(await userRepo.getAll(parsed));
});

URL 语法 :

網址已生产过滤器IR
?status=activeeq('status', 'active')
?age[gte]=18&age[lt]=65and(gte('age', 18), lt('age', 65))
?role[in]=admin,editorin_('role', ['admin', 'editor'])
?name[contains]=johnlike('name', '%john%')
?price[between]=10,100and(gte(...), lte(...))
?deletedAt[exists]=falseisNull('deletedAt')
?sort=-createdAt,name{ createdAt: -1, name: 1 } 联 合 国 .sort(中文(简体) )
?select=name,email,-password{ name: 1, email: 1, password: 0 }
?page=2&limit=50解析查询上的页形字段
?after=eyJ2Ij...不透明的密钥集光标

方便插件的事件名称

import { HOOK_EVENTS, HOOK_PRIORITY } from '@classytic/repo-core/hooks';

// Cross-kit plugin — works identically on mongokit, sqlitekit, pgkit, prismakit.
export function stampOrgId(orgId: string): Plugin {
  return {
    name: 'stamp-org-id',
    apply(repo) {
      repo.on(HOOK_EVENTS.BEFORE_CREATE, (ctx) => {
        if (!ctx.data?.organizationId) ctx.data = { ...ctx.data, organizationId: orgId };
      }, { priority: HOOK_PRIORITY.POLICY });
    },
  };
}

泰波斯喜欢 'before:craete' 成为编译错误。 订阅给定的套件不会放出的事件是无声的不放出(这是钩子引擎如何工作的),所以插件可以安全地为完整的标准集接线收听器.

能力——特征检测,而非运行时出乎意料

每个工具箱声明 readonly capabilities: RepoCapabilities (必须 StandardRepo 从0.6.0起。 手提式主机分支在启动时一次:

if (!repo.capabilities.arrayOperators) {
  // SQL kit without JSON array rewrites — model tags as a join table
}
if (repo.capabilities.aggregateOps?.percentile) {
  dashboard.enableLatencyPercentiles();
}

那套套间 ConformanceFeatures 是同一类别名—— 一个工具箱在运行时宣布的 恰恰是交叉设备组的验证.

配置驱动验证+事件(0.6.0)

RepositoryBaseOptions 遵循媒体 kit 的配置驱动激活: 通过 插槽和特性点亮; 省去它和接线是惰性的.

import { z } from 'zod';

const repo = createRepository(UserModel, {
  // Any Standard Schema validator — Zod, Valibot, ArkType, Effect.
  schema: z.object({ name: z.string().min(1), email: z.string().email() }),
  updateSchema: z.object({ name: z.string().min(1) }).partial(),

  // Any arc / primitives-compatible EventTransport. Every mutating op then
  // publishes `user.created` / `user.updated` / `user.deleted` / ...
  events: { transport: redisTransport, source: 'commerce' },
});

验证运行于 HOOK_PRIORITY.VALIDATION (150) — 政策插件之后 (有租户印出字段),缓存前. 事件出版从未 操作失败; 向 error:events 钩.

警告 - 弧形主机 : 这些是相同的事件名称 @classytic/arc是因为 eventStrategy: 'auto' 发射 连接回波级桥和弧 用于一个资源双publishes的自动事件无声( arc 双publish) dev-warn 无法看到 repo 层). 每个资源选择一层.

更改实时重播的种子

@classytic/repo-core/sync 出口 ChangeLogStore 合同:仅是附件、承诺命令的文档更改种子。 同步引擎拉动它;服务器发送事件流可以重放它,所以重新连接的客户端 Last-Event-ID (条目: cursor)在光标后获得每个更改,因为每个文档的最新状态如果种子是压缩的. MemoryChangeLogStore 是参考商店 mongokit 的 createChangeLogStore 是一个持久的。 证明一个商店 runChangeLogStoreConformance 从 @classytic/repo-core/testing。 。 。 .

每家店都遵守四条规则 如果有五条规则

  1. 咒语很不透明,每个店都按顺序订购 绝不允许检查站经过后来实施的入境.
  2. append(entry, { session }) 加入呼叫者的交易, 因此种子条目会以商务写入 .
  3. 删除为墓碑(op: 'delete',没有 doc),永无间隙.
  4. since(cursor) 不包含 cursor (单位:千美元)'' 表示从开始)并按光标顺序返回条目; 页时 hasMore,呼应返回 cursor。 。 。 .
  5. 执行可选的商店 prune(before) 保存每个文档的最新状态并返回新视野;光标比它老 CursorExpiredError。 。 。 .
import { isCursorExpiredError, type ChangeLogStore, type ChangesSinceOptions } from '@classytic/repo-core/sync';

// MemoryChangeLogStore and mongokit only: 16-digit cursors, or '' for "from the beginning".
const CURSOR = /^(\d{16})?$/;

type Read = Pick<ChangesSinceOptions, 'tenantId' | 'scopes' | 'sharedScopes'>;

// One pass: every entry after `from`, in cursor order. Call it on connect with Last-Event-ID,
// then on every wake-up (a timer, or a "feed changed" ping) with the cursor it returned.
// The feed is the only source of events, so the hand-off from replay to live has no gap.
async function pump(store: ChangeLogStore, from: string, read: Read, send: (e: unknown) => void) {
  // A malformed cursor, or one ahead of the head (a restored DB, a restarted memory store, a
  // forged id), resyncs: echoing it back would replay nothing until the sequence caught up.
  if (!CURSOR.test(from) || from > (await store.latestCursor())) return { resync: true } as const;
  let cursor = from;
  try {
    for (;;) {
      const page = await store.since(cursor, { ...read, limit: 500 });
      for (const entry of page.changes) send(entry); // SSE `id:` = entry.cursor
      cursor = page.cursor;
      if (!page.hasMore) return { cursor } as const;
    }
  } catch (err) {
    if (isCursorExpiredError(err)) return { resync: true } as const;
    throw err; // a failure, not a resync: see "Errors" below
  }
}
  • 谁能读出什么. 构建 read 在服务器上,从不从请求。 租户读取 tenantId 从认证范围和 未关闭 当没有的时候: since 与 tenantId: undefined 返回两个商店中每个租户的条目,所以一个没有被选中的组织的用户会流出每个人的数据. scopes 财务报告和审定财务报表 sharedScopes 缩放sharedScopes 增加其他租户在这些范围中的条目,所以它们来自服务器配置或服务器侧允许列表. 平台阅读器( 否) tenantId)是一个单独的,有明确入门的分支,如提升或平台范围.
  • Resync. (原始内容存档于2018-10-12). 带着头 latestCursor(read) 在此之前 客户端重新装入其快照并将其作为新的 id:; 继续从中可以重复快照已有的条目,这是安全的(下个点)。 抓取头部 在快照丢失的条目之间. CursorExpiredError (与 isCursorExpiredError,该匹配 name,因此它能幸存两个再录核拷贝在一个图中),而上面的检查失败的光标就是再录信号.
  • 至少从头开始. 通过再连接和再同步,客户端可以两次看到一个条目. 应用条目 (scope, docId, version) 忽视谁 version 并不比持有的要新.
  • 现场直播. 如果中枢从feed以外的地方推动直播事件(arc事件,一个酒吧/子消息),它们不会携带feed光标和SSE id: 无法保持一致。 民意调查 since 作为活源(上),或订阅活的种子条目,先缓冲,再播放,再放下缓冲条目 cursor 实值 <= 最后一个重播.
  • 错误. 任何其他 since 错误是失败: 在服务器上登录原因, 发送固定的 { code, message } (例如 internal_error 并关闭。 永远不要回声错误 : CursorExpiredError 而mongokit的光标错误嵌入了客户端的生光标,驱动程序错误可以命名收藏,索引和查询形状.
  • 固定宽光标. MemoryChangeLogStore 和mongokit薄荷 16位零加序号,所以 CURSOR 而简单的字符串比较对他们是有用的。 其他商店可能不会:将光标视为不透明,除非您知道该商店. 比起未过滤器 latestCursor(): 一个被过滤后的头可以坐在光标后面 a sharedScopes 读者合法持有.
  • 不要在记忆和梦幻中磨蹭. MemoryChangeLogStore 没有 prune,所以它从来没有扔 CursorExpiredError;用于测试和dev,而不是证明再同步路径. 它的序列随过程以零重新开始. Mongokit的店铺契约 compactSuperseded (没有地平线)并且也没有抛出它,所以读者也应该封住它回放多远.
  • 最新状态,不是每个过渡. 之后 prune (或梦幻怪兽) compactSuperseded) 重放输出每个文档的最新条目。 需要每一个过渡的读者决不能缩小这一范围.

输入结果额外

import type { OffsetPaginationResult } from '@classytic/repo-core/pagination';

// Kit adds a typed surface-level extra without breaking cross-kit substitutability.
type MongokitPage<T> = OffsetPaginationResult<T, { warning?: string }>;

function render(page: MongokitPage<User>) {
  if (page.warning) showBanner(page.warning);
  return page.docs.map(userRow);
}

默认 TExtra 实值 Record<string, never> —— 说 OffsetPaginationResult<User> 前后行为相同; 通用可随意忽略.

状态

v. 6.0 — 标准化发布. 需求 RepoCapabilities 特性 检测( 与 ConformanceFeatures),标准Schema验证 插槽、域活性排放(/events, (中文(简体) ), watch() 换食合同, 统一 RetryPolicy + 键 signal 取消,和 recordToFilter 从每个孩子的拷贝中晋升.

由下列人士组成:

  • @classytic/mongokit 3.16 — Repository extends RepositoryBase; 声明 MONGOKIT_CAPABILITIES; 执行 watch() 通过更改流; 钩子引擎, 插件顺序验证器, HOOK_PRIORITY,页码+ HttpError 键入所有源代码。 蒙哥克特自己来的 QueryParser 保持独立.
  • @classytic/sqlitekit ≥0.6 - SqliteRepository extends RepositoryBase; 声明 SQLITEKIT_CAPABILITIES; Filter IR 编译为 Drizzle / 原始 SQL 本土; recordToFilter 从这里消耗.
  • @classytic/arc 2.12 - 适配器对准 SchemaGenerator<TModel>· ; ArcError implements HttpError; 页形接线信封(method 歧视者) toCanonicalList() 与 reply.sendList()。 。 。 .

见 INFRA.md (英语) 用于建筑原则、子路径图、建造/工具决定和pgkit/棱镜路线图.

见 文件/数据寿命周期.md 10亿册——保留/TTL, archiveByFilter 冷藏取出, cursor() 流线扫描、房客清洗、分配钥匙警卫和每后端的硬化/分块/后置游戏本(工具包拥有的与数据库拥有的).

许可证

麻省理工学院——见 自由主义。 。 。 .

商标

密码是麻省理工学院许可的. "classytic","arc",并标志为: 经典专卖局长 并且现在 没有 根据麻省理工学院颁发的许可证——见 贸易商。 。 。 . 叉子必须更名;许可证覆盖了代码,而不是品牌.

版本与发布记录

@classytic/repo-core 共发布 43 个版本,首次发布于 2026/04/20;过去 12 个月发布了 43 个版本。

版本发布日期距今
0.38.0latest2026/11/046 天前
0.37.02026/11/036 天前
0.36.02026/10/2911 天前
0.35.02026/10/2912 天前
0.34.02026/10/2813 天前
0.33.02026/10/2714 天前

关于 @classytic/repo-core 的常见问题

@classytic/repo-core 是什么?

驱动-不可知寄存器原始:钩子,滤镜IR,操作,发卡,缓存合同. 蒙戈基特、斯克利特基特、克基特和棱镜基金会。 从设计上看——这里没有插件船;每个套件都有自己的.

如何安装 @classytic/repo-core?

在项目目录运行 npm install @classytic/repo-core,或使用 pnpm add / yarn add / bun add。

@classytic/repo-core 支持 TypeScript 吗?

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

@classytic/repo-core 支持 ESM 还是 CommonJS?

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

@classytic/repo-core 需要什么版本的 Node.js?

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

@classytic/repo-core 可以商用吗?

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

@classytic/repo-core 的下载量有多少?

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

@classytic/repo-core 还在维护吗?

最新版本 0.38.0 发布于 2026/11/04(6 天前);至今共发布 43 个版本,其中过去 12 个月发布了 43 个;首次发布于 2026/04/20。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。