nexabase-console 1.1.4 → 2.0.0

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.
Files changed (72) hide show
  1. package/README.md +33 -8
  2. package/dist/app/NexaApp.d.ts +63 -0
  3. package/dist/app/NexaApp.js +184 -0
  4. package/dist/app/initializeApp.d.ts +8 -0
  5. package/dist/app/initializeApp.js +27 -0
  6. package/dist/auth/Auth.d.ts +21 -0
  7. package/dist/auth/Auth.js +73 -0
  8. package/dist/auth/authTypes.d.ts +1 -0
  9. package/dist/auth/authTypes.js +2 -0
  10. package/dist/auth/persistence.d.ts +9 -0
  11. package/dist/auth/persistence.js +34 -0
  12. package/dist/errors/NexaError.d.ts +7 -0
  13. package/dist/errors/NexaError.js +61 -0
  14. package/dist/firestore/CollectionReference.d.ts +3 -0
  15. package/dist/firestore/CollectionReference.js +7 -0
  16. package/dist/firestore/DocumentReference.d.ts +3 -0
  17. package/dist/firestore/DocumentReference.js +22 -0
  18. package/dist/firestore/FieldPath.d.ts +7 -0
  19. package/dist/firestore/FieldPath.js +23 -0
  20. package/dist/firestore/FieldValue.d.ts +11 -0
  21. package/dist/firestore/FieldValue.js +21 -0
  22. package/dist/firestore/Firestore.d.ts +8 -0
  23. package/dist/firestore/Firestore.js +24 -0
  24. package/dist/firestore/GeoPoint.d.ts +10 -0
  25. package/dist/firestore/GeoPoint.js +22 -0
  26. package/dist/firestore/Query.d.ts +4 -0
  27. package/dist/firestore/Query.js +172 -0
  28. package/dist/firestore/Snapshot.d.ts +6 -0
  29. package/dist/firestore/Snapshot.js +126 -0
  30. package/dist/firestore/SnapshotManager.d.ts +9 -0
  31. package/dist/firestore/SnapshotManager.js +37 -0
  32. package/dist/firestore/Timestamp.d.ts +16 -0
  33. package/dist/firestore/Timestamp.js +36 -0
  34. package/dist/firestore/batch.d.ts +3 -0
  35. package/dist/firestore/batch.js +79 -0
  36. package/dist/firestore/queryConstraints.d.ts +9 -0
  37. package/dist/firestore/queryConstraints.js +31 -0
  38. package/dist/firestore/transaction.d.ts +5 -0
  39. package/dist/firestore/transaction.js +71 -0
  40. package/dist/firestore/writes.d.ts +15 -0
  41. package/dist/firestore/writes.js +146 -0
  42. package/dist/index.d.ts +34 -206
  43. package/dist/index.js +56 -1022
  44. package/dist/persistence/IndexedDB.d.ts +8 -0
  45. package/dist/persistence/IndexedDB.js +37 -0
  46. package/dist/persistence/LocalStore.d.ts +12 -0
  47. package/dist/persistence/LocalStore.js +78 -0
  48. package/dist/persistence/MutationQueue.d.ts +22 -0
  49. package/dist/persistence/MutationQueue.js +145 -0
  50. package/dist/realtime/RealtimeConnection.d.ts +7 -0
  51. package/dist/realtime/RealtimeConnection.js +19 -0
  52. package/dist/realtime/RealtimeDatabase.d.ts +10 -0
  53. package/dist/realtime/RealtimeDatabase.js +62 -0
  54. package/dist/storage/Storage.d.ts +12 -0
  55. package/dist/storage/Storage.js +41 -0
  56. package/dist/storage/StorageReference.d.ts +13 -0
  57. package/dist/storage/StorageReference.js +20 -0
  58. package/dist/storage/UploadTask.d.ts +10 -0
  59. package/dist/storage/UploadTask.js +22 -0
  60. package/dist/sync/ConflictResolver.d.ts +3 -0
  61. package/dist/sync/ConflictResolver.js +24 -0
  62. package/dist/sync/SyncEngine.d.ts +13 -0
  63. package/dist/sync/SyncEngine.js +47 -0
  64. package/dist/transport/HttpClient.d.ts +8 -0
  65. package/dist/transport/HttpClient.js +42 -0
  66. package/dist/transport/SSEClient.d.ts +15 -0
  67. package/dist/transport/SSEClient.js +96 -0
  68. package/dist/types/index.d.ts +150 -0
  69. package/dist/types/index.js +2 -0
  70. package/dist/utils/helpers.d.ts +7 -0
  71. package/dist/utils/helpers.js +124 -0
  72. package/package.json +1 -1
