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

@mostajs/replicator

v0.2.2

Published

Replication manager for @mostajs — master/slave CQRS, cross-dialect CDC, read routing, failover. Ships with a `scaffoldReplicatorService` emitter.

Downloads

544

Readme

@mostajs/replicator

Replication manager for @mostajs — master/slave CQRS, cross-dialect CDC, read routing, failover.

Install

npm install @mostajs/replicator @mostajs/orm @mostajs/mproject

Usage

import { ReplicationManager } from '@mostajs/replicator'
import { ProjectManager } from '@mostajs/mproject'

const pm = new ProjectManager()
const replicator = new ReplicationManager(pm)

// Phase 3 — CQRS: add replicas to a project
await replicator.addReplica('secuaccess', {
  name: 'master',
  role: 'master',
  dialect: 'postgres',
  uri: 'postgresql://user:pass@master:5432/secuaccess',
})

await replicator.addReplica('secuaccess', {
  name: 'slave-1',
  role: 'slave',
  dialect: 'postgres',
  uri: 'postgresql://user:pass@slave1:5432/secuaccess',
  lagTolerance: 5000,
})

// Read routing
replicator.setReadRouting('secuaccess', 'least-lag')
const readService = replicator.resolveReadService('secuaccess')

// Status
replicator.getReplicaStatus('secuaccess')
// → [{ role: 'master', lag: 0 }, { role: 'slave', lag: 120 }]

// Failover
await replicator.promoteToMaster('secuaccess', 'slave-1')

// Phase 4 — Cross-dialect CDC
replicator.addReplicationRule({
  name: 'pg-to-mongo',
  source: 'secuaccess',
  target: 'analytics',
  mode: 'cdc',
  collections: ['users', 'clients'],
  conflictResolution: 'source-wins',
})

await replicator.sync('pg-to-mongo')

// Persistence
replicator.enableAutoPersist('replicator-tree.json')

// Cleanup
await replicator.disconnectAll()

API

| Method | Phase | Description | |---|---|---| | addReplica(project, config) | 3 | Add master or slave to a project | | removeReplica(project, name) | 3 | Disconnect and remove replica | | setReadRouting(project, strategy) | 3 | Set routing: round-robin, least-lag, random | | getReplicaStatus(project) | 3 | List replicas with status and lag | | promoteToMaster(project, name) | 3 | Failover: promote slave to master | | resolveReadService(project) | 3 | Get EntityService for reading (routed) | | addReplicationRule(rule) | 4 | Add cross-dialect CDC rule | | removeReplicationRule(name) | 4 | Remove a rule | | listRules() | 4 | List all replication rules | | sync(ruleName) | 4 | Manual sync trigger | | getSyncStats(ruleName) | 4 | Get sync statistics | | loadFromFile(path) | — | Load config from JSON | | saveToFile(path) | — | Save config to JSON | | enableAutoPersist(path) | — | Auto-save after changes | | disconnectAll() | — | Clean shutdown |

Consistency model

@mostajs/replicator operates on three distinct levels of consistency. Knowing which level you're on avoids surprises under load or during failover.

1. Local transactions — ACID ✅

Every write against a single replica (master or slave) is ACID, delegated to the underlying @mostajs/orm dialect (see @mostajs/orm → Transactions). Wrap your operations in $transaction and you get BEGIN / COMMIT / ROLLBACK — identical semantics to using the SGBD directly.

const writeService = replicator.resolveWriteService('secuaccess')
await writeService.dialect.$transaction(async (tx) => {
  await tx.update('accounts', { id: 'a' }, { $inc: { balance: -50 } })
  await tx.update('accounts', { id: 'b' }, { $inc: { balance:  50 } })
})

2. Master → slave replication — eventual consistency (default)

By design, slaves are updated asynchronously from the master. A successful commit on the master is visible on the master immediately and on the slaves after some lag (typically milliseconds, bounded by lagTolerance). This is the standard CQRS trade-off : fast writes, scalable reads, eventual convergence.

Implications :

  • Read-your-own-writes is only guaranteed when routing to the master.
  • read-routing: 'least-lag' picks the freshest slave but does not eliminate lag.
  • promoteToMaster() during failover may lose in-flight writes that didn't replicate in time — set lagTolerance: 0 on critical replicas to block promotion if behind.

3. Cross-dialect CDC rules — eventual + idempotent

addReplicationRule({ mode: 'cdc', source, target }) captures changes on one dialect (e.g. PostgreSQL) and replays them on another (e.g. MongoDB). This is eventually consistent with at-least-once delivery. Rules must be idempotent (use upsert on target) — the replicator does not provide distributed ACID across heterogeneous engines.

Use CDC for : analytics mirrors, search indexes, audit trails, cross-region read replicas. Do not use CDC for : financial transactions spanning two engines, inventory decrements across systems, anything needing rollback across dialects. For those, keep the transactional boundary inside a single dialect.

Cheat sheet

| You need… | Use | |---|---| | Atomic multi-row write on one SGBD | Local $transaction (level 1) | | Scale reads of a single project | Master + slaves (level 2) | | Survive a node crash | promoteToMaster() + lagTolerance: 0 | | Mirror PG → Mongo / Elasticsearch | addReplicationRule({ mode: 'cdc' }) (level 3) | | Distributed transaction across dialects | ❌ not supported — redesign around saga/compensation |

Scaffolding a service (v0.2.0+)

Rather than writing a sync loop by hand, use the built-in scaffolder to emit a ready-to-run services/replicator.mjs into your project :

# via the standalone bin
npx @mostajs/replicator scaffold --dir . --force

# or programmatically
import { scaffoldReplicatorService } from '@mostajs/replicator'
const result = scaffoldReplicatorService({ projectDir: '.', force: true })
console.log(result.action, result.path)

The emitted file :

  • Loads .env + .mostajs/config.env
  • Loads .mostajs/replicator-tree.json (configured via mostajs menu r)
  • Runs a tick loop every REPLICATOR_INTERVAL_MS (default 30s)
  • Calls rm.sync(rule.name) for every enabled CDC rule
  • Logs stats per rule, exits cleanly on SIGTERM

Wire it into your package.json :

"scripts": {
  "replicator": "node services/replicator.mjs"
}

Then : npm run replicator or add it to concurrently alongside next dev.

@mostajs/[email protected]+ automates the full setup (scaffold + package.json patch + concurrently install) via menu r → s.

License

AGPL-3.0-or-later — (c) 2026 Dr Hamid MADANI [email protected]