edmaxlabs-core 2.5.0 → 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 +422 -461
- 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 +422 -461
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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
|
};
|
|
@@ -129,91 +133,123 @@ var HttpsRequest = class {
|
|
|
129
133
|
|
|
130
134
|
// src/authentication/Authentication.ts
|
|
131
135
|
var _Authentication = class _Authentication {
|
|
132
|
-
// Re-entrancy guard
|
|
133
136
|
constructor() {
|
|
134
|
-
this.eUser = null;
|
|
135
|
-
this.client = null;
|
|
136
|
-
this.app = null;
|
|
137
|
-
// Dedicated lightweight instance for auth
|
|
138
137
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
139
|
-
this.
|
|
140
|
-
|
|
138
|
+
this.eventWaiters = /* @__PURE__ */ new Map();
|
|
139
|
+
this.unsubscribers = /* @__PURE__ */ new Set();
|
|
140
|
+
this.isSameCredentials = (a, b) => {
|
|
141
|
+
if (!a && !b)
|
|
142
|
+
return true;
|
|
143
|
+
if (!a || !b)
|
|
144
|
+
return false;
|
|
145
|
+
return JSON.stringify(a.toMap()) === JSON.stringify(b.toMap());
|
|
146
|
+
};
|
|
141
147
|
this.createUserWithEmailAndPassword = async ({
|
|
142
148
|
email,
|
|
143
149
|
password
|
|
144
150
|
}) => {
|
|
151
|
+
this.eUser = void 0;
|
|
145
152
|
this.saveCredentials(null);
|
|
146
|
-
const app = this.app
|
|
147
|
-
const
|
|
148
|
-
|
|
153
|
+
const app = this.app?.getDatabase;
|
|
154
|
+
const data = await app?.collection("users").query.where({
|
|
155
|
+
key: "email",
|
|
156
|
+
op: "===",
|
|
157
|
+
value: email
|
|
158
|
+
}).get();
|
|
159
|
+
if (data.length > 0) {
|
|
149
160
|
throw new Error("Email Already In Use.");
|
|
150
161
|
}
|
|
151
|
-
const userRef = await app
|
|
162
|
+
const userRef = await app?.collection("users").add({
|
|
152
163
|
email,
|
|
153
164
|
password,
|
|
154
|
-
token: this.client.getConfig().token,
|
|
155
165
|
logged: true,
|
|
156
|
-
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
|
+
}
|
|
157
175
|
});
|
|
158
|
-
if (!userRef?.id) {
|
|
159
|
-
throw new Error("Something went wrong
|
|
176
|
+
if (!userRef?.id || userRef?.id === "") {
|
|
177
|
+
throw new Error("Something went wrong try Again.");
|
|
160
178
|
}
|
|
161
|
-
|
|
162
|
-
this.saveCredentials(
|
|
163
|
-
return
|
|
179
|
+
this.eUser = Credentials.fromMap(userRef);
|
|
180
|
+
this.saveCredentials(this.eUser);
|
|
181
|
+
return Credentials.fromMap(userRef.toMap());
|
|
164
182
|
};
|
|
165
183
|
this.signInWithEmailAndPassword = async ({
|
|
166
184
|
email,
|
|
167
185
|
password
|
|
168
186
|
}) => {
|
|
169
|
-
const app = this.app
|
|
170
|
-
const data = await app
|
|
171
|
-
|
|
187
|
+
const app = this.app?.getDatabase;
|
|
188
|
+
const data = await app?.collection("users").query.where({
|
|
189
|
+
key: "email",
|
|
190
|
+
op: "===",
|
|
191
|
+
value: email
|
|
192
|
+
}).where({
|
|
193
|
+
key: "password",
|
|
194
|
+
op: "===",
|
|
195
|
+
value: password
|
|
196
|
+
}).get();
|
|
197
|
+
if (data === void 0) {
|
|
198
|
+
throw new Error("Auth Failed.");
|
|
199
|
+
}
|
|
200
|
+
if (data?.length === 0) {
|
|
172
201
|
throw new Error("User Not Found.");
|
|
173
202
|
}
|
|
174
|
-
const
|
|
175
|
-
await app
|
|
203
|
+
const _data = data[0];
|
|
204
|
+
await app?.collection("users").doc(_data.id).update({
|
|
176
205
|
logged: true,
|
|
177
206
|
lastLogged: Date.now()
|
|
178
207
|
});
|
|
179
|
-
|
|
180
|
-
this.saveCredentials(
|
|
181
|
-
return
|
|
182
|
-
};
|
|
183
|
-
this.signOut = async () => {
|
|
184
|
-
const current = this.currentUser();
|
|
185
|
-
if (!current)
|
|
186
|
-
return;
|
|
187
|
-
const app = this.app.getDatabase;
|
|
188
|
-
await app.collection("users").doc(current.uid).update({
|
|
189
|
-
logged: false,
|
|
190
|
-
lastLogged: Date.now()
|
|
191
|
-
});
|
|
192
|
-
this.saveCredentials(null);
|
|
208
|
+
this.eUser = Credentials.fromMap(_data);
|
|
209
|
+
this.saveCredentials(this.eUser);
|
|
210
|
+
return Credentials.fromMap(_data);
|
|
193
211
|
};
|
|
194
212
|
this.deleteUser = async () => {
|
|
195
|
-
if (!this.eUser)
|
|
213
|
+
if (!this.eUser) {
|
|
196
214
|
throw new Error("No User Signed in");
|
|
197
|
-
|
|
198
|
-
|
|
215
|
+
}
|
|
216
|
+
const app = this.app?.getDatabase;
|
|
217
|
+
const userRef = await app?.collection("users").doc(this.eUser.uid).delete();
|
|
218
|
+
if (userRef) {
|
|
219
|
+
throw new Error("Something went wrong try Again.");
|
|
220
|
+
}
|
|
221
|
+
this.eUser = void 0;
|
|
222
|
+
this.saveCredentials(null);
|
|
223
|
+
};
|
|
224
|
+
this.signOut = async () => {
|
|
225
|
+
const app = this.app?.getDatabase;
|
|
226
|
+
const luid = this.currentUser();
|
|
227
|
+
this.eUser = void 0;
|
|
199
228
|
this.saveCredentials(null);
|
|
229
|
+
if (luid)
|
|
230
|
+
await app?.collection("users").doc(luid?.uid).update({
|
|
231
|
+
logged: false,
|
|
232
|
+
lastLogged: Date.now()
|
|
233
|
+
});
|
|
234
|
+
return;
|
|
200
235
|
};
|
|
201
|
-
// Optional: Rules verification
|
|
202
236
|
this.rules = async (path, context) => {
|
|
203
237
|
const res = await new HttpsRequest({
|
|
204
238
|
method: "POST" /* POST */,
|
|
205
|
-
endpoint:
|
|
239
|
+
endpoint: this.client.getBaseUrl() + "/auth/rules/verify",
|
|
206
240
|
headers: {
|
|
207
241
|
authorization: this.client.getConfig().token,
|
|
208
242
|
project: this.client.getConfig().project
|
|
209
243
|
},
|
|
210
|
-
body: {
|
|
244
|
+
body: {
|
|
245
|
+
path,
|
|
246
|
+
context
|
|
247
|
+
}
|
|
211
248
|
}).sendRequest();
|
|
212
249
|
return res;
|
|
213
250
|
};
|
|
214
|
-
if (_Authentication.instance)
|
|
251
|
+
if (_Authentication.instance)
|
|
215
252
|
return _Authentication.instance;
|
|
216
|
-
}
|
|
217
253
|
this.client = EdmaxLabs.instance;
|
|
218
254
|
this.app = new EdmaxLabs({
|
|
219
255
|
token: "auth",
|
|
@@ -222,16 +258,26 @@ var _Authentication = class _Authentication {
|
|
|
222
258
|
_Authentication.instance = this;
|
|
223
259
|
this.restoreSession();
|
|
224
260
|
}
|
|
261
|
+
restoreSession() {
|
|
262
|
+
const saved = this.currentUser();
|
|
263
|
+
if (saved) {
|
|
264
|
+
this.eUser = saved;
|
|
265
|
+
this.emitValue("creds", saved);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
// Better singleton
|
|
225
269
|
static getInstance() {
|
|
226
270
|
if (!_Authentication.instance) {
|
|
227
271
|
_Authentication.instance = new _Authentication();
|
|
228
272
|
}
|
|
229
273
|
return _Authentication.instance;
|
|
230
274
|
}
|
|
231
|
-
// ====================== EVENT SYSTEM ======================
|
|
232
275
|
emitValue(key, value) {
|
|
233
276
|
const listeners = this.eventListeners.get(key) || [];
|
|
234
|
-
|
|
277
|
+
listeners.forEach((l) => l(value));
|
|
278
|
+
const waiters = this.eventWaiters.get(key) || [];
|
|
279
|
+
waiters.forEach((resolve) => resolve(value));
|
|
280
|
+
this.eventWaiters.delete(key);
|
|
235
281
|
}
|
|
236
282
|
onValue(key, callback) {
|
|
237
283
|
const listeners = this.eventListeners.get(key) || [];
|
|
@@ -242,7 +288,6 @@ var _Authentication = class _Authentication {
|
|
|
242
288
|
this.eventListeners.set(key, filtered);
|
|
243
289
|
};
|
|
244
290
|
}
|
|
245
|
-
// ====================== CREDENTIALS ======================
|
|
246
291
|
currentUser() {
|
|
247
292
|
const data = localStorage.getItem("eauth");
|
|
248
293
|
if (!data)
|
|
@@ -265,71 +310,50 @@ var _Authentication = class _Authentication {
|
|
|
265
310
|
this.eUser = credentials;
|
|
266
311
|
this.emitValue("creds", credentials);
|
|
267
312
|
}
|
|
268
|
-
|
|
269
|
-
if (!a && !b)
|
|
270
|
-
return true;
|
|
271
|
-
if (!a || !b)
|
|
272
|
-
return false;
|
|
273
|
-
return a.uid === b.uid && a.displayInfo.email === b.displayInfo.email && a.lastLogged === b.lastLogged;
|
|
274
|
-
}
|
|
275
|
-
restoreSession() {
|
|
276
|
-
const saved = this.currentUser();
|
|
277
|
-
if (saved) {
|
|
278
|
-
this.eUser = saved;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
// ====================== MAIN AUTH STATE LISTENER ======================
|
|
282
|
-
/**
|
|
283
|
-
* Main method to listen to authentication state changes.
|
|
284
|
-
* This listener stays alive for the entire app lifetime.
|
|
285
|
-
*/
|
|
313
|
+
// Main auth state listener - should be called ONCE at app root
|
|
286
314
|
authState({
|
|
287
315
|
onChange,
|
|
288
316
|
onSignOut,
|
|
289
317
|
onDeleted
|
|
290
318
|
}) {
|
|
291
319
|
let userDocUnsubscribe;
|
|
292
|
-
let credsUnsub;
|
|
293
320
|
const handleCredsChange = (creds) => {
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
userDocUnsubscribe = void 0;
|
|
321
|
+
if (userDocUnsubscribe) {
|
|
322
|
+
userDocUnsubscribe();
|
|
323
|
+
userDocUnsubscribe = void 0;
|
|
324
|
+
}
|
|
299
325
|
if (!creds) {
|
|
300
326
|
this.eUser = null;
|
|
301
327
|
onSignOut?.();
|
|
302
|
-
this.isHandlingChange = false;
|
|
303
328
|
return;
|
|
304
329
|
}
|
|
305
|
-
const userRef = this.app
|
|
330
|
+
const userRef = this.app?.getDatabase.collection("users").doc(creds.uid);
|
|
331
|
+
if (!userRef)
|
|
332
|
+
return;
|
|
306
333
|
userDocUnsubscribe = userRef.onSnapshot(
|
|
307
334
|
(snapshot, change) => {
|
|
335
|
+
console.log(snapshot);
|
|
308
336
|
if (change === "delete") {
|
|
309
337
|
this.saveCredentials(null);
|
|
310
338
|
onSignOut?.();
|
|
311
339
|
onDeleted?.();
|
|
312
|
-
this.isHandlingChange = false;
|
|
313
340
|
return;
|
|
314
341
|
}
|
|
315
342
|
if (change === "insert" || change === "update") {
|
|
316
|
-
if (!snapshot
|
|
317
|
-
this.isHandlingChange = false;
|
|
343
|
+
if (!snapshot)
|
|
318
344
|
return;
|
|
319
|
-
}
|
|
320
345
|
if (snapshot.data.logged === false || snapshot.data.logged === "false") {
|
|
321
346
|
this.saveCredentials(null);
|
|
322
347
|
onSignOut?.();
|
|
323
|
-
this.isHandlingChange = false;
|
|
324
348
|
return;
|
|
325
349
|
}
|
|
326
350
|
const newCreds = Credentials.fromMap(snapshot.toMap());
|
|
327
351
|
if (!this.isSameCredentials(this.eUser, newCreds)) {
|
|
352
|
+
this.eUser = newCreds;
|
|
328
353
|
this.saveCredentials(newCreds);
|
|
329
354
|
onChange(newCreds);
|
|
330
355
|
}
|
|
331
356
|
}
|
|
332
|
-
this.isHandlingChange = false;
|
|
333
357
|
}
|
|
334
358
|
);
|
|
335
359
|
};
|
|
@@ -338,10 +362,10 @@ var _Authentication = class _Authentication {
|
|
|
338
362
|
this.eUser = initialUser;
|
|
339
363
|
onChange(initialUser);
|
|
340
364
|
}
|
|
341
|
-
credsUnsub = this.onValue("creds", handleCredsChange);
|
|
342
365
|
handleCredsChange(initialUser);
|
|
366
|
+
const credsUnsub = this.onValue("creds", handleCredsChange);
|
|
343
367
|
return () => {
|
|
344
|
-
credsUnsub
|
|
368
|
+
credsUnsub();
|
|
345
369
|
userDocUnsubscribe?.();
|
|
346
370
|
};
|
|
347
371
|
}
|
|
@@ -368,308 +392,6 @@ var DocumentSnapshot = class _DocumentSnapshot {
|
|
|
368
392
|
}
|
|
369
393
|
};
|
|
370
394
|
|
|
371
|
-
// src/database/Timestamp.ts
|
|
372
|
-
var Timestamp = class _Timestamp {
|
|
373
|
-
constructor(seconds, nanoseconds = 0) {
|
|
374
|
-
this.seconds = seconds;
|
|
375
|
-
this.nanoseconds = nanoseconds;
|
|
376
|
-
}
|
|
377
|
-
static now() {
|
|
378
|
-
return _Timestamp.fromMillis(Date.now());
|
|
379
|
-
}
|
|
380
|
-
static fromDate(date) {
|
|
381
|
-
return new _Timestamp(
|
|
382
|
-
Math.floor(date.getTime() / 1e3),
|
|
383
|
-
date.getTime() % 1e3 * 1e6
|
|
384
|
-
);
|
|
385
|
-
}
|
|
386
|
-
static fromMillis(ms) {
|
|
387
|
-
return new _Timestamp(Math.floor(ms / 1e3), ms % 1e3 * 1e6);
|
|
388
|
-
}
|
|
389
|
-
static fromMongo(dateObj) {
|
|
390
|
-
return _Timestamp.fromMillis(dateObj.getTime());
|
|
391
|
-
}
|
|
392
|
-
static fromJSON(obj) {
|
|
393
|
-
return new _Timestamp(obj.seconds, obj.nanoseconds);
|
|
394
|
-
}
|
|
395
|
-
toDate() {
|
|
396
|
-
return new Date(this.toMillis());
|
|
397
|
-
}
|
|
398
|
-
toMillis() {
|
|
399
|
-
return this.seconds * 1e3 + Math.floor(this.nanoseconds / 1e6);
|
|
400
|
-
}
|
|
401
|
-
sinceEpoch() {
|
|
402
|
-
return this.seconds;
|
|
403
|
-
}
|
|
404
|
-
toMongo() {
|
|
405
|
-
return new Date(this.toMillis());
|
|
406
|
-
}
|
|
407
|
-
format(pattern = "dd-MM-yyyy HH:mm:ss") {
|
|
408
|
-
const d = this.toDate();
|
|
409
|
-
const monthsShort = [
|
|
410
|
-
"Jan",
|
|
411
|
-
"Feb",
|
|
412
|
-
"Mar",
|
|
413
|
-
"Apr",
|
|
414
|
-
"May",
|
|
415
|
-
"Jun",
|
|
416
|
-
"Jul",
|
|
417
|
-
"Aug",
|
|
418
|
-
"Sep",
|
|
419
|
-
"Oct",
|
|
420
|
-
"Nov",
|
|
421
|
-
"Dec"
|
|
422
|
-
];
|
|
423
|
-
const monthsLong = [
|
|
424
|
-
"January",
|
|
425
|
-
"February",
|
|
426
|
-
"March",
|
|
427
|
-
"April",
|
|
428
|
-
"May",
|
|
429
|
-
"June",
|
|
430
|
-
"July",
|
|
431
|
-
"August",
|
|
432
|
-
"September",
|
|
433
|
-
"October",
|
|
434
|
-
"November",
|
|
435
|
-
"December"
|
|
436
|
-
];
|
|
437
|
-
const hours = d.getHours();
|
|
438
|
-
const hours12 = hours % 12 === 0 ? 12 : hours % 12;
|
|
439
|
-
const ampm = hours < 12 ? "AM" : "PM";
|
|
440
|
-
const map = {
|
|
441
|
-
dd: String(d.getDate()).padStart(2, "0"),
|
|
442
|
-
MM: String(d.getMonth() + 1).padStart(2, "0"),
|
|
443
|
-
yyyy: d.getFullYear(),
|
|
444
|
-
HH: String(d.getHours()).padStart(2, "0"),
|
|
445
|
-
mm: String(d.getMinutes()).padStart(2, "0"),
|
|
446
|
-
ss: String(d.getSeconds()).padStart(2, "0"),
|
|
447
|
-
SSS: String(d.getMilliseconds()).padStart(3, "0"),
|
|
448
|
-
MMM: monthsShort[d.getMonth()],
|
|
449
|
-
MMMM: monthsLong[d.getMonth()],
|
|
450
|
-
a: ampm.toLowerCase(),
|
|
451
|
-
// am/pm
|
|
452
|
-
A: ampm
|
|
453
|
-
// AM/PM
|
|
454
|
-
};
|
|
455
|
-
return pattern.replace(
|
|
456
|
-
/MMMM|MMM|dd|MM|yyyy|HH|mm|ss|SSS|a|A/g,
|
|
457
|
-
(match) => String(map[match])
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
compare(other) {
|
|
461
|
-
if (this.seconds !== other.seconds) {
|
|
462
|
-
return this.seconds - other.seconds;
|
|
463
|
-
}
|
|
464
|
-
return this.nanoseconds - other.nanoseconds;
|
|
465
|
-
}
|
|
466
|
-
relative(fmt = "dd-MM-yyyy HH:mm:ss") {
|
|
467
|
-
const now = Date.now();
|
|
468
|
-
const diff = now - this.toMillis();
|
|
469
|
-
const sec = Math.floor(diff / 1e3);
|
|
470
|
-
if (sec < 60)
|
|
471
|
-
return `${sec}s ago`;
|
|
472
|
-
const min = Math.floor(sec / 60);
|
|
473
|
-
if (min < 60)
|
|
474
|
-
return `${min}m ago`;
|
|
475
|
-
const hrs = Math.floor(min / 60);
|
|
476
|
-
if (hrs < 24)
|
|
477
|
-
return `${hrs}h ago`;
|
|
478
|
-
const days = Math.floor(hrs / 24);
|
|
479
|
-
if (days < 3)
|
|
480
|
-
return `${days}d ago`;
|
|
481
|
-
return this.format(fmt);
|
|
482
|
-
}
|
|
483
|
-
add({
|
|
484
|
-
seconds = 0,
|
|
485
|
-
minutes = 0,
|
|
486
|
-
hours = 0,
|
|
487
|
-
days = 0
|
|
488
|
-
}) {
|
|
489
|
-
const totalMs = seconds * 1e3 + minutes * 6e4 + hours * 36e5 + days * 864e5;
|
|
490
|
-
return _Timestamp.fromMillis(this.toMillis() + totalMs);
|
|
491
|
-
}
|
|
492
|
-
weekdayName() {
|
|
493
|
-
return ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][this.toDate().getDay()];
|
|
494
|
-
}
|
|
495
|
-
toJSON() {
|
|
496
|
-
return {
|
|
497
|
-
_type: "timestamp",
|
|
498
|
-
seconds: this.seconds,
|
|
499
|
-
nanoseconds: this.nanoseconds
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
toString() {
|
|
503
|
-
return this.format("yyyy-MM-dd HH:mm:ss");
|
|
504
|
-
}
|
|
505
|
-
};
|
|
506
|
-
|
|
507
|
-
// src/database/ArraySnapshot.ts
|
|
508
|
-
var ArraySnapshot = class _ArraySnapshot {
|
|
509
|
-
constructor(id = "", index, data, dt) {
|
|
510
|
-
this.data = data;
|
|
511
|
-
this.id = id;
|
|
512
|
-
this.dt = dt ?? Timestamp.now();
|
|
513
|
-
}
|
|
514
|
-
static fromMap(map) {
|
|
515
|
-
const index = map.index ?? map.index ?? -1;
|
|
516
|
-
const id = map.id ?? map._id ?? "";
|
|
517
|
-
const document2 = map.data ?? map.document ?? map;
|
|
518
|
-
return new _ArraySnapshot(id, index, document2);
|
|
519
|
-
}
|
|
520
|
-
toMap() {
|
|
521
|
-
return {
|
|
522
|
-
id: this.id ?? "",
|
|
523
|
-
data: this.data,
|
|
524
|
-
dt: this.dt
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
};
|
|
528
|
-
|
|
529
|
-
// src/database/Array.ts
|
|
530
|
-
var Array2 = class {
|
|
531
|
-
constructor(app, collection, key, id) {
|
|
532
|
-
this.app = app;
|
|
533
|
-
this.collection = collection;
|
|
534
|
-
this.key = key;
|
|
535
|
-
this.docID = id;
|
|
536
|
-
this.persistence = app.offline().persistence;
|
|
537
|
-
this.syncEngine = app.offline().syncEngine;
|
|
538
|
-
this.localStore = app.offline().localStore;
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Get current array elements (offline-first: prefers local cache)
|
|
542
|
-
*/
|
|
543
|
-
async show() {
|
|
544
|
-
if (this.persistence) {
|
|
545
|
-
const doc = await this.persistence.getDoc(this.collection, this.docID);
|
|
546
|
-
if (doc?.exists && !doc.deleted) {
|
|
547
|
-
const arrayData = doc.data[this.key] || [];
|
|
548
|
-
return arrayData.map((item) => ArraySnapshot.fromMap(item));
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
if (typeof navigator === "undefined" || navigator.onLine) {
|
|
552
|
-
this.refreshFromRemote().catch(() => {
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
return [];
|
|
556
|
-
}
|
|
557
|
-
async refreshFromRemote() {
|
|
558
|
-
try {
|
|
559
|
-
const res = await new HttpsRequest({
|
|
560
|
-
method: "POST" /* POST */,
|
|
561
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/show`,
|
|
562
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
563
|
-
body: {
|
|
564
|
-
collection: this.collection,
|
|
565
|
-
document: this.docID,
|
|
566
|
-
array: this.key
|
|
567
|
-
}
|
|
568
|
-
}).sendRequest();
|
|
569
|
-
if (!res?.success || !globalThis.Array.isArray(res.documents)) {
|
|
570
|
-
return [];
|
|
571
|
-
}
|
|
572
|
-
const snapshots = res.documents.filter((d) => d != null).map((d) => ArraySnapshot.fromMap(d));
|
|
573
|
-
if (this.persistence) {
|
|
574
|
-
const currentDoc = await this.persistence.getDoc(this.collection, this.docID);
|
|
575
|
-
if (currentDoc) {
|
|
576
|
-
await this.persistence.upsertDoc({
|
|
577
|
-
...currentDoc,
|
|
578
|
-
data: { ...currentDoc.data, [this.key]: res.documents },
|
|
579
|
-
pending: 0,
|
|
580
|
-
status: "synced",
|
|
581
|
-
lastSyncedAt: Date.now()
|
|
582
|
-
});
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
return snapshots;
|
|
586
|
-
} catch {
|
|
587
|
-
return [];
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
async push(data, id) {
|
|
591
|
-
const payload = {
|
|
592
|
-
data,
|
|
593
|
-
id: id || this.persistence?.createLocalId?.() || void 0,
|
|
594
|
-
dt: Timestamp.now()
|
|
595
|
-
};
|
|
596
|
-
if (this.persistence) {
|
|
597
|
-
await this.persistence.enqueueMutation({
|
|
598
|
-
mutationId: this.persistence.createMutationId(),
|
|
599
|
-
collection: this.collection,
|
|
600
|
-
documentId: this.docID,
|
|
601
|
-
type: "array_push",
|
|
602
|
-
// custom type
|
|
603
|
-
payload: { arrayKey: this.key, ...payload }
|
|
604
|
-
});
|
|
605
|
-
this.syncEngine?.flush().catch(console.error);
|
|
606
|
-
const doc = await this.persistence.getDoc(this.collection, this.docID);
|
|
607
|
-
if (doc) {
|
|
608
|
-
const updatedArray = [...doc.data[this.key] || [], payload];
|
|
609
|
-
const snap = ArraySnapshot.fromMap(payload);
|
|
610
|
-
this.localStore?.emitDocument(
|
|
611
|
-
this.collection,
|
|
612
|
-
this.docID,
|
|
613
|
-
DocumentSnapshot.fromMap({ ...doc.data, [this.key]: updatedArray }),
|
|
614
|
-
"update"
|
|
615
|
-
);
|
|
616
|
-
}
|
|
617
|
-
return ArraySnapshot.fromMap(payload);
|
|
618
|
-
}
|
|
619
|
-
const res = await new HttpsRequest({
|
|
620
|
-
method: "POST" /* POST */,
|
|
621
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/push`,
|
|
622
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
623
|
-
body: {
|
|
624
|
-
collection: this.collection,
|
|
625
|
-
document: this.docID,
|
|
626
|
-
array: this.key,
|
|
627
|
-
...payload
|
|
628
|
-
}
|
|
629
|
-
}).sendRequest();
|
|
630
|
-
return res?.success ? ArraySnapshot.fromMap(res.document) : null;
|
|
631
|
-
}
|
|
632
|
-
// Similar pattern for update, insert, remove, get...
|
|
633
|
-
// (I'll show one more as example; apply the same logic to the rest)
|
|
634
|
-
async update(position, data, id) {
|
|
635
|
-
if (this.persistence) {
|
|
636
|
-
await this.persistence.enqueueMutation({
|
|
637
|
-
mutationId: this.persistence.createMutationId(),
|
|
638
|
-
collection: this.collection,
|
|
639
|
-
documentId: this.docID,
|
|
640
|
-
type: "array_update",
|
|
641
|
-
payload: { arrayKey: this.key, position, data, id }
|
|
642
|
-
});
|
|
643
|
-
this.syncEngine?.flush().catch(console.error);
|
|
644
|
-
return ArraySnapshot.fromMap({ ...data, id });
|
|
645
|
-
}
|
|
646
|
-
const res = await new HttpsRequest({
|
|
647
|
-
method: "POST" /* POST */,
|
|
648
|
-
endpoint: `${this.app.getBaseUrl()}/db/array/update`,
|
|
649
|
-
headers: { authorization: this.app.getConfig().token, "x-project": this.app.getConfig().project },
|
|
650
|
-
body: {
|
|
651
|
-
collection: this.collection,
|
|
652
|
-
document: this.docID,
|
|
653
|
-
array: this.key,
|
|
654
|
-
position,
|
|
655
|
-
data,
|
|
656
|
-
id
|
|
657
|
-
}
|
|
658
|
-
}).sendRequest();
|
|
659
|
-
return res?.success ? ArraySnapshot.fromMap(res.document) : null;
|
|
660
|
-
}
|
|
661
|
-
// TODO: Implement insert(), get(), remove() using the exact same offline pattern as push/update
|
|
662
|
-
async insert(position, data, id) {
|
|
663
|
-
return null;
|
|
664
|
-
}
|
|
665
|
-
async get(position) {
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
async remove(position) {
|
|
669
|
-
return null;
|
|
670
|
-
}
|
|
671
|
-
};
|
|
672
|
-
|
|
673
395
|
// src/utils/documentNomalizer.ts
|
|
674
396
|
function normalizePayload(payload) {
|
|
675
397
|
const raw = payload?.document ?? payload?.data ?? payload;
|
|
@@ -689,11 +411,8 @@ function validateDocumentData(data, operation) {
|
|
|
689
411
|
if (data === null || data === void 0) {
|
|
690
412
|
throw new Error(`${operation}: data cannot be null or undefined`);
|
|
691
413
|
}
|
|
692
|
-
if (typeof data !== "object") {
|
|
693
|
-
throw new Error(`${operation}: data must be
|
|
694
|
-
}
|
|
695
|
-
if (data instanceof Array2) {
|
|
696
|
-
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`);
|
|
697
416
|
}
|
|
698
417
|
const reservedFields = ["id", "_id", "_createdAt", "_updatedAt", "_deleted"];
|
|
699
418
|
for (const field of reservedFields) {
|
|
@@ -701,18 +420,18 @@ function validateDocumentData(data, operation) {
|
|
|
701
420
|
throw new Error(`${operation}: '${field}' is a reserved field and cannot be set manually`);
|
|
702
421
|
}
|
|
703
422
|
}
|
|
704
|
-
const dataSize = JSON.stringify(data).length;
|
|
705
|
-
if (dataSize > 1024 * 1024) {
|
|
706
|
-
throw new Error(`${operation}: document size (${Math.round(dataSize / 1024)}KB) exceeds maximum allowed size (1MB)`);
|
|
707
|
-
}
|
|
708
423
|
try {
|
|
709
|
-
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
|
+
}
|
|
710
428
|
} catch {
|
|
711
429
|
throw new Error(`${operation}: data contains circular references`);
|
|
712
430
|
}
|
|
713
431
|
}
|
|
714
432
|
var DocumentRef = class {
|
|
715
433
|
constructor(app, collection, id) {
|
|
434
|
+
this._isUpdating = false;
|
|
716
435
|
this.app = app;
|
|
717
436
|
this.collection = collection;
|
|
718
437
|
this.id = id;
|
|
@@ -720,58 +439,60 @@ var DocumentRef = class {
|
|
|
720
439
|
this.syncEngine = app.offline().syncEngine;
|
|
721
440
|
this.localStore = app.offline().localStore;
|
|
722
441
|
}
|
|
442
|
+
// ====================== GET ======================
|
|
723
443
|
async get() {
|
|
724
444
|
if (this.persistence) {
|
|
725
445
|
try {
|
|
726
446
|
const localSnap = await this.persistence.getDocSnapshot(this.collection, this.id);
|
|
727
447
|
if (localSnap)
|
|
728
|
-
return localSnap;
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
localOnly: false,
|
|
755
|
-
status: "pending"
|
|
756
|
-
});
|
|
757
|
-
await this.persistence.enqueueMutation({
|
|
758
|
-
mutationId: this.persistence.createMutationId(),
|
|
759
|
-
collection: this.collection,
|
|
760
|
-
documentId: this.id,
|
|
761
|
-
type: "insert",
|
|
762
|
-
// or "create" if you want to distinguish truly new docs
|
|
763
|
-
payload: data
|
|
764
|
-
});
|
|
765
|
-
const snap = DocumentSnapshot.fromMap(updated.data);
|
|
766
|
-
this.localStore?.emitDocument(this.collection, this.id, snap, "update");
|
|
767
|
-
const currentCollection = await this.persistence.getCollectionSnapshots(this.collection);
|
|
768
|
-
this.localStore?.emitCollection(this.collection, currentCollection, "update", this.id);
|
|
769
|
-
this.syncEngine?.flush().catch(console.error);
|
|
770
|
-
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
|
+
}
|
|
771
474
|
}
|
|
475
|
+
// ====================== UPDATE ======================
|
|
772
476
|
async update(data) {
|
|
773
|
-
|
|
774
|
-
|
|
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
|
+
}
|
|
775
496
|
const old = await this.persistence.getDoc(this.collection, this.id);
|
|
776
497
|
if (!old || old.deleted)
|
|
777
498
|
return null;
|
|
@@ -801,9 +522,49 @@ var DocumentRef = class {
|
|
|
801
522
|
this.localStore?.notifyCollectionChanged(this.collection, this.id, "update");
|
|
802
523
|
this.syncEngine?.flush().catch(console.error);
|
|
803
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;
|
|
804
543
|
}
|
|
805
|
-
|
|
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;
|
|
806
566
|
}
|
|
567
|
+
// ====================== DELETE ======================
|
|
807
568
|
async delete() {
|
|
808
569
|
if (this.persistence) {
|
|
809
570
|
await this.persistence.markDeleted(this.collection, this.id, 1);
|
|
@@ -819,19 +580,22 @@ var DocumentRef = class {
|
|
|
819
580
|
this.syncEngine?.flush().catch(console.error);
|
|
820
581
|
return true;
|
|
821
582
|
}
|
|
822
|
-
|
|
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;
|
|
823
593
|
}
|
|
824
|
-
//
|
|
825
|
-
// return new Array(this.app, this.collection, key, this.id);
|
|
826
|
-
// }
|
|
594
|
+
// ====================== SNAPSHOT ======================
|
|
827
595
|
onSnapshot(callback) {
|
|
828
|
-
if (this.
|
|
596
|
+
if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
|
|
829
597
|
this.app.offline().realtimeBridge?.watchDocument(this.collection, this.id);
|
|
830
|
-
return this.
|
|
831
|
-
this.collection,
|
|
832
|
-
this.id,
|
|
833
|
-
callback
|
|
834
|
-
) || (() => {
|
|
598
|
+
return this.localStore.subscribeToDocument(this.collection, this.id, callback) || (() => {
|
|
835
599
|
});
|
|
836
600
|
}
|
|
837
601
|
return this.app.rtdb().subscribeToDocumentRaw(this.collection, this.id, (snapshot, change) => {
|
|
@@ -1059,15 +823,42 @@ var CollectionRef = class {
|
|
|
1059
823
|
}
|
|
1060
824
|
return local;
|
|
1061
825
|
}
|
|
1062
|
-
|
|
1063
|
-
|
|
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
|
+
}
|
|
1064
852
|
}
|
|
1065
853
|
async refreshFromRemote() {
|
|
1066
854
|
try {
|
|
1067
855
|
const res = await new HttpsRequest({
|
|
1068
856
|
method: "POST" /* POST */,
|
|
1069
857
|
endpoint: `${this.app.getBaseUrl()}/db/read`,
|
|
1070
|
-
headers: {
|
|
858
|
+
headers: {
|
|
859
|
+
authorization: this.app.getConfig().token,
|
|
860
|
+
"x-project": this.app.getConfig().project
|
|
861
|
+
},
|
|
1071
862
|
body: {
|
|
1072
863
|
collection: this.collection,
|
|
1073
864
|
filter: {}
|
|
@@ -1093,7 +884,8 @@ var CollectionRef = class {
|
|
|
1093
884
|
delete d._id;
|
|
1094
885
|
return DocumentSnapshot.fromMap(d);
|
|
1095
886
|
});
|
|
1096
|
-
} catch {
|
|
887
|
+
} catch (error) {
|
|
888
|
+
console.error("[EdmaxLabs] refreshFromRemote failed:", error);
|
|
1097
889
|
return [];
|
|
1098
890
|
}
|
|
1099
891
|
}
|
|
@@ -1123,13 +915,24 @@ var CollectionRef = class {
|
|
|
1123
915
|
this.syncEngine?.flush().catch(console.error);
|
|
1124
916
|
return snap;
|
|
1125
917
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
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);
|
|
1128
931
|
}
|
|
1129
932
|
onSnapshot(callback) {
|
|
1130
|
-
if (this.app.offline().
|
|
933
|
+
if (this.persistence && this.localStore && this.app.offline().realtimeBridge) {
|
|
1131
934
|
this.app.offline().realtimeBridge?.watchCollection(this.collection);
|
|
1132
|
-
return this.
|
|
935
|
+
return this.localStore.subscribeToCollection(this.collection, callback) ?? (() => {
|
|
1133
936
|
});
|
|
1134
937
|
}
|
|
1135
938
|
return this.app.rtdb().subscribeToCollectionRaw(this.collection, (payload, changes) => {
|
|
@@ -2722,6 +2525,164 @@ var _EdmaxLabs = class _EdmaxLabs {
|
|
|
2722
2525
|
_EdmaxLabs.instance = null;
|
|
2723
2526
|
var EdmaxLabs = _EdmaxLabs;
|
|
2724
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
|
+
|
|
2725
2686
|
// src/index.ts
|
|
2726
2687
|
var src_default = EdmaxLabs;
|
|
2727
2688
|
// Annotate the CommonJS export names for ESM import in node:
|