edmaxlabs-core 1.2.4 → 1.3.5

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/dist/index.d.cts CHANGED
@@ -1,7 +1,3 @@
1
- import * as idb from 'idb';
2
- import * as _socket_io_component_emitter from '@socket.io/component-emitter';
3
- import { Socket } from 'socket.io-client';
4
-
5
1
  interface TimestampData {
6
2
  seconds: number;
7
3
  nanoseconds: number;
@@ -57,20 +53,28 @@ declare class Credentials {
57
53
  toMap(): Record<string, any>;
58
54
  }
59
55
 
56
+ type Listener<T = any> = (value: T) => void;
60
57
  declare class Authentication {
61
58
  static instance: Authentication | null;
62
59
  private eUser?;
63
- private client;
64
- private app;
60
+ private client?;
61
+ private app?;
62
+ private eventListeners;
63
+ private eventWaiters;
64
+ private unsubscribers;
65
65
  constructor();
66
- private init;
66
+ private isSameCredentials;
67
+ private restoreSession;
68
+ static getInstance(): Authentication;
69
+ emitValue<T>(key: string, value: T): void;
70
+ onValue<T>(key: string, callback: Listener<T>): () => void;
71
+ currentUser(): Credentials | null;
67
72
  private saveCredentials;
68
- currentUser: () => Credentials | undefined;
69
- authState: ({ onChange, onSignOut, onDeleted, }: {
70
- onChange: (data: Credentials) => void;
71
- onDeleted?: () => void;
73
+ authState({ onChange, onSignOut, onDeleted, }: {
74
+ onChange: (user: Credentials) => void;
72
75
  onSignOut?: () => void;
73
- }) => () => void;
76
+ onDeleted?: () => void;
77
+ }): () => void;
74
78
  createUserWithEmailAndPassword: ({ email, password, }: {
75
79
  email: string;
76
80
  password: string;
@@ -87,6 +91,14 @@ declare class Authentication {
87
91
  }>;
88
92
  }
89
93
 
94
+ declare class DocumentSnapshot {
95
+ id: string;
96
+ data: Record<string, any>;
97
+ private constructor();
98
+ static fromMap(map: Record<string, any>): DocumentSnapshot;
99
+ toMap(): Record<string, any>;
100
+ }
101
+
90
102
  declare class ArraySnapshot {
91
103
  id?: string;
92
104
  data: Record<string, any>;
@@ -97,12 +109,19 @@ declare class ArraySnapshot {
97
109
  }
98
110
 
99
111
  declare class Array {
100
- db: EdmaxLabs;
101
- collection: string;
102
- key: string;
103
- docID: string;
104
- constructor(db: EdmaxLabs, collection: string, key: string, id: string);
112
+ private app;
113
+ readonly collection: string;
114
+ readonly key: string;
115
+ readonly docID: string;
116
+ private persistence;
117
+ private syncEngine;
118
+ private localStore;
119
+ constructor(app: EdmaxLabs, collection: string, key: string, id: string);
120
+ /**
121
+ * Get current array elements (offline-first: prefers local cache)
122
+ */
105
123
  show(): Promise<ArraySnapshot[]>;
124
+ private refreshFromRemote;
106
125
  push(data: any, id?: string): Promise<ArraySnapshot | null>;
107
126
  update(position: number, data: any, id?: string): Promise<ArraySnapshot | null>;
108
127
  insert(position: number, data: any, id?: string): Promise<ArraySnapshot | null>;
@@ -110,36 +129,59 @@ declare class Array {
110
129
  remove(position: number): Promise<ArraySnapshot | null>;
111
130
  }
112
131
 
113
- declare class DocumentSnapshot {
114
- id: string;
115
- data: Record<string, any>;
116
- private constructor();
117
- static fromMap(map: Record<string, any>): DocumentSnapshot;
118
- toMap(): Record<string, any>;
119
- }
120
-
121
132
  declare class DocumentRef {
122
- db: EdmaxLabs;
123
- collection: string;
124
- id: string;
125
- constructor(db: EdmaxLabs, collection: string, id: string);
133
+ private app;
134
+ readonly collection: string;
135
+ readonly id: string;
136
+ private persistence;
137
+ private syncEngine;
138
+ private localStore;
139
+ constructor(app: EdmaxLabs, collection: string, id: string);
126
140
  get(): Promise<DocumentSnapshot | null>;
127
- set(data: any): Promise<DocumentSnapshot | null>;
128
- update(data: any): Promise<DocumentSnapshot | null>;
129
- delete(): Promise<DocumentSnapshot | null>;
141
+ private refreshFromRemote;
142
+ set(data: Record<string, any>): Promise<DocumentSnapshot | null>;
143
+ update(data: Record<string, any>): Promise<DocumentSnapshot | null>;
144
+ delete(): Promise<boolean>;
130
145
  array(key: string): Array;
131
- /**
132
- * Realtime listener for a single document
133
- */
134
- onSnapshot(callback: (snapshot: DocumentSnapshot, change: string) => void): () => void;
146
+ onSnapshot(callback: (snapshot: DocumentSnapshot | null, change: string) => void): () => void;
135
147
  }
136
148
 
137
- declare class CollectionRef$1 {
138
- db: EdmaxLabs;
139
- constructor(db: EdmaxLabs);
140
- getIDB: (collection: string) => Promise<idb.IDBPDatabase<unknown>>;
141
- syncPendingWrite(id: string, collection?: string): Promise<boolean | undefined>;
142
- syncPendingWrites(collection: string): Promise<void>;
149
+ type ChangeType = "init" | "local_create" | "local_update" | "local_delete" | "remote_create" | "remote_update" | "remote_delete" | "sync" | "error";
150
+ type DocListener = (snapshot: DocumentSnapshot | null, change: ChangeType) => void;
151
+ /**
152
+ * For collection listeners we pass:
153
+ * - snapshots: all current documents in the collection
154
+ * - change: what triggered this emission
155
+ * - changedDocId?: which specific document changed (useful for granular updates)
156
+ */
157
+ type CollectionListener = (snapshots: DocumentSnapshot[], change: ChangeType, changedDocId?: string) => void;
158
+ declare class LocalStore {
159
+ private documentListeners;
160
+ private collectionListeners;
161
+ private docKey;
162
+ subscribeToDocument(collection: string, id: string, callback: DocListener): () => void;
163
+ subscribeToCollection(collection: string, callback: CollectionListener): () => void;
164
+ /**
165
+ * Notify all listeners for a specific document
166
+ */
167
+ emitDocument(collection: string, id: string, snapshot: DocumentSnapshot | null, change: ChangeType): void;
168
+ /**
169
+ * Notify all listeners for a collection.
170
+ * This is the most important fix — collection listeners now receive the full list.
171
+ */
172
+ emitCollection(collection: string, snapshots: DocumentSnapshot[], // ← Changed from single snapshot
173
+ change: ChangeType, changedDocId?: string): void;
174
+ /**
175
+ * Clear all listeners (useful for testing or when persistence is disabled)
176
+ */
177
+ clearAllListeners(): void;
178
+ /**
179
+ * Get current listener count (for debugging / dev tools)
180
+ */
181
+ get listenerCount(): {
182
+ documents: number;
183
+ collections: number;
184
+ };
143
185
  }
144
186
 
145
187
  type FilterExpression = {
@@ -148,48 +190,53 @@ type FilterExpression = {
148
190
  value: any;
149
191
  };
150
192
  declare class Query {
151
- db: EdmaxLabs;
152
- collection: string;
193
+ private app;
194
+ private collection;
153
195
  private filter;
154
- constructor(db: EdmaxLabs, collection: string);
155
- where(expression: FilterExpression): this;
196
+ constructor(app: EdmaxLabs, collection: string);
197
+ where(expression: FilterExpression): Query;
156
198
  private buildFilter;
157
199
  get(): Promise<DocumentSnapshot[]>;
158
- update(): Promise<DocumentSnapshot[]>;
159
- onSnapshot(callback: (snapshot: DocumentSnapshot, change: string) => void): () => void;
200
+ private refreshFromRemote;
201
+ update(data: Record<string, any>): Promise<any>;
202
+ onSnapshot(callback: CollectionListener): () => void;
160
203
  }
161
204
 
162
205
  declare class CollectionRef {
163
- db: EdmaxLabs;
164
- collection: string;
165
- persistence: CollectionRef$1;
166
- constructor(db: EdmaxLabs, collection: string);
206
+ private app;
207
+ readonly collection: string;
208
+ private persistence;
209
+ private syncEngine;
210
+ private localStore;
211
+ constructor(app: EdmaxLabs, collection: string);
167
212
  doc(id: string): DocumentRef;
168
213
  get query(): Query;
169
214
  get(): Promise<DocumentSnapshot[]>;
170
- add(data: any): Promise<DocumentSnapshot | null>;
171
- /**
172
- * Realtime listener for entire collection
173
- */
174
- onSnapshot(callback: (snapshot: DocumentSnapshot, change: string) => void): () => void;
215
+ private refreshFromRemote;
216
+ add(data: Record<string, any>): Promise<DocumentSnapshot | null>;
217
+ onSnapshot(callback: CollectionListener): () => void;
175
218
  }
176
219
 
177
220
  declare class Batch {
178
- app: EdmaxLabs;
179
- ops: any[];
221
+ private app;
222
+ private ops;
180
223
  constructor(app: EdmaxLabs);
181
- set(docRef: DocumentRef, data: any): this;
182
- update(docRef: DocumentRef, data: any): this;
183
- delete(docRef: DocumentRef): this;
224
+ set(docRef: DocumentRef, data: any): Batch;
225
+ update(docRef: DocumentRef, data: any): Batch;
226
+ delete(docRef: DocumentRef): Batch;
184
227
  commit(): Promise<any>;
185
228
  }
186
229
 
187
230
  declare class Database {
188
- app: EdmaxLabs;
231
+ private app;
189
232
  constructor(app: EdmaxLabs);
190
233
  collection(name: string): CollectionRef;
191
234
  doc(path: string): DocumentRef;
192
235
  batch(): Batch;
236
+ /**
237
+ * Server-side transaction (unchanged, but improved error handling)
238
+ * This remains online-only for now — offline transactions are complex and can be added later as opt-in.
239
+ */
193
240
  runTransaction(transactionFn: (tx: {
194
241
  ops: any[];
195
242
  get: (docRef: DocumentRef) => Promise<any>;
@@ -197,25 +244,45 @@ declare class Database {
197
244
  update: (docRef: DocumentRef, data: any) => void;
198
245
  delete: (docRef: DocumentRef) => void;
199
246
  }) => Promise<void>): Promise<any>;
247
+ /**
248
+ * Returns true if persistence is enabled and we are currently offline
249
+ */
250
+ private get shouldUseOffline();
251
+ /**
252
+ * Returns the SyncEngine if persistence is enabled
253
+ */
254
+ private get syncEngine();
255
+ /**
256
+ * Returns the RealtimeBridge if persistence is enabled
257
+ */
258
+ private get realtimeBridge();
200
259
  }
201
260
 
202
261
  declare class Realtime {
203
- db: EdmaxLabs;
204
- socket: Socket | null;
205
- events: string[];
206
- constructor(db: EdmaxLabs);
207
- connect(): Socket<_socket_io_component_emitter.DefaultEventsMap, _socket_io_component_emitter.DefaultEventsMap>;
262
+ private app;
263
+ private socket;
264
+ private readonly events;
265
+ private subscriptions;
266
+ constructor(app: EdmaxLabs);
267
+ private connect;
268
+ private setupSocketListeners;
208
269
  on(event: string, cb: (...args: any[]) => void): void;
209
270
  off(event: string, cb?: (...args: any[]) => void): void;
210
271
  emit(event: string, data: any): void;
211
272
  /**
212
- * Realtime listener for entire collection
273
+ * Low-level collection subscription (used by RealtimeBridge)
213
274
  */
214
- subscribeToCollection(collection: string, callback: (snapshot: DocumentSnapshot, change: string) => void, filter?: Record<string, any>): () => void;
275
+ subscribeToCollectionRaw(collection: string, callback: (payload: any, change: string) => void, filter?: Record<string, any>): () => void;
215
276
  /**
216
- * Realtime listener for a single document
277
+ * Low-level document subscription (used by RealtimeBridge)
217
278
  */
218
- subscribeToDocument(collection: string, id: string, callback: (snapshot: DocumentSnapshot, change: string) => void): () => void;
279
+ subscribeToDocumentRaw(collection: string, id: string, callback: (payload: any, change: string) => void): () => void;
280
+ private normalizePayload;
281
+ private registerSubscription;
282
+ private resubscribeAll;
283
+ private cleanupSubscription;
284
+ disconnect(): void;
285
+ dispose(): void;
219
286
  }
220
287
 
221
288
  declare class Functions {
@@ -224,10 +291,108 @@ declare class Functions {
224
291
  call(functionName: string, data?: Record<string, any>): Promise<any>;
225
292
  }
226
293
 
227
- declare class Hosting {
228
- app: EdmaxLabs;
229
- constructor(app: EdmaxLabs);
230
- createSubdomain(name: string): Promise<any>;
294
+ type LocalDocStatus = "synced" | "pending" | "failed";
295
+ interface LocalDoc {
296
+ key: string;
297
+ collection: string;
298
+ id: string;
299
+ data: Record<string, any>;
300
+ exists: boolean;
301
+ pending: number;
302
+ status: LocalDocStatus;
303
+ localOnly: boolean;
304
+ deleted: boolean;
305
+ updatedAt: number;
306
+ lastSyncedAt?: number;
307
+ revision?: string | number;
308
+ }
309
+
310
+ type MutationType = "create" | "update" | "delete" | "array_push" | "array_update" | "array_insert" | "array_remove";
311
+ type MutationStatus = "pending" | "syncing" | "failed";
312
+ interface MutationRecord {
313
+ mutationId: string;
314
+ collection: string;
315
+ documentId: string;
316
+ type: MutationType;
317
+ payload: Record<string, any> | null;
318
+ status: MutationStatus;
319
+ createdAt: number;
320
+ updatedAt: number;
321
+ retryCount: number;
322
+ error?: string;
323
+ baseRevision?: string | number;
324
+ }
325
+
326
+ declare class Persistence {
327
+ private dbPromise;
328
+ constructor();
329
+ private docKey;
330
+ private now;
331
+ private getDb;
332
+ getDoc(collection: string, id: string): Promise<LocalDoc | null>;
333
+ getDocSnapshot(collection: string, id: string): Promise<DocumentSnapshot | null>;
334
+ getCollection(collection: string): Promise<LocalDoc[]>;
335
+ getCollectionSnapshots(collection: string): Promise<DocumentSnapshot[]>;
336
+ upsertDoc(input: Omit<LocalDoc, "key" | "updatedAt"> & {
337
+ updatedAt?: number;
338
+ }): Promise<LocalDoc>;
339
+ markDeleted(collection: string, id: string, pending?: number): Promise<LocalDoc>;
340
+ enqueueMutation(mutation: Omit<MutationRecord, "updatedAt" | "createdAt" | "retryCount" | "status">): Promise<MutationRecord>;
341
+ getPendingMutations(): Promise<MutationRecord[]>;
342
+ setMutationStatus(mutationId: string, status: MutationRecord["status"], error?: string): Promise<MutationRecord | null>;
343
+ removeMutation(mutationId: string): Promise<void>;
344
+ replaceDocId(collection: string, oldId: string, newId: string): Promise<LocalDoc | null>;
345
+ applyRemoteDoc(collection: string, data: Record<string, any>, revision?: string | number): Promise<LocalDoc | null>;
346
+ applyRemoteDelete(collection: string, id: string): Promise<LocalDoc | null>;
347
+ createLocalId(): string;
348
+ createMutationId(): string;
349
+ clearAll(): Promise<void>;
350
+ getStorageUsage(): Promise<number>;
351
+ }
352
+
353
+ declare class RealtimeBridge {
354
+ private app;
355
+ private persistence;
356
+ private store;
357
+ private collectionUnsubs;
358
+ private documentUnsubs;
359
+ constructor(app: EdmaxLabs, persistence: Persistence, store: LocalStore);
360
+ private docKey;
361
+ watchCollection(collection: string, filter?: Record<string, any>): () => void;
362
+ watchDocument(collection: string, id: string): () => void;
363
+ private emitCurrentDocument;
364
+ private emitCurrentCollection;
365
+ private handleRemoteCreateOrUpdate;
366
+ private handleRemoteDelete;
367
+ /**
368
+ * Call this when Socket.IO reconnects or when going online
369
+ * Replays pending mutations + refreshes all active subscriptions from cache
370
+ */
371
+ onReconnect(): Promise<void>;
372
+ /**
373
+ * Clean up all subscriptions (call from EdmaxLabs destructor if needed)
374
+ */
375
+ dispose(): void;
376
+ }
377
+
378
+ declare class SyncEngine {
379
+ private app;
380
+ private persistence;
381
+ private store;
382
+ private realtimeBridge;
383
+ private syncing;
384
+ private retryTimeout;
385
+ private readonly MAX_RETRIES;
386
+ private readonly BASE_RETRY_DELAY;
387
+ constructor(app: EdmaxLabs, persistence: Persistence, store: LocalStore, realtimeBridge?: RealtimeBridge);
388
+ private onNetworkOnline;
389
+ flush(): Promise<void>;
390
+ private syncCreate;
391
+ private syncUpdate;
392
+ private syncDelete;
393
+ private scheduleRetry;
394
+ forceSync(): Promise<void>;
395
+ dispose(): void;
231
396
  }
232
397
 
233
398
  declare class StorageSnapshot {
@@ -239,8 +404,8 @@ declare class StorageSnapshot {
239
404
  }
240
405
 
241
406
  declare class StorageRef {
242
- db: EdmaxLabs;
243
- constructor(db: EdmaxLabs);
407
+ app: EdmaxLabs;
408
+ constructor(app: EdmaxLabs);
244
409
  get(id: string): Promise<StorageSnapshot | null | undefined>;
245
410
  getMeta(id: string): Promise<StorageSnapshot | null | undefined>;
246
411
  upload(srcFile: File): Promise<StorageSnapshot | null | undefined>;
@@ -259,31 +424,44 @@ declare class Storage {
259
424
  interface EdmaxLabsConfig {
260
425
  token: string;
261
426
  project: string;
427
+ persistence?: boolean;
428
+ default_bucket?: string;
262
429
  }
263
430
  declare class EdmaxLabs {
264
431
  static instance: EdmaxLabs | null;
265
432
  private baseUrl;
266
433
  private socketUrl;
267
- private auth;
268
434
  private _db;
435
+ private _realtime;
269
436
  private _hosting;
270
- private realtime;
437
+ private _config;
271
438
  private persistence;
272
- private persistence_worker;
273
- constructor({ token, project }: EdmaxLabsConfig);
274
- static allowPersistence(enable: boolean): void;
439
+ private localStore;
440
+ private syncEngine;
441
+ private realtimeBridge;
442
+ constructor(config: EdmaxLabsConfig);
443
+ /** Recommended for most React apps (creates fresh instance) */
444
+ static create(config: EdmaxLabsConfig): EdmaxLabs;
445
+ /** Firebase-style singleton (kept for backward compatibility) */
275
446
  static initialize(config: EdmaxLabsConfig): EdmaxLabs;
276
- get getPersistence(): boolean;
277
- get getAuth(): Record<string, any>;
278
- get getBaseUrl(): string;
279
- get getSocketUrl(): string;
280
- get database(): Database;
281
- get storage(): Storage;
282
- get hosting(): Hosting;
283
- get functions(): Functions;
284
- get rtdb(): Realtime;
285
- sync(id: string, collection?: string): Promise<boolean | undefined>;
286
- get authentication(): Authentication;
447
+ get getDatabase(): Database;
448
+ get getFunctions(): Functions;
449
+ get getStorage(): Storage;
450
+ get getAuthentication(): Authentication;
451
+ /** New clean offline namespace - highly recommended */
452
+ offline(): {
453
+ readonly persistence: Persistence | null;
454
+ readonly localStore: LocalStore | null;
455
+ readonly syncEngine: SyncEngine | null;
456
+ readonly realtimeBridge: RealtimeBridge | null;
457
+ readonly enabled: boolean;
458
+ };
459
+ getConfig(): EdmaxLabsConfig;
460
+ getBaseUrl(): string;
461
+ getSocketUrl(): string;
462
+ rtdb(): Realtime;
463
+ /** Call this when your app unmounts or user logs out */
464
+ dispose(): void;
287
465
  }
288
466
 
289
- export { ArraySnapshot, Credentials, DisplayInfo, DocumentSnapshot, StorageSnapshot, Timestamp, EdmaxLabs as default };
467
+ export { ArraySnapshot, Authentication, Credentials, DisplayInfo, DocumentSnapshot, EdmaxLabsConfig, StorageSnapshot, Timestamp, EdmaxLabs as default };