npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cicctencent/json-orm

v0.1.10

Published

TypeORM-compatible JSON file storage engine — datasource facade, repository shim, query builder and in-memory SQL evaluator, with injectable storage root (framework-agnostic).

Readme

@cicctencent/json-orm

TypeORM 兼容的 JSON 文件存储引擎(框架无关、业务解耦的公共包):以 JSON 文件承载 data/<表名>/<主键>.json 的落盘存储,提供 TypeORM DataSource / Repository 子集、 QueryBuilder 与内存 SQL 求值器,作为无数据库场景下的透明持久化回退层。

本包为独立公共包,不依赖 @midwayjs/*、@cicctencent/* 或任何应用内部模块; 承载其运行所需的目录解析、非实体表注册等应用决策,由接入方以配置注入。

安装

作为 pnpm workspace 包接入(推荐):

// app/work-space/package.json
{
  "dependencies": {
    "@cicctencent/json-orm": "workspace:*"
  }
}
pnpm install

运行时依赖(peerDependencies,宿主需已安装):typeorm@^0.3.31、reflect-metadata@^0.2.2。

配置注入(使用前必做)

引擎不内置任何存储根目录或非实体表注册策略;首次访问前必须调用 configureJsonStorage() 注入:

import {
  configureJsonStorage,
  getJsonDataSource,
  createJsonRepository,
  sqlTableFor,
  FsStorageBackend,
  type JsonStorageBackend,
} from '@cicctencent/json-orm';

// 存储根目录(如 <data>/db)与非实体表注册(仅经原始 SQL 访问、无 @Entity 定义的表)
configureJsonStorage({
  dbDir: '/path/to/data/db',
  nonEntityTables: {
    some_raw_table: { primaryColumn: 'id' },
  },
});
  • dbDir 未注入而触发集合落盘时,引擎抛出明确错误并指引先调用 configureJsonStorage({ dbDir }),不会静默回退到隐式目录。
  • nonEntityTables 可选;注入后 isNonEntityTable / getNonEntityPrimaryColumn / getNonEntityUniqueColumns / listNonEntityTables 按注入数据返回,原始 SQL 的幂等 upsert 按注入主键/唯一列判重。
  • 配置支持分次部分注入、后写覆盖;测试可调用 resetJsonStorageForTest() 隔离。

存储后端(可注入、异步)

引擎全部持久化 I/O 经可注入的 JsonStorageBackend(异步接口)完成,不直接依赖 fs: readFile / writeFileAtomic(内部 tmp+rename 原子写)/ readDir / deleteFile / mkdir / stat(跨进程缓存失效检测)/ withLock(跨进程临界区互斥)/ nextId (后端级原子自增)/ ensureSeqAtLeast(显式大主键抬升序列器)。

默认后端为 FsStorageBackend(fs/promises 实现,等价于改造前的本地 JSON 落盘)。 注入方式:

// 方式 A:仅注入 dbDir(自动构造 FsStorageBackend,向后兼容)
configureJsonStorage({ dbDir: '/path/to/data/db' });

// 方式 B:注入自定义后端(如对象存储 COS/S3),优先于 dbDir
const cosBackend: JsonStorageBackend = createCosBackend(/* ... */);
configureJsonStorage({ backend: cosBackend });

所有数据访问方法(find / save / update / delete / upsert / query、 原生 SQL 的 SELECT/INSERT/UPDATE/DELETE 求值、集合读写、序列分配)全程异步, 数据路径无阻塞式同步 I/O。调用方对接 DataSource / Repository / EntityManager 公共 API 时无需改动(这些方法本就返回 Promise)。

多进程安全

  • 序列器唯一:nextId() 的分配在后端 withLock 临界区内完成读-改-写,跨进程严格 递增且唯一;显式写入大于当前序列器的主键时经 ensureSeqAtLeast 抬升,避免后续撞车。
  • 跨进程缓存失效:每个进程在内存保留整表快照以支撑 SQL 扫描性能;读取前经 backend.stat(表目录) 检测变更(etag 优先,两侧均无 etag 时以 mtimeMs 增大 为准),若他进程已变更则重载。节流由 reloadTtlMs (configureJsonStorage({ reloadTtlMs }),默认 0 即每次访问校验)调控;>0 时 在 TTL 窗口内可复用缓存(权衡:可能短暂读到他进程写入前的快照)。
  • 写路径强制校验:write / delete / nextId 不受 reloadTtlMs 节流,写入前 必定先做一次失效校验,保证写入后记录的水位与内存快照一致,不掩蔽他进程写入。
  • 单行写仍用 tmp+rename 文件级原子(临时文件名含 pid 与随机量,并发写同一行不产生 交错内容,也不抛 ENOENT),不引入整表写锁。
  • 锁归属校验:withLock 以令牌标识持有者,释放前校验令牌;锁被判定为孤儿并由 他人取得后,原持有者不会误删他人锁(避免两进程同时进入临界区)。

