@x12i/memorix-writer
v1.34.0
Published
Descriptor-driven write layer for Memorix entity/event records and content objects
Downloads
3,985
Readme
@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
- Catalox —
memorix-write-descriptors,memorix-object-type-descriptors. Catalog metadata lives in MongoDB (memorix-cataloxvia@x12i/catalox/mongo). Firestore is not used for Memorix Catalox. - MongoDB — built-in
MemorixDataTier+ collection-name CRUD (no separate@x12i/memorixpackage) - @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:27017import {
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_URIAdvanced (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:
- Explicit override (API/code parameter)
- Write descriptor
target.targetCollection - Entity descriptor content type
collection MEMORIX_ENTITIES_COLLECTION_*/MEMORIX_EVENTS_COLLECTION_*env vars- Write descriptor
target.targetCollectionCandidates - Content-type slice
{collectionPrefix}-{postfix}(e.g.assets-analysis) - Built-in defaults for known
entityTypevalues - Heuristic (
<entityType>or<entityType>-events) - 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:validateApply 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:applySeed 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 testMongo collection I/O is built in (insertMemorixCollectionDocument, etc. under src/mongo/).
Publish
npm run build
npm publish --access publicOr from the x12i workspace root: ./scripts/publish-memorix-packages.sh
