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
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
|
|
3
|
+
// ─── Configuration ────────────────────────────────────────────────────────────
|
|
4
|
+
|
|
5
|
+
export interface SyncConfig {
|
|
6
|
+
/** Local MongoDB URI. Default: 'mongodb://localhost:27017' */
|
|
7
|
+
localUri?: string;
|
|
8
|
+
/** MongoDB Atlas connection string. Required for sync; omit for local-only mode. */
|
|
9
|
+
atlasUri?: string;
|
|
10
|
+
/** Database name. Default: 'mongofire' */
|
|
11
|
+
dbName?: string;
|
|
12
|
+
/** Collection names to sync. Required. */
|
|
13
|
+
collections: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Polling interval in milliseconds.
|
|
16
|
+
* Default: 5000 (5s) when realtime:true, 30000 (30s) when realtime:false
|
|
17
|
+
*/
|
|
18
|
+
syncInterval?: number;
|
|
19
|
+
/** Upload/download batch size. Default: 200 */
|
|
20
|
+
batchSize?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Owner key for multi-tenant filtering.
|
|
23
|
+
* Pass '*' to sync all owners (default), a string for a specific owner,
|
|
24
|
+
* or a function that returns the current owner key.
|
|
25
|
+
*/
|
|
26
|
+
syncOwner?: string | (() => string);
|
|
27
|
+
/**
|
|
28
|
+
* Enable real-time sync via Atlas Change Streams.
|
|
29
|
+
* Requires a MongoDB Atlas cluster or a local replica set.
|
|
30
|
+
* Falls back to polling if unavailable.
|
|
31
|
+
* Default: false
|
|
32
|
+
*/
|
|
33
|
+
realtime?: boolean;
|
|
34
|
+
/** Called after each successful sync cycle. */
|
|
35
|
+
onSync?: (result: SyncResult) => void;
|
|
36
|
+
/** Called when a sync cycle throws an unexpected error. */
|
|
37
|
+
onError?: (err: Error) => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ─── Results ──────────────────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
export interface SyncResult {
|
|
43
|
+
/** Docs downloaded from Atlas to local */
|
|
44
|
+
downloaded: number;
|
|
45
|
+
/** Docs uploaded from local to Atlas */
|
|
46
|
+
uploaded: number;
|
|
47
|
+
/** Delete operations applied */
|
|
48
|
+
deleted: number;
|
|
49
|
+
/** Operations that failed (network errors, etc.) */
|
|
50
|
+
failed: number;
|
|
51
|
+
/** Operations that produced a version conflict */
|
|
52
|
+
conflicts: number;
|
|
53
|
+
/** Upload attempts that were automatically retried */
|
|
54
|
+
retried: number;
|
|
55
|
+
/** Ops skipped via compression (offline create+delete, superseded updates) */
|
|
56
|
+
skipped: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface SyncStatus {
|
|
60
|
+
/** Whether Atlas is currently reachable */
|
|
61
|
+
online: boolean;
|
|
62
|
+
/** Total local ops not yet synced to Atlas */
|
|
63
|
+
pending: number;
|
|
64
|
+
/** Pending create operations */
|
|
65
|
+
creates: number;
|
|
66
|
+
/** Pending update operations */
|
|
67
|
+
updates: number;
|
|
68
|
+
/** Pending delete operations */
|
|
69
|
+
deletes: number;
|
|
70
|
+
/** Whether real-time change streams are active */
|
|
71
|
+
realtime: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface OfflineResult {
|
|
75
|
+
error: 'offline';
|
|
76
|
+
pending: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Plugin ───────────────────────────────────────────────────────────────────
|
|
80
|
+
|
|
81
|
+
export interface PluginOptions {
|
|
82
|
+
/**
|
|
83
|
+
* Dot-notation path to the field used as the owner key for multi-tenant
|
|
84
|
+
* filtering. Example: 'userId' or 'org._id'.
|
|
85
|
+
* Defaults to 'global' if not set.
|
|
86
|
+
*/
|
|
87
|
+
ownerField?: string;
|
|
88
|
+
/** Batch size for insertMany / updateMany / deleteMany hooks. Default: 200 */
|
|
89
|
+
batchSize?: number;
|
|
90
|
+
/** Concurrent recordChange calls per batch. Default: 4 */
|
|
91
|
+
concurrency?: number;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Events ───────────────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
export interface MongoFireEvents {
|
|
97
|
+
/** Emitted once after start() completes successfully */
|
|
98
|
+
ready: [];
|
|
99
|
+
/** Emitted after each sync cycle with the result */
|
|
100
|
+
sync: [result: SyncResult];
|
|
101
|
+
/** Emitted when Atlas connection is (re)established */
|
|
102
|
+
online: [];
|
|
103
|
+
/** Emitted when Atlas becomes unreachable */
|
|
104
|
+
offline: [];
|
|
105
|
+
/** Emitted when real-time change streams activate */
|
|
106
|
+
realtimeStarted: [];
|
|
107
|
+
/** Emitted when real-time change streams are stopped */
|
|
108
|
+
realtimeStopped: [];
|
|
109
|
+
/** Emitted when stop() finishes */
|
|
110
|
+
stopped: [];
|
|
111
|
+
/** Emitted on unexpected sync errors */
|
|
112
|
+
error: [err: Error];
|
|
113
|
+
/**
|
|
114
|
+
* Emitted when a version conflict is detected during upload.
|
|
115
|
+
* The application should inspect conflictData and resolve manually
|
|
116
|
+
* (e.g. fetch latest remote doc and re-apply changes).
|
|
117
|
+
*/
|
|
118
|
+
conflict: [data: ConflictData];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Structured data emitted with the 'conflict' event. */
|
|
122
|
+
export interface ConflictData {
|
|
123
|
+
/** Collection where the conflict occurred */
|
|
124
|
+
collection: string;
|
|
125
|
+
/** Document _id (as string) */
|
|
126
|
+
docId: string;
|
|
127
|
+
/** The opId of the conflicting local operation */
|
|
128
|
+
opId: string;
|
|
129
|
+
/** The local baseVersion that was expected on Atlas */
|
|
130
|
+
localVersion: number;
|
|
131
|
+
/** The actual version currently on Atlas */
|
|
132
|
+
remoteVersion: number;
|
|
133
|
+
/** Operation type: 'create' | 'update' | 'delete' */
|
|
134
|
+
op: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ─── Main class ───────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
export declare class MongoFire extends EventEmitter {
|
|
140
|
+
/**
|
|
141
|
+
* Connect to local and Atlas MongoDB, run an initial sync, and start
|
|
142
|
+
* background polling. Concurrent calls share one init promise.
|
|
143
|
+
*/
|
|
144
|
+
start(config: SyncConfig): Promise<this>;
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Flush pending ops, wait for any active sync to finish, and close all
|
|
148
|
+
* connections.
|
|
149
|
+
* @param timeoutMs Max ms to wait for active sync. Default: 10000
|
|
150
|
+
*/
|
|
151
|
+
stop(timeoutMs?: number): Promise<void>;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Manually trigger a sync cycle.
|
|
155
|
+
* Returns an OfflineResult immediately if Atlas is unreachable.
|
|
156
|
+
*/
|
|
157
|
+
sync(type?: 'required' | 'all'): Promise<SyncResult | OfflineResult>;
|
|
158
|
+
|
|
159
|
+
/** Returns pending op counts and online status. */
|
|
160
|
+
status(): Promise<SyncStatus>;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Delete synced changetrack records older than `days` days.
|
|
164
|
+
* Default: 7 (matches Atlas TTL to keep both sides consistent)
|
|
165
|
+
* @returns Number of records deleted
|
|
166
|
+
*/
|
|
167
|
+
clean(days?: number): Promise<number>;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Returns a Mongoose schema plugin that tracks changes on the given collection.
|
|
171
|
+
*
|
|
172
|
+
* @example
|
|
173
|
+
* import mongofire from 'mongofire';
|
|
174
|
+
* userSchema.plugin(mongofire.plugin('users', { ownerField: 'userId' }));
|
|
175
|
+
*/
|
|
176
|
+
plugin(collectionName: string, options?: PluginOptions): (schema: import('mongoose').Schema) => void;
|
|
177
|
+
|
|
178
|
+
/** Manually activate real-time sync (if Atlas is connected and supports change streams). */
|
|
179
|
+
startRealtime(): Promise<boolean>;
|
|
180
|
+
|
|
181
|
+
/** Stop real-time change stream (polling continues). */
|
|
182
|
+
stopRealtime(): Promise<void>;
|
|
183
|
+
|
|
184
|
+
/** Whether Atlas is currently reachable. */
|
|
185
|
+
readonly online: boolean;
|
|
186
|
+
/** Whether start() has completed. */
|
|
187
|
+
readonly started: boolean;
|
|
188
|
+
/** Whether a sync cycle is currently running. */
|
|
189
|
+
readonly syncing: boolean;
|
|
190
|
+
/** Whether real-time change streams are active. */
|
|
191
|
+
readonly realtimeActive: boolean;
|
|
192
|
+
|
|
193
|
+
// ─── Typed event emitter overloads ────────────────────────────────────────
|
|
194
|
+
on<K extends keyof MongoFireEvents>(event: K, listener: (...args: MongoFireEvents[K]) => void): this;
|
|
195
|
+
once<K extends keyof MongoFireEvents>(event: K, listener: (...args: MongoFireEvents[K]) => void): this;
|
|
196
|
+
emit<K extends keyof MongoFireEvents>(event: K, ...args: MongoFireEvents[K]): boolean;
|
|
197
|
+
off<K extends keyof MongoFireEvents>(event: K, listener: (...args: MongoFireEvents[K]) => void): this;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ─── Direct plugin import ─────────────────────────────────────────────────────
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Raw Mongoose plugin factory for direct schema.plugin() usage.
|
|
204
|
+
* Equivalent to mongofire.plugin(name, opts) but requires `collection` in options.
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* import mongofirePlugin from 'mongofire/plugin';
|
|
208
|
+
* userSchema.plugin(mongofirePlugin, { collection: 'users', ownerField: 'userId' });
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* // Or use the factory helper (matches instance.plugin() return shape):
|
|
212
|
+
* import { factory } from 'mongofire/plugin';
|
|
213
|
+
* userSchema.plugin(factory('users', { ownerField: 'userId' }));
|
|
214
|
+
*/
|
|
215
|
+
export declare function mongofirePlugin(schema: import('mongoose').Schema, options?: PluginOptions & { collection?: string }): void;
|
|
216
|
+
export declare namespace mongofirePlugin {
|
|
217
|
+
function factory(collectionName: string, options?: PluginOptions): (schema: import('mongoose').Schema) => void;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ─── Default export ───────────────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
/** The default MongoFire singleton instance. */
|
|
223
|
+
declare const mongofire: MongoFire & { MongoFire: typeof MongoFire };
|
|
224
|
+
export default mongofire;
|
package/index.cjs
ADDED
package/index.mjs
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mongofire",
|
|
3
|
+
"version": "6.5.1",
|
|
4
|
+
"description": "Offline-first MongoDB sync — Local + Atlas feel like ONE database. Automatic conflict resolution, Mongoose plugin, zero boilerplate.",
|
|
5
|
+
"main": "./index.cjs",
|
|
6
|
+
"types": "./dist/types/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./index.mjs",
|
|
10
|
+
"require": "./index.cjs",
|
|
11
|
+
"types": "./dist/types/index.d.ts",
|
|
12
|
+
"default": "./index.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./plugin": {
|
|
15
|
+
"require": "./dist/src/plugin.js",
|
|
16
|
+
"types": "./dist/types/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"mongofire": "./dist/bin/mongofire.cjs"
|
|
21
|
+
},
|
|
22
|
+
"scripts": {
|
|
23
|
+
"build": "node scripts/build.js",
|
|
24
|
+
"prepublishOnly": "npm run build",
|
|
25
|
+
"test": "node test/smoke.test.js",
|
|
26
|
+
"verify": "npm pack --dry-run",
|
|
27
|
+
"release:patch": "npm version patch && npm publish --access public",
|
|
28
|
+
"release:minor": "npm version minor && npm publish --access public",
|
|
29
|
+
"release:major": "npm version major && npm publish --access public"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/",
|
|
33
|
+
"index.cjs",
|
|
34
|
+
"index.mjs",
|
|
35
|
+
"README.md",
|
|
36
|
+
"CHANGELOG.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"keywords": [
|
|
40
|
+
"mongodb",
|
|
41
|
+
"mongoose",
|
|
42
|
+
"sync",
|
|
43
|
+
"atlas",
|
|
44
|
+
"offline-first",
|
|
45
|
+
"local-first",
|
|
46
|
+
"conflict-resolution",
|
|
47
|
+
"replication",
|
|
48
|
+
"offline",
|
|
49
|
+
"sync-engine",
|
|
50
|
+
"realtime",
|
|
51
|
+
"change-streams",
|
|
52
|
+
"typescript"
|
|
53
|
+
],
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18.0.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"dotenv": "^16.0.0"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"javascript-obfuscator": "^4.1.1"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"mongodb": ">=4.0.0",
|
|
66
|
+
"mongoose": ">=7.0.0"
|
|
67
|
+
},
|
|
68
|
+
"peerDependenciesMeta": {
|
|
69
|
+
"mongoose": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"repository": {
|
|
74
|
+
"type": "git",
|
|
75
|
+
"url": "https://github.com/YOURNAME/mongofire.git"
|
|
76
|
+
},
|
|
77
|
+
"homepage": "https://github.com/YOURNAME/mongofire#readme",
|
|
78
|
+
"bugs": {
|
|
79
|
+
"url": "https://github.com/YOURNAME/mongofire/issues"
|
|
80
|
+
}
|
|
81
|
+
}
|