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.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(data[0].id).update({
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(data[0].toMap());
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 an object`);
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
- return null;
710
- } catch (error) {
711
- console.error("[EdmaxLabs] Error reading from cache:", error);
712
- }
713
- }
714
- return await this.app.getDatabase.collection(this.collection).doc(this.id).get();
715
- }
716
- async set(data) {
717
- validateDocumentData(data, "DocumentRef.set");
718
- if (!this.persistence) {
719
- const res = await new HttpsRequest({
720
- method: "POST" /* POST */,
721
- endpoint: `${this.app.getBaseUrl()}/db/create`,
722
- headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
723
- body: { collection: this.collection, data: { ...data, id: this.id } }
724
- }).sendRequest();
725
- return res?.success ? DocumentSnapshot.fromMap(data) : null;
726
- }
727
- const updated = await this.persistence.upsertDoc({
728
- collection: this.collection,
729
- id: this.id,
730
- data: { ...data, id: this.id },
731
- exists: true,
732
- deleted: false,
733
- pending: 1,
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
- validateDocumentData(data, "DocumentRef.update");
754
- if (this.persistence) {
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
- return await this.app.getDatabase.collection(this.collection).doc(this.id).update(data);
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
- return await this.app.getDatabase.collection(this.collection).doc(this.id).delete();
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
- // array(key: string): Array {
805
- // return new Array(this.app, this.collection, key, this.id);
806
- // }
562
+ // ====================== SNAPSHOT ======================
807
563
  onSnapshot(callback) {
808
- if (this.app.offline().persistence && this.app.offline().localStore && this.app.offline().realtimeBridge) {
564
+ if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
809
565
  this.app.offline().realtimeBridge?.watchDocument(this.collection, this.id);
810
- return this.app.offline().localStore?.subscribeToDocument(
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
- return await this.app.getDatabase.collection(this.collection).get();
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: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
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
- return await this.app.getDatabase.collection(this.collection).add(data);
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().persistence) {
901
+ if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
1111
902
  this.app.offline().realtimeBridge?.watchCollection(this.collection);
1112
- return this.app.offline().localStore?.subscribeToCollection(this.collection, callback) ?? (() => {
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 {