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.
@@ -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
@@ -0,0 +1,4 @@
1
+ 'use strict';
2
+ // Entry point → obfuscated dist/ (built by: npm run build)
3
+ // Never edit this file — edit src/index.cjs instead
4
+ module.exports = require('./dist/src/index.cjs');
package/index.mjs ADDED
@@ -0,0 +1,6 @@
1
+ import { createRequire } from 'module';
2
+ const require = createRequire(import.meta.url);
3
+ // Entry point → obfuscated dist/ (built by: npm run build)
4
+ const mf = require('./dist/src/index.cjs');
5
+ export default mf;
6
+ export const MongoFire = mf.MongoFire;
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
+ }