Skip to content

Integrations

Use Object Diff from other packages and apps through the public API only. Core never depends on consumers.

Previous: Plugins · Next: Performance

Principles

  1. Import from @jayoncode/object-diff or documented subpaths (/core, /patch, /merge, /query, /stats, /formatter, /plugins, /view).
  2. No hard coupling into core — adapters and audit packages live elsewhere.
  3. Prefer docs + examples first; dedicated adapter packages only when a framework needs lifecycle wrappers.
  4. Zero new peer dependencies on the core package.

Consumer map

ConsumerTypical APIsNotes
Forms / @jayoncode/form-intelligencehasChanges, diff, serializeDirty check + audit trail
Session / @jayoncode/browser-lifecyclediff, hasChangesOptional snapshot compare
Audit / loggingdiff, serialize, /statsChange records as events
Collaboration / sync/merge, patch, applyPatchConflict-aware merge
React / Vue / AngularRoot + thin adapters laterNot in core
Node.js servicesSame coreNo DOM assumptions

Form dirty check + audit

ts
import { diff, hasChanges, serialize } from "@jayoncode/object-diff";

async function auditForm(saved: unknown, draft: unknown, log: (msg: string) => Promise<void>) {
  if (!hasChanges(saved, draft)) {
    return;
  }

  await log(serialize(diff(saved, draft), "markdown"));
}

Runnable sketch: packages/object-diff/examples/form-dirty-audit.ts.

Form Intelligence end-to-end recipe (plugin + form.diffFrom*): FI Patterns → Dirty audit / patch.

Session snapshot diffs

Compare last persisted session state to the current snapshot (e.g. after a tab focus or idle restore):

ts
import { diff, hasChanges } from "@jayoncode/object-diff";

function sessionChanged(previous: unknown, current: unknown): boolean {
  return hasChanges(previous, current);
}

function sessionChangelog(previous: unknown, current: unknown) {
  return diff(previous, current).changes;
}

Runnable sketch: packages/object-diff/examples/session-snapshot.ts.

Audit events

Map change records into your event bus — Object Diff stays a pure producer:

ts
import { diff } from "@jayoncode/object-diff";

type AuditEvent = {
  readonly type: "object-diff.change";
  readonly path: string;
  readonly changeType: string;
  readonly previous?: unknown;
  readonly current?: unknown;
};

function toAuditEvents(before: unknown, after: unknown): AuditEvent[] {
  return diff(before, after).changes.map((change) => ({
    type: "object-diff.change",
    path: change.path,
    changeType: change.type,
    ...(change.previous !== undefined ? { previous: change.previous } : {}),
    ...(change.current !== undefined ? { current: change.current } : {}),
  }));
}

Runnable sketch: packages/object-diff/examples/audit-events.ts.

Collaboration merge

ts
import { merge } from "@jayoncode/object-diff/merge";

const result = merge(localDraft, remoteDraft, {
  base: lastSynced,
  strategy: "latest-wins",
  // identityKey: "id", // merge list items by id when drafts contain arrays of entities
});

if (result.conflicts.length > 0) {
  // surface conflicts in UI (path, reason, identity); value is still a usable merge
}

Runnable sketch: packages/object-diff/examples/merge-collaboration.ts.

Optional plugin host

Only when you need matchers, custom formatters, or hooks:

ts
import { createEngine } from "@jayoncode/object-diff/plugins";

const engine = createEngine({ plugins: [/* … */] });
engine.diff(saved, draft);

Core free functions remain the default path — createEngine is optional. Full plugin contract (matchers, formatters, merge strategies, hooks): Plugins.

Anti-patterns

AvoidPrefer
Importing walker / internal modulesPublic entrypoints only
Putting form/UI lifecycle inside coreSeparate adapter packages
Auto-registering plugins on importExplicit createEngine({ plugins })
Coupling to DOM / framework globalsPlain snapshots in, plain results out

Examples

See packages/object-diff/examples/ and the playground.

An ecosystem of independent, headless TypeScript libraries engineered for modern web development. Every package includes interactive playgrounds and documentation that evolves alongside the code.