公开 API 概览

| 类别 | 导出 | | --- | --- | | 配置 | configureJsonStorage / resetJsonStorageForTest / JsonStorageOptions / NonEntityTableInfo / JsonStorageBackend / JsonStorageFileStat / FsStorageBackend | | 数据源 | JsonDataSource / JsonEntityManager / getJsonDataSource / resetJsonDataSourceForTest / sqlTableFor | | 仓库 | JsonRepository / createJsonRepository / FindOptionsLoose | | 查询/SQL | JsonQueryBuilder / executeRawQuery / SqlTable / GetSqlTable / toParams | | 集合 | JsonCollection / getCollection / resetCollectionsForTest / JsonRow | | 实体元数据 | resolveEntityMeta / resolveEntityMetaByTable / JsonEntityMeta / JsonEntityColumn 及非实体表四函数 | | SQL 词法/表达式 | tokenizeSql / TokenCursor / matchesWhere / looseEqual / compareValues / likeMatch / parseExpr / evalExpr / truthy / hasAggregate / AGG_FUNCS / Expr / EvalContext / AliasInfo / jsonPathGet / jsonUnquote / toDate / mysqlDatetime |

完整类型与行为见本仓库 openspec/specs/json-orm-storage-engine/spec.md。

支持的 TypeORM 能力矩阵

下表列出本引擎对 TypeORM 公共 API 的覆盖情况。✓ 实现 = 行为与 TypeORM 一致; ~ 垫片 = 运行时可达但语义有差异(见下节);× 不支持 = 暂未实现,调用会抛错或静默无操作。

Repository(实体仓库)

| 能力 | 状态 | 说明 | | --- | --- | --- | | find / findBy / findOne / findOneBy / findAndCount / findAndCountBy | ✓ | 对象条件 + 排序/分页/select 子集 | | findOneOrFail / findOneByOrFail | ✓ | 未命中抛 EntityNotFoundError | | count / countBy | ✓ | 含对象条件 | | save / insert | ✓ | 自增主键、UUID 主键、UPSERT、ODKU 语义 | | update / delete / increment / decrement | ✓ | 空条件 fail-loud(禁止全表误伤) | | deleteAll / updateAll | ✓ | 显式全表操作(带守卫) | | upsert | ✓ | conflictPaths 默认主键,可指定唯一列 | | softDelete / restore / softRemove / recover | ✓ | 依赖 @DeleteDateColumn | | exists / exist / existsBy | ✓ | | | sum / average / minimum / maximum | ✓ | 支持 where 过滤与列名 | | preload / merge / hasId / getId / create | ✓ | | | clear | ✓ | 清空整表 | | query / sql / createQueryBuilder | ✓ / ~ | 原生 SQL + 查询构造器 | | find 的 relations 选项 | × | 关系/嵌套实体未实现(见已知限制) |

EntityManager(JsonEntityManager)

全部读写方法(save / find / count / increment / upsert / softDelete / query … 共约 40 个)逐一对齐 Repository,并额外提供 updateAll / deleteAll。manager 字段、createEntityManager() 亦可获取。

DataSource(JsonDataSource)

| 能力 | 状态 | | --- | --- | | initialize / connect / destroy / close / setOptions | ✓ | | getRepository / getTreeRepository / getCustomRepository / getMetadata / hasMetadata | ✓ | | synchronize / dropDatabase / runMigrations / undoLastMigration / showMigrations | ~(空操作,见下节) | | createQueryRunner | ~(hasTable 恒 true,schema 方法为 no-op) | | transaction / createEntityManager / query / sql | ~ / ✓ |

