mongofire 6.5.3 → 6.5.6
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 +35 -67
- package/README.md +338 -155
- package/bin/mongofire.cjs +1039 -654
- package/dist/bin/mongofire.cjs +1 -851
- package/dist/src/changetrack.js +1 -1
- package/dist/src/compactor.js +1 -0
- package/dist/src/connection.js +1 -1
- package/dist/src/device.js +1 -1
- package/dist/src/diff.js +1 -0
- package/dist/src/field-merge.js +1 -0
- package/dist/src/index.cjs +1 -1
- package/dist/src/local-manager.js +1 -0
- package/dist/src/plugin.js +1 -1
- package/dist/src/plugin.mjs +6 -0
- package/dist/src/rate-limiter.js +1 -0
- package/dist/src/reconcile.js +1 -1
- package/dist/src/schema-manager.js +1 -0
- package/dist/src/state.js +1 -1
- package/dist/src/sync.js +1 -1
- package/dist/src/utils.js +1 -1
- package/dist/types/index.d.ts +247 -281
- package/index.cjs +17 -3
- package/index.mjs +7 -2
- package/package.json +18 -16
- package/types/index.d.ts +317 -0
package/index.mjs
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
2
|
const require = createRequire(import.meta.url);
|
|
3
|
-
//
|
|
4
|
-
|
|
3
|
+
// Fall back to src/ if dist/ has not been built yet (same as index.cjs).
|
|
4
|
+
let mf;
|
|
5
|
+
try {
|
|
6
|
+
mf = require('./dist/src/index.cjs');
|
|
7
|
+
} catch (_) {
|
|
8
|
+
mf = require('./src/index.cjs');
|
|
9
|
+
}
|
|
5
10
|
export default mf;
|
|
6
11
|
export const MongoFire = mf.MongoFire;
|
package/package.json
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mongofire",
|
|
3
|
-
"version": "6.5.
|
|
3
|
+
"version": "6.5.6",
|
|
4
4
|
"description": "Offline-first MongoDB sync — Local + Atlas feel like ONE database. Automatic conflict resolution, Mongoose plugin, interactive CLI, zero boilerplate.",
|
|
5
5
|
"main": "./index.cjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": {
|
|
9
|
+
"types": "./types/index.d.ts",
|
|
9
10
|
"import": "./index.mjs",
|
|
10
11
|
"require": "./index.cjs",
|
|
11
|
-
"types": "./dist/types/index.d.ts",
|
|
12
12
|
"default": "./index.cjs"
|
|
13
13
|
},
|
|
14
14
|
"./plugin": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"types": "./types/index.d.ts",
|
|
16
|
+
"import": "./dist/src/plugin.mjs",
|
|
17
|
+
"require": "./dist/src/plugin.js"
|
|
17
18
|
}
|
|
18
19
|
},
|
|
19
20
|
"bin": {
|
|
@@ -24,15 +25,16 @@
|
|
|
24
25
|
"prepublishOnly": "npm run build",
|
|
25
26
|
"test": "node test/smoke.test.js",
|
|
26
27
|
"verify": "npm pack --dry-run",
|
|
27
|
-
"
|
|
28
|
-
"release:
|
|
29
|
-
"release:
|
|
28
|
+
"postpublish": "bash scripts/sync-public.sh",
|
|
29
|
+
"release:patch": "npm version patch --no-git-tag-version && npm publish --access public",
|
|
30
|
+
"release:minor": "npm version minor --no-git-tag-version && npm publish --access public",
|
|
31
|
+
"release:major": "npm version major --no-git-tag-version && npm publish --access public"
|
|
30
32
|
},
|
|
31
33
|
"files": [
|
|
32
34
|
"dist/",
|
|
33
|
-
"bin/",
|
|
34
35
|
"index.cjs",
|
|
35
36
|
"index.mjs",
|
|
37
|
+
"types/",
|
|
36
38
|
"README.md",
|
|
37
39
|
"CHANGELOG.md",
|
|
38
40
|
"LICENSE"
|
|
@@ -40,14 +42,14 @@
|
|
|
40
42
|
"keywords": [
|
|
41
43
|
"mongodb",
|
|
42
44
|
"mongoose",
|
|
43
|
-
"sync",
|
|
44
45
|
"atlas",
|
|
46
|
+
"sync",
|
|
45
47
|
"offline-first",
|
|
46
48
|
"local-first",
|
|
47
|
-
"conflict-resolution",
|
|
48
|
-
"replication",
|
|
49
49
|
"offline",
|
|
50
50
|
"sync-engine",
|
|
51
|
+
"replication",
|
|
52
|
+
"conflict-resolution",
|
|
51
53
|
"realtime",
|
|
52
54
|
"change-streams",
|
|
53
55
|
"typescript"
|
|
@@ -57,16 +59,16 @@
|
|
|
57
59
|
"node": ">=18.0.0"
|
|
58
60
|
},
|
|
59
61
|
"dependencies": {
|
|
60
|
-
"
|
|
61
|
-
"inquirer": "^8.0.0"
|
|
62
|
+
"@clack/prompts": "^1.1.0"
|
|
62
63
|
},
|
|
63
64
|
"devDependencies": {
|
|
64
|
-
"javascript-obfuscator": "^4.1.1"
|
|
65
|
+
"javascript-obfuscator": "^4.1.1",
|
|
66
|
+
"terser": "^5.27.0"
|
|
65
67
|
},
|
|
66
68
|
"peerDependencies": {
|
|
69
|
+
"dotenv": ">=16.0.0",
|
|
67
70
|
"mongodb": ">=4.0.0",
|
|
68
|
-
"mongoose": ">=7.0.0"
|
|
69
|
-
"dotenv": ">=16.0.0"
|
|
71
|
+
"mongoose": ">=7.0.0"
|
|
70
72
|
},
|
|
71
73
|
"peerDependenciesMeta": {
|
|
72
74
|
"mongoose": {
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { EventEmitter } from 'events';
|
|
2
|
+
|
|
3
|
+
// ─── Utility types ────────────────────────────────────────────────────────────
|
|
4
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
5
|
+
type Timestamp = Date | number;
|
|
6
|
+
type DocId = string;
|
|
7
|
+
type CircuitBreakerState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
8
|
+
|
|
9
|
+
// ─── JSON Patch / Diff ───────────────────────────────────────────────────────
|
|
10
|
+
export interface JsonPatchOp { op: 'add'|'remove'|'replace'|'move'|'copy'|'test'; path: string; value?: unknown; from?: string; }
|
|
11
|
+
export interface PatchPayload { _mf_patch: JsonPatchOp[]; _mf_full: Record<string,unknown>; _mf_fieldCount: number; _mf_changedFields: string[]; _mf_patchSize: number; _mf_fullSize: number; _mf_field_meta?: FieldMeta; }
|
|
12
|
+
export interface DiffResult { patch: JsonPatchOp[]; fieldCount: number; changedFields: string[]; }
|
|
13
|
+
export interface CompressResult { data: unknown; compressed: boolean; encoding?: 'gzip+base64'; originalSize: number; compressedSize: number; }
|
|
14
|
+
export declare function computeDiff(original: Record<string,unknown>, updated: Record<string,unknown>): DiffResult | null;
|
|
15
|
+
export declare function buildUpdatePayload(doc: Record<string,unknown>, diffResult: DiffResult | null): Record<string,unknown> | PatchPayload;
|
|
16
|
+
export declare function applyPatch(baseDoc: Record<string,unknown> | null, payload: Record<string,unknown> | PatchPayload): Record<string,unknown>;
|
|
17
|
+
export declare function isPatchPayload(payload: unknown): payload is PatchPayload;
|
|
18
|
+
export declare function compressPayload(payload: unknown): Promise<CompressResult>;
|
|
19
|
+
export declare function decompressPayload(data: string | unknown, compressed: boolean): Promise<unknown>;
|
|
20
|
+
|
|
21
|
+
// ─── Field-level Merge ────────────────────────────────────────────────────────
|
|
22
|
+
export interface FieldMetaEntry { ts: number; deviceId: string; version: number; }
|
|
23
|
+
export type FieldMeta = Record<string, FieldMetaEntry>;
|
|
24
|
+
export interface MergeResult { merged: Record<string,unknown>; resolvedFields: Record<string,'local'|'remote'>; conflictedFields: Record<string,{local:unknown;remote:unknown;winner:'local'|'remote'}>; }
|
|
25
|
+
export declare function buildFieldMeta(patch: object[], timestamp: Timestamp, deviceId: string, version: number): FieldMeta;
|
|
26
|
+
export declare function mergeFields(baseDoc: Record<string,unknown>, localMeta: {_mf_field_meta?: FieldMeta}, remotePayload: Record<string,unknown>, remoteMeta: {timestamp:Timestamp;deviceId:string;version:number;field_meta?:FieldMeta}): MergeResult;
|
|
27
|
+
export declare function hasFieldOverlap(opA: {payload?:{_mf_field_meta?:FieldMeta;_mf_patch?:object[]}}, opB: {payload?:{_mf_field_meta?:FieldMeta;_mf_patch?:object[]}}): boolean;
|
|
28
|
+
export declare function mergeScore(mergeResult: Pick<MergeResult,'resolvedFields'|'conflictedFields'>): number;
|
|
29
|
+
|
|
30
|
+
// ─── Schema Manager ───────────────────────────────────────────────────────────
|
|
31
|
+
export type MigrationFn<T=Record<string,unknown>> = (doc: T) => MaybePromise<T>;
|
|
32
|
+
export type ValidatorFn<T=Record<string,unknown>> = (doc: T) => string[];
|
|
33
|
+
export interface ValidationResult { valid: boolean; errors: string[]; }
|
|
34
|
+
export interface SchemaRegistryEntry { collection: string; currentVersion: number; versions: number[]; updatedAt: Date; }
|
|
35
|
+
export declare class SchemaManager {
|
|
36
|
+
register<T=Record<string,unknown>>(collection: string, version: number, migrateFn: MigrationFn<T>): void;
|
|
37
|
+
registerValidator<T=Record<string,unknown>>(collection: string, version: number, validateFn: ValidatorFn<T>): void;
|
|
38
|
+
currentVersion(collection: string): number;
|
|
39
|
+
needsMigration(collection: string, doc: Record<string,unknown>): boolean;
|
|
40
|
+
migrate<T=Record<string,unknown>>(collection: string, doc: T): Promise<T & {_mf_schemaVersion:number}>;
|
|
41
|
+
migrateBatch<T=Record<string,unknown>>(collection: string, docs: T[], concurrency?: number): Promise<Array<T & {_mf_schemaVersion:number}>>;
|
|
42
|
+
validate(collection: string, doc: Record<string,unknown>): ValidationResult;
|
|
43
|
+
persistRegistry(conn?: unknown): Promise<void>;
|
|
44
|
+
getRegistry(conn?: unknown): Promise<SchemaRegistryEntry[]>;
|
|
45
|
+
list(): Array<{collection:string;currentVersion:number;versions:number[]}>;
|
|
46
|
+
}
|
|
47
|
+
export declare const schemaManager: SchemaManager;
|
|
48
|
+
|
|
49
|
+
// ─── Rate Limiter ─────────────────────────────────────────────────────────────
|
|
50
|
+
export interface TokenBucketOptions { capacity?: number; refillRate?: number; initialFill?: number; }
|
|
51
|
+
export interface CircuitBreakerOptions { failureThreshold?: number; successThreshold?: number; timeout?: number; halfOpenMax?: number; name?: string; }
|
|
52
|
+
export interface SyncRateLimiterOptions { tokenBucket?: TokenBucketOptions; circuitBreaker?: CircuitBreakerOptions; }
|
|
53
|
+
export interface CircuitBreakerStats { name: string; state: CircuitBreakerState; failureCount: number; successCount: number; lastFailureAt: number|null; }
|
|
54
|
+
export interface TokenBucketStats { available: number; capacity: number; refillRate: number; throttleCount: number; }
|
|
55
|
+
export interface SyncRateLimiterStats { tokenBucket: TokenBucketStats; circuitBreaker: CircuitBreakerStats; }
|
|
56
|
+
export declare class TokenBucket {
|
|
57
|
+
constructor(opts?: TokenBucketOptions);
|
|
58
|
+
consume(count?: number): boolean;
|
|
59
|
+
waitAndConsume(count?: number, timeoutMs?: number): Promise<boolean>;
|
|
60
|
+
waitTimeMs(count?: number): number;
|
|
61
|
+
readonly available: number;
|
|
62
|
+
}
|
|
63
|
+
export declare class CircuitBreaker {
|
|
64
|
+
constructor(opts?: CircuitBreakerOptions);
|
|
65
|
+
execute<T>(fn: ()=>Promise<T>): Promise<T>;
|
|
66
|
+
reset(): void;
|
|
67
|
+
on(event:'open'|'close'|'halfOpen', listener:(d:{prev:CircuitBreakerState;current:CircuitBreakerState})=>void): this;
|
|
68
|
+
stats(): CircuitBreakerStats;
|
|
69
|
+
readonly state: CircuitBreakerState;
|
|
70
|
+
readonly isOpen: boolean;
|
|
71
|
+
readonly isClosed: boolean;
|
|
72
|
+
}
|
|
73
|
+
export declare class CircuitOpenError extends Error { readonly circuitData: {state:CircuitBreakerState;retryAfterMs:number}; readonly retryAfterMs: number; }
|
|
74
|
+
export declare class SyncRateLimiter {
|
|
75
|
+
constructor(opts?: SyncRateLimiterOptions);
|
|
76
|
+
execute<T>(fn:()=>Promise<T>, tokenCost?: number, timeoutMs?: number): Promise<T>;
|
|
77
|
+
isBlocked(): boolean;
|
|
78
|
+
recordSuccess(): void;
|
|
79
|
+
recordFailure(err?: Error): void;
|
|
80
|
+
stats(): SyncRateLimiterStats;
|
|
81
|
+
reset(): void;
|
|
82
|
+
readonly circuitBreaker: CircuitBreaker;
|
|
83
|
+
readonly tokenBucket: TokenBucket;
|
|
84
|
+
}
|
|
85
|
+
export declare class RateLimitError extends Error { readonly rateLimitData: {availableIn: number}; }
|
|
86
|
+
export declare function backoffDelay(attempt: number, baseDelayMs?: number, maxDelayMs?: number, strategy?: 'full'|'decorrelated'|'exponential'): number;
|
|
87
|
+
|
|
88
|
+
// ─── Log Compactor ────────────────────────────────────────────────────────────
|
|
89
|
+
export interface CompactionOptions { conn?: unknown; retentionDays?: number; batchSize?: number; collection?: string; verbose?: boolean; }
|
|
90
|
+
export interface CompactionResult { scanned: number; deleted: number; kept: number; durationMs: number; }
|
|
91
|
+
export interface CompactionStats { totalSynced: number; oldSynced: number; retentionDays: number; cutoffDate: Date; collections: Array<{collection:string;count:number}>; estimatedDeletable: number; }
|
|
92
|
+
export declare function compactChangelog(opts?: CompactionOptions): Promise<CompactionResult>;
|
|
93
|
+
export declare function getCompactionStats(opts?: Omit<CompactionOptions,'batchSize'>): Promise<CompactionStats>;
|
|
94
|
+
|
|
95
|
+
// ─── Configuration ────────────────────────────────────────────────────────────
|
|
96
|
+
export interface SyncConfig {
|
|
97
|
+
localUri?: string; atlasUri?: string; dbName?: string;
|
|
98
|
+
collections: string[];
|
|
99
|
+
syncInterval?: number; batchSize?: number;
|
|
100
|
+
syncOwner?: string | (()=>string);
|
|
101
|
+
realtime?: boolean;
|
|
102
|
+
onSync?: (result: SyncResult) => void;
|
|
103
|
+
onError?: (err: Error) => void;
|
|
104
|
+
cleanDays?: number;
|
|
105
|
+
reconcileOnStart?: boolean; reconcileFullScan?: boolean;
|
|
106
|
+
rateLimiter?: SyncRateLimiterOptions;
|
|
107
|
+
schemaManager?: SchemaManager;
|
|
108
|
+
/** Enable per-field LWW merge. Firebase can't do this. Default: true */
|
|
109
|
+
fieldLevelMerge?: boolean;
|
|
110
|
+
autoCompact?: boolean; compactEvery?: number; compactRetentionDays?: number;
|
|
111
|
+
compression?: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ─── Results & Status ─────────────────────────────────────────────────────────
|
|
115
|
+
export interface SyncResult { downloaded: number; uploaded: number; deleted: number; failed: number; conflicts: number; retried: number; skipped: number; fieldMerged?: number; }
|
|
116
|
+
export interface SyncStatus { online: boolean; pending: number; creates: number; updates: number; deletes: number; realtime: boolean; circuitBreaker?: CircuitBreakerState; rateLimiter?: SyncRateLimiterStats; }
|
|
117
|
+
export interface OfflineResult { error: 'offline'; pending: number; }
|
|
118
|
+
export interface ConflictRecord { _id: unknown; opId: string; type: 'create'|'update'|'delete'; collection: string; docId: DocId; ownerKey: string; baseVersion: number; version: number; timestamp: Date; lastError?: string; retryCount: number; mergeAttempted?: boolean; }
|
|
119
|
+
export interface ReconcileCollectionResult { collection: string; phase1: {scanned:number;queued:number}; phase2: {scanned:number;queued:number}; totalQueued: number; replicaSet: boolean; error?: string; }
|
|
120
|
+
|
|
121
|
+
// ─── Plugin ───────────────────────────────────────────────────────────────────
|
|
122
|
+
export interface PluginOptions { ownerField?: string; batchSize?: number; concurrency?: number; diffEnabled?: boolean; }
|
|
123
|
+
|
|
124
|
+
// ─── Events ───────────────────────────────────────────────────────────────────
|
|
125
|
+
export interface ConflictData { collection: string; docId: DocId; opId: string; localVersion: number; remoteVersion: number; op: 'create'|'update'|'delete'; fieldMerge?: {attempted:boolean;resolvedFields:Record<string,'local'|'remote'>;conflictedFields:Record<string,{local:unknown;remote:unknown;winner:'local'|'remote'}>;score:number}; }
|
|
126
|
+
export interface MongoFireEvents {
|
|
127
|
+
/**
|
|
128
|
+
* Fires as soon as the local MongoDB connection is live — BEFORE Atlas
|
|
129
|
+
* connect, reconcile, or the sync timers start. Use this to gate
|
|
130
|
+
* `app.listen()` so the server never accepts requests before the DB is ready.
|
|
131
|
+
*/
|
|
132
|
+
localReady: [db: import('mongodb').Db];
|
|
133
|
+
ready: []; sync: [result: SyncResult]; online: []; offline: [];
|
|
134
|
+
realtimeStarted: []; realtimeStopped: []; stopped: []; error: [err: Error];
|
|
135
|
+
conflict: [data: ConflictData];
|
|
136
|
+
fieldMerged: [data: {collection:string;docId:DocId;opId:string;resolvedFields:Record<string,'local'|'remote'>;score:number}];
|
|
137
|
+
reconcileComplete: [result: {collections:ReconcileCollectionResult[];totalQueued:number}];
|
|
138
|
+
conflictResolved: [data: {opId:string;resolution:'retried'|'dismissed'}];
|
|
139
|
+
compacted: [result: CompactionResult];
|
|
140
|
+
circuitOpen: [data: {retryAfterMs:number}]; circuitClose: [];
|
|
141
|
+
migrated: [data: {collection:string;docId:DocId;fromVersion:number;toVersion:number}];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ─── Internal document structure ──────────────────────────────────────────────
|
|
145
|
+
export interface MfDocFields { _mf_version: number; _mf_lastWriterDeviceId: string; _mf_opId: string; _mf_lastOpId: string; _mf_schemaVersion?: number; }
|
|
146
|
+
export interface ChangetrackRecord extends MfDocFields { _id: unknown; opId: string; type: 'create'|'update'|'delete'; collection: string; docId: DocId; payload: Record<string,unknown>|PatchPayload|null; ownerKey: string; deviceId: string; timestamp: Date; updatedAt: Date; baseVersion: number; version: number; lastWriterDeviceId: string; payloadChecksum: string|null; synced: boolean; ackState: 'pending'|'acked'|'verified'|'conflict'|'error'|'compressed'|'superseded'|'dismissed'; retryCount: number; createdAt: Date; syncedAt: Date|null; remoteAckAt: Date|null; checksumVerifiedAt: Date|null; lastError: string|null; applyStatus?: 'pending'|'applied'|'conflict'; }
|
|
147
|
+
export interface DocMetaRecord { _id: string; collection: string; docId: DocId; version: number; updatedAt: Date; lastWriterDeviceId: string; deleted: boolean; ownerKey: string; updatedByOpId: string|null; lastOpId: string|null; payloadChecksum: string|null; createdAt: Date; }
|
|
148
|
+
|
|
149
|
+
// ─── LocalManager ─────────────────────────────────────────────────────────────
|
|
150
|
+
/**
|
|
151
|
+
* Auto-manages the Mongoose connection to local MongoDB.
|
|
152
|
+
* Spawns `mongod` via the system binary if MongoDB is not running,
|
|
153
|
+
* and retries until the connection succeeds.
|
|
154
|
+
*
|
|
155
|
+
* **Developers should never call `mongoose.connect()` directly.**
|
|
156
|
+
* MongoFire calls `ensureConnected()` internally during `start()`.
|
|
157
|
+
*/
|
|
158
|
+
export declare class LocalManager {
|
|
159
|
+
/**
|
|
160
|
+
* Ensure Mongoose is connected to local MongoDB.
|
|
161
|
+
* Idempotent — returns immediately if already connected.
|
|
162
|
+
* Uses `process.env.LOCAL_URI` / `process.env.DB_NAME`, falling back to
|
|
163
|
+
* the supplied values when the env vars are absent.
|
|
164
|
+
*/
|
|
165
|
+
ensureConnected(fallbackUri?: string, fallbackDbName?: string): Promise<void>;
|
|
166
|
+
/**
|
|
167
|
+
* Disconnect Mongoose and stop the spawned `mongod` process (if any).
|
|
168
|
+
* Called automatically by `mongofire.stop()`.
|
|
169
|
+
*/
|
|
170
|
+
closeAll(): Promise<void>;
|
|
171
|
+
readonly isConnected: boolean;
|
|
172
|
+
readonly mongodProcess: import('child_process').ChildProcess | null;
|
|
173
|
+
}
|
|
174
|
+
/** Singleton LocalManager shared across all MongoFire instances in the process. */
|
|
175
|
+
export declare const localManager: LocalManager;
|
|
176
|
+
|
|
177
|
+
// ─── Main class ───────────────────────────────────────────────────────────────
|
|
178
|
+
export declare class MongoFire extends EventEmitter {
|
|
179
|
+
/**
|
|
180
|
+
* Start MongoFire with the given config. Connects to local MongoDB first,
|
|
181
|
+
* then Atlas. The returned Promise resolves when Atlas is connected and the
|
|
182
|
+
* initial sync is complete (or when Atlas is determined to be offline).
|
|
183
|
+
*
|
|
184
|
+
* **Do not call `mongoose.connect()` manually** — MongoFire manages this.
|
|
185
|
+
* Use `localReady` / `waitForLocal()` to gate `app.listen()` instead.
|
|
186
|
+
*/
|
|
187
|
+
start(config: SyncConfig): Promise<this>;
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Returns a Promise that resolves with the native `Db` handle as soon as the
|
|
191
|
+
* LOCAL MongoDB connection is ready — before Atlas connect, reconcile, or
|
|
192
|
+
* the sync timer start.
|
|
193
|
+
*
|
|
194
|
+
* Use this to gate `app.listen()` so incoming requests never arrive before
|
|
195
|
+
* the local database is available:
|
|
196
|
+
*
|
|
197
|
+
* ```js
|
|
198
|
+
* const { localReady } = require('./mongofire');
|
|
199
|
+
* localReady.then(() => app.listen(3000));
|
|
200
|
+
* // or: await mongofire.waitForLocal(); app.listen(3000);
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* If `start()` has not been called yet (and no `mongofire.config.js` was
|
|
204
|
+
* found for auto-start), this Promise rejects after 60 s with a clear
|
|
205
|
+
* "did you forget to call start()?" message.
|
|
206
|
+
*/
|
|
207
|
+
waitForLocal(): Promise<import('mongodb').Db>;
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Convenience alias for `waitForLocal()` — a Promise getter so you can
|
|
211
|
+
* destructure it at module level without calling a function:
|
|
212
|
+
*
|
|
213
|
+
* ```js
|
|
214
|
+
* const { localReady } = require('./mongofire');
|
|
215
|
+
* localReady.then(() => app.listen(3000));
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
readonly localReady: Promise<import('mongodb').Db>;
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Automatically loads `mongofire.config.js` (or `.cjs` / `.mjs`) from
|
|
222
|
+
* `process.cwd()` and calls `start(config)`. Called internally when the
|
|
223
|
+
* package is required outside the CLI context so that users do not need a
|
|
224
|
+
* separate `mongofire.js` entry file.
|
|
225
|
+
*
|
|
226
|
+
* Safe to call manually; `start()` is idempotent.
|
|
227
|
+
*/
|
|
228
|
+
autoStart(): Promise<this>;
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* **Primary developer API** — replaces `app.listen()`.
|
|
232
|
+
*
|
|
233
|
+
* Waits for local MongoDB to be ready, then starts the Express (or any
|
|
234
|
+
* Node `http.Server`) application on the given port. If the local DB
|
|
235
|
+
* fails to connect, logs a descriptive error and calls `process.exit(1)`
|
|
236
|
+
* so the server never starts in a broken state.
|
|
237
|
+
*
|
|
238
|
+
* ```js
|
|
239
|
+
* // server.js
|
|
240
|
+
* import { startApp } from './mongofire.js';
|
|
241
|
+
* startApp(app, process.env.PORT || 3000);
|
|
242
|
+
* ```
|
|
243
|
+
*
|
|
244
|
+
* @param app Any object with a `.listen(port, callback)` method (Express, Fastify, etc.)
|
|
245
|
+
* @param port TCP port to listen on (default: 3000)
|
|
246
|
+
* @returns Promise resolving to the `http.Server` returned by `app.listen()`
|
|
247
|
+
*/
|
|
248
|
+
startApp(app: { listen: (port: number, cb?: (err?: Error) => void) => import('http').Server }, port?: number | string): Promise<import('http').Server>;
|
|
249
|
+
|
|
250
|
+
stop(timeoutMs?: number): Promise<void>;
|
|
251
|
+
sync(type?: 'required'|'all'): Promise<SyncResult|OfflineResult>;
|
|
252
|
+
status(): Promise<SyncStatus>;
|
|
253
|
+
clean(days?: number, opts?: {conflictDays?:number}): Promise<number>;
|
|
254
|
+
conflicts(collection?: string): Promise<ConflictRecord[]>;
|
|
255
|
+
retryConflict(opId: string): Promise<void>;
|
|
256
|
+
dismissConflict(opId: string): Promise<void>;
|
|
257
|
+
/**
|
|
258
|
+
* Drops all MongoFire internal collections and the configured data
|
|
259
|
+
* collections so the next `start()` performs a clean bootstrap from Atlas.
|
|
260
|
+
* **Any unsynced local changes are permanently lost.**
|
|
261
|
+
*/
|
|
262
|
+
resetLocal(): Promise<{ dropped: number }>;
|
|
263
|
+
plugin(collectionName: string, options?: PluginOptions): (schema: import('mongoose').Schema) => void;
|
|
264
|
+
startRealtime(): Promise<boolean>;
|
|
265
|
+
stopRealtime(): Promise<void>;
|
|
266
|
+
reconcile(collectionOrOpts?: string|string[]|{fullScan?:boolean;verbose?:boolean}, opts?: {fullScan?:boolean;verbose?:boolean}): Promise<ReconcileCollectionResult[]>;
|
|
267
|
+
compact(opts?: CompactionOptions): Promise<CompactionResult>;
|
|
268
|
+
rateLimiterStats(): SyncRateLimiterStats | null;
|
|
269
|
+
resetCircuit(): void;
|
|
270
|
+
readonly online: boolean;
|
|
271
|
+
readonly started: boolean;
|
|
272
|
+
readonly syncing: boolean;
|
|
273
|
+
readonly realtimeActive: boolean;
|
|
274
|
+
readonly schemaManager: SchemaManager | null;
|
|
275
|
+
readonly rateLimiter: SyncRateLimiter | null;
|
|
276
|
+
on<K extends keyof MongoFireEvents>(event: K, listener: (...args: MongoFireEvents[K]) => void): this;
|
|
277
|
+
once<K extends keyof MongoFireEvents>(event: K, listener: (...args: MongoFireEvents[K]) => void): this;
|
|
278
|
+
emit<K extends keyof MongoFireEvents>(event: K, ...args: MongoFireEvents[K]): boolean;
|
|
279
|
+
off<K extends keyof MongoFireEvents>(event: K, listener: (...args: MongoFireEvents[K]) => void): this;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// ─── Direct plugin import ─────────────────────────────────────────────────────
|
|
283
|
+
export declare function mongofirePlugin(schema: import('mongoose').Schema, options?: PluginOptions & {collection?: string}): void;
|
|
284
|
+
export declare namespace mongofirePlugin { function factory(collectionName: string, options?: PluginOptions): (schema: import('mongoose').Schema) => void; }
|
|
285
|
+
|
|
286
|
+
// ─── Standalone named exports (destructurable from the package / wrapper) ─────
|
|
287
|
+
/**
|
|
288
|
+
* Starts the Express app after waiting for local MongoDB.
|
|
289
|
+
* Exits the process if the DB is unavailable.
|
|
290
|
+
*
|
|
291
|
+
* ```ts
|
|
292
|
+
* import { startApp } from './mongofire.js';
|
|
293
|
+
* startApp(app, 3000);
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
export declare function startApp(
|
|
297
|
+
app: { listen: (port: number, cb?: (err?: Error) => void) => import('http').Server },
|
|
298
|
+
port?: number | string
|
|
299
|
+
): Promise<import('http').Server>;
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Attaches MongoFire change-tracking to a Mongoose schema.
|
|
303
|
+
*
|
|
304
|
+
* ```ts
|
|
305
|
+
* import { plugin } from './mongofire.js';
|
|
306
|
+
* UserSchema.plugin(plugin('users'));
|
|
307
|
+
* StudentSchema.plugin(plugin('students', { ownerField: 'userId' }));
|
|
308
|
+
* ```
|
|
309
|
+
*/
|
|
310
|
+
export declare function plugin(
|
|
311
|
+
collectionName: string,
|
|
312
|
+
options?: PluginOptions
|
|
313
|
+
): (schema: import('mongoose').Schema) => void;
|
|
314
|
+
|
|
315
|
+
// ─── Default export ───────────────────────────────────────────────────────────
|
|
316
|
+
declare const mongofire: MongoFire & { MongoFire: typeof MongoFire; startApp: typeof startApp; plugin: typeof plugin; };
|
|
317
|
+
export default mongofire;
|