@nuucognition/mesh-convex-component
v1.0.2
Published
Convex components for Mesh backend storage. Defines: - an **operational** component (`mesh`) for operations-first mutations, and - a **snapshot** component (`meshSnapshot`) for full-replace snapshot commits.
Downloads
91
Readme
@nuucognition/mesh-convex-component
Convex components for Mesh backend storage. Defines:
- an operational component (
mesh) for operations-first mutations, and - a snapshot component (
meshSnapshot) for full-replace snapshot commits.
Architecture
This package has three parts:
- Operational Convex component (
src/component/) — schema + operations-first server functions - Snapshot Convex component (
src/component-snapshot/) — schema + full snapshot commit server functions - Client classes (
src/client/) —MeshConvexandMeshConvexSnapshot
Install the component
In your Convex app's convex/convex.config.ts:
import { defineApp } from "convex/server"
import mesh from "@nuucognition/mesh-convex-component/convex.config.js"
import meshSnapshot from "@nuucognition/mesh-convex-component/snapshot/convex.config.js"
const app = defineApp()
app.use(mesh)
app.use(meshSnapshot)
export default appUse the client
import { MeshConvex } from "@nuucognition/mesh-convex-component"
import { MeshConvexSnapshot } from "@nuucognition/mesh-convex-component"
import { components } from "./convex/_generated/api"
const client = new MeshConvex({ component: components.mesh })
const snapshotClient = new MeshConvexSnapshot({ component: components.meshSnapshot })
// In a Convex query/mutation handler:
const raw = await client.loadRaw(ctx, "my-mesh-id")
await client.applyOperations(ctx, "my-mesh-id", [
{
type: "create",
title: "Note Title",
content: "# Content",
},
])
await snapshotClient.commitSnapshot(ctx, "my-mesh-id", {
documents: [{ title: "Doc", content: "# Snapshot" }],
blobs: [],
})Schema
The component creates four tables (prefixed with mesh: in the dashboard):
| Table | Fields | Indexes |
|-------|--------|---------|
| meshes | id, name?, transactionSeq, createdAt, updatedAt | by_mesh_id |
| documents | meshId, title, content, createdAt, updatedAt | by_mesh, by_mesh_title |
| blobs | meshId, blobKey, storageId, createdAt, updatedAt | by_mesh, by_mesh_blobKey |
| transactions | meshId, transactionId, seq, timestamp, author?, source, intent?, historyKind?, historyTargetSeq?, reversible?, nonReversibleReasons?, documentTitles, operationsJson, createdAt | by_mesh, by_mesh_seq, by_mesh_transaction_id |
Component API
Meshes
| Function | Type | Args | Returns |
|----------|------|------|---------|
| meshes.create | mutation | { id, name? } | string |
| meshes.get | query | { id } | mesh object or null |
| meshes.list | query | {} | mesh objects array |
| meshes.exists | query | { id } | boolean |
| meshes.remove | mutation | { id } | boolean (cascades docs + blobs) |
Documents
| Function | Type | Args | Returns |
|----------|------|------|---------|
| documents.save | mutation | { meshId, title, content } | null (upsert) |
| documents.list | query | { meshId } | { title, updatedAt }[] |
| documents.getContent | query | { meshId, title } | string or null |
| documents.getContents | query | { meshId, titles } | { title, content }[] |
| documents.remove | mutation | { meshId, title } | boolean |
Blobs
| Function | Type | Args | Returns |
|----------|------|------|---------|
| blobs.save | mutation | { meshId, blobKey, storageId } | null (upsert) |
| blobs.list | query | { meshId } | { blobKey, updatedAt }[] |
| blobs.getUrl | query | { meshId, blobKey } | string or null |
| blobs.generateUploadUrl | mutation | {} | string |
| blobs.remove | mutation | { meshId, blobKey } | boolean |
| blobs.deleteStorageObject | mutation | { storageId } | null |
Operations
| Function | Type | Args | Returns |
|----------|------|------|---------|
| operations.applyOperations | mutation | { meshId, operationsJson, enableTransactionJournal?, transaction? } | null |
Transactions
| Function | Type | Args | Returns |
|----------|------|------|---------|
| transactions.clear | mutation | { meshId } | number |
| transactions.getCurrentSeq | query | { meshId } | number |
| transactions.getStatus | query | { meshId } | { seq, journalStateHash, currentStateHash, unobservedChanges, lastTransactionSeq? } |
| transactions.sync | mutation | { meshId, author?, ensureIds?, healIntent?, reconcileIntent? } | sync summary |
| transactions.getSince | query | { meshId, seq } | transaction array |
| transactions.getRange | query | { meshId, from, to } | transaction array |
| transactions.getLatest | query | { meshId, n } | transaction array |
| transactions.getByDocument | query | { meshId, title, limit? } | transaction array |
Snapshot
| Function | Type | Args | Returns |
|----------|------|------|---------|
| snapshots.commit | mutation | { meshId, name?, documents, blobs } | null |
Client API (MeshConvex)
| Method | Context | Description |
|--------|---------|-------------|
| loadRaw(ctx, meshId) | query | Load all documents + blob keys for a mesh |
| getContent(ctx, meshId, title) | query | Get single document content (throws if missing) |
| getBlobData(ctx, meshId, blobKey) | query | Fetch blob binary via storage URL |
| getUploadUrl(ctx) | mutation | Get signed URL for blob upload |
| saveBlobFromStorageId(ctx, meshId, blobKey, storageId) | mutation | Save blob record after upload |
| deleteStorageObject(ctx, storageId) | mutation | Delete raw uploaded storage object |
| applyOperations(ctx, meshId, operations) | mutation | Apply an operations-first mutation batch atomically |
| getTransactionStatus(ctx, meshId) | query | Get journal/current state hashes + unobserved-change flag |
| syncTransactions(ctx, meshId, options?) | mutation | Heal missing UUIDs and append inferred reconcile transaction(s) |
Client API (MeshConvexSnapshot)
| Method | Context | Description |
|--------|---------|-------------|
| loadRaw(ctx, meshId) | query | Load all documents + blob keys for a mesh |
| getContent(ctx, meshId, title) | query | Get single document content (throws if missing) |
| getBlobData(ctx, meshId, blobKey) | query | Fetch blob binary via storage URL |
| getUploadUrl(ctx) | mutation | Get signed URL for blob upload |
| deleteStorageObject(ctx, storageId) | mutation | Delete raw uploaded storage object |
| commitSnapshot(ctx, meshId, payload) | mutation | Replace full mesh snapshot atomically |
Exports
| Path | Contents |
|------|----------|
| @nuucognition/mesh-convex-component | MeshConvex, MeshConvexSnapshot, factories, types |
| @nuucognition/mesh-convex-component/convex.config.js | Component definition for app.use() |
| @nuucognition/mesh-convex-component/snapshot/convex.config.js | Snapshot component definition for app.use() |
| @nuucognition/mesh-convex-component/schema | Table schema for convex-test |
| @nuucognition/mesh-convex-component/snapshot/schema | Snapshot table schema for convex-test |
| @nuucognition/mesh-convex-component/test | Test utilities |
Package layout
src/
client/ MeshConvex class and types
component/ Convex component (schema, meshes, documents, blobs)
_generated/ Auto-generated Convex files
convex.config.ts
schema.ts
meshes.ts
documents.ts
blobs.ts
transactions.ts
test.ts Test utilities
index.ts Public exportsRelated packages
@nuucognition/mesh-core— core types and parsing@nuucognition/mesh-adapter-convex— bridge that adds mesh-core parsing on top of this client@nuucognition/mesh-integration-tests— E2E tests for the full pipeline
