edmaxlabs-core 2.4.9 → 2.5.1
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.cjs +332 -383
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.mjs +332 -383
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// src/authentication/Credentials.ts
|
|
2
2
|
var Credentials = class _Credentials {
|
|
3
|
-
constructor(uid, displayInfo, createdAt, updatedAt, lastLogged) {
|
|
3
|
+
constructor(uid, displayInfo, createdAt, updatedAt, lastLogged, logged) {
|
|
4
4
|
this.uid = uid;
|
|
5
5
|
this.displayInfo = displayInfo;
|
|
6
6
|
this.createdAt = createdAt;
|
|
7
7
|
this.updatedAt = updatedAt;
|
|
8
8
|
this.lastLogged = lastLogged;
|
|
9
|
+
this.logged = logged === true;
|
|
9
10
|
}
|
|
10
11
|
static fromMap(map) {
|
|
11
12
|
const uid = map.id ?? map.uid ?? "";
|
|
@@ -14,12 +15,14 @@ var Credentials = class _Credentials {
|
|
|
14
15
|
const createdAt = data.createdAt;
|
|
15
16
|
const updatedAt = data.updatedAt;
|
|
16
17
|
const lastLogged = data.lastLogged;
|
|
18
|
+
const logged = data.logged;
|
|
17
19
|
const result = new _Credentials(
|
|
18
20
|
uid,
|
|
19
21
|
d_info,
|
|
20
22
|
createdAt,
|
|
21
23
|
updatedAt,
|
|
22
|
-
lastLogged
|
|
24
|
+
lastLogged,
|
|
25
|
+
logged
|
|
23
26
|
);
|
|
24
27
|
return result;
|
|
25
28
|
}
|
|
@@ -29,7 +32,8 @@ var Credentials = class _Credentials {
|
|
|
29
32
|
displayInfo: this.displayInfo,
|
|
30
33
|
createdAt: this.createdAt,
|
|
31
34
|
updatedAt: this.updatedAt,
|
|
32
|
-
lastLogged: this.lastLogged
|
|
35
|
+
lastLogged: this.lastLogged,
|
|
36
|
+
logged: this.logged
|
|
33
37
|
};
|
|
34
38
|
}
|
|
35
39
|
};
|
|
@@ -126,9 +130,16 @@ var _Authentication = class _Authentication {
|
|
|
126
130
|
const userRef = await app?.collection("users").add({
|
|
127
131
|
email,
|
|
128
132
|
password,
|
|
129
|
-
token: this.client?.getConfig().token,
|
|
130
133
|
logged: true,
|
|
131
|
-
lastLogged: Date.now()
|
|
134
|
+
lastLogged: Date.now(),
|
|
135
|
+
displayInfo: {
|
|
136
|
+
firstname: email.split("@")[0],
|
|
137
|
+
lastname: "",
|
|
138
|
+
surname: "",
|
|
139
|
+
profile: "./logo.png",
|
|
140
|
+
email,
|
|
141
|
+
phone: ""
|
|
142
|
+
}
|
|
132
143
|
});
|
|
133
144
|
if (!userRef?.id || userRef?.id === "") {
|
|
134
145
|
throw new Error("Something went wrong try Again.");
|
|
@@ -158,13 +169,13 @@ var _Authentication = class _Authentication {
|
|
|
158
169
|
throw new Error("User Not Found.");
|
|
159
170
|
}
|
|
160
171
|
const _data = data[0];
|
|
161
|
-
await app?.collection("users").doc(
|
|
172
|
+
await app?.collection("users").doc(_data.id).update({
|
|
162
173
|
logged: true,
|
|
163
174
|
lastLogged: Date.now()
|
|
164
175
|
});
|
|
165
176
|
this.eUser = Credentials.fromMap(_data);
|
|
166
177
|
this.saveCredentials(this.eUser);
|
|
167
|
-
return Credentials.fromMap(
|
|
178
|
+
return Credentials.fromMap(_data);
|
|
168
179
|
};
|
|
169
180
|
this.deleteUser = async () => {
|
|
170
181
|
if (!this.eUser) {
|
|
@@ -289,6 +300,7 @@ var _Authentication = class _Authentication {
|
|
|
289
300
|
return;
|
|
290
301
|
userDocUnsubscribe = userRef.onSnapshot(
|
|
291
302
|
(snapshot, change) => {
|
|
303
|
+
console.log(snapshot);
|
|
292
304
|
if (change === "delete") {
|
|
293
305
|
this.saveCredentials(null);
|
|
294
306
|
onSignOut?.();
|
|
@@ -348,308 +360,6 @@ var DocumentSnapshot = class _DocumentSnapshot {
|
|
|
348
360
|
}
|
|
349
361
|
};
|
|
350
362
|
|
|
351
|
-
// src/database/Timestamp.ts
|
|
352
|
-
var Timestamp = class _Timestamp {
|
|
353
|
-
constructor(seconds, nanoseconds = 0) {
|
|
354
|
-
this.seconds = seconds;
|
|
355
|
-
this.nanoseconds = nanoseconds;
|
|
356
|
-
}
|
|
357
|
-
static now() {
|
|
358
|
-
return _Timestamp.fromMillis(Date.now());
|
|
359
|
-
}
|
|
360
|
-
static fromDate(date) {
|
|
361
|
-
return new _Timestamp(
|
|
362
|
-
Math.floor(date.getTime() / 1e3),
|
|
363
|
-
date.getTime() % 1e3 * 1e6
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
static fromMillis(ms) {
|
|
367
|
-
return new _Timestamp(Math.floor(ms / 1e3), ms % 1e3 * 1e6);
|
|
368
|
-
}
|
|
369
|
-
static fromMongo(dateObj) {
|
|
370
|
-
return _Timestamp.fromMillis(dateObj.getTime());
|
|
371
|
-
}
|
|
372
|
-
static fromJSON(obj) {
|
|
373
|
-
return new _Timestamp(obj.seconds, obj.nanoseconds);
|
|
374
|
-
}
|
|
375
|
-
toDate() {
|
|
376
|
-
return new Date(this.toMillis());
|
|
377
|
-
}
|
|
378
|
-
toMillis() {
|
|
379
|
-
return this.seconds * 1e3 + Math.floor(this.nanoseconds / 1e6);
|
|
380
|
-
}
|
|
381
|
-
sinceEpoch() {
|
|
382
|
-
return this.seconds;
|
|
383
|
-
}
|
|
384
|
-
toMongo() {
|
|
385
|
-
return new Date(this.toMillis());
|
|
386
|
-
}
|
|
387
|
-
format(pattern = "dd-MM-yyyy HH:mm:ss") {
|
|
388
|
-
const d = this.toDate();
|
|
389
|
-
const monthsShort = [
|
|
390
|
-
"Jan",
|
|
391
|
-
"Feb",
|
|
392
|
-
"Mar",
|
|
393
|
-
"Apr",
|
|
394
|
-
"May",
|
|
395
|
-
"Jun",
|
|
396
|
-
"Jul",
|
|
397
|
-
"Aug",
|
|
398
|
-
"Sep",
|
|
399
|
-
"Oct",
|
|
400
|
-
"Nov",
|
|
401
|
-
"Dec"
|
|
402
|
-
];
|
|
403
|
-
const monthsLong = [
|
|
404
|
-
"January",
|
|
405
|
-
"February",
|
|
406
|
-
"March",
|
|
407
|
-
"April",
|
|
408
|
-
"May",
|
|
409
|
-
"June",
|
|
410
|
-
"July",
|
|
411
|
-
"August",
|
|
412
|
-
"September",
|
|
413
|
-
"October",
|
|
414
|
-
"November",
|
|
415
|
-
"December"
|
|
416
|
-
];
|
|
417
|
-
const hours = d.getHours();
|
|
418
|
-
const hours12 = hours % 12 === 0 ? 12 : hours % 12;
|
|
419
|
-
const ampm = hours < 12 ? "AM" : "PM";
|
|
420
|
-
const map = {
|
|
421
|
-
dd: String(d.getDate()).padStart(2, "0"),
|
|
422
|
-
MM: String(d.getMonth() + 1).padStart(2, "0"),
|
|
423
|
-
yyyy: d.getFullYear(),
|
|
424
|
-
HH: String(d.getHours()).padStart(2, "0"),
|
|
425
|
-
mm: String(d.getMinutes()).padStart(2, "0"),
|
|
426
|
-
ss: String(d.getSeconds()).padStart(2, "0"),
|
|
427
|
-
SSS: String(d.getMilliseconds()).padStart(3, "0"),
|
|
428
|
-
MMM: monthsShort[d.getMonth()],
|
|
429
|
-
MMMM: monthsLong[d.getMonth()],
|
|
430
|
-
a: ampm.toLowerCase(),
|
|
431
|
-
// am/pm
|
|
432
|
-
A: ampm
|
|
433
|
-
// AM/PM
|
|
434
|
-
};
|
|
435
|
-
return pattern.replace(
|
|
436
|
-
/MMMM|MMM|dd|MM|yyyy|HH|mm|ss|SSS|a|A/g,
|
|
437
|
-
(match) => String(map[match])
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
compare(other) {
|
|
441
|
-
if (this.seconds !== other.seconds) {
|
|
442
|
-
return this.seconds - other.seconds;
|
|
443
|
-
}
|
|
444
|
-
return this.nanoseconds - other.nanoseconds;
|
|
445
|
-
}
|
|
446
|
-
relative(fmt = "dd-MM-yyyy HH:mm:ss") {
|
|
447
|
-
const now = Date.now();
|
|
448
|
-
const diff = now - this.toMillis();
|
|
449
|
-
const sec = Math.floor(diff / 1e3);
|
|
450
|
-
if (sec < 60)
|
|
451
|
-
return `${sec}s ago`;
|
|
452
|
-
const min = Math.floor(sec / 60);
|
|
453
|
-
if (min < 60)
|
|
454
|
-
return `${min}m ago`;
|
|
455
|
-
const hrs = Math.floor(min / 60);
|
|
456
|
-
if (hrs < 24)
|
|
457
|
-
return `${hrs}h ago`;
|
|
458
|
-
const days = Math.floor(hrs / 24);
|
|
459
|
-
if (days < 3)
|
|
460
|
-
return `${days}d ago`;
|
|
461
|
-
return this.format(fmt);
|
|
462
|
-
}
|
|
463
|
-
add({
|
|
464
|
-
seconds = 0,
|
|
465
|
-
minutes = 0,
|
|
466
|
-
hours = 0,
|
|
467
|
-
days = 0
|
|
468
|
-
}) {
|
|
469
|
-
const totalMs = seconds * 1e3 + minutes * 6e4 + hours * 36e5 + days * 864e5;
|
|
470
|
-
return _Timestamp.fromMillis(this.toMillis() + totalMs);
|
|
471
|
-
}
|
|
472
|
-
weekdayName() {
|
|
473
|
-
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.toDate().getDay()];
|
|
474
|
-
}
|
|
475
|
-
toJSON() {
|
|
476
|
-
return {
|
|
477
|
-
_type: "timestamp",
|
|
478
|
-
seconds: this.seconds,
|
|
479
|
-
nanoseconds: this.nanoseconds
|
|
480
|
-
};
|
|
481
|
-
}
|
|
482
|
-
toString() {
|
|
483
|
-
return this.format("yyyy-MM-dd HH:mm:ss");
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
// src/database/ArraySnapshot.ts
|
|
488
|
-
var ArraySnapshot = class _ArraySnapshot {
|
|
489
|
-
constructor(id = "", index, data, dt) {
|
|
490
|
-
this.data = data;
|
|
491
|
-
this.id = id;
|
|
492
|
-
this.dt = dt ?? Timestamp.now();
|
|
493
|
-
}
|
|
494
|
-
static fromMap(map) {
|
|
495
|
-
const index = map.index ?? map.index ?? -1;
|
|
496
|
-
const id = map.id ?? map._id ?? "";
|
|
497
|
-
const document2 = map.data ?? map.document ?? map;
|
|
498
|
-
return new _ArraySnapshot(id, index, document2);
|
|
499
|
-
}
|
|
500
|
-
toMap() {
|
|
501
|
-
return {
|
|
502
|
-
id: this.id ?? "",
|
|
503
|
-
data: this.data,
|
|
504
|
-
dt: this.dt
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
};
|
|
508
|
-
|
|
509
|
-
// src/database/Array.ts
|
|
510
|
-
var Array2 = class {
|
|
511
|
-
constructor(app, collection, key, id) {
|
|
512
|
-
this.app = app;
|
|
513
|
-
this.collection = collection;
|
|
514
|
-
this.key = key;
|
|
515
|
-
this.docID = id;
|
|
516
|
-
this.persistence = app.offline().persistence;
|
|
517
|
-
this.syncEngine = app.offline().syncEngine;
|
|
518
|
-
this.localStore = app.offline().localStore;
|
|
519
|
-
}
|
|
520
|
-
/**
|
|
521
|
-
* Get current array elements (offline-first: prefers local cache)
|
|
522
|
-
*/
|
|
523
|
-
async show() {
|
|
524
|
-
if (this.persistence) {
|
|
525
|
-
const doc = await this.persistence.getDoc(this.collection, this.docID);
|
|
526
|
-
if (doc?.exists && !doc.deleted) {
|
|
527
|
-
const arrayData = doc.data[this.key] || [];
|
|
528
|
-
return arrayData.map((item) => ArraySnapshot.fromMap(item));
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
if (typeof navigator === "undefined" || navigator.onLine) {
|
|
532
|
-
this.refreshFromRemote().catch(() => {
|
|
533
|
-
});
|
|
534
|
-
}
|
|
535
|
-
return [];
|
|
536
|
-
}
|
|
537
|
-
async refreshFromRemote() {
|
|
538
|
-
try {
|
|
539
|
-
const res = await new HttpsRequest({
|
|
540
|
-
method: "POST" /* POST */,
|
|
541
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/show`,
|
|
542
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
543
|
-
body: {
|
|
544
|
-
collection: this.collection,
|
|
545
|
-
document: this.docID,
|
|
546
|
-
array: this.key
|
|
547
|
-
}
|
|
548
|
-
}).sendRequest();
|
|
549
|
-
if (!res?.success || !globalThis.Array.isArray(res.documents)) {
|
|
550
|
-
return [];
|
|
551
|
-
}
|
|
552
|
-
const snapshots = res.documents.filter((d) => d != null).map((d) => ArraySnapshot.fromMap(d));
|
|
553
|
-
if (this.persistence) {
|
|
554
|
-
const currentDoc = await this.persistence.getDoc(this.collection, this.docID);
|
|
555
|
-
if (currentDoc) {
|
|
556
|
-
await this.persistence.upsertDoc({
|
|
557
|
-
...currentDoc,
|
|
558
|
-
data: { ...currentDoc.data, [this.key]: res.documents },
|
|
559
|
-
pending: 0,
|
|
560
|
-
status: "synced",
|
|
561
|
-
lastSyncedAt: Date.now()
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
return snapshots;
|
|
566
|
-
} catch {
|
|
567
|
-
return [];
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
async push(data, id) {
|
|
571
|
-
const payload = {
|
|
572
|
-
data,
|
|
573
|
-
id: id || this.persistence?.createLocalId?.() || void 0,
|
|
574
|
-
dt: Timestamp.now()
|
|
575
|
-
};
|
|
576
|
-
if (this.persistence) {
|
|
577
|
-
await this.persistence.enqueueMutation({
|
|
578
|
-
mutationId: this.persistence.createMutationId(),
|
|
579
|
-
collection: this.collection,
|
|
580
|
-
documentId: this.docID,
|
|
581
|
-
type: "array_push",
|
|
582
|
-
// custom type
|
|
583
|
-
payload: { arrayKey: this.key, ...payload }
|
|
584
|
-
});
|
|
585
|
-
this.syncEngine?.flush().catch(console.error);
|
|
586
|
-
const doc = await this.persistence.getDoc(this.collection, this.docID);
|
|
587
|
-
if (doc) {
|
|
588
|
-
const updatedArray = [...doc.data[this.key] || [], payload];
|
|
589
|
-
const snap = ArraySnapshot.fromMap(payload);
|
|
590
|
-
this.localStore?.emitDocument(
|
|
591
|
-
this.collection,
|
|
592
|
-
this.docID,
|
|
593
|
-
DocumentSnapshot.fromMap({ ...doc.data, [this.key]: updatedArray }),
|
|
594
|
-
"update"
|
|
595
|
-
);
|
|
596
|
-
}
|
|
597
|
-
return ArraySnapshot.fromMap(payload);
|
|
598
|
-
}
|
|
599
|
-
const res = await new HttpsRequest({
|
|
600
|
-
method: "POST" /* POST */,
|
|
601
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/push`,
|
|
602
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
603
|
-
body: {
|
|
604
|
-
collection: this.collection,
|
|
605
|
-
document: this.docID,
|
|
606
|
-
array: this.key,
|
|
607
|
-
...payload
|
|
608
|
-
}
|
|
609
|
-
}).sendRequest();
|
|
610
|
-
return res?.success ? ArraySnapshot.fromMap(res.document) : null;
|
|
611
|
-
}
|
|
612
|
-
// Similar pattern for update, insert, remove, get...
|
|
613
|
-
// (I'll show one more as example; apply the same logic to the rest)
|
|
614
|
-
async update(position, data, id) {
|
|
615
|
-
if (this.persistence) {
|
|
616
|
-
await this.persistence.enqueueMutation({
|
|
617
|
-
mutationId: this.persistence.createMutationId(),
|
|
618
|
-
collection: this.collection,
|
|
619
|
-
documentId: this.docID,
|
|
620
|
-
type: "array_update",
|
|
621
|
-
payload: { arrayKey: this.key, position, data, id }
|
|
622
|
-
});
|
|
623
|
-
this.syncEngine?.flush().catch(console.error);
|
|
624
|
-
return ArraySnapshot.fromMap({ ...data, id });
|
|
625
|
-
}
|
|
626
|
-
const res = await new HttpsRequest({
|
|
627
|
-
method: "POST" /* POST */,
|
|
628
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/update`,
|
|
629
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
630
|
-
body: {
|
|
631
|
-
collection: this.collection,
|
|
632
|
-
document: this.docID,
|
|
633
|
-
array: this.key,
|
|
634
|
-
position,
|
|
635
|
-
data,
|
|
636
|
-
id
|
|
637
|
-
}
|
|
638
|
-
}).sendRequest();
|
|
639
|
-
return res?.success ? ArraySnapshot.fromMap(res.document) : null;
|
|
640
|
-
}
|
|
641
|
-
// TODO: Implement insert(), get(), remove() using the exact same offline pattern as push/update
|
|
642
|
-
async insert(position, data, id) {
|
|
643
|
-
return null;
|
|
644
|
-
}
|
|
645
|
-
async get(position) {
|
|
646
|
-
return null;
|
|
647
|
-
}
|
|
648
|
-
async remove(position) {
|
|
649
|
-
return null;
|
|
650
|
-
}
|
|
651
|
-
};
|
|
652
|
-
|
|
653
363
|
// src/utils/documentNomalizer.ts
|
|
654
364
|
function normalizePayload(payload) {
|
|
655
365
|
const raw = payload?.document ?? payload?.data ?? payload;
|
|
@@ -669,11 +379,8 @@ function validateDocumentData(data, operation) {
|
|
|
669
379
|
if (data === null || data === void 0) {
|
|
670
380
|
throw new Error(`${operation}: data cannot be null or undefined`);
|
|
671
381
|
}
|
|
672
|
-
if (typeof data !== "object") {
|
|
673
|
-
throw new Error(`${operation}: data must be
|
|
674
|
-
}
|
|
675
|
-
if (data instanceof Array2) {
|
|
676
|
-
throw new Error(`${operation}: data cannot be an array`);
|
|
382
|
+
if (typeof data !== "object" || Array.isArray(data)) {
|
|
383
|
+
throw new Error(`${operation}: data must be a plain object`);
|
|
677
384
|
}
|
|
678
385
|
const reservedFields = ["id", "_id", "_createdAt", "_updatedAt", "_deleted"];
|
|
679
386
|
for (const field of reservedFields) {
|
|
@@ -681,18 +388,18 @@ function validateDocumentData(data, operation) {
|
|
|
681
388
|
throw new Error(`${operation}: '${field}' is a reserved field and cannot be set manually`);
|
|
682
389
|
}
|
|
683
390
|
}
|
|
684
|
-
const dataSize = JSON.stringify(data).length;
|
|
685
|
-
if (dataSize > 1024 * 1024) {
|
|
686
|
-
throw new Error(`${operation}: document size (${Math.round(dataSize / 1024)}KB) exceeds maximum allowed size (1MB)`);
|
|
687
|
-
}
|
|
688
391
|
try {
|
|
689
|
-
JSON.stringify(data);
|
|
392
|
+
const size = JSON.stringify(data).length;
|
|
393
|
+
if (size > 1024 * 1024) {
|
|
394
|
+
throw new Error(`${operation}: document too large (max 1MB)`);
|
|
395
|
+
}
|
|
690
396
|
} catch {
|
|
691
397
|
throw new Error(`${operation}: data contains circular references`);
|
|
692
398
|
}
|
|
693
399
|
}
|
|
694
400
|
var DocumentRef = class {
|
|
695
401
|
constructor(app, collection, id) {
|
|
402
|
+
this._isUpdating = false;
|
|
696
403
|
this.app = app;
|
|
697
404
|
this.collection = collection;
|
|
698
405
|
this.id = id;
|
|
@@ -700,58 +407,60 @@ var DocumentRef = class {
|
|
|
700
407
|
this.syncEngine = app.offline().syncEngine;
|
|
701
408
|
this.localStore = app.offline().localStore;
|
|
702
409
|
}
|
|
410
|
+
// ====================== GET ======================
|
|
703
411
|
async get() {
|
|
704
412
|
if (this.persistence) {
|
|
705
413
|
try {
|
|
706
414
|
const localSnap = await this.persistence.getDocSnapshot(this.collection, this.id);
|
|
707
415
|
if (localSnap)
|
|
708
|
-
return localSnap;
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
localOnly: false,
|
|
735
|
-
status: "pending"
|
|
736
|
-
});
|
|
737
|
-
await this.persistence.enqueueMutation({
|
|
738
|
-
mutationId: this.persistence.createMutationId(),
|
|
739
|
-
collection: this.collection,
|
|
740
|
-
documentId: this.id,
|
|
741
|
-
type: "insert",
|
|
742
|
-
// or "create" if you want to distinguish truly new docs
|
|
743
|
-
payload: data
|
|
744
|
-
});
|
|
745
|
-
const snap = DocumentSnapshot.fromMap(updated.data);
|
|
746
|
-
this.localStore?.emitDocument(this.collection, this.id, snap, "update");
|
|
747
|
-
const currentCollection = await this.persistence.getCollectionSnapshots(this.collection);
|
|
748
|
-
this.localStore?.emitCollection(this.collection, currentCollection, "update", this.id);
|
|
749
|
-
this.syncEngine?.flush().catch(console.error);
|
|
750
|
-
return snap;
|
|
416
|
+
return localSnap;
|
|
417
|
+
} catch (error) {
|
|
418
|
+
console.error("[EdmaxLabs] Cache read error:", error);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
const res = await new HttpsRequest({
|
|
423
|
+
method: "POST" /* POST */,
|
|
424
|
+
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
425
|
+
headers: {
|
|
426
|
+
authorization: this.app.getConfig().token,
|
|
427
|
+
"x-project": this.app.getConfig().project
|
|
428
|
+
},
|
|
429
|
+
body: {
|
|
430
|
+
collection: this.collection,
|
|
431
|
+
id: this.id,
|
|
432
|
+
single: true
|
|
433
|
+
}
|
|
434
|
+
}).sendRequest();
|
|
435
|
+
if (!res?.success || !res.document)
|
|
436
|
+
return null;
|
|
437
|
+
return DocumentSnapshot.fromMap(res.document);
|
|
438
|
+
} catch (error) {
|
|
439
|
+
console.error(`[DocumentRef] get(${this.collection}/${this.id}) failed:`, error);
|
|
440
|
+
return null;
|
|
441
|
+
}
|
|
751
442
|
}
|
|
443
|
+
// ====================== UPDATE ======================
|
|
752
444
|
async update(data) {
|
|
753
|
-
|
|
754
|
-
|
|
445
|
+
if (this._isUpdating) {
|
|
446
|
+
console.warn(`[DocumentRef] update recursion blocked on ${this.collection}/${this.id}`);
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
this._isUpdating = true;
|
|
450
|
+
try {
|
|
451
|
+
validateDocumentData(data, "DocumentRef.update");
|
|
452
|
+
if (!this.persistence) {
|
|
453
|
+
const res = await new HttpsRequest({
|
|
454
|
+
method: "POST" /* POST */,
|
|
455
|
+
endpoint: `${this.app.getBaseUrl()}/db/update`,
|
|
456
|
+
headers: {
|
|
457
|
+
authorization: this.app.getConfig().token,
|
|
458
|
+
"x-project": this.app.getConfig().project
|
|
459
|
+
},
|
|
460
|
+
body: { collection: this.collection, id: this.id, data }
|
|
461
|
+
}).sendRequest();
|
|
462
|
+
return res?.success ? DocumentSnapshot.fromMap({ ...data, id: this.id }) : null;
|
|
463
|
+
}
|
|
755
464
|
const old = await this.persistence.getDoc(this.collection, this.id);
|
|
756
465
|
if (!old || old.deleted)
|
|
757
466
|
return null;
|
|
@@ -781,9 +490,49 @@ var DocumentRef = class {
|
|
|
781
490
|
this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
|
|
782
491
|
this.syncEngine?.flush().catch(console.error);
|
|
783
492
|
return snap;
|
|
493
|
+
} finally {
|
|
494
|
+
this._isUpdating = false;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// ====================== SET ======================
|
|
498
|
+
async set(data) {
|
|
499
|
+
validateDocumentData(data, "DocumentRef.set");
|
|
500
|
+
if (!this.persistence) {
|
|
501
|
+
const res = await new HttpsRequest({
|
|
502
|
+
method: "POST" /* POST */,
|
|
503
|
+
endpoint: `${this.app.getBaseUrl()}/db/create`,
|
|
504
|
+
headers: {
|
|
505
|
+
authorization: this.app.getConfig().token,
|
|
506
|
+
"x-project": this.app.getConfig().project
|
|
507
|
+
},
|
|
508
|
+
body: { collection: this.collection, data: { ...data, id: this.id } }
|
|
509
|
+
}).sendRequest();
|
|
510
|
+
return res?.success ? DocumentSnapshot.fromMap({ ...data, id: this.id }) : null;
|
|
784
511
|
}
|
|
785
|
-
|
|
512
|
+
const updated = await this.persistence.upsertDoc({
|
|
513
|
+
collection: this.collection,
|
|
514
|
+
id: this.id,
|
|
515
|
+
data: { ...data, id: this.id },
|
|
516
|
+
exists: true,
|
|
517
|
+
deleted: false,
|
|
518
|
+
pending: 1,
|
|
519
|
+
localOnly: false,
|
|
520
|
+
status: "pending"
|
|
521
|
+
});
|
|
522
|
+
await this.persistence.enqueueMutation({
|
|
523
|
+
mutationId: this.persistence.createMutationId(),
|
|
524
|
+
collection: this.collection,
|
|
525
|
+
documentId: this.id,
|
|
526
|
+
type: "insert",
|
|
527
|
+
payload: data
|
|
528
|
+
});
|
|
529
|
+
const snap = DocumentSnapshot.fromMap(updated.data);
|
|
530
|
+
this.localStore?.emitDocument(this.collection, this.id, snap, "update");
|
|
531
|
+
this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
|
|
532
|
+
this.syncEngine?.flush().catch(console.error);
|
|
533
|
+
return snap;
|
|
786
534
|
}
|
|
535
|
+
// ====================== DELETE ======================
|
|
787
536
|
async delete() {
|
|
788
537
|
if (this.persistence) {
|
|
789
538
|
await this.persistence.markDeleted(this.collection, this.id, 1);
|
|
@@ -799,19 +548,22 @@ var DocumentRef = class {
|
|
|
799
548
|
this.syncEngine?.flush().catch(console.error);
|
|
800
549
|
return true;
|
|
801
550
|
}
|
|
802
|
-
|
|
551
|
+
const res = await new HttpsRequest({
|
|
552
|
+
method: "POST" /* POST */,
|
|
553
|
+
endpoint: `${this.app.getBaseUrl()}/db/delete`,
|
|
554
|
+
headers: {
|
|
555
|
+
authorization: this.app.getConfig().token,
|
|
556
|
+
"x-project": this.app.getConfig().project
|
|
557
|
+
},
|
|
558
|
+
body: { collection: this.collection, id: this.id }
|
|
559
|
+
}).sendRequest();
|
|
560
|
+
return !!res?.success;
|
|
803
561
|
}
|
|
804
|
-
//
|
|
805
|
-
// return new Array(this.app, this.collection, key, this.id);
|
|
806
|
-
// }
|
|
562
|
+
// ====================== SNAPSHOT ======================
|
|
807
563
|
onSnapshot(callback) {
|
|
808
|
-
if (this.
|
|
564
|
+
if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
|
|
809
565
|
this.app.offline().realtimeBridge?.watchDocument(this.collection, this.id);
|
|
810
|
-
return this.
|
|
811
|
-
this.collection,
|
|
812
|
-
this.id,
|
|
813
|
-
callback
|
|
814
|
-
) || (() => {
|
|
566
|
+
return this.localStore.subscribeToDocument(this.collection, this.id, callback) || (() => {
|
|
815
567
|
});
|
|
816
568
|
}
|
|
817
569
|
return this.app.rtdb().subscribeToDocumentRaw(this.collection, this.id, (snapshot, change) => {
|
|
@@ -1039,15 +791,42 @@ var CollectionRef = class {
|
|
|
1039
791
|
}
|
|
1040
792
|
return local;
|
|
1041
793
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
794
|
+
try {
|
|
795
|
+
const res = await new HttpsRequest({
|
|
796
|
+
method: "POST" /* POST */,
|
|
797
|
+
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
798
|
+
headers: {
|
|
799
|
+
authorization: this.app.getConfig().token,
|
|
800
|
+
"x-project": this.app.getConfig().project
|
|
801
|
+
},
|
|
802
|
+
body: {
|
|
803
|
+
collection: this.collection,
|
|
804
|
+
filter: {}
|
|
805
|
+
}
|
|
806
|
+
}).sendRequest();
|
|
807
|
+
if (!res?.success || !Array.isArray(res.documents)) {
|
|
808
|
+
return [];
|
|
809
|
+
}
|
|
810
|
+
return res.documents.map((d) => {
|
|
811
|
+
delete d.token;
|
|
812
|
+
d.id = d.id ?? d._id;
|
|
813
|
+
delete d._id;
|
|
814
|
+
return DocumentSnapshot.fromMap(d);
|
|
815
|
+
});
|
|
816
|
+
} catch (error) {
|
|
817
|
+
console.error("[EdmaxLabs] Collection get failed:", error);
|
|
818
|
+
return [];
|
|
819
|
+
}
|
|
1044
820
|
}
|
|
1045
821
|
async refreshFromRemote() {
|
|
1046
822
|
try {
|
|
1047
823
|
const res = await new HttpsRequest({
|
|
1048
824
|
method: "POST" /* POST */,
|
|
1049
825
|
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
1050
|
-
headers: {
|
|
826
|
+
headers: {
|
|
827
|
+
authorization: this.app.getConfig().token,
|
|
828
|
+
"x-project": this.app.getConfig().project
|
|
829
|
+
},
|
|
1051
830
|
body: {
|
|
1052
831
|
collection: this.collection,
|
|
1053
832
|
filter: {}
|
|
@@ -1073,7 +852,8 @@ var CollectionRef = class {
|
|
|
1073
852
|
delete d._id;
|
|
1074
853
|
return DocumentSnapshot.fromMap(d);
|
|
1075
854
|
});
|
|
1076
|
-
} catch {
|
|
855
|
+
} catch (error) {
|
|
856
|
+
console.error("[EdmaxLabs] refreshFromRemote failed:", error);
|
|
1077
857
|
return [];
|
|
1078
858
|
}
|
|
1079
859
|
}
|
|
@@ -1103,13 +883,24 @@ var CollectionRef = class {
|
|
|
1103
883
|
this.syncEngine?.flush().catch(console.error);
|
|
1104
884
|
return snap;
|
|
1105
885
|
}
|
|
1106
|
-
|
|
1107
|
-
|
|
886
|
+
const res = await new HttpsRequest({
|
|
887
|
+
method: "POST" /* POST */,
|
|
888
|
+
endpoint: `${this.app.getBaseUrl()}/db/create`,
|
|
889
|
+
headers: {
|
|
890
|
+
authorization: this.app.getConfig().token,
|
|
891
|
+
"x-project": this.app.getConfig().project
|
|
892
|
+
},
|
|
893
|
+
body: { collection: this.collection, data: { ...data, id: "" } }
|
|
894
|
+
// server will generate id
|
|
895
|
+
}).sendRequest();
|
|
896
|
+
if (!res?.success || !res.document)
|
|
897
|
+
return null;
|
|
898
|
+
return DocumentSnapshot.fromMap(res.document);
|
|
1108
899
|
}
|
|
1109
900
|
onSnapshot(callback) {
|
|
1110
|
-
if (this.app.offline().
|
|
901
|
+
if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
|
|
1111
902
|
this.app.offline().realtimeBridge?.watchCollection(this.collection);
|
|
1112
|
-
return this.
|
|
903
|
+
return this.localStore.subscribeToCollection(this.collection, callback) ?? (() => {
|
|
1113
904
|
});
|
|
1114
905
|
}
|
|
1115
906
|
return this.app.rtdb().subscribeToCollectionRaw(this.collection, (payload, changes) => {
|
|
@@ -2702,6 +2493,164 @@ var _EdmaxLabs = class _EdmaxLabs {
|
|
|
2702
2493
|
_EdmaxLabs.instance = null;
|
|
2703
2494
|
var EdmaxLabs = _EdmaxLabs;
|
|
2704
2495
|
|
|
2496
|
+
// src/database/Timestamp.ts
|
|
2497
|
+
var Timestamp = class _Timestamp {
|
|
2498
|
+
constructor(seconds, nanoseconds = 0) {
|
|
2499
|
+
this.seconds = seconds;
|
|
2500
|
+
this.nanoseconds = nanoseconds;
|
|
2501
|
+
}
|
|
2502
|
+
static now() {
|
|
2503
|
+
return _Timestamp.fromMillis(Date.now());
|
|
2504
|
+
}
|
|
2505
|
+
static fromDate(date) {
|
|
2506
|
+
return new _Timestamp(
|
|
2507
|
+
Math.floor(date.getTime() / 1e3),
|
|
2508
|
+
date.getTime() % 1e3 * 1e6
|
|
2509
|
+
);
|
|
2510
|
+
}
|
|
2511
|
+
static fromMillis(ms) {
|
|
2512
|
+
return new _Timestamp(Math.floor(ms / 1e3), ms % 1e3 * 1e6);
|
|
2513
|
+
}
|
|
2514
|
+
static fromMongo(dateObj) {
|
|
2515
|
+
return _Timestamp.fromMillis(dateObj.getTime());
|
|
2516
|
+
}
|
|
2517
|
+
static fromJSON(obj) {
|
|
2518
|
+
return new _Timestamp(obj.seconds, obj.nanoseconds);
|
|
2519
|
+
}
|
|
2520
|
+
toDate() {
|
|
2521
|
+
return new Date(this.toMillis());
|
|
2522
|
+
}
|
|
2523
|
+
toMillis() {
|
|
2524
|
+
return this.seconds * 1e3 + Math.floor(this.nanoseconds / 1e6);
|
|
2525
|
+
}
|
|
2526
|
+
sinceEpoch() {
|
|
2527
|
+
return this.seconds;
|
|
2528
|
+
}
|
|
2529
|
+
toMongo() {
|
|
2530
|
+
return new Date(this.toMillis());
|
|
2531
|
+
}
|
|
2532
|
+
format(pattern = "dd-MM-yyyy HH:mm:ss") {
|
|
2533
|
+
const d = this.toDate();
|
|
2534
|
+
const monthsShort = [
|
|
2535
|
+
"Jan",
|
|
2536
|
+
"Feb",
|
|
2537
|
+
"Mar",
|
|
2538
|
+
"Apr",
|
|
2539
|
+
"May",
|
|
2540
|
+
"Jun",
|
|
2541
|
+
"Jul",
|
|
2542
|
+
"Aug",
|
|
2543
|
+
"Sep",
|
|
2544
|
+
"Oct",
|
|
2545
|
+
"Nov",
|
|
2546
|
+
"Dec"
|
|
2547
|
+
];
|
|
2548
|
+
const monthsLong = [
|
|
2549
|
+
"January",
|
|
2550
|
+
"February",
|
|
2551
|
+
"March",
|
|
2552
|
+
"April",
|
|
2553
|
+
"May",
|
|
2554
|
+
"June",
|
|
2555
|
+
"July",
|
|
2556
|
+
"August",
|
|
2557
|
+
"September",
|
|
2558
|
+
"October",
|
|
2559
|
+
"November",
|
|
2560
|
+
"December"
|
|
2561
|
+
];
|
|
2562
|
+
const hours = d.getHours();
|
|
2563
|
+
const hours12 = hours % 12 === 0 ? 12 : hours % 12;
|
|
2564
|
+
const ampm = hours < 12 ? "AM" : "PM";
|
|
2565
|
+
const map = {
|
|
2566
|
+
dd: String(d.getDate()).padStart(2, "0"),
|
|
2567
|
+
MM: String(d.getMonth() + 1).padStart(2, "0"),
|
|
2568
|
+
yyyy: d.getFullYear(),
|
|
2569
|
+
HH: String(d.getHours()).padStart(2, "0"),
|
|
2570
|
+
mm: String(d.getMinutes()).padStart(2, "0"),
|
|
2571
|
+
ss: String(d.getSeconds()).padStart(2, "0"),
|
|
2572
|
+
SSS: String(d.getMilliseconds()).padStart(3, "0"),
|
|
2573
|
+
MMM: monthsShort[d.getMonth()],
|
|
2574
|
+
MMMM: monthsLong[d.getMonth()],
|
|
2575
|
+
a: ampm.toLowerCase(),
|
|
2576
|
+
// am/pm
|
|
2577
|
+
A: ampm
|
|
2578
|
+
// AM/PM
|
|
2579
|
+
};
|
|
2580
|
+
return pattern.replace(
|
|
2581
|
+
/MMMM|MMM|dd|MM|yyyy|HH|mm|ss|SSS|a|A/g,
|
|
2582
|
+
(match) => String(map[match])
|
|
2583
|
+
);
|
|
2584
|
+
}
|
|
2585
|
+
compare(other) {
|
|
2586
|
+
if (this.seconds !== other.seconds) {
|
|
2587
|
+
return this.seconds - other.seconds;
|
|
2588
|
+
}
|
|
2589
|
+
return this.nanoseconds - other.nanoseconds;
|
|
2590
|
+
}
|
|
2591
|
+
relative(fmt = "dd-MM-yyyy HH:mm:ss") {
|
|
2592
|
+
const now = Date.now();
|
|
2593
|
+
const diff = now - this.toMillis();
|
|
2594
|
+
const sec = Math.floor(diff / 1e3);
|
|
2595
|
+
if (sec < 60)
|
|
2596
|
+
return `${sec}s ago`;
|
|
2597
|
+
const min = Math.floor(sec / 60);
|
|
2598
|
+
if (min < 60)
|
|
2599
|
+
return `${min}m ago`;
|
|
2600
|
+
const hrs = Math.floor(min / 60);
|
|
2601
|
+
if (hrs < 24)
|
|
2602
|
+
return `${hrs}h ago`;
|
|
2603
|
+
const days = Math.floor(hrs / 24);
|
|
2604
|
+
if (days < 3)
|
|
2605
|
+
return `${days}d ago`;
|
|
2606
|
+
return this.format(fmt);
|
|
2607
|
+
}
|
|
2608
|
+
add({
|
|
2609
|
+
seconds = 0,
|
|
2610
|
+
minutes = 0,
|
|
2611
|
+
hours = 0,
|
|
2612
|
+
days = 0
|
|
2613
|
+
}) {
|
|
2614
|
+
const totalMs = seconds * 1e3 + minutes * 6e4 + hours * 36e5 + days * 864e5;
|
|
2615
|
+
return _Timestamp.fromMillis(this.toMillis() + totalMs);
|
|
2616
|
+
}
|
|
2617
|
+
weekdayName() {
|
|
2618
|
+
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.toDate().getDay()];
|
|
2619
|
+
}
|
|
2620
|
+
toJSON() {
|
|
2621
|
+
return {
|
|
2622
|
+
_type: "timestamp",
|
|
2623
|
+
seconds: this.seconds,
|
|
2624
|
+
nanoseconds: this.nanoseconds
|
|
2625
|
+
};
|
|
2626
|
+
}
|
|
2627
|
+
toString() {
|
|
2628
|
+
return this.format("yyyy-MM-dd HH:mm:ss");
|
|
2629
|
+
}
|
|
2630
|
+
};
|
|
2631
|
+
|
|
2632
|
+
// src/database/ArraySnapshot.ts
|
|
2633
|
+
var ArraySnapshot = class _ArraySnapshot {
|
|
2634
|
+
constructor(id = "", index, data, dt) {
|
|
2635
|
+
this.data = data;
|
|
2636
|
+
this.id = id;
|
|
2637
|
+
this.dt = dt ?? Timestamp.now();
|
|
2638
|
+
}
|
|
2639
|
+
static fromMap(map) {
|
|
2640
|
+
const index = map.index ?? map.index ?? -1;
|
|
2641
|
+
const id = map.id ?? map._id ?? "";
|
|
2642
|
+
const document2 = map.data ?? map.document ?? map;
|
|
2643
|
+
return new _ArraySnapshot(id, index, document2);
|
|
2644
|
+
}
|
|
2645
|
+
toMap() {
|
|
2646
|
+
return {
|
|
2647
|
+
id: this.id ?? "",
|
|
2648
|
+
data: this.data,
|
|
2649
|
+
dt: this.dt
|
|
2650
|
+
};
|
|
2651
|
+
}
|
|
2652
|
+
};
|
|
2653
|
+
|
|
2705
2654
|
// src/index.ts
|
|
2706
2655
|
var src_default = EdmaxLabs;
|
|
2707
2656
|
export {
|