mongofire 6.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/LICENSE +21 -0
- package/README.md +292 -0
- package/dist/bin/mongofire.cjs +2 -0
- package/dist/src/changetrack.js +1 -0
- package/dist/src/connection.js +1 -0
- package/dist/src/device.js +1 -0
- package/dist/src/index.cjs +1 -0
- package/dist/src/plugin.js +1 -0
- package/dist/src/state.js +1 -0
- package/dist/src/sync.js +1 -0
- package/dist/src/utils.js +1 -0
- package/dist/types/index.d.ts +224 -0
- package/index.cjs +4 -0
- package/index.mjs +6 -0
- package/package.json +81 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to MongoFire are documented here.
|
|
4
|
+
|
|
5
|
+
## [6.5.0] — 2026-03-08
|
|
6
|
+
|
|
7
|
+
### Fixed — Critical
|
|
8
|
+
|
|
9
|
+
- **Batch upload data corruption** — operations in the same upload batch could be incorrectly acknowledged, causing sync state to diverge silently. Each operation is now acknowledged individually with its correct status
|
|
10
|
+
- **Atlas connection pool leak** — a failed Atlas health check left the underlying connection open, leaking pool connections and causing every subsequent sync to fail with a connection error. Connection is now fully released on failure
|
|
11
|
+
- **Delete overwrites concurrent update** — a queued delete could silently overwrite a document that another device had updated in the meantime. This is now detected and surfaced as a `conflict` event instead of data loss
|
|
12
|
+
- **Date fields corrupted on sync** — `Date` values in synced documents were being converted to plain ISO strings, breaking date-range queries (`$gt`, `$lt`) on the receiving device. BSON types are now preserved correctly across sync
|
|
13
|
+
- **`syncOwner` error grants access to all data** — if the `syncOwner` function threw an error, MongoFire silently fell back to syncing all owners. Now the sync is aborted and an `error` event is emitted, preventing unintended data access
|
|
14
|
+
|
|
15
|
+
### Fixed — Medium
|
|
16
|
+
|
|
17
|
+
- **Docker / cloned VM device ID collision** — instances sharing the same MAC address and hostname (e.g. Docker replicas, cloned VMs) generated identical device IDs, causing changes from those devices to be silently skipped on other instances. Device IDs are now always unique
|
|
18
|
+
- **`updateOne` hook could track wrong document** — the change tracking hook re-read the document after the update using the original filter, which could match a different document in a concurrent write scenario. The hook now locks on the specific document ID before the update runs
|
|
19
|
+
- **Bootstrap restart from scratch on failure** — if a bootstrap sync was interrupted (crash, network drop), the next start re-downloaded all documents from the beginning. Bootstrap now resumes from the last completed checkpoint
|
|
20
|
+
- **Sync state and device registry created on Atlas** — two local-only internal collections were being created on Atlas unnecessarily, consuming storage and triggering index maintenance. They are now created on local only
|
|
21
|
+
|
|
22
|
+
### Fixed — Minor
|
|
23
|
+
|
|
24
|
+
- **Shared internal object could be accidentally mutated** — an internal configuration object was shared across calls; mutating it in one place could silently affect all subsequent calls. It is now immutable
|
|
25
|
+
- **Multiple app instances share one signal handler** — only the first MongoFire instance received `SIGINT`/`SIGTERM` cleanup. Each instance now manages its own handler and removes it on `stop()`, preventing memory leaks in test environments
|
|
26
|
+
- **`require('mongofire/plugin')` API inconsistency** — the direct plugin import had a different call signature to `mongofire.plugin()`. Both now work the same way, with a `.factory()` helper added for parity
|
|
27
|
+
- **Collection name with special characters causes key collision** — two different collections could produce identical internal keys, leading to metadata corruption. Collection names are now validated at startup with a clear error message
|
|
28
|
+
|
|
29
|
+
### Fixed — Security
|
|
30
|
+
|
|
31
|
+
- **Hardware fingerprint stored on Atlas** — a derived MAC address value was being stored in Atlas, accessible to anyone with database read access. This constituted an unnecessary hardware fingerprint. The field has been removed
|
|
32
|
+
- **Arbitrary collection names accepted** — collection names were not validated, allowing names that could interfere with MongoFire's internal collections. Names are now validated at startup: no special characters that cause key collisions, no reserved prefixes
|
|
33
|
+
- **Oversized documents cause cryptic failure** — documents close to MongoDB's 16MB limit failed with an unhelpful low-level error. MongoFire now checks document size before writing and throws a clear, actionable message
|
|
34
|
+
- **Manual `sync()` calls not rate-limited** — a runaway loop calling `sync()` in rapid succession could hammer Atlas with back-to-back requests. Rapid successive calls are now throttled automatically
|
|
35
|
+
|
|
36
|
+
### Fixed — Performance
|
|
37
|
+
|
|
38
|
+
- **Checksum computed on every document** — a cryptographic checksum was calculated for every document on every sync cycle regardless of whether verification was enabled. It is now skipped by default and only computed when `MONGOFIRE_VERIFY_REMOTE=1`
|
|
39
|
+
- **Bulk update tracking loaded all IDs into memory** — tracking changes for a large `updateMany` operation materialised the full list of matching IDs in memory (100MB+ for millions of documents). The hook now uses a streaming cursor instead
|
|
40
|
+
- **Too many database round-trips during upload** — uploading a large backlog of pending changes required many more database queries than necessary. Batch size has been increased significantly, reducing round-trips by ~60%
|
|
41
|
+
|
|
42
|
+
### Fixed — Reliability
|
|
43
|
+
|
|
44
|
+
- **Conflicts were silently swallowed** — when a version conflict was detected during upload, it was recorded internally but never surfaced to the application. MongoFire now emits a `conflict` event with structured data so you can respond to it
|
|
45
|
+
- **Same-millisecond changes could be missed** — the delta sync cursor used only a timestamp, so two changes with the exact same timestamp could result in one being skipped on the next sync. The cursor now uses a compound position that eliminates this gap
|
|
46
|
+
|
|
47
|
+
### Added
|
|
48
|
+
|
|
49
|
+
- **`conflict` event** — emitted when a version conflict is detected during upload, with `{ collection, docId, localVersion, remoteVersion, op }` payload. See Events section in the README
|
|
50
|
+
- **`ConflictData` TypeScript interface** — fully typed payload for the `conflict` event
|
|
51
|
+
- **`mongofire/plugin` factory export** — `require('mongofire/plugin').factory(name, opts)` matches the `mongofire.plugin()` signature for direct use without the singleton
|
|
52
|
+
- **`MONGOFIRE_COLLECTION_CONCURRENCY` env var** — configure how many collections sync in parallel at runtime (default: 4)
|
|
53
|
+
- **Startup validation for collection names** — invalid names fail immediately with a clear message instead of silently corrupting data at sync time
|
|
54
|
+
|
|
55
|
+
### Changed
|
|
56
|
+
|
|
57
|
+
- `syncInterval` default is now `30000`ms (polling mode) or `5000`ms (when `realtime: true`)
|
|
58
|
+
- `clean()` default changed from 30 days → **7 days**, matching the Atlas-side TTL so both sides stay consistent
|
|
59
|
+
- `plugin()` `concurrency` option is now actually used (was accepted but ignored in previous versions; default: `8`)
|
|
60
|
+
- Device IDs now include random bytes — existing device records are preserved on startup, so this only affects new installations
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## [6.2.0] — 2026-03-08
|
|
65
|
+
|
|
66
|
+
### Fixed — Critical
|
|
67
|
+
- **`start()` concurrent safety** — multiple simultaneous `start()` calls now share one init promise instead of racing
|
|
68
|
+
- **Bootstrap re-trigger bug** — an empty collection no longer forces a full re-bootstrap of all collections
|
|
69
|
+
- **Silent change tracking errors** — errors in the Mongoose hooks are now logged instead of swallowed
|
|
70
|
+
- **Realtime sync not working** — change stream pipeline fix; was silently delivering zero events on most Atlas clusters
|
|
71
|
+
|
|
72
|
+
### Fixed — Medium
|
|
73
|
+
- **`deleteMany` OOM risk** — plugin now streams and batches docs before deletion; removes 10,000-doc silent cap
|
|
74
|
+
- **Session not forwarded in `updateOne` and `deleteOne` hooks** — reads now occur within the same transaction context
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
- Full TypeScript declarations (`types/index.d.ts`) with typed events, config, and result interfaces
|
|
78
|
+
- `require('mongofire/plugin')` subpath export
|
|
79
|
+
- Max retry limit (10 attempts) for permanently failing operations
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## [6.1.0] — initial release
|
|
84
|
+
|
|
85
|
+
- Offline-first sync with Local MongoDB + Atlas
|
|
86
|
+
- Mongoose plugin with hooks for save, update, delete operations
|
|
87
|
+
- Bootstrap + delta oplog sync
|
|
88
|
+
- Automatic conflict resolution (version vector + timestamp + deviceId tiebreaker)
|
|
89
|
+
- Real-time sync via Atlas Change Streams with polling fallback
|
|
90
|
+
- `npx mongofire init / status / clean` CLI
|
|
91
|
+
- Exponential backoff retry with jitter
|
|
92
|
+
- TTL index auto-cleanup of old sync records
|
|
93
|
+
- Multi-tenant `syncOwner` support
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# 🔥 MongoFire
|
|
2
|
+
|
|
3
|
+
> **Offline-first MongoDB sync** — Local + Atlas feel like ONE database.
|
|
4
|
+
> Automatic conflict resolution, Mongoose plugin, zero boilerplate.
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/mongofire)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
MongoFire keeps a **local MongoDB** and **MongoDB Atlas** in sync automatically.
|
|
15
|
+
|
|
16
|
+
- Your app reads/writes to local MongoDB — always fast, works offline
|
|
17
|
+
- MongoFire tracks every change and uploads to Atlas when online
|
|
18
|
+
- Downloads remote changes from Atlas in the background
|
|
19
|
+
- If you go offline, changes queue up and sync when you reconnect
|
|
20
|
+
- Conflicts resolved automatically (last-writer-wins with version vectors)
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install mongofire
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Peer dependencies** (install the ones you use):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm install mongodb mongoose
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Quick Start
|
|
39
|
+
|
|
40
|
+
### 1. Init config files
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx mongofire init
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This creates:
|
|
47
|
+
- `.env` — MongoDB connection strings
|
|
48
|
+
- `mongofire.config.js` — which collections to sync
|
|
49
|
+
- `mongofire.js` — app entry point
|
|
50
|
+
|
|
51
|
+
### 2. Fill in `.env`
|
|
52
|
+
|
|
53
|
+
```env
|
|
54
|
+
ATLAS_URI=mongodb+srv://user:pass@cluster0.xxxxx.mongodb.net/
|
|
55
|
+
LOCAL_URI=mongodb://127.0.0.1:27017
|
|
56
|
+
DB_NAME=myapp
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 3. Start sync in your app
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
// CommonJS
|
|
63
|
+
const mongofire = require('mongofire');
|
|
64
|
+
const config = require('./mongofire.config');
|
|
65
|
+
|
|
66
|
+
await mongofire.start(config);
|
|
67
|
+
|
|
68
|
+
// ESM
|
|
69
|
+
import mongofire from 'mongofire';
|
|
70
|
+
import config from './mongofire.config.js';
|
|
71
|
+
|
|
72
|
+
await mongofire.start(config);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### 4. Add the plugin to your Mongoose schema
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
const mongofire = require('mongofire');
|
|
79
|
+
|
|
80
|
+
const UserSchema = new mongoose.Schema({
|
|
81
|
+
name: String,
|
|
82
|
+
email: String,
|
|
83
|
+
userId: mongoose.Types.ObjectId,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Track changes on the 'users' collection
|
|
87
|
+
// ownerField: field used for per-user data isolation (multi-tenant)
|
|
88
|
+
UserSchema.plugin(mongofire.plugin('users', { ownerField: 'userId' }));
|
|
89
|
+
|
|
90
|
+
const User = mongoose.model('User', UserSchema);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Config Options
|
|
96
|
+
|
|
97
|
+
| Option | Type | Default | Description |
|
|
98
|
+
|---|---|---|---|
|
|
99
|
+
| `collections` | `string[]` | *required* | Collection names to sync |
|
|
100
|
+
| `localUri` | `string` | `mongodb://localhost:27017` | Local MongoDB URI |
|
|
101
|
+
| `atlasUri` | `string` | `null` | Atlas connection string |
|
|
102
|
+
| `dbName` | `string` | `'mongofire'` | Database name |
|
|
103
|
+
| `syncInterval` | `number` | `30000` (polling) / `5000` (realtime) | Polling interval in ms |
|
|
104
|
+
| `batchSize` | `number` | `200` | Docs per upload/download batch |
|
|
105
|
+
| `syncOwner` | `string\|fn` | `'*'` | Owner key for multi-tenant filtering. If a function, throwing will **abort** the sync to prevent unintended data access |
|
|
106
|
+
| `realtime` | `boolean` | `false` | Use Atlas Change Streams for instant sync |
|
|
107
|
+
| `onSync` | `function` | `null` | Called after each sync cycle |
|
|
108
|
+
| `onError` | `function` | `null` | Called on sync errors |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## Events
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
mongofire.on('ready', () => console.log('MongoFire started'));
|
|
116
|
+
mongofire.on('online', () => console.log('Atlas connected'));
|
|
117
|
+
mongofire.on('offline', () => console.log('Working locally'));
|
|
118
|
+
mongofire.on('sync', (r) => console.log('Sync result:', r));
|
|
119
|
+
mongofire.on('conflict', (c) => console.warn('Conflict detected:', c));
|
|
120
|
+
mongofire.on('realtimeStarted', () => console.log('Change streams active'));
|
|
121
|
+
mongofire.on('stopped', () => console.log('Shut down cleanly'));
|
|
122
|
+
mongofire.on('error', (e) => console.error('Sync error:', e));
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### `conflict` event
|
|
126
|
+
|
|
127
|
+
Emitted when a local write conflicts with a concurrent remote change. Use this to notify users or trigger a re-fetch:
|
|
128
|
+
|
|
129
|
+
```js
|
|
130
|
+
mongofire.on('conflict', ({ collection, docId, localVersion, remoteVersion, op }) => {
|
|
131
|
+
console.warn(`Conflict on ${collection}/${docId}`);
|
|
132
|
+
console.warn(` Local was at version ${localVersion}, Atlas is at ${remoteVersion}`);
|
|
133
|
+
// Fetch the latest remote doc and re-apply your changes if needed
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## API
|
|
140
|
+
|
|
141
|
+
### `mongofire.start(config)` → `Promise<MongoFire>`
|
|
142
|
+
Connect and start background sync. Concurrent calls are safe — all await the same init.
|
|
143
|
+
|
|
144
|
+
### `mongofire.stop(timeoutMs?)` → `Promise<void>`
|
|
145
|
+
Flush pending ops, wait for active sync, close all connections. Default timeout: 10 seconds.
|
|
146
|
+
|
|
147
|
+
### `mongofire.sync(type?)` → `Promise<SyncResult>`
|
|
148
|
+
Manually trigger a sync. `type` can be `'required'` (default) or `'all'`. Rapid successive calls are throttled automatically.
|
|
149
|
+
|
|
150
|
+
### `mongofire.status()` → `Promise<SyncStatus>`
|
|
151
|
+
Get pending op counts and online/realtime status.
|
|
152
|
+
|
|
153
|
+
### `mongofire.clean(days?)` → `Promise<number>`
|
|
154
|
+
Delete old sync records older than `days` days (default: **7**). Returns count of deleted records.
|
|
155
|
+
|
|
156
|
+
### `mongofire.plugin(collectionName, options?)`
|
|
157
|
+
Returns a Mongoose schema plugin. Options:
|
|
158
|
+
|
|
159
|
+
| Option | Type | Default | Description |
|
|
160
|
+
|---|---|---|---|
|
|
161
|
+
| `ownerField` | `string` | `null` | Dot-path to owner key field (e.g. `'userId'` or `'org._id'`) |
|
|
162
|
+
| `batchSize` | `number` | `200` | Batch size for bulk operations |
|
|
163
|
+
| `concurrency` | `number` | `8` | Concurrent change tracking calls per batch |
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## Using the plugin directly
|
|
168
|
+
|
|
169
|
+
If you prefer not to use the MongoFire singleton, import the plugin directly:
|
|
170
|
+
|
|
171
|
+
```js
|
|
172
|
+
// Raw Mongoose plugin
|
|
173
|
+
const mongofirePlugin = require('mongofire/plugin');
|
|
174
|
+
UserSchema.plugin(mongofirePlugin, { collection: 'users', ownerField: 'userId' });
|
|
175
|
+
|
|
176
|
+
// Or use the factory helper (same signature as mongofire.plugin())
|
|
177
|
+
const { factory } = require('mongofire/plugin');
|
|
178
|
+
UserSchema.plugin(factory('users', { ownerField: 'userId' }));
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Real-Time Sync
|
|
184
|
+
|
|
185
|
+
Enable instant sync via MongoDB Atlas Change Streams:
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
await mongofire.start({
|
|
189
|
+
// ...
|
|
190
|
+
realtime: true, // requires Atlas cluster or local replica set
|
|
191
|
+
});
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
If Change Streams are unavailable, MongoFire automatically falls back to polling — no crash, no config needed.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Multi-Tenant Usage
|
|
199
|
+
|
|
200
|
+
```js
|
|
201
|
+
// Sync only data belonging to a specific user
|
|
202
|
+
await mongofire.start({
|
|
203
|
+
collections: ['notes', 'tasks'],
|
|
204
|
+
syncOwner: () => currentUser.id, // dynamic — re-evaluated on each sync cycle
|
|
205
|
+
// ...
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
> **Note:** If `syncOwner` throws, the sync is **aborted** and an `error` event is emitted. This prevents unintended access to other users' data.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## CLI
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Create config files in current project
|
|
217
|
+
npx mongofire init
|
|
218
|
+
|
|
219
|
+
# Force overwrite existing config
|
|
220
|
+
npx mongofire init --force
|
|
221
|
+
|
|
222
|
+
# Check pending sync status
|
|
223
|
+
npx mongofire status
|
|
224
|
+
|
|
225
|
+
# Delete old sync records
|
|
226
|
+
npx mongofire clean
|
|
227
|
+
npx mongofire clean --days=7
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## TypeScript
|
|
233
|
+
|
|
234
|
+
Full TypeScript support is included:
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
import mongofire, { MongoFire, SyncConfig, SyncResult, ConflictData } from 'mongofire';
|
|
238
|
+
|
|
239
|
+
const config: SyncConfig = {
|
|
240
|
+
collections: ['users'],
|
|
241
|
+
atlasUri: process.env.ATLAS_URI,
|
|
242
|
+
realtime: true,
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
await mongofire.start(config);
|
|
246
|
+
|
|
247
|
+
mongofire.on('sync', (result: SyncResult) => {
|
|
248
|
+
console.log(`Uploaded: ${result.uploaded}, Downloaded: ${result.downloaded}`);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
mongofire.on('conflict', (data: ConflictData) => {
|
|
252
|
+
console.warn(`Conflict on ${data.collection}/${data.docId}`);
|
|
253
|
+
});
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## How it Works
|
|
259
|
+
|
|
260
|
+
1. **Change Tracking** — Every `save`/`update`/`delete` via Mongoose hooks is recorded locally before syncing to Atlas
|
|
261
|
+
2. **Upload** — Pending local changes are uploaded to Atlas inside MongoDB transactions, with automatic retry and idempotency
|
|
262
|
+
3. **Download (Bootstrap)** — First sync streams all remote docs in batches. Resumable — a crash mid-bootstrap picks up from where it left off
|
|
263
|
+
4. **Download (Delta)** — Subsequent syncs fetch only changes newer than the last seen position, with no gaps even at millisecond boundaries
|
|
264
|
+
5. **Conflict Resolution** — Version number → timestamp → deviceId tiebreaker (deterministic, no coin flip)
|
|
265
|
+
6. **Offline** — All reads/writes work locally. Changes queue up and upload automatically when Atlas reconnects
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Collection Name Rules
|
|
270
|
+
|
|
271
|
+
Collection names passed to `mongofire.plugin()` and `config.collections` must:
|
|
272
|
+
- Start with a letter or digit
|
|
273
|
+
- Contain only letters, digits, underscores (`_`), hyphens (`-`), or dots (`.`)
|
|
274
|
+
- **Not** contain a colon (`:`)
|
|
275
|
+
- **Not** start with `_mf_` (reserved for MongoFire internal use)
|
|
276
|
+
|
|
277
|
+
Invalid names throw a clear error at startup.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Environment Variables
|
|
282
|
+
|
|
283
|
+
| Variable | Default | Description |
|
|
284
|
+
|---|---|---|
|
|
285
|
+
| `MONGOFIRE_VERIFY_REMOTE` | `0` | Set to `1` to verify each uploaded doc with a checksum round-trip |
|
|
286
|
+
| `MONGOFIRE_COLLECTION_CONCURRENCY` | `4` | Number of collections synced in parallel |
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
290
|
+
## License
|
|
291
|
+
|
|
292
|
+
MIT — see [LICENSE](LICENSE)
|