nexabase-console 1.0.4 → 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 +9 -1
- package/dist/index.js +123 -61
- 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>;
|
|
@@ -137,11 +141,15 @@ export declare const query: <T = any>(queryObject: Query<T> | CollectionReferenc
|
|
|
137
141
|
export declare const where: (fieldPath: string, opStr: "==" | ">" | "<" | ">=" | "<=" | "!=", value: any) => QueryConstraint;
|
|
138
142
|
export declare const orderBy: (fieldPath: string, directionStr?: "asc" | "desc") => QueryConstraint;
|
|
139
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>;
|
|
140
146
|
export declare const getDocs: <T = any>(queryOrCollection: Query<T> | CollectionReference<T>) => Promise<QuerySnapshot<T>>;
|
|
141
147
|
export declare const getDoc: <T = any>(docRef: DocumentReference<T>) => Promise<DocumentSnapshot<T>>;
|
|
142
148
|
export declare const setDoc: (docRef: DocumentReference, data: any) => Promise<any>;
|
|
143
149
|
export declare const updateDoc: (docRef: DocumentReference, data: any) => Promise<any>;
|
|
144
150
|
export declare const deleteDoc: (docRef: DocumentReference) => Promise<any>;
|
|
145
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>;
|
|
146
154
|
export declare const writeBatch: (db: NexaApp) => WriteBatch;
|
|
147
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
|
}
|
|
@@ -160,9 +171,7 @@ class NexaApp {
|
|
|
160
171
|
const formData = new FormData();
|
|
161
172
|
formData.append('file', file);
|
|
162
173
|
const response = await this.client.post(`/api/project/${this.projectId}/storage/upload`, formData, {
|
|
163
|
-
headers
|
|
164
|
-
'Content-Type': 'multipart/form-data'
|
|
165
|
-
},
|
|
174
|
+
// We omit Content-Type headers here (interceptor strips it anyway) so the browser naturally generates the boundary
|
|
166
175
|
onUploadProgress: (progressEvent) => {
|
|
167
176
|
if (onProgress && progressEvent.total) {
|
|
168
177
|
const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
|
@@ -403,6 +412,70 @@ const limit = (limitValue) => {
|
|
|
403
412
|
return { type: 'limit', limitValue };
|
|
404
413
|
};
|
|
405
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;
|
|
406
479
|
const getDocs = async (queryOrCollection) => {
|
|
407
480
|
const db = queryOrCollection.db;
|
|
408
481
|
if (db._initPromise)
|
|
@@ -418,10 +491,15 @@ const getDocs = async (queryOrCollection) => {
|
|
|
418
491
|
for (const d of docs) {
|
|
419
492
|
await db._setCache(`${collectionPath}/${d.id}`, d.fields);
|
|
420
493
|
}
|
|
421
|
-
const docsArray = docs.map((d) =>
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
+
});
|
|
425
503
|
return {
|
|
426
504
|
docs: docsArray,
|
|
427
505
|
forEach: (cb) => docsArray.forEach(cb),
|
|
@@ -434,54 +512,7 @@ const getDocs = async (queryOrCollection) => {
|
|
|
434
512
|
if (err && err.response) {
|
|
435
513
|
throw err;
|
|
436
514
|
}
|
|
437
|
-
|
|
438
|
-
Object.keys(db._firestoreCache).forEach((p) => {
|
|
439
|
-
if (p.startsWith(collectionPath + '/')) {
|
|
440
|
-
const parts = p.split('/');
|
|
441
|
-
results.push({
|
|
442
|
-
id: parts[parts.length - 1],
|
|
443
|
-
data: () => db._firestoreCache[p]
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
// Simple offline filters
|
|
448
|
-
let filtered = results;
|
|
449
|
-
for (const c of constraints) {
|
|
450
|
-
if (c.type === 'where' && c.fieldPath && c.opStr) {
|
|
451
|
-
filtered = filtered.filter((docItem) => {
|
|
452
|
-
const val = docItem.data()[c.fieldPath];
|
|
453
|
-
if (c.opStr === '==')
|
|
454
|
-
return val === c.value;
|
|
455
|
-
if (c.opStr === '>')
|
|
456
|
-
return val > c.value;
|
|
457
|
-
if (c.opStr === '<')
|
|
458
|
-
return val < c.value;
|
|
459
|
-
if (c.opStr === '>=')
|
|
460
|
-
return val >= c.value;
|
|
461
|
-
if (c.opStr === '<=')
|
|
462
|
-
return val <= c.value;
|
|
463
|
-
if (c.opStr === '!=')
|
|
464
|
-
return val !== c.value;
|
|
465
|
-
if (c.opStr === 'in')
|
|
466
|
-
return Array.isArray(c.value) && c.value.includes(val);
|
|
467
|
-
if (c.opStr === 'not-in')
|
|
468
|
-
return Array.isArray(c.value) && !c.value.includes(val);
|
|
469
|
-
if (c.opStr === 'array-contains')
|
|
470
|
-
return Array.isArray(val) && val.includes(c.value);
|
|
471
|
-
if (c.opStr === 'array-contains-any') {
|
|
472
|
-
return Array.isArray(val) && Array.isArray(c.value) && val.some((v) => c.value.includes(v));
|
|
473
|
-
}
|
|
474
|
-
return true;
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return {
|
|
479
|
-
docs: filtered,
|
|
480
|
-
forEach: (cb) => filtered.forEach(cb),
|
|
481
|
-
empty: filtered.length === 0,
|
|
482
|
-
size: filtered.length,
|
|
483
|
-
docChanges: () => filtered.map(doc => ({ type: 'added', doc }))
|
|
484
|
-
};
|
|
515
|
+
return (0, exports.getCachedDocs)(queryOrCollection);
|
|
485
516
|
}
|
|
486
517
|
};
|
|
487
518
|
exports.getDocs = getDocs;
|
|
@@ -489,18 +520,24 @@ const getDoc = async (docRef) => {
|
|
|
489
520
|
const db = docRef.db;
|
|
490
521
|
if (db._initPromise)
|
|
491
522
|
await db._initPromise;
|
|
523
|
+
const segments = docRef.path.split('/');
|
|
524
|
+
const docId = segments[segments.length - 1];
|
|
492
525
|
try {
|
|
493
526
|
const res = await db.client.get(`/api/firestore/${db.projectId}/document?docPath=${encodeURIComponent(docRef.path)}`);
|
|
494
527
|
const data = res.data.data;
|
|
495
528
|
await db._setCache(docRef.path, data);
|
|
496
|
-
return {
|
|
529
|
+
return {
|
|
530
|
+
id: docId,
|
|
531
|
+
ref: docRef,
|
|
532
|
+
exists: () => !!data,
|
|
533
|
+
data: () => data
|
|
534
|
+
};
|
|
497
535
|
}
|
|
498
536
|
catch (err) {
|
|
499
537
|
if (err && err.response) {
|
|
500
538
|
throw err;
|
|
501
539
|
}
|
|
502
|
-
|
|
503
|
-
return { exists: () => !!data, data: () => (data || null) };
|
|
540
|
+
return (0, exports.getCachedDoc)(docRef);
|
|
504
541
|
}
|
|
505
542
|
};
|
|
506
543
|
exports.getDoc = getDoc;
|
|
@@ -597,6 +634,19 @@ const onSnapshot = (ref, callback) => {
|
|
|
597
634
|
await db._initPromise;
|
|
598
635
|
db._ensureFirestoreSSE();
|
|
599
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
|
+
}
|
|
600
650
|
const triggerCallback = async () => {
|
|
601
651
|
if (ref.type === 'collection' || ref.type === 'query') {
|
|
602
652
|
const docs = await (0, exports.getDocs)(ref);
|
|
@@ -607,6 +657,7 @@ const onSnapshot = (ref, callback) => {
|
|
|
607
657
|
callback(docData);
|
|
608
658
|
}
|
|
609
659
|
};
|
|
660
|
+
// 2. Fetch the latest live server data and update cache + trigger the callback again
|
|
610
661
|
triggerCallback();
|
|
611
662
|
const listenerId = Math.random().toString(36).substring(2, 9);
|
|
612
663
|
db._snapshotCallbacks[`${ref.type}_${listenerId}_${pathKey}`] = triggerCallback;
|
|
@@ -621,6 +672,17 @@ const onSnapshot = (ref, callback) => {
|
|
|
621
672
|
return () => unsubscribe();
|
|
622
673
|
};
|
|
623
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;
|
|
624
686
|
const writeBatch = (db) => {
|
|
625
687
|
const operations = [];
|
|
626
688
|
const batchObj = {
|
package/package.json
CHANGED