类型说明:JsonRepository / JsonDataSource / JsonQueryBuilder 是 TypeORM 公共接口的兼容垫片, 运行时行为对齐,但未 implements 对应接口(Repository<Entity> / DataSource / SelectQueryBuilder)。消费方如需 const ds: DataSource = getJsonDataSource() 通过类型检查, 暂需 as unknown as DataSource 断言;彻底消除需实现真正的 TypeORM Driver(见已知限制)。

QueryBuilder(JsonQueryBuilder)

| 能力 | 状态 | | --- | --- | | select(含 * / 别名 / DISTINCT)、from / addFrom | ✓ | | where(字符串 / 对象 / whereInIds / Brackets 嵌套) | ✓ | | leftJoin / innerJoin / leftJoinAndSelect | ~(仅笛卡尔积占位,无外键嵌套填充) | | groupBy / having / addGroupBy / addHaving | ✓(聚合 + HAVING) | | orderBy(ASC/DESC、NULLS FIRST/LAST、对象式、空参清除)/ addOrderBy | ✓ | | limit / offset(MySQL LIMIT offset, count)/ skip / take | ✓ | | update().set().where().orderBy().limit() / delete().where().orderBy().limit() | ✓ | | orUpdate(ODKU)/ into(entity, columns) | ✓ | | 取值:getMany / getOne / getManyAndCount / getCount / getExists / getRawMany / getRawOne / getRawAndEntities / getQuery | ✓ | | withDeleted(软删过滤开关) | ✓ | | subQuery / whereExists | × |

FindOperator(对象条件运算符)

Equal · Not · In / Any · IsNull · LessThan / LessThanOrEqual · MoreThan / MoreThanOrEqual · Between · Like / ILike · And · Or · ArrayContains / ArrayContainedBy / ArrayOverlap · JsonContains · Raw ✓

数组运算符作用于 JSON 数组列(PostgreSQL 风格);Raw 接收 (dbColumn, aliases?) => string 表达式,由引擎求值器解释。

原生 SQL 函数(内存求值器)

标量:COALESCE / NULLIF / IF / IFNULL / NOW / CURRENT_TIMESTAMP / UNIX_TIMESTAMP / FROM_UNIXTIME / DATE_FORMAT / TIMESTAMPDIFF / DATEDIFF / ABS / ROUND / FLOOR / CEIL / CONCAT / CONCAT_WS / LEAST / GREATEST / SIGN / MOD / POWER / SQRT / LPAD / RPAD / LOCATE / INSTR / REVERSE / REPLACE / STR_TO_DATE / YEAR / MONTH / DAY / HOUR / MINUTE / SECOND / DATE / CURDATE / CURTIME / DATE_ADD / DATE_SUB / TIMESTAMPADD ·

JSON:JSON_EXTRACT / JSON_UNQUOTE / JSON_CONTAINS / JSON_OVERLAPS / JSON_SET / JSON_OBJECT / JSON_ARRAY · 聚合:COUNT / SUM / AVG / MIN / MAX / GROUP_CONCAT。

完整函数注册表见 json-sql-expr.ts 的 FUNCS / AGG_FUNCS。

写操作返回:UPDATE / DELETE 返回 { affected, raw };原生 SQL 的 UPDATE/DELETE 返回 OkPacket 形态,其中 affectedRows 为匹配行数、changedRows 为实际变更行数 (与 MySQL info 的 Rows matched / Changed 对应)。据此判断「是否真的改到了值」时 应使用 changedRows。

与 MySQL 的语义差异 & 已知限制

本引擎是「单进程 JSON 文件 + 内存 SQL 求值」对 TypeORM 的兼容回退层,而非真实数据库。 下列差异需接入方知悉,避免在语义假设上踩坑。

写入语义

  • 事务是 no-op:DataSource.transaction() / EntityManager.transaction() / QueryRunner 的事务方法直接透传回调执行,不提供跨表/跨语句原子性与回滚。 JSON 模式无法保证「失败整体回滚」;若业务强依赖事务一致性,应改用真实数据库。
  • DDL 是空操作:synchronize() / dropDatabase() / runMigrations() 等为 no-op; createQueryRunner() 仅用于 schema 探测,hasTable 恒返回 true。因此 if (!(await hasTable(t))) await createTable(t) 这类条件建表代码不会执行建表 ——JSON 模式本就无需建表,但调用方需知悉。
  • 多进程一致性:序列器(nextId)与写锁经后端 withLock 保证跨进程唯一与互斥, 但transaction 之外的「读-改-写」临界区仍非强一致;高并发写入建议依赖主键/唯一约束冲突检测。

