nexabase-console 2.1.4 → 2.1.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/dist/app/NexaApp.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export declare class NexaApp {
|
|
|
36
36
|
_activeQueryTimestamps: Map<string, number>;
|
|
37
37
|
constructor(config: NexaConfig);
|
|
38
38
|
get _firestoreCache(): Record<string, any>;
|
|
39
|
+
private _setupVisibilityResync;
|
|
39
40
|
private _setupCrossTabSync;
|
|
40
41
|
_broadcastLocalMutation(path: string, action: string, data?: any): void;
|
|
41
42
|
getPendingWritesCount(): number;
|
package/dist/app/NexaApp.js
CHANGED
|
@@ -50,19 +50,47 @@ class NexaApp {
|
|
|
50
50
|
this.realtimeDatabase = new RealtimeDatabase_1.RealtimeDatabase(this.projectId, this.client, realtimeConn);
|
|
51
51
|
this.storage = new Storage_1.Storage(this.projectId, this.client);
|
|
52
52
|
this._setupCrossTabSync();
|
|
53
|
+
this._setupVisibilityResync();
|
|
53
54
|
}
|
|
54
55
|
get _firestoreCache() {
|
|
55
56
|
return this.localStore.getMemoryCache();
|
|
56
57
|
}
|
|
58
|
+
_setupVisibilityResync() {
|
|
59
|
+
if (typeof document !== 'undefined' && typeof document.addEventListener === 'function') {
|
|
60
|
+
document.addEventListener('visibilitychange', () => {
|
|
61
|
+
if (document.visibilityState === 'visible') {
|
|
62
|
+
// Re-ensure SSE and WS when coming back from background
|
|
63
|
+
if (this._firestoreListening) {
|
|
64
|
+
if (this.wsClient && typeof this.wsClient.ensureConnected === 'function') {
|
|
65
|
+
this.wsClient.ensureConnected();
|
|
66
|
+
}
|
|
67
|
+
if (this.sseClient && typeof this.sseClient.ensureConnected === 'function') {
|
|
68
|
+
this.sseClient.ensureConnected();
|
|
69
|
+
}
|
|
70
|
+
// Notify server that we need latest changes by simulating subscribe again for each path?
|
|
71
|
+
// wsClient handles this on resync event.
|
|
72
|
+
}
|
|
73
|
+
// Optionally sync offline queue
|
|
74
|
+
this._syncOfflineQueue().catch(console.error);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
57
79
|
_setupCrossTabSync() {
|
|
58
80
|
this.syncEngine.listenCrossTab((path, action, data) => {
|
|
81
|
+
const cleanPath = path.replace(/^\/+|\/+$/g, '');
|
|
59
82
|
if (action === 'delete') {
|
|
60
|
-
this.localStore.delete(
|
|
83
|
+
this.localStore.delete(cleanPath);
|
|
61
84
|
}
|
|
62
85
|
else if (data) {
|
|
63
|
-
this.localStore.set(
|
|
86
|
+
this.localStore.set(cleanPath, data);
|
|
87
|
+
}
|
|
88
|
+
this._notifySnapshotCallbacks(cleanPath, action === 'delete' ? 'document_deleted' : 'document_written', data);
|
|
89
|
+
const parts = cleanPath.split('/');
|
|
90
|
+
const colPath = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
|
|
91
|
+
if (colPath) {
|
|
92
|
+
this._notifySnapshotCallbacks(colPath, action === 'delete' ? 'document_deleted' : 'document_written', data);
|
|
64
93
|
}
|
|
65
|
-
this._notifySnapshotCallbacks(path, action === 'delete' ? 'document_deleted' : 'document_written', data);
|
|
66
94
|
});
|
|
67
95
|
}
|
|
68
96
|
_broadcastLocalMutation(path, action, data) {
|
|
@@ -367,17 +395,31 @@ class NexaApp {
|
|
|
367
395
|
const isDelete = type === 'document_deleted' || action === 'delete' || type === 'delete';
|
|
368
396
|
if (targetDocPath && typeof targetDocPath === 'string') {
|
|
369
397
|
const cleanDocPath = targetDocPath.replace(/^\/+|\/+$/g, '');
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
398
|
+
const segments = cleanDocPath.split('/');
|
|
399
|
+
const isCollection = segments.length % 2 === 1;
|
|
400
|
+
if (isCollection && data !== null && typeof data === 'object' && !Array.isArray(data)) {
|
|
401
|
+
// Distribute collection payload to individual docs
|
|
402
|
+
if (!isDelete) {
|
|
403
|
+
for (const docId of Object.keys(data)) {
|
|
404
|
+
const docData = data[docId];
|
|
405
|
+
this._setCache(`${cleanDocPath}/${docId}`, docData);
|
|
406
|
+
this._notifySnapshotCallbacks(`${cleanDocPath}/${docId}`, 'document_written', docData);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
this._notifySnapshotCallbacks(cleanDocPath, isDelete ? 'document_deleted' : 'document_written', data);
|
|
375
410
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
411
|
+
else {
|
|
412
|
+
if (isDelete) {
|
|
413
|
+
this._deleteCache(cleanDocPath);
|
|
414
|
+
}
|
|
415
|
+
else if (data !== undefined && data !== null) {
|
|
416
|
+
this._setCache(cleanDocPath, data);
|
|
417
|
+
}
|
|
418
|
+
const colPath = segments.length > 1 ? segments.slice(0, -1).join('/') : '';
|
|
419
|
+
this._notifySnapshotCallbacks(cleanDocPath, isDelete ? 'document_deleted' : 'document_written', data);
|
|
420
|
+
if (colPath) {
|
|
421
|
+
this._notifySnapshotCallbacks(colPath, isDelete ? 'document_deleted' : 'document_written', data);
|
|
422
|
+
}
|
|
381
423
|
}
|
|
382
424
|
}
|
|
383
425
|
else if (collectionPath && typeof collectionPath === 'string') {
|
|
@@ -13,6 +13,10 @@ export type WriteResult<T = unknown> = {
|
|
|
13
13
|
writtenLocally: true;
|
|
14
14
|
mutationId: string;
|
|
15
15
|
};
|
|
16
|
+
export declare const addDoc: <T = any>(reference: any, // CollectionReference
|
|
17
|
+
data: any, options?: {
|
|
18
|
+
idempotencyKey?: string;
|
|
19
|
+
}) => Promise<DocumentReference<T>>;
|
|
16
20
|
export declare const setDoc: (docRef: DocumentReference, data: any, options?: {
|
|
17
21
|
merge?: boolean;
|
|
18
22
|
idempotencyKey?: string;
|
package/dist/firestore/writes.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getPendingWritesCount = exports.deleteDoc = exports.updateDoc = exports.patchDoc = exports.setDoc = exports.FieldValue = void 0;
|
|
3
|
+
exports.getPendingWritesCount = exports.deleteDoc = exports.updateDoc = exports.patchDoc = exports.setDoc = exports.addDoc = exports.FieldValue = void 0;
|
|
4
4
|
const helpers_1 = require("../utils/helpers");
|
|
5
5
|
const NexaError_1 = require("../errors/NexaError");
|
|
6
6
|
const QueryUtils_1 = require("./QueryUtils");
|
|
@@ -48,14 +48,20 @@ const normalizeError = (error) => {
|
|
|
48
48
|
};
|
|
49
49
|
};
|
|
50
50
|
const safelyNotifyMutation = (db, path, action, data, metadata) => {
|
|
51
|
+
const cleanPath = path.replace(/^\/+|\/+$/g, '');
|
|
51
52
|
try {
|
|
52
|
-
db._broadcastLocalMutation(
|
|
53
|
+
db._broadcastLocalMutation(cleanPath, action, data, metadata);
|
|
53
54
|
}
|
|
54
55
|
catch (e) {
|
|
55
56
|
console.error('[NexaBase] Listener error:', e);
|
|
56
57
|
}
|
|
57
58
|
try {
|
|
58
|
-
db._notifySnapshotCallbacks(
|
|
59
|
+
db._notifySnapshotCallbacks(cleanPath, action === 'delete' ? 'document_deleted' : 'document_written', data, metadata);
|
|
60
|
+
const parts = cleanPath.split('/');
|
|
61
|
+
const colPath = parts.length > 1 ? parts.slice(0, -1).join('/') : '';
|
|
62
|
+
if (colPath) {
|
|
63
|
+
db._notifySnapshotCallbacks(colPath, action === 'delete' ? 'document_deleted' : 'document_written', data, metadata);
|
|
64
|
+
}
|
|
59
65
|
}
|
|
60
66
|
catch (e) {
|
|
61
67
|
console.error('[NexaBase] Listener error:', e);
|
|
@@ -211,6 +217,28 @@ const executeWriteMutation = async (docRef, action, data, options) => {
|
|
|
211
217
|
});
|
|
212
218
|
return { status: 'committed', committed: true, writtenLocally: true, mutationId, data: ackView.data };
|
|
213
219
|
};
|
|
220
|
+
const addDoc = async (reference, // CollectionReference
|
|
221
|
+
data, options) => {
|
|
222
|
+
// We need to import doc, but since writes.ts doesn't have it, we can construct the path.
|
|
223
|
+
// Actually it's easier to just generate an auto ID if we can't import doc.
|
|
224
|
+
// We don't want circular dependencies.
|
|
225
|
+
const db = reference.db;
|
|
226
|
+
const generateAutoId = () => {
|
|
227
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
228
|
+
let autoId = '';
|
|
229
|
+
for (let i = 0; i < 20; i++)
|
|
230
|
+
autoId += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
231
|
+
return autoId;
|
|
232
|
+
};
|
|
233
|
+
const d = {
|
|
234
|
+
type: 'document',
|
|
235
|
+
path: `${reference.path}/${generateAutoId()}`,
|
|
236
|
+
db
|
|
237
|
+
};
|
|
238
|
+
await executeWriteMutation(d, 'set', data, options);
|
|
239
|
+
return d;
|
|
240
|
+
};
|
|
241
|
+
exports.addDoc = addDoc;
|
|
214
242
|
const setDoc = async (docRef, data, options) => {
|
|
215
243
|
return executeWriteMutation(docRef, 'set', data, options);
|
|
216
244
|
};
|
|
@@ -1882,6 +1882,7 @@ class WebSocketClient {
|
|
|
1882
1882
|
return;
|
|
1883
1883
|
}
|
|
1884
1884
|
if (document.visibilityState === 'visible') {
|
|
1885
|
+
this.dispatch('resync', { reason: 'visibility_change' });
|
|
1885
1886
|
if (!this.isLeader && this._coordinationMode === 'shared') {
|
|
1886
1887
|
this.postBroadcast('follower_heartbeat', {
|
|
1887
1888
|
globalListenersCount: this.getGlobalListenersCount(),
|
|
@@ -1893,6 +1894,9 @@ class WebSocketClient {
|
|
|
1893
1894
|
});
|
|
1894
1895
|
this.postBroadcast('follower_sync_request');
|
|
1895
1896
|
}
|
|
1897
|
+
if (this.isLeader && this._socket && !this._socket.connected) {
|
|
1898
|
+
this._socket.connect();
|
|
1899
|
+
}
|
|
1896
1900
|
if (this.hasLocalNeeds() && this._state === 'idle') {
|
|
1897
1901
|
this.ensureConnected();
|
|
1898
1902
|
}
|
package/package.json
CHANGED