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

@x12i/xmemory-equal

v1.7.0

Published

Canonical Thing identity and equality layer for XMemory (MongoDB via @xronoces/xmemory-store data tier)

Downloads

146

Readme

xmemory-equal

Canonical Thing identity and 100% equality layer for XMemory. Stores things (literal text or keys), links them as equivalent, and resolves to a canonical representative. Persists to MongoDB via nx-mongo only (this package does not create Mongo clients or read MONGO_URI).

Install

npm install xmemory-equal nx-mongo

Usage

  1. Create and initialize an nx-mongo helper (your app owns the connection and DB name).

Same Mongo env as @xronoces/xmemory-store: use resolveXmemoryStoreEnv().thingsDb (or tier.env.thingsDb) for your databaseName so role C — corpus / things matches the store’s resolution. Set corpus DB with the store’s keys — e.g. THINGS_DB or MONGO_XMEMORY_META_DB / METADATA_DB (see @xronoces/xmemory-store ENV_KEYS). Never use MONGO_XMEMORY_DB for equal or things data: in xmemory-store that variable resolves opDb (scoped views), not thingsDb.

Single physical database for everything: still set THINGS_DB (or MONGO_XMEMORY_META_DB) to that database name — do not repurpose MONGO_XMEMORY_DB.

import { SimpleMongoHelper } from "nx-mongo";
import { resolveXmemoryStoreEnv } from "@xronoces/xmemory-store";

const mongoUri = process.env.MONGO_URI!;
const env = resolveXmemoryStoreEnv({ mongoUri });
const helper = new SimpleMongoHelper(mongoUri);
await helper.initialize({ databaseName: env.thingsDb });
  1. Create the equal client and ensure indexes:
import { createEqualClient } from "xmemory-equal";

const equal = createEqualClient({
  nxMongo: helper,
  namespace: "myapp",            // optional; default from XMEMORY_NAMESPACE or "default"
  defaultThingType: "generic",   // optional; default from XMEMORY_DEFAULT_THING_TYPE or "generic"
  normalization: "basic",       // optional; default "basic"
  canonicalStrategy: "preferKey" // optional; default "preferKey"
});

await equal.ensureIndexes();
  1. Ensure things, link them, and query:
const ns = "myapp";

// Thing identity = (namespace, thingType, kind, refNorm). Same string can be different things by type.
const aliceKey = await equal.ensureThing({ namespace: ns, thingType: "person_name", kind: "key", ref: "user:alice" });
const aliceLit = await equal.ensureThing({ namespace: ns, thingType: "person_name", kind: "literal", ref: "Alice" });

// Link = same real-world thing (equivalence; transitive)
await equal.link(aliceKey, aliceLit, { reason: "same person" });

// Query
const equivs = await equal.getEquivalents(aliceKey);  // all things in the same group
const same   = await equal.areEqual(aliceKey, aliceLit); // true
const canon  = await equal.resolve(aliceKey);         // canonical representative (by strategy)

ThingType: Use thingType to distinguish what the “something” is (e.g. person_name, company_name, product_name, doc_title, question, prompt, generic). The same ref in two different types are different things unless you explicitly link them. You can omit thingType and it will be filled from defaultThingType config or XMEMORY_DEFAULT_THING_TYPE (default generic).

externalIdentifier: Optional string on ThingRef to store a pointer to an external system (e.g. CRM id, external DB key). Stored in xmemory_things and returned on ThingRecord. If you set one that another thing in the same namespace already has, the client throws ExternalIdentifierConflictError.

metadata / data: Things can carry optional metadata (properties about the thing) and data (payload/snapshot). Pass them on ensureThing(thing, { metadata, data }) or patch later with updateThing(thingOrId, { set: { metadata, data } }) or merge: { metadata, data } for deep-merge. Each update increments version. Reserved metadata keys metadata.db and metadata.collection are the platform-standard scope (see Scope below). Use opts.scope on ensure or setThingScope(thingOrId, { db, collection }) to set them.

Soft delete: deleteThing(thingOrId, { deletedBy?, deleteReason? }) sets deletedAt (and optional deletedBy/deleteReason). By default, all read methods exclude deleted things; use { includeDeleted: true } to include them. purgeThing(thingOrId) hard-deletes the thing and its group membership (and optionally audit links).

API (summary)