关系与实体能力(× 暂不支持)

| 能力 | 说明 | | --- | --- | | 关系(@ManyToOne / @OneToMany / @OneToOne / @ManyToMany / @JoinColumn / @JoinTable) | 装饰器未解析;find({ relations })、leftJoinAndSelect 仅做笛卡尔积占位,不按外键填充嵌套实体 | | EntitySchema | 元数据仅从装饰器(getMetadataArgsStorage)读取,以 EntitySchema 定义的实体查不到 | | 订阅器 / 生命周期钩子(@BeforeInsert 等 11 个 hook、EntitySubscriberInterface) | 写入不经过任何钩子 | | @Tree / TreeRepository | 未实现 | | BaseEntity 静态方法 | 未提供(基于 Repository 的便捷封装缺失) |

SQL 方言缺口

  • 子查询 / CTE:FROM (SELECT …)、IN (SELECT …)、EXISTS (SELECT …)、WITH 暂未实现。
  • 集合运算:UNION / UNION ALL 未实现。
  • 窗口函数:OVER (PARTITION BY …) 未实现(聚合仅支持 GROUP BY 形态)。
  • JOIN 类型:RIGHT / FULL JOIN 未实现;LEFT / INNER 仅笛卡尔积占位(见上)。
  • 标识符:原生 SQL 中双引号标识符("col")在部分路径未支持,建议统一使用反引号或无引号。
  • 上节「原生 SQL 函数」列出的函数为已支持集合,超出部分会抛「未知函数」错误。

类型兼容性(发布须知)

JsonRepository / JsonDataSource / JsonQueryBuilder 是 TypeORM 公共接口的兼容垫片, 运行时行为对齐,但未 implements 对应接口。因此:

const ds: DataSource = getJsonDataSource();          // ❌ 类型检查不过
const ds = getJsonDataSource() as unknown as DataSource; // ✓ 暂需断言

要彻底消除该断层(让消费方获得完整 TypeORM 类型体验),需实现真正的 TypeORM Driver (JsonDriver + JsonQueryRunner + RdbmsSchemaBuilder no-op),使 Repository / EntityManager / QueryBuilder / 关系 / 钩子 / 迁移由 TypeORM 自身提供——这是本引擎实现「完整支持 TypeORM」的前置项。

示例

examples/ 目录提供了可直接运行的完整示例(需先 pnpm build 产出 dist,再用 npx tsx examples/<file>.ts 运行),覆盖最常见接入场景:

| 示例 | 场景 | | --- | --- | | 01-basic-entity.ts | 定义实体 + Repository 的 save/find/findOneBy/findAndCount/update/delete | | 02-query-builder.ts | QueryBuilder:字符串/对象条件、LEFT JOIN、GROUP BY 聚合、分页计数 | | 03-raw-sql.ts | query:SELECT/INSERT/UPDATE/DELETE、ODKU upsert、TRUNCATE;位置/命名参数 | | 04-non-entity-table.ts | 非实体表(仅原始 SQL 访问)+ nonEntityTables 注册 | | 05-custom-backend.ts | 实现 JsonStorageBackend 注入自定义(内存)后端 | | 06-find-operators.ts | Repository where 的 FindOperator:In/Like/Not/IsNull/MoreThan/Between | | 07-sql-functions.ts | 原生 SQL 函数 / 聚合(GROUP BY+HAVING)/ And·Or 组合 / JSON 包含查询 |

关键约定(两套命名空间、合并写语义、多进程安全等)见 examples/README.md。

构建与测试

pnpm build        # 产出 dist/esm(NodeNext)与 dist/cjs,并经 fixup-cjs 修复 .d.ts
pnpm typecheck    # 仅类型检查(ESM 配置)
pnpm test         # vitest 运行 src/__tests__ 下引擎测试(含 157 用例)

接入方注意:本包以 workspace:* 形式被应用消费,应用生产构建/本地开发前 须先产出本包 dist(即先执行 pnpm --filter @cicctencent/json-orm build)。 应用侧 bin/dev.sh 的共享包构建段已注释,沿用「本地预构建 dist」的既有约定。

许可证

MIT