npm.io
1.36.0 • Published 4d ago

@x12i/memorix-writer

Licence
Version
1.36.0
Deps
8
Size
483 kB
Vulns
0
Weekly
0

@x12i/memorix-writer

Descriptor-driven write layer for Memorix. Turns Catalox write descriptors, entity descriptors, validated input, and optional Markdown/text content into Memorix records and external content object pointers.

Shared MongoDB naming rules: docs/MEMORIX-DATABASE-CONVENTIONS.md.

Architecture

  • Cataloxmemorix-write-descriptors, memorix-object-type-descriptors. Catalog metadata lives in MongoDB (memorix-catalox via @x12i/catalox/mongo). Firestore is not used for Memorix Catalox.
  • MongoDB — built-in MemorixDataTier + collection-name CRUD (no separate @x12i/memorix package)
  • @x12i/memorix-descriptors — shared catalog ids and descriptor validation (aligned with retrieval)
  • @x12i/helpers — GCS/S3 content uploads (contentWriters.gcs / contentWriters.s3)
  • @x12i/memorix-retrieval (optional) — returnMode: "retrievalItem"

Quick start

Set MONGO_URI and provide a Catalox client. Database names, entity vs event routing, and collection resolution are handled internally.

MONGO_URI=mongodb://localhost:27017
import {
  createMemorixWriterFromEnv,
  writeMemorixRecord,
} from "@x12i/memorix-writer";

const writer = await createMemorixWriterFromEnv({
  catalox: boundCatalox, // must implement getCatalogItem(appId, catalogId, itemId)
  contentWriters: {
    gcs: myGcsUploader, // uploadObject({ key, body, contentType })
  },
});

const result = await writeMemorixRecord(writer, {
  writeDescriptorId: "asset-analysis-write",
  entityId: "10.150.68.31",
  input: { riskLevel: "HIGH", summary: "..." },
  metadata: { source: { uri: "file://scan.json" } },
  tags: ["risk", "source-scan"],
  narratives: {
    "high-risk": { detectedBy: "risk-worker" },
  },
  content: {
    fields: {
      investigationReport: { body: "# Report\n...", format: "markdown" },
    },
  },
});

await writer.close?.();

Lazy connect (same env, no upfront await):

import { createMemorixWriter, writeMemorixRecord } from "@x12i/memorix-writer";

const writer = createMemorixWriter({ catalox: boundCatalox });
// connects to Mongo on first write using MONGO_URI

Advanced (optional)

Override defaults only when you need to:

Option Purpose
MEMORIX_ENTITIES_DB / MEMORIX_EVENTS_DB Non-default database names
MEMORIX_ENTITIES_COLLECTION_* / MEMORIX_EVENTS_COLLECTION_* Per-type collection overrides
memorix Inject a pre-configured MemorixDataTier instead of built-in Mongo connect
mongo Reuse an existing MongoClient
processEnv Custom env object (default: process.env)

Collection resolution

