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

xmemory-equal

v1.4.0

Published

Canonical Thing identity and equality layer for XMemory (MongoDB via nx-mongo)

Downloads

423

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):
import { SimpleMongoHelper } from "nx-mongo";

const helper = new SimpleMongoHelper(process.env.MONGO_URI!);
await helper.initialize({ databaseName: process.env.MONGO_XMEMORY_DB ?? "xmemory" });
  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. |

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. |

Important: This package does not read MONGO_URI or any DB name. Your app must create and initialize nx-mongo and pass it in as nxMongo.

Scope (metadata.db, metadata.collection)

Things can be scoped to a logical db and collection via reserved metadata keys metadata.db and metadata.collection. Scoper and Mapper rely on this; 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).

Testing

Real DB tests require a running MongoDB and a .env with MONGO_URI and MONGO_XMEMORY_DB:

npm test

Or run the TypeScript test directly:

npm run test:ts

License

ISC