edmaxlabs-core 2.5.0 → 2.5.2
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 +419 -466
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -14
- package/dist/index.d.ts +14 -14
- package/dist/index.mjs +419 -466
- 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
|
};
|
|
@@ -97,91 +101,123 @@ var HttpsRequest = class {
|
|
|
97
101
|
|
|
98
102
|
// src/authentication/Authentication.ts
|
|
99
103
|
var _Authentication = class _Authentication {
|
|
100
|
-
// Re-entrancy guard
|
|
101
104
|
constructor() {
|
|
102
|
-
this.eUser = null;
|
|
103
|
-
this.client = null;
|
|
104
|
-
this.app = null;
|
|
105
|
-
// Dedicated lightweight instance for auth
|
|
106
105
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
107
|
-
this.
|
|
108
|
-
|
|
106
|
+
this.eventWaiters = /* @__PURE__ */ new Map();
|
|
107
|
+
this.unsubscribers = /* @__PURE__ */ new Set();
|
|
108
|
+
this.isSameCredentials = (a, b) => {
|
|
109
|
+
if (!a && !b)
|
|
110
|
+
return true;
|
|
111
|
+
if (!a || !b)
|
|
112
|
+
return false;
|
|
113
|
+
return JSON.stringify(a.toMap()) === JSON.stringify(b.toMap());
|
|
114
|
+
};
|
|
109
115
|
this.createUserWithEmailAndPassword = async ({
|
|
110
116
|
email,
|
|
111
117
|
password
|
|
112
118
|
}) => {
|
|
119
|
+
this.eUser = void 0;
|
|
113
120
|
this.saveCredentials(null);
|
|
114
|
-
const app = this.app
|
|
115
|
-
const
|
|
116
|
-
|
|
121
|
+
const app = this.app?.getDatabase;
|
|
122
|
+
const data = await app?.collection("users").query.where({
|
|
123
|
+
key: "email",
|
|
124
|
+
op: "===",
|
|
125
|
+
value: email
|
|
126
|
+
}).get();
|
|
127
|
+
if (data.length > 0) {
|
|
117
128
|
throw new Error("Email Already In Use.");
|
|
118
129
|
}
|
|
119
|
-
const userRef = await app
|
|
130
|
+
const userRef = await app?.collection("users").add({
|
|
120
131
|
email,
|
|
121
132
|
password,
|
|
122
|
-
token: this.client.getConfig().token,
|
|
123
133
|
logged: true,
|
|
124
|
-
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
|
+
}
|
|
125
143
|
});
|
|
126
|
-
if (!userRef?.id) {
|
|
127
|
-
throw new Error("Something went wrong
|
|
144
|
+
if (!userRef?.id || userRef?.id === "") {
|
|
145
|
+
throw new Error("Something went wrong try Again.");
|
|
128
146
|
}
|
|
129
|
-
|
|
130
|
-
this.saveCredentials(
|
|
131
|
-
return
|
|
147
|
+
this.eUser = Credentials.fromMap(userRef);
|
|
148
|
+
this.saveCredentials(this.eUser);
|
|
149
|
+
return Credentials.fromMap(userRef.toMap());
|
|
132
150
|
};
|
|
133
151
|
this.signInWithEmailAndPassword = async ({
|
|
134
152
|
email,
|
|
135
153
|
password
|
|
136
154
|
}) => {
|
|
137
|
-
const app = this.app
|
|
138
|
-
const data = await app
|
|
139
|
-
|
|
155
|
+
const app = this.app?.getDatabase;
|
|
156
|
+
const data = await app?.collection("users").query.where({
|
|
157
|
+
key: "email",
|
|
158
|
+
op: "===",
|
|
159
|
+
value: email
|
|
160
|
+
}).where({
|
|
161
|
+
key: "password",
|
|
162
|
+
op: "===",
|
|
163
|
+
value: password
|
|
164
|
+
}).get();
|
|
165
|
+
if (data === void 0) {
|
|
166
|
+
throw new Error("Auth Failed.");
|
|
167
|
+
}
|
|
168
|
+
if (data?.length === 0) {
|
|
140
169
|
throw new Error("User Not Found.");
|
|
141
170
|
}
|
|
142
|
-
const
|
|
143
|
-
await app
|
|
171
|
+
const _data = data[0];
|
|
172
|
+
await app?.collection("users").doc(_data.id).update({
|
|
144
173
|
logged: true,
|
|
145
174
|
lastLogged: Date.now()
|
|
146
175
|
});
|
|
147
|
-
|
|
148
|
-
this.saveCredentials(
|
|
149
|
-
return
|
|
150
|
-
};
|
|
151
|
-
this.signOut = async () => {
|
|
152
|
-
const current = this.currentUser();
|
|
153
|
-
if (!current)
|
|
154
|
-
return;
|
|
155
|
-
const app = this.app.getDatabase;
|
|
156
|
-
await app.collection("users").doc(current.uid).update({
|
|
157
|
-
logged: false,
|
|
158
|
-
lastLogged: Date.now()
|
|
159
|
-
});
|
|
160
|
-
this.saveCredentials(null);
|
|
176
|
+
this.eUser = Credentials.fromMap(_data);
|
|
177
|
+
this.saveCredentials(this.eUser);
|
|
178
|
+
return Credentials.fromMap(_data);
|
|
161
179
|
};
|
|
162
180
|
this.deleteUser = async () => {
|
|
163
|
-
if (!this.eUser)
|
|
181
|
+
if (!this.eUser) {
|
|
164
182
|
throw new Error("No User Signed in");
|
|
165
|
-
|
|
166
|
-
|
|
183
|
+
}
|
|
184
|
+
const app = this.app?.getDatabase;
|
|
185
|
+
const userRef = await app?.collection("users").doc(this.eUser.uid).delete();
|
|
186
|
+
if (userRef) {
|
|
187
|
+
throw new Error("Something went wrong try Again.");
|
|
188
|
+
}
|
|
189
|
+
this.eUser = void 0;
|
|
190
|
+
this.saveCredentials(null);
|
|
191
|
+
};
|
|
192
|
+
this.signOut = async () => {
|
|
193
|
+
const app = this.app?.getDatabase;
|
|
194
|
+
const luid = this.currentUser();
|
|
195
|
+
this.eUser = void 0;
|
|
167
196
|
this.saveCredentials(null);
|
|
197
|
+
if (luid)
|
|
198
|
+
await app?.collection("users").doc(luid?.uid).update({
|
|
199
|
+
logged: false,
|
|
200
|
+
lastLogged: Date.now()
|
|
201
|
+
});
|
|
202
|
+
return;
|
|
168
203
|
};
|
|
169
|
-
// Optional: Rules verification
|
|
170
204
|
this.rules = async (path, context) => {
|
|
171
205
|
const res = await new HttpsRequest({
|
|
172
206
|
method: "POST" /* POST */,
|
|
173
|
-
endpoint:
|
|
207
|
+
endpoint: this.client.getBaseUrl() + "/auth/rules/verify",
|
|
174
208
|
headers: {
|
|
175
209
|
authorization: this.client.getConfig().token,
|
|
176
210
|
project: this.client.getConfig().project
|
|
177
211
|
},
|
|
178
|
-
body: {
|
|
212
|
+
body: {
|
|
213
|
+
path,
|
|
214
|
+
context
|
|
215
|
+
}
|
|
179
216
|
}).sendRequest();
|
|
180
217
|
return res;
|
|
181
218
|
};
|
|
182
|
-
if (_Authentication.instance)
|
|
219
|
+
if (_Authentication.instance)
|
|
183
220
|
return _Authentication.instance;
|
|
184
|
-
}
|
|
185
221
|
this.client = EdmaxLabs.instance;
|
|
186
222
|
this.app = new EdmaxLabs({
|
|
187
223
|
token: "auth",
|
|
@@ -190,16 +226,26 @@ var _Authentication = class _Authentication {
|
|
|
190
226
|
_Authentication.instance = this;
|
|
191
227
|
this.restoreSession();
|
|
192
228
|
}
|
|
229
|
+
restoreSession() {
|
|
230
|
+
const saved = this.currentUser();
|
|
231
|
+
if (saved) {
|
|
232
|
+
this.eUser = saved;
|
|
233
|
+
this.emitValue("creds", saved);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Better singleton
|
|
193
237
|
static getInstance() {
|
|
194
238
|
if (!_Authentication.instance) {
|
|
195
239
|
_Authentication.instance = new _Authentication();
|
|
196
240
|
}
|
|
197
241
|
return _Authentication.instance;
|
|
198
242
|
}
|
|
199
|
-
// ====================== EVENT SYSTEM ======================
|
|
200
243
|
emitValue(key, value) {
|
|
201
244
|
const listeners = this.eventListeners.get(key) || [];
|
|
202
|
-
|
|
245
|
+
listeners.forEach((l) => l(value));
|
|
246
|
+
const waiters = this.eventWaiters.get(key) || [];
|
|
247
|
+
waiters.forEach((resolve) => resolve(value));
|
|
248
|
+
this.eventWaiters.delete(key);
|
|
203
249
|
}
|
|
204
250
|
onValue(key, callback) {
|
|
205
251
|
const listeners = this.eventListeners.get(key) || [];
|
|
@@ -210,7 +256,6 @@ var _Authentication = class _Authentication {
|
|
|
210
256
|
this.eventListeners.set(key, filtered);
|
|
211
257
|
};
|
|
212
258
|
}
|
|
213
|
-
// ====================== CREDENTIALS ======================
|
|
214
259
|
currentUser() {
|
|
215
260
|
const data = localStorage.getItem("eauth");
|
|
216
261
|
if (!data)
|
|
@@ -233,71 +278,50 @@ var _Authentication = class _Authentication {
|
|
|
233
278
|
this.eUser = credentials;
|
|
234
279
|
this.emitValue("creds", credentials);
|
|
235
280
|
}
|
|
236
|
-
|
|
237
|
-
if (!a && !b)
|
|
238
|
-
return true;
|
|
239
|
-
if (!a || !b)
|
|
240
|
-
return false;
|
|
241
|
-
return a.uid === b.uid && a.displayInfo.email === b.displayInfo.email && a.lastLogged === b.lastLogged;
|
|
242
|
-
}
|
|
243
|
-
restoreSession() {
|
|
244
|
-
const saved = this.currentUser();
|
|
245
|
-
if (saved) {
|
|
246
|
-
this.eUser = saved;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
// ====================== MAIN AUTH STATE LISTENER ======================
|
|
250
|
-
/**
|
|
251
|
-
* Main method to listen to authentication state changes.
|
|
252
|
-
* This listener stays alive for the entire app lifetime.
|
|
253
|
-
*/
|
|
281
|
+
// Main auth state listener - should be called ONCE at app root
|
|
254
282
|
authState({
|
|
255
283
|
onChange,
|
|
256
284
|
onSignOut,
|
|
257
285
|
onDeleted
|
|
258
286
|
}) {
|
|
259
287
|
let userDocUnsubscribe;
|
|
260
|
-
let credsUnsub;
|
|
261
288
|
const handleCredsChange = (creds) => {
|
|
262
|
-
if (
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
userDocUnsubscribe = void 0;
|
|
289
|
+
if (userDocUnsubscribe) {
|
|
290
|
+
userDocUnsubscribe();
|
|
291
|
+
userDocUnsubscribe = void 0;
|
|
292
|
+
}
|
|
267
293
|
if (!creds) {
|
|
268
294
|
this.eUser = null;
|
|
269
295
|
onSignOut?.();
|
|
270
|
-
this.isHandlingChange = false;
|
|
271
296
|
return;
|
|
272
297
|
}
|
|
273
|
-
const userRef = this.app
|
|
298
|
+
const userRef = this.app?.getDatabase.collection("users").doc(creds.uid);
|
|
299
|
+
if (!userRef)
|
|
300
|
+
return;
|
|
274
301
|
userDocUnsubscribe = userRef.onSnapshot(
|
|
275
302
|
(snapshot, change) => {
|
|
303
|
+
console.log(snapshot);
|
|
276
304
|
if (change === "delete") {
|
|
277
305
|
this.saveCredentials(null);
|
|
278
306
|
onSignOut?.();
|
|
279
307
|
onDeleted?.();
|
|
280
|
-
this.isHandlingChange = false;
|
|
281
308
|
return;
|
|
282
309
|
}
|
|
283
310
|
if (change === "insert" || change === "update") {
|
|
284
|
-
if (!snapshot
|
|
285
|
-
this.isHandlingChange = false;
|
|
311
|
+
if (!snapshot)
|
|
286
312
|
return;
|
|
287
|
-
}
|
|
288
313
|
if (snapshot.data.logged === false || snapshot.data.logged === "false") {
|
|
289
314
|
this.saveCredentials(null);
|
|
290
315
|
onSignOut?.();
|
|
291
|
-
this.isHandlingChange = false;
|
|
292
316
|
return;
|
|
293
317
|
}
|
|
294
318
|
const newCreds = Credentials.fromMap(snapshot.toMap());
|
|
295
319
|
if (!this.isSameCredentials(this.eUser, newCreds)) {
|
|
320
|
+
this.eUser = newCreds;
|
|
296
321
|
this.saveCredentials(newCreds);
|
|
297
322
|
onChange(newCreds);
|
|
298
323
|
}
|
|
299
324
|
}
|
|
300
|
-
this.isHandlingChange = false;
|
|
301
325
|
}
|
|
302
326
|
);
|
|
303
327
|
};
|
|
@@ -306,10 +330,10 @@ var _Authentication = class _Authentication {
|
|
|
306
330
|
this.eUser = initialUser;
|
|
307
331
|
onChange(initialUser);
|
|
308
332
|
}
|
|
309
|
-
credsUnsub = this.onValue("creds", handleCredsChange);
|
|
310
333
|
handleCredsChange(initialUser);
|
|
334
|
+
const credsUnsub = this.onValue("creds", handleCredsChange);
|
|
311
335
|
return () => {
|
|
312
|
-
credsUnsub
|
|
336
|
+
credsUnsub();
|
|
313
337
|
userDocUnsubscribe?.();
|
|
314
338
|
};
|
|
315
339
|
}
|
|
@@ -336,308 +360,6 @@ var DocumentSnapshot = class _DocumentSnapshot {
|
|
|
336
360
|
}
|
|
337
361
|
};
|
|
338
362
|
|
|
339
|
-
// src/database/Timestamp.ts
|
|
340
|
-
var Timestamp = class _Timestamp {
|
|
341
|
-
constructor(seconds, nanoseconds = 0) {
|
|
342
|
-
this.seconds = seconds;
|
|
343
|
-
this.nanoseconds = nanoseconds;
|
|
344
|
-
}
|
|
345
|
-
static now() {
|
|
346
|
-
return _Timestamp.fromMillis(Date.now());
|
|
347
|
-
}
|
|
348
|
-
static fromDate(date) {
|
|
349
|
-
return new _Timestamp(
|
|
350
|
-
Math.floor(date.getTime() / 1e3),
|
|
351
|
-
date.getTime() % 1e3 * 1e6
|
|
352
|
-
);
|
|
353
|
-
}
|
|
354
|
-
static fromMillis(ms) {
|
|
355
|
-
return new _Timestamp(Math.floor(ms / 1e3), ms % 1e3 * 1e6);
|
|
356
|
-
}
|
|
357
|
-
static fromMongo(dateObj) {
|
|
358
|
-
return _Timestamp.fromMillis(dateObj.getTime());
|
|
359
|
-
}
|
|
360
|
-
static fromJSON(obj) {
|
|
361
|
-
return new _Timestamp(obj.seconds, obj.nanoseconds);
|
|
362
|
-
}
|
|
363
|
-
toDate() {
|
|
364
|
-
return new Date(this.toMillis());
|
|
365
|
-
}
|
|
366
|
-
toMillis() {
|
|
367
|
-
return this.seconds * 1e3 + Math.floor(this.nanoseconds / 1e6);
|
|
368
|
-
}
|
|
369
|
-
sinceEpoch() {
|
|
370
|
-
return this.seconds;
|
|
371
|
-
}
|
|
372
|
-
toMongo() {
|
|
373
|
-
return new Date(this.toMillis());
|
|
374
|
-
}
|
|
375
|
-
format(pattern = "dd-MM-yyyy HH:mm:ss") {
|
|
376
|
-
const d = this.toDate();
|
|
377
|
-
const monthsShort = [
|
|
378
|
-
"Jan",
|
|
379
|
-
"Feb",
|
|
380
|
-
"Mar",
|
|
381
|
-
"Apr",
|
|
382
|
-
"May",
|
|
383
|
-
"Jun",
|
|
384
|
-
"Jul",
|
|
385
|
-
"Aug",
|
|
386
|
-
"Sep",
|
|
387
|
-
"Oct",
|
|
388
|
-
"Nov",
|
|
389
|
-
"Dec"
|
|
390
|
-
];
|
|
391
|
-
const monthsLong = [
|
|
392
|
-
"January",
|
|
393
|
-
"February",
|
|
394
|
-
"March",
|
|
395
|
-
"April",
|
|
396
|
-
"May",
|
|
397
|
-
"June",
|
|
398
|
-
"July",
|
|
399
|
-
"August",
|
|
400
|
-
"September",
|
|
401
|
-
"October",
|
|
402
|
-
"November",
|
|
403
|
-
"December"
|
|
404
|
-
];
|
|
405
|
-
const hours = d.getHours();
|
|
406
|
-
const hours12 = hours % 12 === 0 ? 12 : hours % 12;
|
|
407
|
-
const ampm = hours < 12 ? "AM" : "PM";
|
|
408
|
-
const map = {
|
|
409
|
-
dd: String(d.getDate()).padStart(2, "0"),
|
|
410
|
-
MM: String(d.getMonth() + 1).padStart(2, "0"),
|
|
411
|
-
yyyy: d.getFullYear(),
|
|
412
|
-
HH: String(d.getHours()).padStart(2, "0"),
|
|
413
|
-
mm: String(d.getMinutes()).padStart(2, "0"),
|
|
414
|
-
ss: String(d.getSeconds()).padStart(2, "0"),
|
|
415
|
-
SSS: String(d.getMilliseconds()).padStart(3, "0"),
|
|
416
|
-
MMM: monthsShort[d.getMonth()],
|
|
417
|
-
MMMM: monthsLong[d.getMonth()],
|
|
418
|
-
a: ampm.toLowerCase(),
|
|
419
|
-
// am/pm
|
|
420
|
-
A: ampm
|
|
421
|
-
// AM/PM
|
|
422
|
-
};
|
|
423
|
-
return pattern.replace(
|
|
424
|
-
/MMMM|MMM|dd|MM|yyyy|HH|mm|ss|SSS|a|A/g,
|
|
425
|
-
(match) => String(map[match])
|
|
426
|
-
);
|
|
427
|
-
}
|
|
428
|
-
compare(other) {
|
|
429
|
-
if (this.seconds !== other.seconds) {
|
|
430
|
-
return this.seconds - other.seconds;
|
|
431
|
-
}
|
|
432
|
-
return this.nanoseconds - other.nanoseconds;
|
|
433
|
-
}
|
|
434
|
-
relative(fmt = "dd-MM-yyyy HH:mm:ss") {
|
|
435
|
-
const now = Date.now();
|
|
436
|
-
const diff = now - this.toMillis();
|
|
437
|
-
const sec = Math.floor(diff / 1e3);
|
|
438
|
-
if (sec < 60)
|
|
439
|
-
return `${sec}s ago`;
|
|
440
|
-
const min = Math.floor(sec / 60);
|
|
441
|
-
if (min < 60)
|
|
442
|
-
return `${min}m ago`;
|
|
443
|
-
const hrs = Math.floor(min / 60);
|
|
444
|
-
if (hrs < 24)
|
|
445
|
-
return `${hrs}h ago`;
|
|
446
|
-
const days = Math.floor(hrs / 24);
|
|
447
|
-
if (days < 3)
|
|
448
|
-
return `${days}d ago`;
|
|
449
|
-
return this.format(fmt);
|
|
450
|
-
}
|
|
451
|
-
add({
|
|
452
|
-
seconds = 0,
|
|
453
|
-
minutes = 0,
|
|
454
|
-
hours = 0,
|
|
455
|
-
days = 0
|
|
456
|
-
}) {
|
|
457
|
-
const totalMs = seconds * 1e3 + minutes * 6e4 + hours * 36e5 + days * 864e5;
|
|
458
|
-
return _Timestamp.fromMillis(this.toMillis() + totalMs);
|
|
459
|
-
}
|
|
460
|
-
weekdayName() {
|
|
461
|
-
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.toDate().getDay()];
|
|
462
|
-
}
|
|
463
|
-
toJSON() {
|
|
464
|
-
return {
|
|
465
|
-
_type: "timestamp",
|
|
466
|
-
seconds: this.seconds,
|
|
467
|
-
nanoseconds: this.nanoseconds
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
toString() {
|
|
471
|
-
return this.format("yyyy-MM-dd HH:mm:ss");
|
|
472
|
-
}
|
|
473
|
-
};
|
|
474
|
-
|
|
475
|
-
// src/database/ArraySnapshot.ts
|
|
476
|
-
var ArraySnapshot = class _ArraySnapshot {
|
|
477
|
-
constructor(id = "", index, data, dt) {
|
|
478
|
-
this.data = data;
|
|
479
|
-
this.id = id;
|
|
480
|
-
this.dt = dt ?? Timestamp.now();
|
|
481
|
-
}
|
|
482
|
-
static fromMap(map) {
|
|
483
|
-
const index = map.index ?? map.index ?? -1;
|
|
484
|
-
const id = map.id ?? map._id ?? "";
|
|
485
|
-
const document2 = map.data ?? map.document ?? map;
|
|
486
|
-
return new _ArraySnapshot(id, index, document2);
|
|
487
|
-
}
|
|
488
|
-
toMap() {
|
|
489
|
-
return {
|
|
490
|
-
id: this.id ?? "",
|
|
491
|
-
data: this.data,
|
|
492
|
-
dt: this.dt
|
|
493
|
-
};
|
|
494
|
-
}
|
|
495
|
-
};
|
|
496
|
-
|
|
497
|
-
// src/database/Array.ts
|
|
498
|
-
var Array2 = class {
|
|
499
|
-
constructor(app, collection, key, id) {
|
|
500
|
-
this.app = app;
|
|
501
|
-
this.collection = collection;
|
|
502
|
-
this.key = key;
|
|
503
|
-
this.docID = id;
|
|
504
|
-
this.persistence = app.offline().persistence;
|
|
505
|
-
this.syncEngine = app.offline().syncEngine;
|
|
506
|
-
this.localStore = app.offline().localStore;
|
|
507
|
-
}
|
|
508
|
-
/**
|
|
509
|
-
* Get current array elements (offline-first: prefers local cache)
|
|
510
|
-
*/
|
|
511
|
-
async show() {
|
|
512
|
-
if (this.persistence) {
|
|
513
|
-
const doc = await this.persistence.getDoc(this.collection, this.docID);
|
|
514
|
-
if (doc?.exists && !doc.deleted) {
|
|
515
|
-
const arrayData = doc.data[this.key] || [];
|
|
516
|
-
return arrayData.map((item) => ArraySnapshot.fromMap(item));
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
if (typeof navigator === "undefined" || navigator.onLine) {
|
|
520
|
-
this.refreshFromRemote().catch(() => {
|
|
521
|
-
});
|
|
522
|
-
}
|
|
523
|
-
return [];
|
|
524
|
-
}
|
|
525
|
-
async refreshFromRemote() {
|
|
526
|
-
try {
|
|
527
|
-
const res = await new HttpsRequest({
|
|
528
|
-
method: "POST" /* POST */,
|
|
529
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/show`,
|
|
530
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
531
|
-
body: {
|
|
532
|
-
collection: this.collection,
|
|
533
|
-
document: this.docID,
|
|
534
|
-
array: this.key
|
|
535
|
-
}
|
|
536
|
-
}).sendRequest();
|
|
537
|
-
if (!res?.success || !globalThis.Array.isArray(res.documents)) {
|
|
538
|
-
return [];
|
|
539
|
-
}
|
|
540
|
-
const snapshots = res.documents.filter((d) => d != null).map((d) => ArraySnapshot.fromMap(d));
|
|
541
|
-
if (this.persistence) {
|
|
542
|
-
const currentDoc = await this.persistence.getDoc(this.collection, this.docID);
|
|
543
|
-
if (currentDoc) {
|
|
544
|
-
await this.persistence.upsertDoc({
|
|
545
|
-
...currentDoc,
|
|
546
|
-
data: { ...currentDoc.data, [this.key]: res.documents },
|
|
547
|
-
pending: 0,
|
|
548
|
-
status: "synced",
|
|
549
|
-
lastSyncedAt: Date.now()
|
|
550
|
-
});
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
return snapshots;
|
|
554
|
-
} catch {
|
|
555
|
-
return [];
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
async push(data, id) {
|
|
559
|
-
const payload = {
|
|
560
|
-
data,
|
|
561
|
-
id: id || this.persistence?.createLocalId?.() || void 0,
|
|
562
|
-
dt: Timestamp.now()
|
|
563
|
-
};
|
|
564
|
-
if (this.persistence) {
|
|
565
|
-
await this.persistence.enqueueMutation({
|
|
566
|
-
mutationId: this.persistence.createMutationId(),
|
|
567
|
-
collection: this.collection,
|
|
568
|
-
documentId: this.docID,
|
|
569
|
-
type: "array_push",
|
|
570
|
-
// custom type
|
|
571
|
-
payload: { arrayKey: this.key, ...payload }
|
|
572
|
-
});
|
|
573
|
-
this.syncEngine?.flush().catch(console.error);
|
|
574
|
-
const doc = await this.persistence.getDoc(this.collection, this.docID);
|
|
575
|
-
if (doc) {
|
|
576
|
-
const updatedArray = [...doc.data[this.key] || [], payload];
|
|
577
|
-
const snap = ArraySnapshot.fromMap(payload);
|
|
578
|
-
this.localStore?.emitDocument(
|
|
579
|
-
this.collection,
|
|
580
|
-
this.docID,
|
|
581
|
-
DocumentSnapshot.fromMap({ ...doc.data, [this.key]: updatedArray }),
|
|
582
|
-
"update"
|
|
583
|
-
);
|
|
584
|
-
}
|
|
585
|
-
return ArraySnapshot.fromMap(payload);
|
|
586
|
-
}
|
|
587
|
-
const res = await new HttpsRequest({
|
|
588
|
-
method: "POST" /* POST */,
|
|
589
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/push`,
|
|
590
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
591
|
-
body: {
|
|
592
|
-
collection: this.collection,
|
|
593
|
-
document: this.docID,
|
|
594
|
-
array: this.key,
|
|
595
|
-
...payload
|
|
596
|
-
}
|
|
597
|
-
}).sendRequest();
|
|
598
|
-
return res?.success ? ArraySnapshot.fromMap(res.document) : null;
|
|
599
|
-
}
|
|
600
|
-
// Similar pattern for update, insert, remove, get...
|
|
601
|
-
// (I'll show one more as example; apply the same logic to the rest)
|
|
602
|
-
async update(position, data, id) {
|
|
603
|
-
if (this.persistence) {
|
|
604
|
-
await this.persistence.enqueueMutation({
|
|
605
|
-
mutationId: this.persistence.createMutationId(),
|
|
606
|
-
collection: this.collection,
|
|
607
|
-
documentId: this.docID,
|
|
608
|
-
type: "array_update",
|
|
609
|
-
payload: { arrayKey: this.key, position, data, id }
|
|
610
|
-
});
|
|
611
|
-
this.syncEngine?.flush().catch(console.error);
|
|
612
|
-
return ArraySnapshot.fromMap({ ...data, id });
|
|
613
|
-
}
|
|
614
|
-
const res = await new HttpsRequest({
|
|
615
|
-
method: "POST" /* POST */,
|
|
616
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/update`,
|
|
617
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
618
|
-
body: {
|
|
619
|
-
collection: this.collection,
|
|
620
|
-
document: this.docID,
|
|
621
|
-
array: this.key,
|
|
622
|
-
position,
|
|
623
|
-
data,
|
|
624
|
-
id
|
|
625
|
-
}
|
|
626
|
-
}).sendRequest();
|
|
627
|
-
return res?.success ? ArraySnapshot.fromMap(res.document) : null;
|
|
628
|
-
}
|
|
629
|
-
// TODO: Implement insert(), get(), remove() using the exact same offline pattern as push/update
|
|
630
|
-
async insert(position, data, id) {
|
|
631
|
-
return null;
|
|
632
|
-
}
|
|
633
|
-
async get(position) {
|
|
634
|
-
return null;
|
|
635
|
-
}
|
|
636
|
-
async remove(position) {
|
|
637
|
-
return null;
|
|
638
|
-
}
|
|
639
|
-
};
|
|
640
|
-
|
|
641
363
|
// src/utils/documentNomalizer.ts
|
|
642
364
|
function normalizePayload(payload) {
|
|
643
365
|
const raw = payload?.document ?? payload?.data ?? payload;
|
|
@@ -657,30 +379,19 @@ function validateDocumentData(data, operation) {
|
|
|
657
379
|
if (data === null || data === void 0) {
|
|
658
380
|
throw new Error(`${operation}: data cannot be null or undefined`);
|
|
659
381
|
}
|
|
660
|
-
if (typeof data !== "object") {
|
|
661
|
-
throw new Error(`${operation}: data must be
|
|
662
|
-
}
|
|
663
|
-
if (data instanceof Array2) {
|
|
664
|
-
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`);
|
|
665
384
|
}
|
|
666
|
-
const reservedFields = ["
|
|
385
|
+
const reservedFields = ["_id", "_createdAt", "_updatedAt", "_deleted", "_v", "_s"];
|
|
667
386
|
for (const field of reservedFields) {
|
|
668
387
|
if (field in data) {
|
|
669
388
|
throw new Error(`${operation}: '${field}' is a reserved field and cannot be set manually`);
|
|
670
389
|
}
|
|
671
390
|
}
|
|
672
|
-
const dataSize = JSON.stringify(data).length;
|
|
673
|
-
if (dataSize > 1024 * 1024) {
|
|
674
|
-
throw new Error(`${operation}: document size (${Math.round(dataSize / 1024)}KB) exceeds maximum allowed size (1MB)`);
|
|
675
|
-
}
|
|
676
|
-
try {
|
|
677
|
-
JSON.stringify(data);
|
|
678
|
-
} catch {
|
|
679
|
-
throw new Error(`${operation}: data contains circular references`);
|
|
680
|
-
}
|
|
681
391
|
}
|
|
682
392
|
var DocumentRef = class {
|
|
683
393
|
constructor(app, collection, id) {
|
|
394
|
+
this._isUpdating = false;
|
|
684
395
|
this.app = app;
|
|
685
396
|
this.collection = collection;
|
|
686
397
|
this.id = id;
|
|
@@ -688,58 +399,60 @@ var DocumentRef = class {
|
|
|
688
399
|
this.syncEngine = app.offline().syncEngine;
|
|
689
400
|
this.localStore = app.offline().localStore;
|
|
690
401
|
}
|
|
402
|
+
// ====================== GET ======================
|
|
691
403
|
async get() {
|
|
692
404
|
if (this.persistence) {
|
|
693
405
|
try {
|
|
694
406
|
const localSnap = await this.persistence.getDocSnapshot(this.collection, this.id);
|
|
695
407
|
if (localSnap)
|
|
696
|
-
return localSnap;
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
localOnly: false,
|
|
723
|
-
status: "pending"
|
|
724
|
-
});
|
|
725
|
-
await this.persistence.enqueueMutation({
|
|
726
|
-
mutationId: this.persistence.createMutationId(),
|
|
727
|
-
collection: this.collection,
|
|
728
|
-
documentId: this.id,
|
|
729
|
-
type: "insert",
|
|
730
|
-
// or "create" if you want to distinguish truly new docs
|
|
731
|
-
payload: data
|
|
732
|
-
});
|
|
733
|
-
const snap = DocumentSnapshot.fromMap(updated.data);
|
|
734
|
-
this.localStore?.emitDocument(this.collection, this.id, snap, "update");
|
|
735
|
-
const currentCollection = await this.persistence.getCollectionSnapshots(this.collection);
|
|
736
|
-
this.localStore?.emitCollection(this.collection, currentCollection, "update", this.id);
|
|
737
|
-
this.syncEngine?.flush().catch(console.error);
|
|
738
|
-
return snap;
|
|
408
|
+
return localSnap;
|
|
409
|
+
} catch (error) {
|
|
410
|
+
console.error("[EdmaxLabs] Cache read error:", error);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
try {
|
|
414
|
+
const res = await new HttpsRequest({
|
|
415
|
+
method: "POST" /* POST */,
|
|
416
|
+
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
417
|
+
headers: {
|
|
418
|
+
authorization: this.app.getConfig().token,
|
|
419
|
+
"x-project": this.app.getConfig().project
|
|
420
|
+
},
|
|
421
|
+
body: {
|
|
422
|
+
collection: this.collection,
|
|
423
|
+
id: this.id,
|
|
424
|
+
single: true
|
|
425
|
+
}
|
|
426
|
+
}).sendRequest();
|
|
427
|
+
if (!res?.success || !res.document)
|
|
428
|
+
return null;
|
|
429
|
+
return DocumentSnapshot.fromMap(res.document);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
console.error(`[DocumentRef] get(${this.collection}/${this.id}) failed:`, error);
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
739
434
|
}
|
|
435
|
+
// ====================== UPDATE ======================
|
|
740
436
|
async update(data) {
|
|
741
|
-
|
|
742
|
-
|
|
437
|
+
if (this._isUpdating) {
|
|
438
|
+
console.warn(`[DocumentRef] update recursion blocked on ${this.collection}/${this.id}`);
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
this._isUpdating = true;
|
|
442
|
+
try {
|
|
443
|
+
validateDocumentData(data, "DocumentRef.update");
|
|
444
|
+
if (!this.persistence) {
|
|
445
|
+
const res = await new HttpsRequest({
|
|
446
|
+
method: "POST" /* POST */,
|
|
447
|
+
endpoint: `${this.app.getBaseUrl()}/db/update`,
|
|
448
|
+
headers: {
|
|
449
|
+
authorization: this.app.getConfig().token,
|
|
450
|
+
"x-project": this.app.getConfig().project
|
|
451
|
+
},
|
|
452
|
+
body: { collection: this.collection, id: this.id, data }
|
|
453
|
+
}).sendRequest();
|
|
454
|
+
return res?.success ? DocumentSnapshot.fromMap({ ...data, id: this.id }) : null;
|
|
455
|
+
}
|
|
743
456
|
const old = await this.persistence.getDoc(this.collection, this.id);
|
|
744
457
|
if (!old || old.deleted)
|
|
745
458
|
return null;
|
|
@@ -769,9 +482,49 @@ var DocumentRef = class {
|
|
|
769
482
|
this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
|
|
770
483
|
this.syncEngine?.flush().catch(console.error);
|
|
771
484
|
return snap;
|
|
485
|
+
} finally {
|
|
486
|
+
this._isUpdating = false;
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// ====================== SET ======================
|
|
490
|
+
async set(data) {
|
|
491
|
+
validateDocumentData(data, "DocumentRef.set");
|
|
492
|
+
if (!this.persistence) {
|
|
493
|
+
const res = await new HttpsRequest({
|
|
494
|
+
method: "POST" /* POST */,
|
|
495
|
+
endpoint: `${this.app.getBaseUrl()}/db/create`,
|
|
496
|
+
headers: {
|
|
497
|
+
authorization: this.app.getConfig().token,
|
|
498
|
+
"x-project": this.app.getConfig().project
|
|
499
|
+
},
|
|
500
|
+
body: { collection: this.collection, data: { ...data, id: this.id } }
|
|
501
|
+
}).sendRequest();
|
|
502
|
+
return res?.success ? DocumentSnapshot.fromMap({ ...data, id: this.id }) : null;
|
|
772
503
|
}
|
|
773
|
-
|
|
504
|
+
const updated = await this.persistence.upsertDoc({
|
|
505
|
+
collection: this.collection,
|
|
506
|
+
id: this.id,
|
|
507
|
+
data: { ...data, id: this.id },
|
|
508
|
+
exists: true,
|
|
509
|
+
deleted: false,
|
|
510
|
+
pending: 1,
|
|
511
|
+
localOnly: false,
|
|
512
|
+
status: "pending"
|
|
513
|
+
});
|
|
514
|
+
await this.persistence.enqueueMutation({
|
|
515
|
+
mutationId: this.persistence.createMutationId(),
|
|
516
|
+
collection: this.collection,
|
|
517
|
+
documentId: this.id,
|
|
518
|
+
type: "insert",
|
|
519
|
+
payload: data
|
|
520
|
+
});
|
|
521
|
+
const snap = DocumentSnapshot.fromMap(updated.data);
|
|
522
|
+
this.localStore?.emitDocument(this.collection, this.id, snap, "update");
|
|
523
|
+
this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
|
|
524
|
+
this.syncEngine?.flush().catch(console.error);
|
|
525
|
+
return snap;
|
|
774
526
|
}
|
|
527
|
+
// ====================== DELETE ======================
|
|
775
528
|
async delete() {
|
|
776
529
|
if (this.persistence) {
|
|
777
530
|
await this.persistence.markDeleted(this.collection, this.id, 1);
|
|
@@ -787,19 +540,22 @@ var DocumentRef = class {
|
|
|
787
540
|
this.syncEngine?.flush().catch(console.error);
|
|
788
541
|
return true;
|
|
789
542
|
}
|
|
790
|
-
|
|
543
|
+
const res = await new HttpsRequest({
|
|
544
|
+
method: "POST" /* POST */,
|
|
545
|
+
endpoint: `${this.app.getBaseUrl()}/db/delete`,
|
|
546
|
+
headers: {
|
|
547
|
+
authorization: this.app.getConfig().token,
|
|
548
|
+
"x-project": this.app.getConfig().project
|
|
549
|
+
},
|
|
550
|
+
body: { collection: this.collection, id: this.id }
|
|
551
|
+
}).sendRequest();
|
|
552
|
+
return !!res?.success;
|
|
791
553
|
}
|
|
792
|
-
//
|
|
793
|
-
// return new Array(this.app, this.collection, key, this.id);
|
|
794
|
-
// }
|
|
554
|
+
// ====================== SNAPSHOT ======================
|
|
795
555
|
onSnapshot(callback) {
|
|
796
|
-
if (this.
|
|
556
|
+
if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
|
|
797
557
|
this.app.offline().realtimeBridge?.watchDocument(this.collection, this.id);
|
|
798
|
-
return this.
|
|
799
|
-
this.collection,
|
|
800
|
-
this.id,
|
|
801
|
-
callback
|
|
802
|
-
) || (() => {
|
|
558
|
+
return this.localStore.subscribeToDocument(this.collection, this.id, callback) || (() => {
|
|
803
559
|
});
|
|
804
560
|
}
|
|
805
561
|
return this.app.rtdb().subscribeToDocumentRaw(this.collection, this.id, (snapshot, change) => {
|
|
@@ -1027,15 +783,42 @@ var CollectionRef = class {
|
|
|
1027
783
|
}
|
|
1028
784
|
return local;
|
|
1029
785
|
}
|
|
1030
|
-
|
|
1031
|
-
|
|
786
|
+
try {
|
|
787
|
+
const res = await new HttpsRequest({
|
|
788
|
+
method: "POST" /* POST */,
|
|
789
|
+
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
790
|
+
headers: {
|
|
791
|
+
authorization: this.app.getConfig().token,
|
|
792
|
+
"x-project": this.app.getConfig().project
|
|
793
|
+
},
|
|
794
|
+
body: {
|
|
795
|
+
collection: this.collection,
|
|
796
|
+
filter: {}
|
|
797
|
+
}
|
|
798
|
+
}).sendRequest();
|
|
799
|
+
if (!res?.success || !Array.isArray(res.documents)) {
|
|
800
|
+
return [];
|
|
801
|
+
}
|
|
802
|
+
return res.documents.map((d) => {
|
|
803
|
+
delete d.token;
|
|
804
|
+
d.id = d.id ?? d._id;
|
|
805
|
+
delete d._id;
|
|
806
|
+
return DocumentSnapshot.fromMap(d);
|
|
807
|
+
});
|
|
808
|
+
} catch (error) {
|
|
809
|
+
console.error("[EdmaxLabs] Collection get failed:", error);
|
|
810
|
+
return [];
|
|
811
|
+
}
|
|
1032
812
|
}
|
|
1033
813
|
async refreshFromRemote() {
|
|
1034
814
|
try {
|
|
1035
815
|
const res = await new HttpsRequest({
|
|
1036
816
|
method: "POST" /* POST */,
|
|
1037
817
|
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
1038
|
-
headers: {
|
|
818
|
+
headers: {
|
|
819
|
+
authorization: this.app.getConfig().token,
|
|
820
|
+
"x-project": this.app.getConfig().project
|
|
821
|
+
},
|
|
1039
822
|
body: {
|
|
1040
823
|
collection: this.collection,
|
|
1041
824
|
filter: {}
|
|
@@ -1061,7 +844,8 @@ var CollectionRef = class {
|
|
|
1061
844
|
delete d._id;
|
|
1062
845
|
return DocumentSnapshot.fromMap(d);
|
|
1063
846
|
});
|
|
1064
|
-
} catch {
|
|
847
|
+
} catch (error) {
|
|
848
|
+
console.error("[EdmaxLabs] refreshFromRemote failed:", error);
|
|
1065
849
|
return [];
|
|
1066
850
|
}
|
|
1067
851
|
}
|
|
@@ -1091,13 +875,24 @@ var CollectionRef = class {
|
|
|
1091
875
|
this.syncEngine?.flush().catch(console.error);
|
|
1092
876
|
return snap;
|
|
1093
877
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
878
|
+
const res = await new HttpsRequest({
|
|
879
|
+
method: "POST" /* POST */,
|
|
880
|
+
endpoint: `${this.app.getBaseUrl()}/db/create`,
|
|
881
|
+
headers: {
|
|
882
|
+
authorization: this.app.getConfig().token,
|
|
883
|
+
"x-project": this.app.getConfig().project
|
|
884
|
+
},
|
|
885
|
+
body: { collection: this.collection, data: { ...data, id: "" } }
|
|
886
|
+
// server will generate id
|
|
887
|
+
}).sendRequest();
|
|
888
|
+
if (!res?.success || !res.document)
|
|
889
|
+
return null;
|
|
890
|
+
return DocumentSnapshot.fromMap(res.document);
|
|
1096
891
|
}
|
|
1097
892
|
onSnapshot(callback) {
|
|
1098
|
-
if (this.app.offline().
|
|
893
|
+
if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
|
|
1099
894
|
this.app.offline().realtimeBridge?.watchCollection(this.collection);
|
|
1100
|
-
return this.
|
|
895
|
+
return this.localStore.subscribeToCollection(this.collection, callback) ?? (() => {
|
|
1101
896
|
});
|
|
1102
897
|
}
|
|
1103
898
|
return this.app.rtdb().subscribeToCollectionRaw(this.collection, (payload, changes) => {
|
|
@@ -2690,6 +2485,164 @@ var _EdmaxLabs = class _EdmaxLabs {
|
|
|
2690
2485
|
_EdmaxLabs.instance = null;
|
|
2691
2486
|
var EdmaxLabs = _EdmaxLabs;
|
|
2692
2487
|
|
|
2488
|
+
// src/database/Timestamp.ts
|
|
2489
|
+
var Timestamp = class _Timestamp {
|
|
2490
|
+
constructor(seconds, nanoseconds = 0) {
|
|
2491
|
+
this.seconds = seconds;
|
|
2492
|
+
this.nanoseconds = nanoseconds;
|
|
2493
|
+
}
|
|
2494
|
+
static now() {
|
|
2495
|
+
return _Timestamp.fromMillis(Date.now());
|
|
2496
|
+
}
|
|
2497
|
+
static fromDate(date) {
|
|
2498
|
+
return new _Timestamp(
|
|
2499
|
+
Math.floor(date.getTime() / 1e3),
|
|
2500
|
+
date.getTime() % 1e3 * 1e6
|
|
2501
|
+
);
|
|
2502
|
+
}
|
|
2503
|
+
static fromMillis(ms) {
|
|
2504
|
+
return new _Timestamp(Math.floor(ms / 1e3), ms % 1e3 * 1e6);
|
|
2505
|
+
}
|
|
2506
|
+
static fromMongo(dateObj) {
|
|
2507
|
+
return _Timestamp.fromMillis(dateObj.getTime());
|
|
2508
|
+
}
|
|
2509
|
+
static fromJSON(obj) {
|
|
2510
|
+
return new _Timestamp(obj.seconds, obj.nanoseconds);
|
|
2511
|
+
}
|
|
2512
|
+
toDate() {
|
|
2513
|
+
return new Date(this.toMillis());
|
|
2514
|
+
}
|
|
2515
|
+
toMillis() {
|
|
2516
|
+
return this.seconds * 1e3 + Math.floor(this.nanoseconds / 1e6);
|
|
2517
|
+
}
|
|
2518
|
+
sinceEpoch() {
|
|
2519
|
+
return this.seconds;
|
|
2520
|
+
}
|
|
2521
|
+
toMongo() {
|
|
2522
|
+
return new Date(this.toMillis());
|
|
2523
|
+
}
|
|
2524
|
+
format(pattern = "dd-MM-yyyy HH:mm:ss") {
|
|
2525
|
+
const d = this.toDate();
|
|
2526
|
+
const monthsShort = [
|
|
2527
|
+
"Jan",
|
|
2528
|
+
"Feb",
|
|
2529
|
+
"Mar",
|
|
2530
|
+
"Apr",
|
|
2531
|
+
"May",
|
|
2532
|
+
"Jun",
|
|
2533
|
+
"Jul",
|
|
2534
|
+
"Aug",
|
|
2535
|
+
"Sep",
|
|
2536
|
+
"Oct",
|
|
2537
|
+
"Nov",
|
|
2538
|
+
"Dec"
|
|
2539
|
+
];
|
|
2540
|
+
const monthsLong = [
|
|
2541
|
+
"January",
|
|
2542
|
+
"February",
|
|
2543
|
+
"March",
|
|
2544
|
+
"April",
|
|
2545
|
+
"May",
|
|
2546
|
+
"June",
|
|
2547
|
+
"July",
|
|
2548
|
+
"August",
|
|
2549
|
+
"September",
|
|
2550
|
+
"October",
|
|
2551
|
+
"November",
|
|
2552
|
+
"December"
|
|
2553
|
+
];
|
|
2554
|
+
const hours = d.getHours();
|
|
2555
|
+
const hours12 = hours % 12 === 0 ? 12 : hours % 12;
|
|
2556
|
+
const ampm = hours < 12 ? "AM" : "PM";
|
|
2557
|
+
const map = {
|
|
2558
|
+
dd: String(d.getDate()).padStart(2, "0"),
|
|
2559
|
+
MM: String(d.getMonth() + 1).padStart(2, "0"),
|
|
2560
|
+
yyyy: d.getFullYear(),
|
|
2561
|
+
HH: String(d.getHours()).padStart(2, "0"),
|
|
2562
|
+
mm: String(d.getMinutes()).padStart(2, "0"),
|
|
2563
|
+
ss: String(d.getSeconds()).padStart(2, "0"),
|
|
2564
|
+
SSS: String(d.getMilliseconds()).padStart(3, "0"),
|
|
2565
|
+
MMM: monthsShort[d.getMonth()],
|
|
2566
|
+
MMMM: monthsLong[d.getMonth()],
|
|
2567
|
+
a: ampm.toLowerCase(),
|
|
2568
|
+
// am/pm
|
|
2569
|
+
A: ampm
|
|
2570
|
+
// AM/PM
|
|
2571
|
+
};
|
|
2572
|
+
return pattern.replace(
|
|
2573
|
+
/MMMM|MMM|dd|MM|yyyy|HH|mm|ss|SSS|a|A/g,
|
|
2574
|
+
(match) => String(map[match])
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
compare(other) {
|
|
2578
|
+
if (this.seconds !== other.seconds) {
|
|
2579
|
+
return this.seconds - other.seconds;
|
|
2580
|
+
}
|
|
2581
|
+
return this.nanoseconds - other.nanoseconds;
|
|
2582
|
+
}
|
|
2583
|
+
relative(fmt = "dd-MM-yyyy HH:mm:ss") {
|
|
2584
|
+
const now = Date.now();
|
|
2585
|
+
const diff = now - this.toMillis();
|
|
2586
|
+
const sec = Math.floor(diff / 1e3);
|
|
2587
|
+
if (sec < 60)
|
|
2588
|
+
return `${sec}s ago`;
|
|
2589
|
+
const min = Math.floor(sec / 60);
|
|
2590
|
+
if (min < 60)
|
|
2591
|
+
return `${min}m ago`;
|
|
2592
|
+
const hrs = Math.floor(min / 60);
|
|
2593
|
+
if (hrs < 24)
|
|
2594
|
+
return `${hrs}h ago`;
|
|
2595
|
+
const days = Math.floor(hrs / 24);
|
|
2596
|
+
if (days < 3)
|
|
2597
|
+
return `${days}d ago`;
|
|
2598
|
+
return this.format(fmt);
|
|
2599
|
+
}
|
|
2600
|
+
add({
|
|
2601
|
+
seconds = 0,
|
|
2602
|
+
minutes = 0,
|
|
2603
|
+
hours = 0,
|
|
2604
|
+
days = 0
|
|
2605
|
+
}) {
|
|
2606
|
+
const totalMs = seconds * 1e3 + minutes * 6e4 + hours * 36e5 + days * 864e5;
|
|
2607
|
+
return _Timestamp.fromMillis(this.toMillis() + totalMs);
|
|
2608
|
+
}
|
|
2609
|
+
weekdayName() {
|
|
2610
|
+
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.toDate().getDay()];
|
|
2611
|
+
}
|
|
2612
|
+
toJSON() {
|
|
2613
|
+
return {
|
|
2614
|
+
_type: "timestamp",
|
|
2615
|
+
seconds: this.seconds,
|
|
2616
|
+
nanoseconds: this.nanoseconds
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
toString() {
|
|
2620
|
+
return this.format("yyyy-MM-dd HH:mm:ss");
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
|
|
2624
|
+
// src/database/ArraySnapshot.ts
|
|
2625
|
+
var ArraySnapshot = class _ArraySnapshot {
|
|
2626
|
+
constructor(id = "", index, data, dt) {
|
|
2627
|
+
this.data = data;
|
|
2628
|
+
this.id = id;
|
|
2629
|
+
this.dt = dt ?? Timestamp.now();
|
|
2630
|
+
}
|
|
2631
|
+
static fromMap(map) {
|
|
2632
|
+
const index = map.index ?? map.index ?? -1;
|
|
2633
|
+
const id = map.id ?? map._id ?? "";
|
|
2634
|
+
const document2 = map.data ?? map.document ?? map;
|
|
2635
|
+
return new _ArraySnapshot(id, index, document2);
|
|
2636
|
+
}
|
|
2637
|
+
toMap() {
|
|
2638
|
+
return {
|
|
2639
|
+
id: this.id ?? "",
|
|
2640
|
+
data: this.data,
|
|
2641
|
+
dt: this.dt
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
};
|
|
2645
|
+
|
|
2693
2646
|
// src/index.ts
|
|
2694
2647
|
var src_default = EdmaxLabs;
|
|
2695
2648
|
export {
|