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 CHANGED
@@ -32,12 +32,13 @@ module.exports = __toCommonJS(src_exports);
32
32
 
33
33
  // src/authentication/Credentials.ts
34
34
  var Credentials = class _Credentials {
35
- constructor(uid, displayInfo, createdAt, updatedAt, lastLogged) {
35
+ constructor(uid, displayInfo, createdAt, updatedAt, lastLogged, logged) {
36
36
  this.uid = uid;
37
37
  this.displayInfo = displayInfo;
38
38
  this.createdAt = createdAt;
39
39
  this.updatedAt = updatedAt;
40
40
  this.lastLogged = lastLogged;
41
+ this.logged = logged === true;
41
42
  }
42
43
  static fromMap(map) {
43
44
  const uid = map.id ?? map.uid ?? "";
@@ -46,12 +47,14 @@ var Credentials = class _Credentials {
46
47
  const createdAt = data.createdAt;
47
48
  const updatedAt = data.updatedAt;
48
49
  const lastLogged = data.lastLogged;
50
+ const logged = data.logged;
49
51
  const result = new _Credentials(
50
52
  uid,
51
53
  d_info,
52
54
  createdAt,
53
55
  updatedAt,
54
- lastLogged
56
+ lastLogged,
57
+ logged
55
58
  );
56
59
  return result;
57
60
  }
@@ -61,7 +64,8 @@ var Credentials = class _Credentials {
61
64
  displayInfo: this.displayInfo,
62
65
  createdAt: this.createdAt,
63
66
  updatedAt: this.updatedAt,
64
- lastLogged: this.lastLogged
67
+ lastLogged: this.lastLogged,
68
+ logged: this.logged
65
69
  };
66
70
  }
67
71
  };
@@ -158,9 +162,16 @@ var _Authentication = class _Authentication {
158
162
  const userRef = await app?.collection("users").add({
159
163
  email,
160
164
  password,
161
- token: this.client?.getConfig().token,
162
165
  logged: true,
163
- lastLogged: Date.now()
166
+ lastLogged: Date.now(),
167
+ displayInfo: {
168
+ firstname: email.split("@")[0],
169
+ lastname: "",
170
+ surname: "",
171
+ profile: "./logo.png",
172
+ email,
173
+ phone: ""
174
+ }
164
175
  });
165
176
  if (!userRef?.id || userRef?.id === "") {
166
177
  throw new Error("Something went wrong try Again.");
@@ -190,13 +201,13 @@ var _Authentication = class _Authentication {
190
201
  throw new Error("User Not Found.");
191
202
  }
192
203
  const _data = data[0];
193
- await app?.collection("users").doc(data[0].id).update({
204
+ await app?.collection("users").doc(_data.id).update({
194
205
  logged: true,
195
206
  lastLogged: Date.now()
196
207
  });
197
208
  this.eUser = Credentials.fromMap(_data);
198
209
  this.saveCredentials(this.eUser);
199
- return Credentials.fromMap(data[0].toMap());
210
+ return Credentials.fromMap(_data);
200
211
  };
201
212
  this.deleteUser = async () => {
202
213
  if (!this.eUser) {
@@ -321,6 +332,7 @@ var _Authentication = class _Authentication {
321
332
  return;
322
333
  userDocUnsubscribe = userRef.onSnapshot(
323
334
  (snapshot, change) => {
335
+ console.log(snapshot);
324
336
  if (change === "delete") {
325
337
  this.saveCredentials(null);
326
338
  onSignOut?.();
@@ -380,308 +392,6 @@ var DocumentSnapshot = class _DocumentSnapshot {
380
392
  }
381
393
  };
382
394
 
383
- // src/database/Timestamp.ts
384
- var Timestamp = class _Timestamp {
385
- constructor(seconds, nanoseconds = 0) {
386
- this.seconds = seconds;
387
- this.nanoseconds = nanoseconds;
388
- }
389
- static now() {
390
- return _Timestamp.fromMillis(Date.now());
391
- }
392
- static fromDate(date) {
393
- return new _Timestamp(
394
- Math.floor(date.getTime() / 1e3),
395
- date.getTime() % 1e3 * 1e6
396
- );
397
- }
398
- static fromMillis(ms) {
399
- return new _Timestamp(Math.floor(ms / 1e3), ms % 1e3 * 1e6);
400
- }
401
- static fromMongo(dateObj) {
402
- return _Timestamp.fromMillis(dateObj.getTime());
403
- }
404
- static fromJSON(obj) {
405
- return new _Timestamp(obj.seconds, obj.nanoseconds);
406
- }
407
- toDate() {
408
- return new Date(this.toMillis());
409
- }
410
- toMillis() {
411
- return this.seconds * 1e3 + Math.floor(this.nanoseconds / 1e6);
412
- }
413
- sinceEpoch() {
414
- return this.seconds;
415
- }
416
- toMongo() {
417
- return new Date(this.toMillis());
418
- }
419
- format(pattern = "dd-MM-yyyy HH:mm:ss") {
420
- const d = this.toDate();
421
- const monthsShort = [
422
- "Jan",
423
- "Feb",
424
- "Mar",
425
- "Apr",
426
- "May",
427
- "Jun",
428
- "Jul",
429
- "Aug",
430
- "Sep",
431
- "Oct",
432
- "Nov",
433
- "Dec"
434
- ];
435
- const monthsLong = [
436
- "January",
437
- "February",
438
- "March",
439
- "April",
440
- "May",
441
- "June",
442
- "July",
443
- "August",
444
- "September",
445
- "October",
446
- "November",
447
- "December"
448
- ];
449
- const hours = d.getHours();
450
- const hours12 = hours % 12 === 0 ? 12 : hours % 12;
451
- const ampm = hours < 12 ? "AM" : "PM";
452
- const map = {
453
- dd: String(d.getDate()).padStart(2, "0"),
454
- MM: String(d.getMonth() + 1).padStart(2, "0"),
455
- yyyy: d.getFullYear(),
456
- HH: String(d.getHours()).padStart(2, "0"),
457
- mm: String(d.getMinutes()).padStart(2, "0"),
458
- ss: String(d.getSeconds()).padStart(2, "0"),
459
- SSS: String(d.getMilliseconds()).padStart(3, "0"),
460
- MMM: monthsShort[d.getMonth()],
461
- MMMM: monthsLong[d.getMonth()],
462
- a: ampm.toLowerCase(),
463
- // am/pm
464
- A: ampm
465
- // AM/PM
466
- };
467
- return pattern.replace(
468
- /MMMM|MMM|dd|MM|yyyy|HH|mm|ss|SSS|a|A/g,
469
- (match) => String(map[match])
470
- );
471
- }
472
- compare(other) {
473
- if (this.seconds !== other.seconds) {
474
- return this.seconds - other.seconds;
475
- }
476
- return this.nanoseconds - other.nanoseconds;
477
- }
478
- relative(fmt = "dd-MM-yyyy HH:mm:ss") {
479
- const now = Date.now();
480
- const diff = now - this.toMillis();
481
- const sec = Math.floor(diff / 1e3);
482
- if (sec < 60)
483
- return `${sec}s ago`;
484
- const min = Math.floor(sec / 60);
485
- if (min < 60)
486
- return `${min}m ago`;
487
- const hrs = Math.floor(min / 60);
488
- if (hrs < 24)
489
- return `${hrs}h ago`;
490
- const days = Math.floor(hrs / 24);
491
- if (days < 3)
492
- return `${days}d ago`;
493
- return this.format(fmt);
494
- }
495
- add({
496
- seconds = 0,
497
- minutes = 0,
498
- hours = 0,
499
- days = 0
500
- }) {
501
- const totalMs = seconds * 1e3 + minutes * 6e4 + hours * 36e5 + days * 864e5;
502
- return _Timestamp.fromMillis(this.toMillis() + totalMs);
503
- }
504
- weekdayName() {
505
- return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.toDate().getDay()];
506
- }
507
- toJSON() {
508
- return {
509
- _type: "timestamp",
510
- seconds: this.seconds,
511
- nanoseconds: this.nanoseconds
512
- };
513
- }
514
- toString() {
515
- return this.format("yyyy-MM-dd HH:mm:ss");
516
- }
517
- };
518
-
519
- // src/database/ArraySnapshot.ts
520
- var ArraySnapshot = class _ArraySnapshot {
521
- constructor(id = "", index, data, dt) {
522
- this.data = data;
523
- this.id = id;
524
- this.dt = dt ?? Timestamp.now();
525
- }
526
- static fromMap(map) {
527
- const index = map.index ?? map.index ?? -1;
528
- const id = map.id ?? map._id ?? "";
529
- const document2 = map.data ?? map.document ?? map;
530
- return new _ArraySnapshot(id, index, document2);
531
- }
532
- toMap() {
533
- return {
534
- id: this.id ?? "",
535
- data: this.data,
536
- dt: this.dt
537
- };
538
- }
539
- };
540
-
541
- // src/database/Array.ts
542
- var Array2 = class {
543
- constructor(app, collection, key, id) {
544
- this.app = app;
545
- this.collection = collection;
546
- this.key = key;
547
- this.docID = id;
548
- this.persistence = app.offline().persistence;
549
- this.syncEngine = app.offline().syncEngine;
550
- this.localStore = app.offline().localStore;
551
- }
552
- /**
553
- * Get current array elements (offline-first: prefers local cache)
554
- */
555
- async show() {
556
- if (this.persistence) {
557
- const doc = await this.persistence.getDoc(this.collection, this.docID);
558
- if (doc?.exists && !doc.deleted) {
559
- const arrayData = doc.data[this.key] || [];
560
- return arrayData.map((item) => ArraySnapshot.fromMap(item));
561
- }
562
- }
563
- if (typeof navigator === "undefined" || navigator.onLine) {
564
- this.refreshFromRemote().catch(() => {
565
- });
566
- }
567
- return [];
568
- }
569
- async refreshFromRemote() {
570
- try {
571
- const res = await new HttpsRequest({
572
- method: "POST" /* POST */,
573
- endpoint: `${this.app.getBaseUrl()}/db/array/show`,
574
- headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
575
- body: {
576
- collection: this.collection,
577
- document: this.docID,
578
- array: this.key
579
- }
580
- }).sendRequest();
581
- if (!res?.success || !globalThis.Array.isArray(res.documents)) {
582
- return [];
583
- }
584
- const snapshots = res.documents.filter((d) => d != null).map((d) => ArraySnapshot.fromMap(d));
585
- if (this.persistence) {
586
- const currentDoc = await this.persistence.getDoc(this.collection, this.docID);
587
- if (currentDoc) {
588
- await this.persistence.upsertDoc({
589
- ...currentDoc,
590
- data: { ...currentDoc.data, [this.key]: res.documents },
591
- pending: 0,
592
- status: "synced",
593
- lastSyncedAt: Date.now()
594
- });
595
- }
596
- }
597
- return snapshots;
598
- } catch {
599
- return [];
600
- }
601
- }
602
- async push(data, id) {
603
- const payload = {
604
- data,
605
- id: id || this.persistence?.createLocalId?.() || void 0,
606
- dt: Timestamp.now()
607
- };
608
- if (this.persistence) {
609
- await this.persistence.enqueueMutation({
610
- mutationId: this.persistence.createMutationId(),
611
- collection: this.collection,
612
- documentId: this.docID,
613
- type: "array_push",
614
- // custom type
615
- payload: { arrayKey: this.key, ...payload }
616
- });
617
- this.syncEngine?.flush().catch(console.error);
618
- const doc = await this.persistence.getDoc(this.collection, this.docID);
619
- if (doc) {
620
- const updatedArray = [...doc.data[this.key] || [], payload];
621
- const snap = ArraySnapshot.fromMap(payload);
622
- this.localStore?.emitDocument(
623
- this.collection,
624
- this.docID,
625
- DocumentSnapshot.fromMap({ ...doc.data, [this.key]: updatedArray }),
626
- "update"
627
- );
628
- }
629
- return ArraySnapshot.fromMap(payload);
630
- }
631
- const res = await new HttpsRequest({
632
- method: "POST" /* POST */,
633
- endpoint: `${this.app.getBaseUrl()}/db/array/push`,
634
- headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
635
- body: {
636
- collection: this.collection,
637
- document: this.docID,
638
- array: this.key,
639
- ...payload
640
- }
641
- }).sendRequest();
642
- return res?.success ? ArraySnapshot.fromMap(res.document) : null;
643
- }
644
- // Similar pattern for update, insert, remove, get...
645
- // (I'll show one more as example; apply the same logic to the rest)
646
- async update(position, data, id) {
647
- if (this.persistence) {
648
- await this.persistence.enqueueMutation({
649
- mutationId: this.persistence.createMutationId(),
650
- collection: this.collection,
651
- documentId: this.docID,
652
- type: "array_update",
653
- payload: { arrayKey: this.key, position, data, id }
654
- });
655
- this.syncEngine?.flush().catch(console.error);
656
- return ArraySnapshot.fromMap({ ...data, id });
657
- }
658
- const res = await new HttpsRequest({
659
- method: "POST" /* POST */,
660
- endpoint: `${this.app.getBaseUrl()}/db/array/update`,
661
- headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
662
- body: {
663
- collection: this.collection,
664
- document: this.docID,
665
- array: this.key,
666
- position,
667
- data,
668
- id
669
- }
670
- }).sendRequest();
671
- return res?.success ? ArraySnapshot.fromMap(res.document) : null;
672
- }
673
- // TODO: Implement insert(), get(), remove() using the exact same offline pattern as push/update
674
- async insert(position, data, id) {
675
- return null;
676
- }
677
- async get(position) {
678
- return null;
679
- }
680
- async remove(position) {
681
- return null;
682
- }
683
- };
684
-
685
395
  // src/utils/documentNomalizer.ts
686
396
  function normalizePayload(payload) {
687
397
  const raw = payload?.document ?? payload?.data ?? payload;
@@ -701,11 +411,8 @@ function validateDocumentData(data, operation) {
701
411
  if (data === null || data === void 0) {
702
412
  throw new Error(`${operation}: data cannot be null or undefined`);
703
413
  }
704
- if (typeof data !== "object") {
705
- throw new Error(`${operation}: data must be an object`);
706
- }
707
- if (data instanceof Array2) {
708
- throw new Error(`${operation}: data cannot be an array`);
414
+ if (typeof data !== "object" || Array.isArray(data)) {
415
+ throw new Error(`${operation}: data must be a plain object`);
709
416
  }
710
417
  const reservedFields = ["id", "_id", "_createdAt", "_updatedAt", "_deleted"];
711
418
  for (const field of reservedFields) {
@@ -713,18 +420,18 @@ function validateDocumentData(data, operation) {
713
420
  throw new Error(`${operation}: '${field}' is a reserved field and cannot be set manually`);
714
421
  }
715
422
  }
716
- const dataSize = JSON.stringify(data).length;
717
- if (dataSize > 1024 * 1024) {
718
- throw new Error(`${operation}: document size (${Math.round(dataSize / 1024)}KB) exceeds maximum allowed size (1MB)`);
719
- }
720
423
  try {
721
- JSON.stringify(data);
424
+ const size = JSON.stringify(data).length;
425
+ if (size > 1024 * 1024) {
426
+ throw new Error(`${operation}: document too large (max 1MB)`);
427
+ }
722
428
  } catch {
723
429
  throw new Error(`${operation}: data contains circular references`);
724
430
  }
725
431
  }
726
432
  var DocumentRef = class {
727
433
  constructor(app, collection, id) {
434
+ this._isUpdating = false;
728
435
  this.app = app;
729
436
  this.collection = collection;
730
437
  this.id = id;
@@ -732,58 +439,60 @@ var DocumentRef = class {
732
439
  this.syncEngine = app.offline().syncEngine;
733
440
  this.localStore = app.offline().localStore;
734
441
  }
442
+ // ====================== GET ======================
735
443
  async get() {
736
444
  if (this.persistence) {
737
445
  try {
738
446
  const localSnap = await this.persistence.getDocSnapshot(this.collection, this.id);
739
447
  if (localSnap)
740
- return localSnap;
741
- return null;
742
- } catch (error) {
743
- console.error("[EdmaxLabs] Error reading from cache:", error);
744
- }
745
- }
746
- return await this.app.getDatabase.collection(this.collection).doc(this.id).get();
747
- }
748
- async set(data) {
749
- validateDocumentData(data, "DocumentRef.set");
750
- if (!this.persistence) {
751
- const res = await new HttpsRequest({
752
- method: "POST" /* POST */,
753
- endpoint: `${this.app.getBaseUrl()}/db/create`,
754
- headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
755
- body: { collection: this.collection, data: { ...data, id: this.id } }
756
- }).sendRequest();
757
- return res?.success ? DocumentSnapshot.fromMap(data) : null;
758
- }
759
- const updated = await this.persistence.upsertDoc({
760
- collection: this.collection,
761
- id: this.id,
762
- data: { ...data, id: this.id },
763
- exists: true,
764
- deleted: false,
765
- pending: 1,
766
- localOnly: false,
767
- status: "pending"
768
- });
769
- await this.persistence.enqueueMutation({
770
- mutationId: this.persistence.createMutationId(),
771
- collection: this.collection,
772
- documentId: this.id,
773
- type: "insert",
774
- // or "create" if you want to distinguish truly new docs
775
- payload: data
776
- });
777
- const snap = DocumentSnapshot.fromMap(updated.data);
778
- this.localStore?.emitDocument(this.collection, this.id, snap, "update");
779
- const currentCollection = await this.persistence.getCollectionSnapshots(this.collection);
780
- this.localStore?.emitCollection(this.collection, currentCollection, "update", this.id);
781
- this.syncEngine?.flush().catch(console.error);
782
- return snap;
448
+ return localSnap;
449
+ } catch (error) {
450
+ console.error("[EdmaxLabs] Cache read error:", error);
451
+ }
452
+ }
453
+ try {
454
+ const res = await new HttpsRequest({
455
+ method: "POST" /* POST */,
456
+ endpoint: `${this.app.getBaseUrl()}/db/read`,
457
+ headers: {
458
+ authorization: this.app.getConfig().token,
459
+ "x-project": this.app.getConfig().project
460
+ },
461
+ body: {
462
+ collection: this.collection,
463
+ id: this.id,
464
+ single: true
465
+ }
466
+ }).sendRequest();
467
+ if (!res?.success || !res.document)
468
+ return null;
469
+ return DocumentSnapshot.fromMap(res.document);
470
+ } catch (error) {
471
+ console.error(`[DocumentRef] get(${this.collection}/${this.id}) failed:`, error);
472
+ return null;
473
+ }
783
474
  }
475
+ // ====================== UPDATE ======================
784
476
  async update(data) {
785
- validateDocumentData(data, "DocumentRef.update");
786
- if (this.persistence) {
477
+ if (this._isUpdating) {
478
+ console.warn(`[DocumentRef] update recursion blocked on ${this.collection}/${this.id}`);
479
+ return null;
480
+ }
481
+ this._isUpdating = true;
482
+ try {
483
+ validateDocumentData(data, "DocumentRef.update");
484
+ if (!this.persistence) {
485
+ const res = await new HttpsRequest({
486
+ method: "POST" /* POST */,
487
+ endpoint: `${this.app.getBaseUrl()}/db/update`,
488
+ headers: {
489
+ authorization: this.app.getConfig().token,
490
+ "x-project": this.app.getConfig().project
491
+ },
492
+ body: { collection: this.collection, id: this.id, data }
493
+ }).sendRequest();
494
+ return res?.success ? DocumentSnapshot.fromMap({ ...data, id: this.id }) : null;
495
+ }
787
496
  const old = await this.persistence.getDoc(this.collection, this.id);
788
497
  if (!old || old.deleted)
789
498
  return null;
@@ -813,9 +522,49 @@ var DocumentRef = class {
813
522
  this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
814
523
  this.syncEngine?.flush().catch(console.error);
815
524
  return snap;
525
+ } finally {
526
+ this._isUpdating = false;
527
+ }
528
+ }
529
+ // ====================== SET ======================
530
+ async set(data) {
531
+ validateDocumentData(data, "DocumentRef.set");
532
+ if (!this.persistence) {
533
+ const res = await new HttpsRequest({
534
+ method: "POST" /* POST */,
535
+ endpoint: `${this.app.getBaseUrl()}/db/create`,
536
+ headers: {
537
+ authorization: this.app.getConfig().token,
538
+ "x-project": this.app.getConfig().project
539
+ },
540
+ body: { collection: this.collection, data: { ...data, id: this.id } }
541
+ }).sendRequest();
542
+ return res?.success ? DocumentSnapshot.fromMap({ ...data, id: this.id }) : null;
816
543
  }
817
- return await this.app.getDatabase.collection(this.collection).doc(this.id).update(data);
544
+ const updated = await this.persistence.upsertDoc({
545
+ collection: this.collection,
546
+ id: this.id,
547
+ data: { ...data, id: this.id },
548
+ exists: true,
549
+ deleted: false,
550
+ pending: 1,
551
+ localOnly: false,
552
+ status: "pending"
553
+ });
554
+ await this.persistence.enqueueMutation({
555
+ mutationId: this.persistence.createMutationId(),
556
+ collection: this.collection,
557
+ documentId: this.id,
558
+ type: "insert",
559
+ payload: data
560
+ });
561
+ const snap = DocumentSnapshot.fromMap(updated.data);
562
+ this.localStore?.emitDocument(this.collection, this.id, snap, "update");
563
+ this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
564
+ this.syncEngine?.flush().catch(console.error);
565
+ return snap;
818
566
  }
567
+ // ====================== DELETE ======================
819
568
  async delete() {
820
569
  if (this.persistence) {
821
570
  await this.persistence.markDeleted(this.collection, this.id, 1);
@@ -831,19 +580,22 @@ var DocumentRef = class {
831
580
  this.syncEngine?.flush().catch(console.error);
832
581
  return true;
833
582
  }
834
- return await this.app.getDatabase.collection(this.collection).doc(this.id).delete();
583
+ const res = await new HttpsRequest({
584
+ method: "POST" /* POST */,
585
+ endpoint: `${this.app.getBaseUrl()}/db/delete`,
586
+ headers: {
587
+ authorization: this.app.getConfig().token,
588
+ "x-project": this.app.getConfig().project
589
+ },
590
+ body: { collection: this.collection, id: this.id }
591
+ }).sendRequest();
592
+ return !!res?.success;
835
593
  }
836
- // array(key: string): Array {
837
- // return new Array(this.app, this.collection, key, this.id);
838
- // }
594
+ // ====================== SNAPSHOT ======================
839
595
  onSnapshot(callback) {
840
- if (this.app.offline().persistence && this.app.offline().localStore && this.app.offline().realtimeBridge) {
596
+ if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
841
597
  this.app.offline().realtimeBridge?.watchDocument(this.collection, this.id);
842
- return this.app.offline().localStore?.subscribeToDocument(
843
- this.collection,
844
- this.id,
845
- callback
846
- ) || (() => {
598
+ return this.localStore.subscribeToDocument(this.collection, this.id, callback) || (() => {
847
599
  });
848
600
  }
849
601
  return this.app.rtdb().subscribeToDocumentRaw(this.collection, this.id, (snapshot, change) => {
@@ -1071,15 +823,42 @@ var CollectionRef = class {
1071
823
  }
1072
824
  return local;
1073
825
  }
1074
- ;
1075
- return await this.app.getDatabase.collection(this.collection).get();
826
+ try {
827
+ const res = await new HttpsRequest({
828
+ method: "POST" /* POST */,
829
+ endpoint: `${this.app.getBaseUrl()}/db/read`,
830
+ headers: {
831
+ authorization: this.app.getConfig().token,
832
+ "x-project": this.app.getConfig().project
833
+ },
834
+ body: {
835
+ collection: this.collection,
836
+ filter: {}
837
+ }
838
+ }).sendRequest();
839
+ if (!res?.success || !Array.isArray(res.documents)) {
840
+ return [];
841
+ }
842
+ return res.documents.map((d) => {
843
+ delete d.token;
844
+ d.id = d.id ?? d._id;
845
+ delete d._id;
846
+ return DocumentSnapshot.fromMap(d);
847
+ });
848
+ } catch (error) {
849
+ console.error("[EdmaxLabs] Collection get failed:", error);
850
+ return [];
851
+ }
1076
852
  }
1077
853
  async refreshFromRemote() {
1078
854
  try {
1079
855
  const res = await new HttpsRequest({
1080
856
  method: "POST" /* POST */,
1081
857
  endpoint: `${this.app.getBaseUrl()}/db/read`,
1082
- headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
858
+ headers: {
859
+ authorization: this.app.getConfig().token,
860
+ "x-project": this.app.getConfig().project
861
+ },
1083
862
  body: {
1084
863
  collection: this.collection,
1085
864
  filter: {}
@@ -1105,7 +884,8 @@ var CollectionRef = class {
1105
884
  delete d._id;
1106
885
  return DocumentSnapshot.fromMap(d);
1107
886
  });
1108
- } catch {
887
+ } catch (error) {
888
+ console.error("[EdmaxLabs] refreshFromRemote failed:", error);
1109
889
  return [];
1110
890
  }
1111
891
  }
@@ -1135,13 +915,24 @@ var CollectionRef = class {
1135
915
  this.syncEngine?.flush().catch(console.error);
1136
916
  return snap;
1137
917
  }
1138
- ;
1139
- return await this.app.getDatabase.collection(this.collection).add(data);
918
+ const res = await new HttpsRequest({
919
+ method: "POST" /* POST */,
920
+ endpoint: `${this.app.getBaseUrl()}/db/create`,
921
+ headers: {
922
+ authorization: this.app.getConfig().token,
923
+ "x-project": this.app.getConfig().project
924
+ },
925
+ body: { collection: this.collection, data: { ...data, id: "" } }
926
+ // server will generate id
927
+ }).sendRequest();
928
+ if (!res?.success || !res.document)
929
+ return null;
930
+ return DocumentSnapshot.fromMap(res.document);
1140
931
  }
1141
932
  onSnapshot(callback) {
1142
- if (this.app.offline().persistence) {
933
+ if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
1143
934
  this.app.offline().realtimeBridge?.watchCollection(this.collection);
1144
- return this.app.offline().localStore?.subscribeToCollection(this.collection, callback) ?? (() => {
935
+ return this.localStore.subscribeToCollection(this.collection, callback) ?? (() => {
1145
936
  });
1146
937
  }
1147
938
  return this.app.rtdb().subscribeToCollectionRaw(this.collection, (payload, changes) => {
@@ -2734,6 +2525,164 @@ var _EdmaxLabs = class _EdmaxLabs {
2734
2525
  _EdmaxLabs.instance = null;
2735
2526
  var EdmaxLabs = _EdmaxLabs;
2736
2527
 
2528
+ // src/database/Timestamp.ts
2529
+ var Timestamp = class _Timestamp {
2530
+ constructor(seconds, nanoseconds = 0) {
2531
+ this.seconds = seconds;
2532
+ this.nanoseconds = nanoseconds;
2533
+ }
2534
+ static now() {
2535
+ return _Timestamp.fromMillis(Date.now());
2536
+ }
2537
+ static fromDate(date) {
2538
+ return new _Timestamp(
2539
+ Math.floor(date.getTime() / 1e3),
2540
+ date.getTime() % 1e3 * 1e6
2541
+ );
2542
+ }
2543
+ static fromMillis(ms) {
2544
+ return new _Timestamp(Math.floor(ms / 1e3), ms % 1e3 * 1e6);
2545
+ }
2546
+ static fromMongo(dateObj) {
2547
+ return _Timestamp.fromMillis(dateObj.getTime());
2548
+ }
2549
+ static fromJSON(obj) {
2550
+ return new _Timestamp(obj.seconds, obj.nanoseconds);
2551
+ }
2552
+ toDate() {
2553
+ return new Date(this.toMillis());
2554
+ }
2555
+ toMillis() {
2556
+ return this.seconds * 1e3 + Math.floor(this.nanoseconds / 1e6);
2557
+ }
2558
+ sinceEpoch() {
2559
+ return this.seconds;
2560
+ }
2561
+ toMongo() {
2562
+ return new Date(this.toMillis());
2563
+ }
2564
+ format(pattern = "dd-MM-yyyy HH:mm:ss") {
2565
+ const d = this.toDate();
2566
+ const monthsShort = [
2567
+ "Jan",
2568
+ "Feb",
2569
+ "Mar",
2570
+ "Apr",
2571
+ "May",
2572
+ "Jun",
2573
+ "Jul",
2574
+ "Aug",
2575
+ "Sep",
2576
+ "Oct",
2577
+ "Nov",
2578
+ "Dec"
2579
+ ];
2580
+ const monthsLong = [
2581
+ "January",
2582
+ "February",
2583
+ "March",
2584
+ "April",
2585
+ "May",
2586
+ "June",
2587
+ "July",
2588
+ "August",
2589
+ "September",
2590
+ "October",
2591
+ "November",
2592
+ "December"
2593
+ ];
2594
+ const hours = d.getHours();
2595
+ const hours12 = hours % 12 === 0 ? 12 : hours % 12;
2596
+ const ampm = hours < 12 ? "AM" : "PM";
2597
+ const map = {
2598
+ dd: String(d.getDate()).padStart(2, "0"),
2599
+ MM: String(d.getMonth() + 1).padStart(2, "0"),
2600
+ yyyy: d.getFullYear(),
2601
+ HH: String(d.getHours()).padStart(2, "0"),
2602
+ mm: String(d.getMinutes()).padStart(2, "0"),
2603
+ ss: String(d.getSeconds()).padStart(2, "0"),
2604
+ SSS: String(d.getMilliseconds()).padStart(3, "0"),
2605
+ MMM: monthsShort[d.getMonth()],
2606
+ MMMM: monthsLong[d.getMonth()],
2607
+ a: ampm.toLowerCase(),
2608
+ // am/pm
2609
+ A: ampm
2610
+ // AM/PM
2611
+ };
2612
+ return pattern.replace(
2613
+ /MMMM|MMM|dd|MM|yyyy|HH|mm|ss|SSS|a|A/g,
2614
+ (match) => String(map[match])
2615
+ );
2616
+ }
2617
+ compare(other) {
2618
+ if (this.seconds !== other.seconds) {
2619
+ return this.seconds - other.seconds;
2620
+ }
2621
+ return this.nanoseconds - other.nanoseconds;
2622
+ }
2623
+ relative(fmt = "dd-MM-yyyy HH:mm:ss") {
2624
+ const now = Date.now();
2625
+ const diff = now - this.toMillis();
2626
+ const sec = Math.floor(diff / 1e3);
2627
+ if (sec < 60)
2628
+ return `${sec}s ago`;
2629
+ const min = Math.floor(sec / 60);
2630
+ if (min < 60)
2631
+ return `${min}m ago`;
2632
+ const hrs = Math.floor(min / 60);
2633
+ if (hrs < 24)
2634
+ return `${hrs}h ago`;
2635
+ const days = Math.floor(hrs / 24);
2636
+ if (days < 3)
2637
+ return `${days}d ago`;
2638
+ return this.format(fmt);
2639
+ }
2640
+ add({
2641
+ seconds = 0,
2642
+ minutes = 0,
2643
+ hours = 0,
2644
+ days = 0
2645
+ }) {
2646
+ const totalMs = seconds * 1e3 + minutes * 6e4 + hours * 36e5 + days * 864e5;
2647
+ return _Timestamp.fromMillis(this.toMillis() + totalMs);
2648
+ }
2649
+ weekdayName() {
2650
+ return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.toDate().getDay()];
2651
+ }
2652
+ toJSON() {
2653
+ return {
2654
+ _type: "timestamp",
2655
+ seconds: this.seconds,
2656
+ nanoseconds: this.nanoseconds
2657
+ };
2658
+ }
2659
+ toString() {
2660
+ return this.format("yyyy-MM-dd HH:mm:ss");
2661
+ }
2662
+ };
2663
+
2664
+ // src/database/ArraySnapshot.ts
2665
+ var ArraySnapshot = class _ArraySnapshot {
2666
+ constructor(id = "", index, data, dt) {
2667
+ this.data = data;
2668
+ this.id = id;
2669
+ this.dt = dt ?? Timestamp.now();
2670
+ }
2671
+ static fromMap(map) {
2672
+ const index = map.index ?? map.index ?? -1;
2673
+ const id = map.id ?? map._id ?? "";
2674
+ const document2 = map.data ?? map.document ?? map;
2675
+ return new _ArraySnapshot(id, index, document2);
2676
+ }
2677
+ toMap() {
2678
+ return {
2679
+ id: this.id ?? "",
2680
+ data: this.data,
2681
+ dt: this.dt
2682
+ };
2683
+ }
2684
+ };
2685
+
2737
2686
  // src/index.ts
2738
2687
  var src_default = EdmaxLabs;
2739
2688
  // Annotate the CommonJS export names for ESM import in node: