nexabase-console 1.0.3 → 1.0.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.ts +17 -2
- package/dist/index.js +152 -66
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -39,11 +39,15 @@ export interface Query<T = any> {
|
|
|
39
39
|
constraints?: QueryConstraint[];
|
|
40
40
|
}
|
|
41
41
|
export interface DocumentSnapshot<T = any> {
|
|
42
|
+
id: string;
|
|
43
|
+
ref: DocumentReference<T>;
|
|
42
44
|
exists: () => boolean;
|
|
43
45
|
data: () => T | null;
|
|
44
46
|
}
|
|
45
47
|
export interface QueryDocumentSnapshot<T = any> {
|
|
46
48
|
id: string;
|
|
49
|
+
ref: DocumentReference<T>;
|
|
50
|
+
exists: () => boolean;
|
|
47
51
|
data: () => T;
|
|
48
52
|
}
|
|
49
53
|
export interface QuerySnapshot<T = any> {
|
|
@@ -51,7 +55,7 @@ export interface QuerySnapshot<T = any> {
|
|
|
51
55
|
forEach: (callback: (doc: QueryDocumentSnapshot<T>) => void) => void;
|
|
52
56
|
empty: boolean;
|
|
53
57
|
size: number;
|
|
54
|
-
docChanges
|
|
58
|
+
docChanges: () => any[];
|
|
55
59
|
}
|
|
56
60
|
export interface DatabaseReference {
|
|
57
61
|
get: () => Promise<any>;
|
|
@@ -59,10 +63,16 @@ export interface DatabaseReference {
|
|
|
59
63
|
onDataChanged: (callback: (data: any) => void) => void;
|
|
60
64
|
}
|
|
61
65
|
export interface StorageReference {
|
|
62
|
-
|
|
66
|
+
name: string;
|
|
67
|
+
fullPath: string;
|
|
68
|
+
put: (file: File | Blob, onProgress?: (percent: number) => void) => Promise<{
|
|
63
69
|
url: string;
|
|
64
70
|
success: boolean;
|
|
65
71
|
}>;
|
|
72
|
+
delete: () => Promise<{
|
|
73
|
+
success: boolean;
|
|
74
|
+
message?: string;
|
|
75
|
+
}>;
|
|
66
76
|
}
|
|
67
77
|
export interface WriteBatch {
|
|
68
78
|
set: (docRef: DocumentReference, data: any) => WriteBatch;
|
|
@@ -111,6 +121,7 @@ export declare class NexaApp {
|
|
|
111
121
|
};
|
|
112
122
|
storage(): {
|
|
113
123
|
ref: (path?: string) => StorageReference;
|
|
124
|
+
refFromURL: (url: string) => StorageReference;
|
|
114
125
|
};
|
|
115
126
|
_initFirestoreState(): Promise<void>;
|
|
116
127
|
_getOfflineQueue(): Promise<OfflineJob[]>;
|
|
@@ -130,11 +141,15 @@ export declare const query: <T = any>(queryObject: Query<T> | CollectionReferenc
|
|
|
130
141
|
export declare const where: (fieldPath: string, opStr: "==" | ">" | "<" | ">=" | "<=" | "!=", value: any) => QueryConstraint;
|
|
131
142
|
export declare const orderBy: (fieldPath: string, directionStr?: "asc" | "desc") => QueryConstraint;
|
|
132
143
|
export declare const limit: (limitValue: number) => QueryConstraint;
|
|
144
|
+
export declare const getCachedDocs: <T = any>(queryOrCollection: Query<T> | CollectionReference<T>) => QuerySnapshot<T>;
|
|
145
|
+
export declare const getCachedDoc: <T = any>(docRef: DocumentReference<T>) => DocumentSnapshot<T>;
|
|
133
146
|
export declare const getDocs: <T = any>(queryOrCollection: Query<T> | CollectionReference<T>) => Promise<QuerySnapshot<T>>;
|
|
134
147
|
export declare const getDoc: <T = any>(docRef: DocumentReference<T>) => Promise<DocumentSnapshot<T>>;
|
|
135
148
|
export declare const setDoc: (docRef: DocumentReference, data: any) => Promise<any>;
|
|
136
149
|
export declare const updateDoc: (docRef: DocumentReference, data: any) => Promise<any>;
|
|
137
150
|
export declare const deleteDoc: (docRef: DocumentReference) => Promise<any>;
|
|
138
151
|
export declare const onSnapshot: (ref: CollectionReference | DocumentReference | Query, callback: (snapshot: any) => void) => (() => void);
|
|
152
|
+
export declare const enableIndexedDbPersistence: (db: NexaApp) => Promise<void>;
|
|
153
|
+
export declare const enableOfflinePersistence: (db: NexaApp) => Promise<void>;
|
|
139
154
|
export declare const writeBatch: (db: NexaApp) => WriteBatch;
|
|
140
155
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.writeBatch = exports.onSnapshot = exports.deleteDoc = exports.updateDoc = exports.setDoc = exports.getDoc = exports.getDocs = exports.limit = exports.orderBy = exports.where = exports.query = exports.doc = exports.collection = exports.getFirestore = exports.initializeApp = exports.NexaApp = void 0;
|
|
6
|
+
exports.writeBatch = exports.enableOfflinePersistence = exports.enableIndexedDbPersistence = exports.onSnapshot = exports.deleteDoc = exports.updateDoc = exports.setDoc = exports.getDoc = exports.getDocs = exports.getCachedDoc = exports.getCachedDocs = exports.limit = exports.orderBy = exports.where = exports.query = exports.doc = exports.collection = exports.getFirestore = exports.initializeApp = exports.NexaApp = void 0;
|
|
7
7
|
const axios_1 = __importDefault(require("axios"));
|
|
8
8
|
// -----------------------------------------------------------------
|
|
9
9
|
// Main NexaApp SDK Class
|
|
@@ -29,18 +29,29 @@ class NexaApp {
|
|
|
29
29
|
this.token = savedToken;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
// 3. Smart Endpoint Resolution: Auto-detect domain/host if none is specified
|
|
33
|
+
let defaultEndpoint = 'https://db.nexabase.id';
|
|
34
|
+
if (typeof window !== 'undefined' && window.location) {
|
|
35
|
+
// Default to the current page origin in dev/proxy preview environments so it auto-points to /api/*
|
|
36
|
+
defaultEndpoint = window.location.origin;
|
|
37
|
+
}
|
|
38
|
+
this.endpoint = config.endpoint || defaultEndpoint;
|
|
33
39
|
this.client = axios_1.default.create({
|
|
34
40
|
baseURL: this.endpoint,
|
|
35
41
|
headers: {
|
|
36
42
|
'Content-Type': 'application/json'
|
|
37
43
|
}
|
|
38
44
|
});
|
|
39
|
-
// Request interceptor to automatically
|
|
45
|
+
// Request interceptor to attach bearer token and automatically resolve the axios boundary issue for file uploads
|
|
40
46
|
this.client.interceptors.request.use((req) => {
|
|
41
47
|
if (this.token) {
|
|
42
48
|
req.headers.Authorization = `Bearer ${this.token}`;
|
|
43
49
|
}
|
|
50
|
+
// 2. Fix Axios Multipart Boundary on File Upload
|
|
51
|
+
// If Content-Type is multipart/form-data, delete it so axios/browser automatically appends the boundary header
|
|
52
|
+
if (req.headers && req.headers['Content-Type'] === 'multipart/form-data') {
|
|
53
|
+
delete req.headers['Content-Type'];
|
|
54
|
+
}
|
|
44
55
|
return req;
|
|
45
56
|
});
|
|
46
57
|
}
|
|
@@ -151,14 +162,36 @@ class NexaApp {
|
|
|
151
162
|
// -----------------------------------------------------------------
|
|
152
163
|
storage() {
|
|
153
164
|
return {
|
|
154
|
-
ref: (path = '') =>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
ref: (path = '') => {
|
|
166
|
+
const name = path.substring(path.lastIndexOf('/') + 1) || path;
|
|
167
|
+
return {
|
|
168
|
+
name,
|
|
169
|
+
fullPath: path,
|
|
170
|
+
put: async (file, onProgress) => {
|
|
171
|
+
const formData = new FormData();
|
|
172
|
+
formData.append('file', file);
|
|
173
|
+
const response = await this.client.post(`/api/project/${this.projectId}/storage/upload`, formData, {
|
|
174
|
+
// We omit Content-Type headers here (interceptor strips it anyway) so the browser naturally generates the boundary
|
|
175
|
+
onUploadProgress: (progressEvent) => {
|
|
176
|
+
if (onProgress && progressEvent.total) {
|
|
177
|
+
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
|
178
|
+
onProgress(percent);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
return response.data;
|
|
183
|
+
},
|
|
184
|
+
delete: async () => {
|
|
185
|
+
const response = await this.client.delete(`/api/project/${this.projectId}/storage/${encodeURIComponent(name)}`);
|
|
186
|
+
return response.data;
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
refFromURL: (url) => {
|
|
191
|
+
const cleanUrl = url.split('?')[0];
|
|
192
|
+
const filename = decodeURIComponent(cleanUrl.substring(cleanUrl.lastIndexOf('/') + 1));
|
|
193
|
+
return this.storage().ref(filename);
|
|
194
|
+
}
|
|
162
195
|
};
|
|
163
196
|
}
|
|
164
197
|
// -----------------------------------------------------------------
|
|
@@ -379,6 +412,70 @@ const limit = (limitValue) => {
|
|
|
379
412
|
return { type: 'limit', limitValue };
|
|
380
413
|
};
|
|
381
414
|
exports.limit = limit;
|
|
415
|
+
// 1. Helper to retrieve docs from the local Offline cache (Cache-First)
|
|
416
|
+
const getCachedDocs = (queryOrCollection) => {
|
|
417
|
+
const db = queryOrCollection.db;
|
|
418
|
+
const collectionPath = queryOrCollection.path;
|
|
419
|
+
const constraints = 'constraints' in queryOrCollection ? queryOrCollection.constraints || [] : [];
|
|
420
|
+
const results = [];
|
|
421
|
+
Object.keys(db._firestoreCache).forEach((p) => {
|
|
422
|
+
if (p.startsWith(collectionPath + '/')) {
|
|
423
|
+
const parts = p.split('/');
|
|
424
|
+
const docId = parts[parts.length - 1];
|
|
425
|
+
const docRef = (0, exports.doc)(db, `${collectionPath}/${docId}`);
|
|
426
|
+
results.push({
|
|
427
|
+
id: docId,
|
|
428
|
+
ref: docRef,
|
|
429
|
+
exists: () => true,
|
|
430
|
+
data: () => db._firestoreCache[p]
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
// Simple offline filters
|
|
435
|
+
let filtered = results;
|
|
436
|
+
for (const c of constraints) {
|
|
437
|
+
if (c.type === 'where' && c.fieldPath && c.opStr) {
|
|
438
|
+
filtered = filtered.filter((docItem) => {
|
|
439
|
+
const val = docItem.data()[c.fieldPath];
|
|
440
|
+
if (c.opStr === '==')
|
|
441
|
+
return val === c.value;
|
|
442
|
+
if (c.opStr === '>')
|
|
443
|
+
return val > c.value;
|
|
444
|
+
if (c.opStr === '<')
|
|
445
|
+
return val < c.value;
|
|
446
|
+
if (c.opStr === '>=')
|
|
447
|
+
return val >= c.value;
|
|
448
|
+
if (c.opStr === '<=')
|
|
449
|
+
return val <= c.value;
|
|
450
|
+
if (c.opStr === '!=')
|
|
451
|
+
return val !== c.value;
|
|
452
|
+
return true;
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
docs: filtered,
|
|
458
|
+
forEach: (cb) => filtered.forEach(cb),
|
|
459
|
+
empty: filtered.length === 0,
|
|
460
|
+
size: filtered.length,
|
|
461
|
+
docChanges: () => filtered.map(doc => ({ type: 'added', doc }))
|
|
462
|
+
};
|
|
463
|
+
};
|
|
464
|
+
exports.getCachedDocs = getCachedDocs;
|
|
465
|
+
// 1. Helper to retrieve single doc from the local Offline cache (Cache-First)
|
|
466
|
+
const getCachedDoc = (docRef) => {
|
|
467
|
+
const db = docRef.db;
|
|
468
|
+
const segments = docRef.path.split('/');
|
|
469
|
+
const docId = segments[segments.length - 1];
|
|
470
|
+
const data = db._firestoreCache[docRef.path];
|
|
471
|
+
return {
|
|
472
|
+
id: docId,
|
|
473
|
+
ref: docRef,
|
|
474
|
+
exists: () => !!data,
|
|
475
|
+
data: () => (data || null)
|
|
476
|
+
};
|
|
477
|
+
};
|
|
478
|
+
exports.getCachedDoc = getCachedDoc;
|
|
382
479
|
const getDocs = async (queryOrCollection) => {
|
|
383
480
|
const db = queryOrCollection.db;
|
|
384
481
|
if (db._initPromise)
|
|
@@ -394,10 +491,15 @@ const getDocs = async (queryOrCollection) => {
|
|
|
394
491
|
for (const d of docs) {
|
|
395
492
|
await db._setCache(`${collectionPath}/${d.id}`, d.fields);
|
|
396
493
|
}
|
|
397
|
-
const docsArray = docs.map((d) =>
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
494
|
+
const docsArray = docs.map((d) => {
|
|
495
|
+
const docRef = (0, exports.doc)(db, `${collectionPath}/${d.id}`);
|
|
496
|
+
return {
|
|
497
|
+
id: d.id,
|
|
498
|
+
ref: docRef,
|
|
499
|
+
exists: () => true,
|
|
500
|
+
data: () => d.fields
|
|
501
|
+
};
|
|
502
|
+
});
|
|
401
503
|
return {
|
|
402
504
|
docs: docsArray,
|
|
403
505
|
forEach: (cb) => docsArray.forEach(cb),
|
|
@@ -410,54 +512,7 @@ const getDocs = async (queryOrCollection) => {
|
|
|
410
512
|
if (err && err.response) {
|
|
411
513
|
throw err;
|
|
412
514
|
}
|
|
413
|
-
|
|
414
|
-
Object.keys(db._firestoreCache).forEach((p) => {
|
|
415
|
-
if (p.startsWith(collectionPath + '/')) {
|
|
416
|
-
const parts = p.split('/');
|
|
417
|
-
results.push({
|
|
418
|
-
id: parts[parts.length - 1],
|
|
419
|
-
data: () => db._firestoreCache[p]
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
// Simple offline filters
|
|
424
|
-
let filtered = results;
|
|
425
|
-
for (const c of constraints) {
|
|
426
|
-
if (c.type === 'where' && c.fieldPath && c.opStr) {
|
|
427
|
-
filtered = filtered.filter((docItem) => {
|
|
428
|
-
const val = docItem.data()[c.fieldPath];
|
|
429
|
-
if (c.opStr === '==')
|
|
430
|
-
return val === c.value;
|
|
431
|
-
if (c.opStr === '>')
|
|
432
|
-
return val > c.value;
|
|
433
|
-
if (c.opStr === '<')
|
|
434
|
-
return val < c.value;
|
|
435
|
-
if (c.opStr === '>=')
|
|
436
|
-
return val >= c.value;
|
|
437
|
-
if (c.opStr === '<=')
|
|
438
|
-
return val <= c.value;
|
|
439
|
-
if (c.opStr === '!=')
|
|
440
|
-
return val !== c.value;
|
|
441
|
-
if (c.opStr === 'in')
|
|
442
|
-
return Array.isArray(c.value) && c.value.includes(val);
|
|
443
|
-
if (c.opStr === 'not-in')
|
|
444
|
-
return Array.isArray(c.value) && !c.value.includes(val);
|
|
445
|
-
if (c.opStr === 'array-contains')
|
|
446
|
-
return Array.isArray(val) && val.includes(c.value);
|
|
447
|
-
if (c.opStr === 'array-contains-any') {
|
|
448
|
-
return Array.isArray(val) && Array.isArray(c.value) && val.some((v) => c.value.includes(v));
|
|
449
|
-
}
|
|
450
|
-
return true;
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
return {
|
|
455
|
-
docs: filtered,
|
|
456
|
-
forEach: (cb) => filtered.forEach(cb),
|
|
457
|
-
empty: filtered.length === 0,
|
|
458
|
-
size: filtered.length,
|
|
459
|
-
docChanges: () => filtered.map(doc => ({ type: 'added', doc }))
|
|
460
|
-
};
|
|
515
|
+
return (0, exports.getCachedDocs)(queryOrCollection);
|
|
461
516
|
}
|
|
462
517
|
};
|
|
463
518
|
exports.getDocs = getDocs;
|
|
@@ -465,18 +520,24 @@ const getDoc = async (docRef) => {
|
|
|
465
520
|
const db = docRef.db;
|
|
466
521
|
if (db._initPromise)
|
|
467
522
|
await db._initPromise;
|
|
523
|
+
const segments = docRef.path.split('/');
|
|
524
|
+
const docId = segments[segments.length - 1];
|
|
468
525
|
try {
|
|
469
526
|
const res = await db.client.get(`/api/firestore/${db.projectId}/document?docPath=${encodeURIComponent(docRef.path)}`);
|
|
470
527
|
const data = res.data.data;
|
|
471
528
|
await db._setCache(docRef.path, data);
|
|
472
|
-
return {
|
|
529
|
+
return {
|
|
530
|
+
id: docId,
|
|
531
|
+
ref: docRef,
|
|
532
|
+
exists: () => !!data,
|
|
533
|
+
data: () => data
|
|
534
|
+
};
|
|
473
535
|
}
|
|
474
536
|
catch (err) {
|
|
475
537
|
if (err && err.response) {
|
|
476
538
|
throw err;
|
|
477
539
|
}
|
|
478
|
-
|
|
479
|
-
return { exists: () => !!data, data: () => (data || null) };
|
|
540
|
+
return (0, exports.getCachedDoc)(docRef);
|
|
480
541
|
}
|
|
481
542
|
};
|
|
482
543
|
exports.getDoc = getDoc;
|
|
@@ -573,6 +634,19 @@ const onSnapshot = (ref, callback) => {
|
|
|
573
634
|
await db._initPromise;
|
|
574
635
|
db._ensureFirestoreSSE();
|
|
575
636
|
const pathKey = ref.path;
|
|
637
|
+
// 1. Native Offline Cache/Persistence Priority: Emits cached snapshot IMMEDIATELY to trigger instant optimistic rendering!
|
|
638
|
+
if (ref.type === 'collection' || ref.type === 'query') {
|
|
639
|
+
const cached = (0, exports.getCachedDocs)(ref);
|
|
640
|
+
if (cached && cached.docs.length > 0) {
|
|
641
|
+
callback(cached);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
const cached = (0, exports.getCachedDoc)(ref);
|
|
646
|
+
if (cached && cached.exists()) {
|
|
647
|
+
callback(cached);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
576
650
|
const triggerCallback = async () => {
|
|
577
651
|
if (ref.type === 'collection' || ref.type === 'query') {
|
|
578
652
|
const docs = await (0, exports.getDocs)(ref);
|
|
@@ -583,6 +657,7 @@ const onSnapshot = (ref, callback) => {
|
|
|
583
657
|
callback(docData);
|
|
584
658
|
}
|
|
585
659
|
};
|
|
660
|
+
// 2. Fetch the latest live server data and update cache + trigger the callback again
|
|
586
661
|
triggerCallback();
|
|
587
662
|
const listenerId = Math.random().toString(36).substring(2, 9);
|
|
588
663
|
db._snapshotCallbacks[`${ref.type}_${listenerId}_${pathKey}`] = triggerCallback;
|
|
@@ -597,6 +672,17 @@ const onSnapshot = (ref, callback) => {
|
|
|
597
672
|
return () => unsubscribe();
|
|
598
673
|
};
|
|
599
674
|
exports.onSnapshot = onSnapshot;
|
|
675
|
+
// Supporting standard Firebase modular options functions
|
|
676
|
+
const enableIndexedDbPersistence = async (db) => {
|
|
677
|
+
if (db._initPromise)
|
|
678
|
+
await db._initPromise;
|
|
679
|
+
};
|
|
680
|
+
exports.enableIndexedDbPersistence = enableIndexedDbPersistence;
|
|
681
|
+
const enableOfflinePersistence = async (db) => {
|
|
682
|
+
if (db._initPromise)
|
|
683
|
+
await db._initPromise;
|
|
684
|
+
};
|
|
685
|
+
exports.enableOfflinePersistence = enableOfflinePersistence;
|
|
600
686
|
const writeBatch = (db) => {
|
|
601
687
|
const operations = [];
|
|
602
688
|
const batchObj = {
|
package/package.json
CHANGED