@@ -0,0 +1,8 @@
1
+ export declare class IndexedDB {
2
+ private dbName;
3
+ private idb;
4
+ constructor(dbName: string);
5
+ init(): Promise<void>;
6
+ getRawDB(): IDBDatabase | null;
7
+ }
8
+ export { IndexedDB as IndexedDBStore };
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IndexedDBStore = exports.IndexedDB = void 0;
4
+ class IndexedDB {
5
+ constructor(dbName) {
6
+ this.idb = null;
7
+ this.dbName = dbName;
8
+ }
9
+ async init() {
10
+ if (typeof window === 'undefined' || !window.indexedDB)
11
+ return;
12
+ return new Promise((resolve, reject) => {
13
+ const request = indexedDB.open(this.dbName, 1);
14
+ request.onupgradeneeded = (e) => {
15
+ const db = e.target.result;
16
+ if (!db.objectStoreNames.contains('offline_cache')) {
17
+ db.createObjectStore('offline_cache', { keyPath: 'path' });
18
+ }
19
+ if (!db.objectStoreNames.contains('offline_queue')) {
20
+ db.createObjectStore('offline_queue', { keyPath: 'id' });
21
+ }
22
+ };
23
+ request.onsuccess = (e) => {
24
+ this.idb = e.target.result;
25
+ resolve();
26
+ };
27
+ request.onerror = (e) => {
28
+ reject(e.target.error);
29
+ };
30
+ });
31
+ }
32
+ getRawDB() {
33
+ return this.idb;
34
+ }
35
+ }
36
+ exports.IndexedDB = IndexedDB;
37
+ exports.IndexedDBStore = IndexedDB;
@@ -0,0 +1,12 @@
1
+ import { IndexedDB } from './IndexedDB';
2
+ export declare class LocalStore {
3
+ private indexedDB;
4
+ private inMemoryCache;
5
+ constructor(indexedDB: IndexedDB);
6
+ set(path: string, data: any): Promise<void>;
7
+ getInMemory(path: string): any;
8
+ getMemoryCache(): Record<string, any>;
9
+ get(path: string): Promise<any>;
10
+ delete(path: string): Promise<void>;
11
+ loadAll(): Promise<Record<string, any>>;
12
+ }
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalStore = void 0;
4
+ class LocalStore {
5
+ constructor(indexedDB) {
6
+ this.inMemoryCache = {};
7
+ this.indexedDB = indexedDB;
8
+ }
9
+ async set(path, data) {
10
+ this.inMemoryCache[path] = data;
11
+ const db = this.indexedDB.getRawDB();
12
+ if (!db)
13
+ return;
14
+ return new Promise((resolve, reject) => {
15
+ const tx = db.transaction('offline_cache', 'readwrite');
16
+ const store = tx.objectStore('offline_cache');
17
+ const req = store.put({ path, data, timestamp: Date.now() });
18
+ req.onsuccess = () => resolve();
19
+ req.onerror = () => reject(req.error);
20
+ });
21
+ }
22
+ getInMemory(path) {
23
+ return this.inMemoryCache[path] || null;
24
+ }
25
+ getMemoryCache() {
26
+ return this.inMemoryCache;
27
+ }
28
+ async get(path) {
29
+ if (this.inMemoryCache[path])
30
+ return this.inMemoryCache[path];
31
+ const db = this.indexedDB.getRawDB();
32
+ if (!db)
33
+ return null;
34
+ return new Promise((resolve, reject) => {
35
+ const tx = db.transaction('offline_cache', 'readonly');
36
+ const store = tx.objectStore('offline_cache');
37
+ const req = store.get(path);
38
+ req.onsuccess = () => {
39
+ const data = req.result ? req.result.data : null;
40
+ if (data)
41
+ this.inMemoryCache[path] = data;
42
+ resolve(data);
43
+ };
44
+ req.onerror = () => reject(req.error);
45
+ });
46
+ }
47
+ async delete(path) {
48
+ delete this.inMemoryCache[path];
49
+ const db = this.indexedDB.getRawDB();
50
+ if (!db)
51
+ return;
52
+ return new Promise((resolve, reject) => {
53
+ const tx = db.transaction('offline_cache', 'readwrite');
54
+ const store = tx.objectStore('offline_cache');
55
+ const req = store.delete(path);
56
+ req.onsuccess = () => resolve();
57
+ req.onerror = () => reject(req.error);
58
+ });
59
+ }
60
+ async loadAll() {
61
+ const db = this.indexedDB.getRawDB();
62
+ if (!db)
63
+ return this.inMemoryCache;
64
+ return new Promise((resolve, reject) => {
65
+ const tx = db.transaction('offline_cache', 'readonly');
66
+ const store = tx.objectStore('offline_cache');
67
+ const req = store.getAll();
68
+ req.onsuccess = () => {
69
+ req.result.forEach((item) => {
70
+ this.inMemoryCache[item.path] = item.data;
71
+ });
72
+ resolve(this.inMemoryCache);
73
+ };
74
+ req.onerror = () => reject(req.error);
75
+ });
76
+ }
77
+ }
78
+ exports.LocalStore = LocalStore;
@@ -0,0 +1,22 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { OfflineJob } from '../types/index';
3
+ import { IndexedDB } from './IndexedDB';
4
+ export declare class MutationQueue {
5
+ private projectId;
6
+ private indexedDB;
7
+ private client;
8
+ private inMemoryQueue;
9
+ private inflightWrites;
10
+ private isSyncing;
11
+ constructor(projectId: string, indexedDB: IndexedDB, client: AxiosInstance);
12
+ init(): Promise<void>;
13
+ loadQueue(): Promise<OfflineJob[]>;
14
+ addJob(job: OfflineJob): Promise<void>;
15
+ saveQueue(queue: OfflineJob[]): Promise<void>;
16
+ removeJob(jobId: string): Promise<void>;
17
+ markInflight(path: string): void;
18
+ unmarkInflight(path: string): void;
19
+ hasPendingWrites(path: string): boolean;
20
+ getQueue(): OfflineJob[];
21
+ syncQueue(): Promise<void>;
22
+ }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MutationQueue = void 0;
4
+ class MutationQueue {
5
+ constructor(projectId, indexedDB, client) {
6
+ this.inMemoryQueue = [];
7
+ this.inflightWrites = new Set();
8
+ this.isSyncing = false;
9
+ this.projectId = projectId;
10
+ this.indexedDB = indexedDB;
11
+ this.client = client;
12
+ }
13
+ async init() {
14
+ await this.loadQueue();
15
+ }
16
+ async loadQueue() {
17
+ const db = this.indexedDB.getRawDB();
18
+ if (!db)
19
+ return this.inMemoryQueue;
20
+ return new Promise((resolve, reject) => {
21
+ const tx = db.transaction('offline_queue', 'readonly');
22
+ const store = tx.objectStore('offline_queue');
23
+ const req = store.getAll();
24
+ req.onsuccess = () => {
25
+ this.inMemoryQueue = req.result || [];
26
+ resolve(this.inMemoryQueue);
27
+ };
28
+ req.onerror = () => reject(req.error);
29
+ });
30
+ }
31
+ async addJob(job) {
32
+ job.state = job.state || 'pending';
33
+ job.timestamp = job.timestamp || Date.now();
34
+ this.inMemoryQueue.push(job);
35
+ const db = this.indexedDB.getRawDB();
36
+ if (!db)
37
+ return;
38
+ return new Promise((resolve, reject) => {
39
+ const tx = db.transaction('offline_queue', 'readwrite');
40
+ const store = tx.objectStore('offline_queue');
41
+ const req = store.put(job);
42
+ req.onsuccess = () => resolve();
43
+ req.onerror = () => reject(req.error);
44
+ });
45
+ }
46
+ async saveQueue(queue) {
47
+ this.inMemoryQueue = queue;
48
+ const db = this.indexedDB.getRawDB();
49
+ if (!db)
50
+ return;
51
+ return new Promise((resolve, reject) => {
52
+ const tx = db.transaction('offline_queue', 'readwrite');
53
+ const store = tx.objectStore('offline_queue');
54
+ const clearReq = store.clear();
55
+ clearReq.onsuccess = () => {
56
+ if (queue.length === 0) {
57
+ resolve();
58
+ return;
59
+ }
60
+ let added = 0;
61
+ queue.forEach((item) => {
62
+ const req = store.put(item);
63
+ req.onsuccess = () => {
64
+ added++;
65
+ if (added === queue.length)
66
+ resolve();
67
+ };
68
+ req.onerror = () => reject(req.error);
69
+ });
70
+ };
71
+ clearReq.onerror = () => reject(clearReq.error);
72
+ });
73
+ }
74
+ async removeJob(jobId) {
75
+ const updated = this.inMemoryQueue.filter((j) => j.id !== jobId);
76
+ await this.saveQueue(updated);
77
+ }
78
+ markInflight(path) {
79
+ this.inflightWrites.add(path);
80
+ }
81
+ unmarkInflight(path) {
82
+ this.inflightWrites.delete(path);
83
+ }
84
+ hasPendingWrites(path) {
85
+ if (this.inflightWrites.has(path))
86
+ return true;
87
+ for (const p of this.inflightWrites) {
88
+ if (p.startsWith(path + '/'))
89
+ return true;
90
+ }
91
+ return this.inMemoryQueue.some((job) => job.state === 'pending' && (job.path === path || job.path.startsWith(path + '/')));
92
+ }
93
+ getQueue() {
94
+ return [...this.inMemoryQueue];
95
+ }
96
+ async syncQueue() {
97
+ if (this.isSyncing || this.inMemoryQueue.length === 0)
98
+ return;
99
+ if (typeof navigator !== 'undefined' && !navigator.onLine)
100
+ return;
101
+ this.isSyncing = true;
102
+ const queue = [...this.inMemoryQueue];
103
+ for (const job of queue) {
104
+ try {
105
+ job.state = 'pending';
106
+ const headers = job.idempotencyKey ? { 'X-Idempotency-Key': job.idempotencyKey } : {};
107
+ if (job.type === 'set') {
108
+ await this.client.post(`/api/firestore/${this.projectId}/document`, {
109
+ docPath: job.path,
110
+ data: job.data,
111
+ idempotencyKey: job.idempotencyKey,
112
+ merge: job.merge
113
+ }, { headers });
114
+ }
115
+ else if (job.type === 'patch' || job.type === 'update') {
116
+ await this.client.patch(`/api/firestore/${this.projectId}/document`, {
117
+ docPath: job.path,
118
+ data: job.data,
119
+ idempotencyKey: job.idempotencyKey
120
+ }, { headers });
121
+ }
122
+ else if (job.type === 'delete') {
123
+ await this.client.delete(`/api/firestore/${this.projectId}/document?docPath=${encodeURIComponent(job.path)}`, { headers });
124
+ }
125
+ job.state = 'acknowledged';
126
+ await this.removeJob(job.id);
127
+ }
128
+ catch (err) {
129
+ if (err.response && err.response.status >= 400 && err.response.status < 500) {
130
+ job.state = 'rejected';
131
+ job.lastError = err.response?.data?.message || err.message;
132
+ console.error(`[MutationQueue] Discarding rejected job (${job.type} on ${job.path}):`, err.response.data);
133
+ await this.removeJob(job.id);
134
+ }
135
+ else {
136
+ job.retryCount = (job.retryCount || 0) + 1;
137
+ await this.saveQueue(this.inMemoryQueue);
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ this.isSyncing = false;
143
+ }
144
+ }
145
+ exports.MutationQueue = MutationQueue;
@@ -0,0 +1,7 @@
1
+ import { SSEClient } from '../transport/SSEClient';
2
+ export declare class RealtimeConnection {
3
+ private sseClient;
4
+ constructor(sseClient: SSEClient);
5
+ subscribeToPath(path: string, callback: (value: any) => void): () => void;
6
+ close(): void;
7
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RealtimeConnection = void 0;
4
+ class RealtimeConnection {
5
+ constructor(sseClient) {
6
+ this.sseClient = sseClient;
7
+ }
8
+ subscribeToPath(path, callback) {
9
+ return this.sseClient.addListener('data_changed', (payload) => {
10
+ if (payload && payload.path === path) {
11
+ callback(payload.value);
12
+ }
13
+ });
14
+ }
15
+ close() {
16
+ this.sseClient.close();
17
+ }
18
+ }
19
+ exports.RealtimeConnection = RealtimeConnection;
@@ -0,0 +1,10 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { DatabaseReference } from '../types/index';
3
+ import { RealtimeConnection } from './RealtimeConnection';
4
+ export declare class RealtimeDatabase {
5
+ private projectId;
6
+ private client;
7
+ private connection;
8
+ constructor(projectId: string, client: AxiosInstance, connection: RealtimeConnection);
9
+ ref(path: string): DatabaseReference;
10
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RealtimeDatabase = void 0;
4
+ const NexaError_1 = require("../errors/NexaError");
5
+ class RealtimeDatabase {
6
+ constructor(projectId, client, connection) {
7
+ this.projectId = projectId;
8
+ this.client = client;
9
+ this.connection = connection;
10
+ }
11
+ ref(path) {
12
+ return {
13
+ path,
14
+ set: async (value) => {
15
+ try {
16
+ const res = await this.client.post(`/api/project/${this.projectId}/database/set`, { path, value });
17
+ return res.data;
18
+ }
19
+ catch (err) {
20
+ throw (0, NexaError_1.toNexaError)(err);
21
+ }
22
+ },
23
+ get: async () => {
24
+ try {
25
+ const res = await this.client.get(`/api/project/${this.projectId}/database/get?path=${encodeURIComponent(path)}`);
26
+ return res.data;
27
+ }
28
+ catch (err) {
29
+ throw (0, NexaError_1.toNexaError)(err);
30
+ }
31
+ },
32
+ update: async (value) => {
33
+ try {
34
+ const res = await this.client.patch(`/api/project/${this.projectId}/database/update`, { path, value });
35
+ return res.data;
36
+ }
37
+ catch (err) {
38
+ throw (0, NexaError_1.toNexaError)(err);
39
+ }
40
+ },
41
+ remove: async () => {
42
+ try {
43
+ const res = await this.client.delete(`/api/project/${this.projectId}/database/remove?path=${encodeURIComponent(path)}`);
44
+ return res.data;
45
+ }
46
+ catch (err) {
47
+ throw (0, NexaError_1.toNexaError)(err);
48
+ }
49
+ },
50
+ onValue: (callback) => {
51
+ const removeListener = this.connection.subscribeToPath(path, callback);
52
+ // Fetch initial state
53
+ this.client
54
+ .get(`/api/project/${this.projectId}/database/get?path=${encodeURIComponent(path)}`)
55
+ .then((res) => callback(res.data.value))
56
+ .catch(() => { });
57
+ return removeListener;
58
+ }
59
+ };
60
+ }
61
+ }
62
+ exports.RealtimeDatabase = RealtimeDatabase;
@@ -0,0 +1,12 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { UploadOptions } from '../types/index';
3
+ export declare class Storage {
4
+ private projectId;
5
+ private client;
6
+ constructor(projectId: string, client: AxiosInstance);
7
+ uploadFile(path: string, file: File | Blob, options?: UploadOptions): Promise<{
8
+ url: string;
9
+ path: string;
10
+ }>;
11
+ getDownloadURL(path: string): Promise<string>;
12
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Storage = void 0;
4
+ const UploadTask_1 = require("./UploadTask");
5
+ const NexaError_1 = require("../errors/NexaError");
6
+ class Storage {
7
+ constructor(projectId, client) {
8
+ this.projectId = projectId;
9
+ this.client = client;
10
+ }
11
+ async uploadFile(path, file, options) {
12
+ try {
13
+ const uploadTask = new UploadTask_1.UploadTask(path, file, options);
14
+ const formData = new FormData();
15
+ formData.append('file', file);
16
+ formData.append('path', path);
17
+ const res = await this.client.post(`/api/project/${this.projectId}/storage/upload`, formData, {
18
+ onUploadProgress: (progressEvent) => {
19
+ if (progressEvent.total) {
20
+ const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
21
+ uploadTask.notifyProgress(percent);
22
+ }
23
+ }
24
+ });
25
+ return res.data;
26
+ }
27
+ catch (err) {
28
+ throw (0, NexaError_1.toNexaError)(err);
29
+ }
30
+ }
31
+ async getDownloadURL(path) {
32
+ try {
33
+ const res = await this.client.get(`/api/project/${this.projectId}/storage/url?path=${encodeURIComponent(path)}`);
34
+ return res.data.url;
35
+ }
36
+ catch (err) {
37
+ throw (0, NexaError_1.toNexaError)(err);
38
+ }
39
+ }
40
+ }
41
+ exports.Storage = Storage;
@@ -0,0 +1,13 @@
1
+ import { StorageReference as IStorageReference, UploadOptions } from '../types/index';
2
+ import { Storage } from './Storage';
3
+ export declare class StorageReferenceImpl implements IStorageReference {
4
+ path: string;
5
+ private storage;
6
+ constructor(path: string, storage: Storage);
7
+ upload(file: File | Blob, options?: UploadOptions): Promise<{
8
+ url: string;
9
+ path: string;
10
+ }>;
11
+ getDownloadURL(): Promise<string>;
12
+ }
13
+ export declare const storageRef: (storage: Storage, path: string) => IStorageReference;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.storageRef = exports.StorageReferenceImpl = void 0;
4
+ class StorageReferenceImpl {
5
+ constructor(path, storage) {
6
+ this.path = path;
7
+ this.storage = storage;
8
+ }
9
+ async upload(file, options) {
10
+ return this.storage.uploadFile(this.path, file, options);
11
+ }
12
+ async getDownloadURL() {
13
+ return this.storage.getDownloadURL(this.path);
14
+ }
15
+ }
16
+ exports.StorageReferenceImpl = StorageReferenceImpl;
17
+ const storageRef = (storage, path) => {
18
+ return new StorageReferenceImpl(path, storage);
19
+ };
20
+ exports.storageRef = storageRef;
@@ -0,0 +1,10 @@
1
+ import { UploadOptions } from '../types/index';
2
+ export declare class UploadTask {
3
+ private file;
4
+ private path;
5
+ private options?;
6
+ constructor(path: string, file: File | Blob, options?: UploadOptions);
7
+ getFile(): File | Blob;
8
+ getPath(): string;
9
+ notifyProgress(percent: number): void;
10
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UploadTask = void 0;
4
+ class UploadTask {
5
+ constructor(path, file, options) {
6
+ this.path = path;
7
+ this.file = file;
8
+ this.options = options;
9
+ }
10
+ getFile() {
11
+ return this.file;
12
+ }
13
+ getPath() {
14
+ return this.path;
15
+ }
16
+ notifyProgress(percent) {
17
+ if (this.options && this.options.onProgress) {
18
+ this.options.onProgress(percent);
19
+ }
20
+ }
21
+ }
22
+ exports.UploadTask = UploadTask;
@@ -0,0 +1,3 @@
1
+ export declare class ConflictResolver {
2
+ static resolveWithRetry<T>(operation: () => Promise<T>, maxAttempts?: number): Promise<T>;
3
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConflictResolver = void 0;
4
+ class ConflictResolver {
5
+ static async resolveWithRetry(operation, maxAttempts = 5) {
6
+ let attempt = 0;
7
+ while (true) {
8
+ attempt++;
9
+ try {
10
+ return await operation();
11
+ }
12
+ catch (err) {
13
+ const isConflict = err && err.response && err.response.status === 409;
14
+ if (isConflict && attempt < maxAttempts) {
15
+ console.warn(`[ConflictResolver] Retrying operation due to concurrency conflict (Attempt ${attempt}/${maxAttempts})`);
16
+ await new Promise((resolve) => setTimeout(resolve, Math.random() * 100 * attempt));
17
+ continue;
18
+ }
19
+ throw err;
20
+ }
21
+ }
22
+ }
23
+ }
24
+ exports.ConflictResolver = ConflictResolver;
@@ -0,0 +1,13 @@
1
+ import { MutationQueue } from '../persistence/MutationQueue';
2
+ import { SSEClient } from '../transport/SSEClient';
3
+ export declare class SyncEngine {
4
+ private projectId;
5
+ private mutationQueue;
6
+ private sseClient;
7
+ private broadcastChannel;
8
+ constructor(projectId: string, mutationQueue: MutationQueue, sseClient: SSEClient);
9
+ private initCrossTabSync;
10
+ broadcastLocalMutation(path: string, action: string, data?: any): void;
11
+ listenCrossTab(onMutation: (path: string, action: string, data?: any) => void): () => void;
12
+ syncOfflineMutations(): Promise<void>;
13
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SyncEngine = void 0;
4
+ class SyncEngine {
5
+ constructor(projectId, mutationQueue, sseClient) {
6
+ this.broadcastChannel = null;
7
+ this.projectId = projectId;
8
+ this.mutationQueue = mutationQueue;
9
+ this.sseClient = sseClient;
10
+ this.initCrossTabSync();
11
+ }
12
+ initCrossTabSync() {
13
+ if (typeof window !== 'undefined' && 'BroadcastChannel' in window && !this.broadcastChannel) {
14
+ try {
15
+ this.broadcastChannel = new BroadcastChannel(`nexabase_tab_sync_${this.projectId}`);
16
+ }
17
+ catch (e) {
18
+ console.warn('[SyncEngine] BroadcastChannel initialization failed:', e);
19
+ }
20
+ }
21
+ }
22
+ broadcastLocalMutation(path, action, data) {
23
+ if (this.broadcastChannel) {
24
+ try {
25
+ this.broadcastChannel.postMessage({ type: 'LOCAL_MUTATION', path, action, data });
26
+ }
27
+ catch (e) { }
28
+ }
29
+ }
30
+ listenCrossTab(onMutation) {
31
+ if (!this.broadcastChannel)
32
+ return () => { };
33
+ const handler = (event) => {
34
+ if (event.data && event.data.type === 'LOCAL_MUTATION') {
35
+ onMutation(event.data.path, event.data.action, event.data.data);
36
+ }
37
+ };
38
+ this.broadcastChannel.addEventListener('message', handler);
39
+ return () => {
40
+ this.broadcastChannel.removeEventListener('message', handler);
41
+ };
42
+ }
43
+ async syncOfflineMutations() {
44
+ await this.mutationQueue.syncQueue();
45
+ }
46
+ }
47
+ exports.SyncEngine = SyncEngine;
@@ -0,0 +1,8 @@
1
+ import { AxiosInstance } from 'axios';
2
+ export declare class HttpClient {
3
+ private client;
4
+ private getToken;
5
+ constructor(endpoint: string, getTokenFn: () => string | null);
6
+ getAxiosInstance(): AxiosInstance;
7
+ setBaseURL(url: string): void;
8
+ }