Writes target memorix-entities, memorix-events, or memorix-knowledge based on the write descriptor (target.kind, defaulting from the descriptor's identity.idField config). Database names follow Memorix Database Conventions (MEMORIX_ENTITIES_DB, MEMORIX_EVENTS_DB, MEMORIX_KNOWLEDGE_DB, …).

Identity model

Every record this package writes uses the memorix-record.2.0 envelope — a nested identity object, not flat top-level id fields:

{
  "identity": {
    "recordId": "rec_...",
    "target": "entity",
    "targetId": "10.150.68.31",
    "objectType": "assets",
    "contentType": "analysis",
    "version": "memorix-record.2.0"
  },
  "lifecycle": { "createdAt": "...", "modifiedAt": "...", "status": "active" },
  "concept": { "kind": "assets", "name": "10.150.68.31" },
  "data": { "...": "..." }
}

entityId / eventId / knowledgeId are request-time parameter names on writeMemorixRecord — pass exactly one to say which target and which id value you're writing:

Target Request field Written to
entity entityId identity.target: "entity", identity.targetId
event eventId identity.target: "event", identity.targetId
knowledge knowledgeId identity.target: "knowledge", identity.targetId

They are never stored as flat root fields, and there is no client-facing idField concept — clients read/write identity via the identity object (and, for narrative/search context, the concept object). This package will not add a flat-id or idField public API even on request, since it would break the memorix-record.2.0 contract for every downstream consumer. See MEMORIX-DATABASE-CONVENTIONS.md for the full shape and rationale.

Internally, @x12i/memorix-writer tolerates reading older, pre-2.0 records that only have a flat entityId/eventId (see docs/GRAPH-RUNS.md § Identity resolution) so graph-run tracking keeps working during a tenant's migration window. That fallback is read-only, internal, and not exported — it does not change what gets written, and it is not something applications should rely on or target.

Collection name resolution order:

  1. Explicit override (API/code parameter)
  2. Write descriptor target.targetCollection
  3. Entity descriptor content type collection
  4. MEMORIX_ENTITIES_COLLECTION_* / MEMORIX_EVENTS_COLLECTION_* env vars
  5. Write descriptor target.targetCollectionCandidates
  6. Content-type slice {collectionPrefix}-{postfix} (e.g. assets-analysis)
  7. Built-in defaults for known entityType values
  8. Heuristic (<entityType> or <entityType>-events)
  9. Verify the collection exists in the target database (skipped for dryRun / validateOnly)

For content-type slices, step 6 runs before canonical entity defaults so assets + analysis still resolves to assets-analysis unless overridden explicitly.

Generic Annotations

writeMemorixRecord and writeMemorixRecords expose generic annotation hooks that work across entity, event, and knowledge targets:

Request field Written shape
metadata _memorix.metadata
tags _memorix.tags
narratives narratives.{key}

Narrative keys use the same top-level narratives.{key} shape consumed by @x12i/memorix-retrieval helpers such as fetchMemorixNarrativeRecords. This is only a generic tag/write primitive; applications still own narrative detection, descriptor design, and domain-specific relation names.

Operations

Operation Default Notes
add yes Always inserts a new record
upsert descriptor-gated Requires conflict.matchBy
patch descriptor-gated Updates writable fields on existing row
replace descriptor-gated Full replace

Catalox seeds

Catalog metadata for this package (write descriptors, entity descriptors) is managed in MongoDB — the memorix-catalox database. The manifest below is the source of truth for what gets applied there.

Validate the write descriptor seed:

npm run catalox:seed:write-descriptors:validate

Apply the manifest to Mongo memorix-catalox (uses MONGO_URI + MEMORIX_CATALOX_DB) — this is the normal way to publish or update descriptors:

npm run catalox:seed:write-descriptors:apply

Seed file: catalox-seeds/memorix-write-descriptors.manifest.json

Shipped write descriptors include asset-analysis-write (entity) and corpus helpers content-documents-snapshot-write / content-documents-chunk-write (knowledge target). Record builders: @x12i/memorix-corpus.

Acceptance criteria

ID Status
AC-1 Generic descriptor-driven write
AC-2 Prefix/postfix collection resolution
AC-3 Identity: exactly one of entityId / eventId / knowledgeId, written to identity.targetId
AC-4 Default operation add
AC-5 Upsert only when allowed
AC-6 Patch only when allowed
AC-7 Writable field allowlist / unknown fields
AC-8 Required fields
AC-9 Content body → object storage + pointer
AC-10 Existing content pointer
AC-11 Content maxBytes
AC-12 No secrets in descriptors
AC-13 Idempotency
AC-14 dryRun
AC-15 validateOnly
AC-16 Retrieval delegated when configured
AC-17 Generic metadata, tags, narrative tags, and knowledge writes

Graph-run tracking (_graphRuns) — MRX-FRS-001

System-only APIs for Exellix jobs graph execution tracking. Domain writeMemorixRecord rejects _graphRuns in input.

import {
  markGraphRunStarted,
  markGraphRunFailed,
  writeGraphRunResult,
  clearGraphRun,
  ensureGraphRunIndexes,
} from "@x12i/memorix-writer";
API Purpose
markGraphRunStarted Stamp in_progress (idempotent per jobRunId)
markGraphRunFailed Terminal failure stamp
writeGraphRunResult Upsert result doc + stamp source done (best-effort compensation)
clearGraphRun Force re-run via $unset
ensureGraphRunIndexes Opt-in planning-query indexes

Full contract: docs/GRAPH-RUNS.md.

Job-type run tracking (_jobTypeRuns) — MRX-CR-003

Parallel stamp APIs keyed by jobTypeId (shared GraphRunEntry shape). Domain writes reject _jobTypeRuns in input.

import {
  markJobTypeRunStarted,
  markJobTypeRunCompleted,
  markJobTypeRunFailed,
  ensureJobTypeRunIndexes,
} from "@x12i/memorix-writer";

Full contract: docs/JOB-TYPE-RUNS.md.

Entity collection bootstrap — MRX-CR-005

import { initializeEntityCollections } from "@x12i/memorix-writer";

await initializeEntityCollections(client, "assets", {
  contentTypes: ["core", "inferences"],
  graphRunIndexes: true,
  jobTypeRunIndexes: true,
});

Idempotent: creates missing collections and ensures standard indexes (idx_updatedAt, idx_graphRuns, optional idx_jobTypeRuns, etc.).

Development

npm run build
npm test

Mongo collection I/O is built in (insertMemorixCollectionDocument, etc. under src/mongo/).

Publish

npm run build
npm publish --access public

Or from the x12i workspace root: ./scripts/publish-memorix-packages.sh