| Method | Description | |--------|-------------| | ensureThing(thing, opts?) | Upsert thing; optional opts.metadata, opts.data, opts.scope (writes metadata.db, metadata.collection). Returns record with version, metadata, data. | | updateThing(thingOrId, patch) | Patch with set (replace), merge (deep-merge), unset, unsetPaths. Reserved keys metadata.db/metadata.collection can be set/merged via metadata. Increments version. | | setThingScope(thingOrId, { db, collection }) | Set reserved scope on a thing (merge into metadata). Returns updated ThingRecord. | | deleteThing(thingOrId, opts?) | Soft delete (sets deletedAt). Returns { deleted: boolean }. | | purgeThing(thingOrId) | Hard delete thing and group membership. | | getThing(thing, opts?) | Read-only by identity. Returns null if not found or deleted (unless includeDeleted). | | getThingById(id, opts?) | Read by id. | | getThingByExternalIdentifier(ns, extId, opts?) | Read by namespace + externalIdentifier. | | listThings(query) | List with thingTypes, kinds, db, collection (scope filters), externalIdentifierPrefix, updatedAfter/updatedBefore, includeDeleted (default false), limit/offset. | | link(a, b, meta?) | Ensure both things, create/merge equivalence group; idempotent. | | getEquivalents(thing, opts?) | All things in the same group (default excludes deleted). | | areEqual(a, b) | true if same thing id or same group. | | resolve(thing, opts?) | Canonical representative (default excludes deleted; recomputes if canonical is deleted). | | setCanonical(thing) | When canonicalStrategy === "manualPinned", set the group’s canonical. | | getEqualityLinks(thingOrId) | Audit edges where this thing is an endpoint. | | getGroupLinks(groupId) | Audit edges within the group. | | ensureIndexes() | Create required indexes on xmemory_* collections. | | normalize(value) | Apply configured normalization. | | inferenceFromFieldInferable(field) | Map aifunctions-js FieldInferableThingInference for ensureThing (see Data tier note on aifunctions-js). |

Config and env

Options can be set in code or via environment (env is used as default when the option is omitted):

| Option | Env | Default | Description | |--------|-----|---------|-------------| | namespace | XMEMORY_NAMESPACE | "default" | Logical partition / tenant. | | defaultThingType | XMEMORY_DEFAULT_THING_TYPE | "generic" | Default thingType when omitted on ThingRef (e.g. person_name, doc_title, generic). | | normalization | XMEMORY_NORMALIZATION | "basic" | none | basic | lowercase | unicode_nfkc. | | canonicalStrategy | XMEMORY_CANONICAL_STRATEGY | "preferKey" | disabled | preferKey | preferLiteral | firstSeen | lexicographic | manualPinned. | | collectionPrefix | XMEMORY_COLLECTION_PREFIX | "xmemory" | Collection names are {prefix}_things, {prefix}_groups, etc. | | auditLinks | — | true | Write audit rows to xmemory_links on link. |

Data tier (nxMongo / xmemory-store)

This package does not read MONGO_URI or any DB name. You pass a data-tier provider that implements NxMongoProvider: getDb(): Db and withTransaction(callback): Promise<T>.

  • With nx-mongo: Create and initialize a SimpleMongoHelper, then pass it as nxMongo (as in the Usage example above).

  • With @xronoces/xmemory-store: createXmemoryDataTier() / resolveXmemoryStoreEnv() define tier Mongo layout and env. The tier’s NxMongoClient is for store tier operations only; it is not the same contract as equal’s NxMongoProvider (raw driver Db + withTransaction). For xmemory-equal, open mongoUri + thingsDb with nx-mongo (or any NxMongoProvider) — same values as tier.env (ENV_KEYS / THINGS_DB / MONGO_XMEMORY_META_DB; not MONGO_XMEMORY_DB, which is opDb).

import { createXmemoryDataTier } from "@xronoces/xmemory-store";
import { SimpleMongoHelper } from "nx-mongo";

const tier = createXmemoryDataTier(/* optional env overrides */);
await tier.init();

const equalHelper = new SimpleMongoHelper(tier.env.mongoUri);
await equalHelper.initialize({ databaseName: tier.env.thingsDb });

const equal = createEqualClient({ nxMongo: equalHelper, namespace: "myapp" });
await equal.ensureIndexes();

Dependencies (package.json): @xronoces/xmemory-store and aifunctions-js are pinned for version alignment with the wider stack. Core equal code does not call an LLM; it only persists ThingInference on things. To map aifunctions-js FieldInferable into ThingInference, use inferenceFromFieldInferable().

Any object that implements getDb() and withTransaction() is accepted.

Scope (metadata.db, metadata.collection)

Things can be scoped to a logical db and collection via reserved metadata keys metadata.db and metadata.collection. Other layers in the XMemory stack may read these fields; we do not scope using data.

  • Set on create: ensureThing(thing, { scope: { db: "mydb", collection: "items" } })
  • Set or change later: setThingScope(thingOrId, { db, collection }) or updateThing(thingOrId, { merge: { metadata: { db, collection } } })
  • Retrieve by scope: listThings({ thingTypes: ["doc_title"], db: "mydb", collection: "items" })

