@blumintinc/microdiff

中文文档
1.1.0发布于 2026/04/30MITTypeScriptESM + CJS零依赖近 30 天下载 4528

Small, fast, zero-dependency deep diff with pluggable atomic-type recognition. Fork of AsyncBanana/microdiff.

原始使用文档
文档目录6 节
  1. What this fork adds
  2. Why this fork exists
  3. Install
  4. Usage
  5. API
  6. License

@blumintinc/microdiff

Small, fast, zero-dependency deep diff with pluggable atomic-type recognition. Fork of AsyncBanana/microdiff at v1.5.0.

What this fork adds

Two options on the diff() API let callers configure which values are treated as atomic leaves (compared by value, never recursed into):

interface MicrodiffOptions {
  cyclesFix: boolean;
  isAtomic: (value: object) => boolean;
  isEqualAtomic: (a: unknown, b: unknown) => boolean;
}
  • isAtomic(value) — return true to treat value as a leaf.
  • isEqualAtomic(a, b) — equality test for two atomic values. Object.is reference/NaN equality is checked separately by diff(), so this only handles value-equal-but-distinct-reference cases.

When omitted, both options fall back to the published defaults (defaultIsAtomic, defaultEqualAtomic). Defaults treat the following as atomic:

  • Date, RegExp, String, Number (upstream microdiff parity)
  • Temporal.* family (upstream microdiff parity)
  • Firestore Timestamp shapes (real firebase-admin/firestore instances and plain {_seconds, _nanoseconds} maps left by structuredClone / deepmerge) — added in 1.1.0

Firestore Timestamp recognition is structural (no firebase-admin dependency) and on by default because BluMint's primary consumer is Firestore-based. Override isAtomic (return false) to disable.

Why this fork exists

Upstream microdiff hardcodes its atomic-type list at module scope. There is no extension hook. For our use case (Firestore Timestamp instances embedded in documents), upstream descends into the Timestamp's _seconds / _nanoseconds private fields and emits per-property CHANGE diffs — which materialize as malformed Firestore writes. The fork exposes the existing recursion gate as a caller-provided predicate so consumers can teach diff() about their own atomic types — and additionally builds Firestore Timestamp recognition into the defaults.

Calls with no options are a strict superset of upstream: identical for everything upstream handles, plus correct treatment of Firestore Timestamps.

Install

npm install @blumintinc/microdiff

Usage

Firestore Timestamps are atomic by default (no options needed):

import diff from "@blumintinc/microdiff";
import { Timestamp } from "firebase-admin/firestore";

const before = { lastUpdated: Timestamp.fromMillis(1000) };
const after = { lastUpdated: Timestamp.fromMillis(2000) };

diff(before, after);
// → [{ type: "CHANGE", path: ["lastUpdated"], value: <Timestamp 2000>, oldValue: <Timestamp 1000> }]
//   No `_seconds`/`_nanoseconds` descent. Timestamp identity preserved in `value` and `oldValue`.

Treat additional custom classes as atomic by extending defaultIsAtomic:

import diff, { defaultIsAtomic, defaultEqualAtomic } from "@blumintinc/microdiff";
import { GeoPoint } from "firebase-admin/firestore";

const isGeoPoint = (v: object) => v instanceof GeoPoint;

diff(before, after, {
  isAtomic: (v) => isGeoPoint(v) || defaultIsAtomic(v),
  isEqualAtomic: (a, b) => {
    if (isGeoPoint(a as object) && isGeoPoint(b as object)) {
      return (a as GeoPoint).isEqual(b as GeoPoint);
    }
    return defaultEqualAtomic(a, b);
  },
});

Note the composition pattern: extend defaultIsAtomic instead of replacing it, otherwise Date / RegExp / Temporal.* / Firestore Timestamps lose their atomic status.

API

ExportDescription
diff (default)The deep-diff function. Drop-in API-compatible with upstream microdiff.
defaultIsAtomicDefault predicate. Matches Date, RegExp, String, Number, Temporal.*, and Firestore Timestamp shapes.
defaultEqualAtomicDefault equality. Timestamps compare via defaultTimestampToMillis; Temporal via String(); rich types via NaN-aware numeric/string coerce.
defaultIsTimestampShapeStructural duck-type predicate for Firestore Timestamps (real instances + plain-map shapes).
defaultTimestampToMillisExtracts epoch ms from any Firestore Timestamp shape.
defaultRichTypesFrozen readonly string[] of the four rich-type constructor names.
defaultTemporalTypesFrozen readonly string[] of the Temporal API constructor names available on globalThis.Temporal.
Difference<TData>Discriminated union of DifferenceCreate / DifferenceRemove / DifferenceChange. Generic over TData for typed paths and values.
MicrodiffOptionsOptions shape. All fields optional via Partial<MicrodiffOptions> at the call site.

License

MIT, preserved from upstream. See LICENSE for the full text including the BluMint modification notice.

版本与发布记录

@blumintinc/microdiff 共发布 2 个版本,首次发布于 2026/04/29;过去 12 个月发布了 2 个版本。

版本发布日期距今
1.1.0latest2026/04/306 个月前
1.0.02026/04/296 个月前

关于 @blumintinc/microdiff 的常见问题

@blumintinc/microdiff 是什么?

Small, fast, zero-dependency deep diff with pluggable atomic-type recognition. Fork of AsyncBanana/microdiff.

如何安装 @blumintinc/microdiff?

在项目目录运行 npm install @blumintinc/microdiff,或使用 pnpm add / yarn add / bun add。

@blumintinc/microdiff 支持 TypeScript 吗?

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

@blumintinc/microdiff 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@blumintinc/microdiff 可以商用吗?

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

@blumintinc/microdiff 的下载量有多少?

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

@blumintinc/microdiff 还在维护吗?

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