Multi-DB limitation: This package writes to a single MongoDB database (the one configured in your nx-mongo helper). The metadata.db / metadata.collection values are stored as strings and used only for filtering and application-level grouping. They do not change which MongoDB database or collection is used for persistence. To use multiple MongoDB databases you must create separate nx-mongo helpers (and equal clients) per database.

Identity rule

Thing identity is (namespace, thingType, kind, refNorm). So the same string can exist as different things:

  • "Apple" as thingType: "product_name" and "Apple" as thingType: "company_name" are two distinct things unless you explicitly link() them.

Collections (in the DB selected by nx-mongo)

  • xmemory_things — thing rows: namespace, thingType, kind, refRaw, refNorm, optional externalIdentifier, metadata (optional reserved metadata.db, metadata.collection), data, deletedAt, deletedBy, deleteReason, version. Unique on (namespace, thingType, kind, refNorm); sparse index on (namespace, externalIdentifier); indexes on (namespace, thingType, updatedAt) and (namespace, thingType, metadata.db, metadata.collection, updatedAt).
  • xmemory_groups — equivalence groups (optional canonical, size, mergedIntoGroupId).
  • xmemory_thing_group — thing ↔ group membership.
  • xmemory_links — audit edges for links (optional).

Downstream consumers of the xmemory-records-mapper (or any code reading from these collections): see docs/downstream.md for identity vs relations, ref/scope changes, Thing shape, and equality mode.

Shared XMemory Mongo env (with @xronoces/xmemory-store)

Source of truth: @xronoces/xmemory-storeresolveXmemoryStoreEnv(), ENV_KEYS, and docs/spec.md (§2 Mongo binding). This package does not fork that contract. Host wiring beyond “open thingsDb for equal” belongs in xmemory-store (and other upstream) docs, not here.

Mirror of the store role table (see store README “What this package does”):

| Role | Concern | Default DB (store) | Env keys (ENV_KEYS in xmemory-store) | |------|---------|--------------------|----------------------------------------| | B | Scoping maps | xmemory-meta | MAPS_DB: METADATA_DB, MONGO_XMEMORY_META_DB, MONGO_XMEMORY_METADATA_DB | | C | Things corpus (equal + store tier) | xmemory-meta | THINGS_DB: THINGS_DB, MONGO_XMEMORY_META_DB; if unset → same as mapsDb | | D | Scoped views / op | xmemory_op | OP_DB: MONGO_XMEMORY_DB, VIEWS_DB, MONGO_XMEMORY_OPERATIONAL_DB |

Equal’s nx-mongo databaseName must be thingsDb, never opDb. MONGO_XMEMORY_DB is role D only in the store.

Release checklist for store changes: docs/check-with-xmemory-store.md.

Testing

npm test runs: tsc, integration typecheck (test/store-alignment.types.ts), Tier-1 Node tests (env resolution + inferenceFromFieldInferable), then compiled real-DB checks via test/run-dist.mjs (requires Mongo).

Migration: If you previously used MONGO_XMEMORY_DB as the test database for equal, that variable is operational DB in xmemory-store. Set corpus DB the store way: THINGS_DB and/or MONGO_XMEMORY_META_DB (see ENV_KEYS.THINGS_DB and mapsDb / thingsDb fallback in store).

Real DB env (same as production: resolveXmemoryStoreEnv({ mongoUri }).thingsDb — no duplicate parsing here)

| Variable | Role | |----------|------| | MONGO_URI | Required for DB tests (ENV_KEYS.MONGO_URI in store) | | THINGS_DB, MONGO_XMEMORY_META_DB, … | Corpus thingsDbexactly as xmemory-store resolves (see table above; METADATA_DB affects mapsDb, hence thingsDb when THINGS_DB unset) | | XMEMORY_TEST_DATABASE | Test-only: force Mongo database name (overrides thingsDb; CI isolation) | | MONGO_EQUAL_TEST_DB, XMEMORY_EQUAL_TEST_DATABASE | Deprecated aliases for XMEMORY_TEST_DATABASE |

Optional

  • XMEMORY_TEST_STORE_TIER_COEXISTENCE=1 — after main suite, test/test.ts runs createXmemoryDataTier().init(), a second SimpleMongoHelper on tier.env.thingsDb, minimal ensureThing, then tier.close(). Deprecated: RUN_STORE_TIER_COEXISTENCE, XMEMORY_EQUAL_TEST_STORE_TIER_COEXISTENCE.
  • XMEMORY_TEST_LIVE_LLM=1 and OPENROUTER_API_KEYnpm run test:live (optional). Deprecated: RUN_LIVE_LLM, XMEMORY_EQUAL_TEST_LIVE_LLM.
npm test
npm run test:ts
npm run test:tier1
npm run typecheck:integration
npm run test:live

See .env.example for a template.

License

ISC