@ztimson/momentum 0.52.0 → 0.52.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 CHANGED
@@ -289,7 +289,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
289
289
  }
290
290
  }
291
291
  function blackOrWhite(background) {
292
- const exploded = background.match(background.length >= 6 ? /\w\w/g : /\w/g);
292
+ const exploded = background == null ? void 0 : background.match(background.length >= 6 ? /\w\w/g : /\w/g);
293
293
  if (!exploded) return "black";
294
294
  const [r2, g, b2] = exploded.map((hex) => parseInt(hex, 16));
295
295
  const luminance = (0.299 * r2 + 0.587 * g + 0.114 * b2) / 255;
@@ -667,31 +667,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
667
667
  __publicField2(_Http, "headers", {});
668
668
  let Http = _Http;
669
669
  const CliEffects = {
670
- CLEAR: "\x1B[0m",
671
- BRIGHT: "\x1B[1m",
672
- DIM: "\x1B[2m",
673
- UNDERSCORE: "\x1B[4m",
674
- BLINK: "\x1B[5m",
675
- REVERSE: "\x1B[7m",
676
- HIDDEN: "\x1B[8m"
670
+ CLEAR: "\x1B[0m"
677
671
  };
678
672
  const CliForeground = {
679
- BLACK: "\x1B[30m",
680
673
  RED: "\x1B[31m",
681
- GREEN: "\x1B[32m",
682
674
  YELLOW: "\x1B[33m",
683
675
  BLUE: "\x1B[34m",
684
- MAGENTA: "\x1B[35m",
685
- CYAN: "\x1B[36m",
686
- LIGHT_GREY: "\x1B[37m",
687
- GREY: "\x1B[90m",
688
- LIGHT_RED: "\x1B[91m",
689
- LIGHT_GREEN: "\x1B[92m",
690
- LIGHT_YELLOW: "\x1B[93m",
691
- LIGHT_BLUE: "\x1B[94m",
692
- LIGHT_MAGENTA: "\x1B[95m",
693
- LIGHT_CYAN: "\x1B[96m",
694
- WHITE: "\x1B[97m"
676
+ LIGHT_GREY: "\x1B[37m"
695
677
  };
696
678
  var LOG_LEVEL = /* @__PURE__ */ ((LOG_LEVEL2) => {
697
679
  LOG_LEVEL2[LOG_LEVEL2["ERROR"] = 0] = "ERROR";
@@ -860,7 +842,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
860
842
  const parsedFind = makeArray(filter).map((pe) => new PathEvent(pe));
861
843
  return parsedTarget.filter((t) => {
862
844
  if (!t.fullPath && t.all) return true;
863
- return !!parsedFind.find((f) => f.fullPath.startsWith(t.fullPath) && (f.all || t.all || t.methods.intersection(f.methods).length));
845
+ return !!parsedFind.find((f) => (t.fullPath.startsWith(f.fullPath) || f.fullPath.startsWith(t.fullPath)) && (f.all || t.all || t.methods.intersection(f.methods).length));
864
846
  });
865
847
  }
866
848
  /**
@@ -990,6 +972,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
990
972
  if (token) this.token = token;
991
973
  }
992
974
  }
975
+ /** Current API token */
993
976
  get token() {
994
977
  return this._token;
995
978
  }
@@ -1003,12 +986,21 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1003
986
  }
1004
987
  this.emit(PES`api/token:${token ? "u" : "d"}`, token);
1005
988
  }
989
+ /**
990
+ * Fetch current Momentum status
991
+ * @return {Promise<Health>}
992
+ */
1006
993
  healthcheck() {
1007
994
  return this.request({ url: "/api/healthcheck" }).then((resp) => {
1008
995
  this.emit(PES`api/healthcheck:r`, resp);
1009
996
  return resp;
1010
997
  });
1011
998
  }
999
+ /**
1000
+ * Create API request
1001
+ * @param {HttpRequestOptions} options Request options
1002
+ * @return {Promise} Response
1003
+ */
1012
1004
  request(options) {
1013
1005
  const key = JSONSanitize(options);
1014
1006
  const method = options.method == "GET" ? "r" : options.method == "POST" ? "c" : options.method == "DELETE" ? "d" : "u";
@@ -1042,6 +1034,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1042
1034
  __publicField(this, "cache", new Cache("_id"));
1043
1035
  this.api = typeof api == "string" ? new Api(api) : api;
1044
1036
  }
1037
+ /**
1038
+ * All saved actions
1039
+ * @param {boolean} reload Will use cached response if false
1040
+ * @return {Promise<Action[]>} List of saved actions
1041
+ */
1045
1042
  async all(reload) {
1046
1043
  if (!reload && this.cache.complete) return this.cache.all();
1047
1044
  return this.api.request({ url: `/api/` + PES`actions` }).then((resp) => {
@@ -1050,6 +1047,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1050
1047
  return resp;
1051
1048
  });
1052
1049
  }
1050
+ /**
1051
+ * Delete an existing action
1052
+ * @param {string} id Action ID
1053
+ * @return {Promise<void>} Delete complete
1054
+ */
1053
1055
  delete(id) {
1054
1056
  if (!id) throw new Error("Cannot delete action, missing ID");
1055
1057
  return this.api.request({ url: `/api/` + PES`actions/${id}`, method: "DELETE" }).then(() => {
@@ -1057,7 +1059,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1057
1059
  this.emit(PES`actions/${id}:d`, id);
1058
1060
  });
1059
1061
  }
1060
- read(id, reload = false) {
1062
+ /**
1063
+ * Fetch action info
1064
+ * @param {string} id Action ID
1065
+ * @param {boolean} reload Will use cached response if false
1066
+ * @return {Promise<Action | null>} Requested action
1067
+ */
1068
+ read(id, reload) {
1061
1069
  if (!id) throw new Error("Cannot read action, missing ID");
1062
1070
  const cached = this.cache.get(id);
1063
1071
  if (!reload && cached) return Promise.resolve(cached);
@@ -1067,15 +1075,20 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1067
1075
  return action;
1068
1076
  });
1069
1077
  }
1070
- run(path, opts = {}) {
1071
- if (!path) throw new Error("Cannot run action, missing path");
1072
- return this.api.request({ url: `/api/` + PES`actions/run/${path}`, ...opts });
1073
- }
1074
- runById(action, opts = {}) {
1075
- const id = typeof action == "string" ? action : action == null ? void 0 : action._id;
1076
- if (!id) throw new Error("Cannot run action, missing ID");
1078
+ /**
1079
+ * Manually trigger an actions execution
1080
+ * @param {string} id Action ID
1081
+ * @param {HttpRequestOptions} opts Additional arguments
1082
+ * @return {Promise<ActionResult>} Result of action
1083
+ */
1084
+ runById(id, opts = {}) {
1077
1085
  return this.api.request({ url: "/api/" + PES`actions/run-by-id/${id}`, method: "POST", ...opts });
1078
1086
  }
1087
+ /**
1088
+ * Update an action
1089
+ * @param {Action} action The new action
1090
+ * @return {Promise<Action>} Saved action
1091
+ */
1079
1092
  update(action) {
1080
1093
  return this.api.request({
1081
1094
  url: `/api/` + PES`actions/${action._id}`,
@@ -1094,6 +1107,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1094
1107
  __publicField(this, "api");
1095
1108
  this.api = typeof api == "string" ? new Api(api) : api;
1096
1109
  }
1110
+ /**
1111
+ * Ask the AI assistant a question
1112
+ * @param {string} question Users question
1113
+ * @param {any} context Additional data to aid response
1114
+ * @return {Promise<string>} AI's response
1115
+ */
1097
1116
  ask(question, context) {
1098
1117
  if (!question) throw new Error("Cannot ask AI, missing question");
1099
1118
  return this.api.request({ url: `/api/` + PES`ai`, method: "POST", body: {
@@ -1104,9 +1123,17 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1104
1123
  return response;
1105
1124
  });
1106
1125
  }
1126
+ /**
1127
+ * Clear AI assistant memory & context
1128
+ * @return {Promise<void>} Resolves once complete
1129
+ */
1107
1130
  clear() {
1108
1131
  return this.api.request({ url: "/api/" + PES`ai`, method: "DELETE" }).then(() => this.emit(PES`ai:d`, this.api.token));
1109
1132
  }
1133
+ /**
1134
+ * Current chat history
1135
+ * @return {Promise<{role: string, content: string}[]>}
1136
+ */
1110
1137
  history() {
1111
1138
  return this.api.request({ url: "/api/" + PES`ai` }).then((resp) => {
1112
1139
  this.emit(PES`ai:r`, resp);
@@ -1120,6 +1147,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1120
1147
  __publicField(this, "api");
1121
1148
  this.api = typeof api == "string" ? new Api(api) : api;
1122
1149
  }
1150
+ /**
1151
+ * Perform IP trace
1152
+ * @param {string} ip IP address to trace
1153
+ * @return {Promise<IpTrace>} Trace results
1154
+ */
1123
1155
  ipTrace(ip) {
1124
1156
  if (!ip) throw new Error("Cannot trace, missing IP");
1125
1157
  return this.api.request({ url: `/api/` + PES`analytics/trace/${ip}` }).then((resp) => {
@@ -1133,33 +1165,70 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1133
1165
  super();
1134
1166
  this.api = api;
1135
1167
  }
1168
+ /**
1169
+ * Fetch all tokens for user
1170
+ * @param {string} username User to search
1171
+ * @return {Promise<UserToken[]>} List of tokens
1172
+ */
1136
1173
  all(username) {
1137
1174
  return this.api.request({ url: "/" + PES`api/auth/tokens/${username}` }).then((resp) => {
1138
1175
  this.emit(PES`token/${username}:r`, resp);
1139
1176
  return resp;
1140
1177
  });
1141
1178
  }
1179
+ /**
1180
+ * Create a new user token
1181
+ * @param {{name: string, owner: string, expire: null | Date}} token Token settings
1182
+ * @return {Promise<UserToken>} Crated token
1183
+ */
1142
1184
  create(token) {
1143
1185
  return this.api.request({ url: "/" + PES`api/auth/tokens/${token.owner}`, body: token }).then((resp) => {
1144
1186
  this.emit(PES`token/${token.owner}:c`, token);
1145
1187
  return resp;
1146
1188
  });
1147
1189
  }
1190
+ /**
1191
+ * Delete an existing user token
1192
+ * @param {string} id Token ID
1193
+ * @return {Promise<void>} Resolves once complete
1194
+ */
1148
1195
  delete(id) {
1149
1196
  return this.api.request({ url: "/" + PES`api/auth/tokens/${id}`, method: "DELETE" }).then(() => this.emit(PES`token/${id}:d`, id));
1150
1197
  }
1151
1198
  }
1152
1199
  class Totp {
1153
1200
  constructor(api) {
1201
+ /**
1202
+ * Enable 2FA for user
1203
+ * @param {string} username User to reset
1204
+ * @return {Promise<void>} Resolves once complete
1205
+ */
1154
1206
  __publicField(this, "enable", this.reset);
1155
1207
  this.api = api;
1156
1208
  }
1209
+ /**
1210
+ * Disable 2FA for user
1211
+ * @param {string} username User to disable 2FA for
1212
+ * @return {Promise<void>} Resolves once complete
1213
+ */
1157
1214
  disable(username) {
1158
1215
  return this.api.request({ url: "/" + PES`api/auth/totp/${username}`, method: "DELETE" });
1159
1216
  }
1217
+ /**
1218
+ * Reset users 2FA
1219
+ * @param {string} username User to reset
1220
+ * @return {Promise<void>} Resolves once complete
1221
+ */
1160
1222
  reset(username) {
1161
1223
  return this.api.request({ url: "/" + PES`api/auth/totp/${username}`, method: "POST" });
1162
1224
  }
1225
+ /**
1226
+ * Setup 2FA authentication method
1227
+ * @param {string} username User to setup
1228
+ * @param {string} method Authenticator type
1229
+ * @param {string | null} totp null to being process, 2FA code to validate method
1230
+ * @return {Promise<void>} Resolves once complete
1231
+ */
1163
1232
  setup(username, method = "app", totp) {
1164
1233
  return this.api.request({ url: "/" + PES`api/auth/totp/${username}`, body: clean({
1165
1234
  method,
@@ -1171,7 +1240,9 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1171
1240
  constructor(api, opts = {}) {
1172
1241
  super();
1173
1242
  __publicField(this, "api");
1243
+ /** Manage user tokens */
1174
1244
  __publicField(this, "token");
1245
+ /** Manage user 2FA */
1175
1246
  __publicField(this, "totp");
1176
1247
  __publicField(this, "_permissions", []);
1177
1248
  __publicField(this, "_user");
@@ -1208,6 +1279,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1208
1279
  else this.user = null;
1209
1280
  });
1210
1281
  }
1282
+ /** Get current user permissions */
1211
1283
  get permissions() {
1212
1284
  return this._permissions;
1213
1285
  }
@@ -1215,19 +1287,33 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1215
1287
  this._permissions = perms;
1216
1288
  this.emit(PES`auth/permissions:u`, this._permissions);
1217
1289
  }
1290
+ /** Get current user, undefined if not yet initialized */
1218
1291
  get user() {
1219
1292
  return this._user;
1220
1293
  }
1294
+ /** Update user info without changing the session */
1221
1295
  set user(user) {
1222
1296
  if (!isEqual(this.user, user)) {
1223
1297
  this._user = user ? user : null;
1224
1298
  this.emit(PES`auth/user:u`, this._user);
1225
1299
  }
1226
1300
  }
1301
+ /**
1302
+ * Check if origin is recognized & whitelisted
1303
+ * @param {string} host Origin to check
1304
+ * @return {Promise<void>} Resolves in known, 401 code otherwise
1305
+ */
1227
1306
  knownHost(host = location.origin) {
1228
1307
  if (host.startsWith("/")) return Promise.resolve();
1229
1308
  return this.api.request({ url: `/api/auth/known-host?host=${encodeURI(new URL(host).origin)}` });
1230
1309
  }
1310
+ /**
1311
+ * Login a user & return the account
1312
+ * @param {string} username username
1313
+ * @param {string} password user's password
1314
+ * @param {{totp: string, expire: null | number | Date}} opts 2FA code and expiry options (null to never expire)
1315
+ * @return {Promise<User | null>} User account on success
1316
+ */
1231
1317
  login(username, password, opts) {
1232
1318
  if (!username || !password) throw new Error("Cannot login, missing username or password");
1233
1319
  return this.api.request({
@@ -1246,6 +1332,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1246
1332
  return user;
1247
1333
  });
1248
1334
  }
1335
+ /**
1336
+ * Login via Momentum single sign on
1337
+ * @param {string} host Host origin attempting to login
1338
+ * @return {Promise<string>} Token on success
1339
+ */
1249
1340
  loginRedirect(host = location.origin) {
1250
1341
  return new Promise((res, rej) => {
1251
1342
  var _a;
@@ -1255,10 +1346,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1255
1346
  if (event.origin != origin || data.sender != origin) return;
1256
1347
  if (!data.token) return rej("Unknown response from login");
1257
1348
  done = true;
1258
- this.api.token = event.data.token;
1349
+ this.api.token = data.token;
1259
1350
  window.removeEventListener("message", listener);
1260
- res(event.data.token);
1261
1351
  win.close();
1352
+ res(data.token);
1262
1353
  });
1263
1354
  win = window.open(encodeURI(`${(_a = this.opts) == null ? void 0 : _a.loginUrl}?redirect=postmessage&host=${host}`), "_blank");
1264
1355
  if (!win) {
@@ -1270,18 +1361,26 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1270
1361
  });
1271
1362
  });
1272
1363
  }
1364
+ /**
1365
+ * Logout current user
1366
+ */
1273
1367
  logout() {
1274
1368
  this.emit(PES`auth/logout:d`, this.user);
1275
1369
  this.api.token = null;
1276
1370
  this.user = null;
1277
1371
  }
1278
- async register(u) {
1372
+ /**
1373
+ * Create a new user with login
1374
+ * @param {Partial<User> & {password: string}} user User data with password
1375
+ * @return {Promise<User>} Registered user data
1376
+ */
1377
+ async register(user) {
1279
1378
  var _a;
1280
- if (!u.username || !u.password) throw new Error("Cannot register user, missing username or password");
1281
- const user = await this.api.request({ url: "/api/auth/register", body: { ...u } });
1282
- if ((_a = user == null ? void 0 : user.image) == null ? void 0 : _a.startsWith("/")) user.image = `${this.api.url}${user.image}?token=${this.api.token}`;
1283
- this.emit(PES`auth/register:c`, user);
1284
- return user;
1379
+ if (!user.username || !user.password) throw new Error("Cannot register user, missing username or password");
1380
+ const u = await this.api.request({ url: "/api/auth/register", body: { ...user } });
1381
+ if ((_a = u == null ? void 0 : u.image) == null ? void 0 : _a.startsWith("/")) u.image = `${this.api.url}${u.image}?token=${this.api.token}`;
1382
+ this.emit(PES`auth/register:c`, u);
1383
+ return u;
1285
1384
  }
1286
1385
  reset(emailOrPass, token) {
1287
1386
  if (!emailOrPass) throw new Error("Cannot reset password, missing email or token");
@@ -1296,6 +1395,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1296
1395
  this.emit(PES`auth/reset:${token ? "u" : "c"}`, token || emailOrPass);
1297
1396
  });
1298
1397
  }
1398
+ /**
1399
+ * Get session information
1400
+ * @param {string} token Token to fetch session info for
1401
+ * @param {boolean} set Set as current active session
1402
+ * @return {Promise<{token: string, user: User, permissions: string[], custom: any} | null>} Session information
1403
+ */
1299
1404
  async session(token, set = false) {
1300
1405
  if (!token) token = this.api.token;
1301
1406
  const session = await this.api.request({
@@ -1312,6 +1417,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1312
1417
  }
1313
1418
  return session;
1314
1419
  }
1420
+ /**
1421
+ * Update password for user
1422
+ * @param {string} username User to reset
1423
+ * @param {string} password New user password
1424
+ * @param {string} oldPassword Old password for validation
1425
+ * @return {Promise<void>} Resolves once complete
1426
+ */
1315
1427
  async updatePassword(username, password, oldPassword) {
1316
1428
  if (!username || !password) throw new Error("Cannot update password, missing username or password");
1317
1429
  return this.api.request({
@@ -1333,9 +1445,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1333
1445
  this.settings = settings;
1334
1446
  this.pushSubscription.then((resp) => this.notifications = !!resp);
1335
1447
  }
1448
+ /** Check if mobile device */
1336
1449
  get mobile() {
1337
1450
  return ["android", "ios"].includes(this.platform);
1338
1451
  }
1452
+ /** Are notifications enabled */
1339
1453
  get notifications() {
1340
1454
  return this._notifications;
1341
1455
  }
@@ -1343,6 +1457,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1343
1457
  this._notifications = enabled;
1344
1458
  this.emit(PES`client/notifications:${enabled ? "c" : "d"}`, enabled);
1345
1459
  }
1460
+ /** Get the current platform type */
1346
1461
  get platform() {
1347
1462
  if (!this._platform) {
1348
1463
  const userAgent = navigator.userAgent || navigator.vendor;
@@ -1355,6 +1470,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1355
1470
  }
1356
1471
  return this._platform;
1357
1472
  }
1473
+ /** Get Push Subscription info */
1358
1474
  get pushSubscription() {
1359
1475
  var _a;
1360
1476
  if (!((_a = navigator.serviceWorker) == null ? void 0 : _a.controller)) return Promise.resolve(null);
@@ -1363,115 +1479,86 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1363
1479
  return (_a2 = sw == null ? void 0 : sw.pushManager) == null ? void 0 : _a2.getSubscription();
1364
1480
  });
1365
1481
  }
1482
+ /** Check if running as a PWA */
1366
1483
  get pwa() {
1367
1484
  if (this._pwa == null)
1368
1485
  this._pwa = matchMedia("(display-mode: standalone)").matches || (navigator == null ? void 0 : navigator.standalone) || document.referrer.includes("android-app://");
1369
1486
  return this._pwa;
1370
1487
  }
1371
- async inject(reload = false, firstLoad = true) {
1372
- var _a, _b, _c, _d, _e, _f;
1373
- let settings = this.settings.cache;
1374
- if (firstLoad) this.settings.all(false, reload).then(() => this.inject(false, false)).catch(() => {
1375
- });
1376
- if (settings["title"] && !document.head.querySelector('meta[property="og:title"]')) {
1377
- const meta = document.createElement("meta");
1378
- meta.setAttribute("property", "og:title");
1379
- meta.content = settings["title"];
1380
- document.head.append(meta);
1381
- }
1382
- if (!document.querySelector('meta[property="og:type"]')) {
1383
- const meta = document.createElement("meta");
1384
- meta.setAttribute("property", "og:type");
1385
- meta.content = "article";
1386
- document.head.append(meta);
1387
- }
1388
- if (settings["public_url"] && !document.querySelector('meta[property="og:url"]')) {
1389
- const meta = document.createElement("meta");
1390
- meta.setAttribute("property", "og:url");
1391
- meta.content = settings["public_url"];
1392
- document.head.append(meta);
1393
- }
1394
- if (settings["description"]) {
1395
- if (!document.querySelector('meta[property="og:description"]')) {
1396
- const meta = document.createElement("meta");
1397
- meta.setAttribute("property", "og:description");
1398
- meta.content = settings["description"];
1399
- document.head.append(meta);
1400
- }
1401
- if (!document.querySelector('meta[property="description"]')) {
1402
- const meta = document.createElement("meta");
1403
- meta.setAttribute("property", "description");
1404
- meta.content = settings["description"];
1405
- document.head.append(meta);
1406
- }
1407
- if (!document.querySelector('meta[property="twitter:card"]')) {
1408
- const meta = document.createElement("meta");
1409
- meta.setAttribute("property", "twitter:card");
1410
- meta.content = settings["description"];
1411
- document.head.append(meta);
1412
- }
1413
- }
1414
- if (settings["public_url"] && !document.querySelector('meta[property="og:image"]')) {
1415
- const meta = document.createElement("meta");
1416
- meta.setAttribute("property", "og:image");
1417
- meta.content = settings["public_url"] + "/favicon.ico";
1418
- document.head.append(meta);
1419
- }
1420
- if (!document.querySelector('meta[name="mobile-web-app-capable"]')) {
1421
- const meta = document.createElement("meta");
1422
- meta.name = "mobile-web-app-capable";
1423
- meta.content = "yes";
1424
- document.head.append(meta);
1425
- }
1426
- if (!document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]')) {
1427
- const meta = document.createElement("meta");
1428
- meta.name = "apple-mobile-web-app-status-bar-style";
1429
- meta.content = "default";
1430
- document.head.append(meta);
1431
- }
1432
- if (settings["title"] && !document.querySelector('meta[name="apple-mobile-web-app-title"]')) {
1433
- const meta = document.createElement("meta");
1434
- meta.name = "apple-mobile-web-app-title";
1435
- meta.content = settings["title"];
1436
- document.head.append(meta);
1437
- }
1438
- if (settings["public_url"] && !document.querySelector('link[rel="apple-touch-icon"]')) {
1439
- const meta = document.createElement("link");
1440
- meta.rel = "apple-touch-icon";
1441
- meta.href = settings["public_url"] + "/favicon.ico";
1442
- document.head.append(meta);
1443
- }
1444
- if (settings["public_url"] && !document.querySelector('link[rel="apple-touch-startup-image"]')) {
1445
- const meta = document.createElement("link");
1446
- meta.rel = "apple-touch-startup-image";
1447
- meta.href = settings["public_url"] + "/favicon.ico";
1448
- document.head.append(meta);
1449
- }
1488
+ /**
1489
+ * Inject the client theme settings, meta tags & PWA prompt
1490
+ * @param {boolean} reload Reload settings
1491
+ * @return {Promise<void>} Resolves on completion
1492
+ */
1493
+ async inject(reload) {
1494
+ var _a, _b, _c, _d, _e, _f, _g;
1495
+ const meta = (key, name, value) => {
1496
+ const exists = document.querySelector(`meta[${key}="${name}"]`);
1497
+ if (value === void 0 || exists) return exists;
1498
+ const meta2 = document.createElement("meta");
1499
+ meta2.setAttribute(key, name);
1500
+ meta2.content = value;
1501
+ document.head.append(meta2);
1502
+ };
1503
+ if (!this.settings.cache.keys().length) reload = true;
1504
+ let settings = await (reload ? this.settings.all(false, true) : this.settings.cache);
1505
+ if (reload == null) this.settings.all(false, true).then(() => this.inject(false));
1450
1506
  if (settings["title"]) document.querySelectorAll(".momentum-title").forEach((el) => el.innerText = settings["title"]);
1451
1507
  if (settings["description"]) document.querySelectorAll(".momentum-description").forEach((el) => el.innerText = settings["description"]);
1452
1508
  if (settings["version"]) document.querySelectorAll(".momentum-version").forEach((el) => el.innerText = settings["version"]);
1453
1509
  if (settings["logo"]) document.querySelectorAll(".momentum-logo").forEach((el) => el.src = settings["logo"]);
1454
- if (settings["theme"]) {
1455
- document.body.classList.add(((_a = settings["theme"]) == null ? void 0 : _a.darkMode) ? "theme-dark" : "theme-light");
1456
- document.body.classList.remove(((_b = settings["theme"]) == null ? void 0 : _b.darkMode) ? "theme-light" : "theme-dark");
1457
- document.body.style.setProperty("--theme-backdrop", (_c = settings["theme"]) == null ? void 0 : _c.background);
1458
- document.body.style.setProperty("--theme-primary", (_d = settings["theme"]) == null ? void 0 : _d.primary);
1459
- document.body.style.setProperty("--theme-accent", (_e = settings["theme"]) == null ? void 0 : _e.accent);
1460
- document.body.style.setProperty("--theme-contrast", blackOrWhite((_f = settings["theme"]) == null ? void 0 : _f.background));
1461
- }
1510
+ meta("name", "description", settings["description"]);
1511
+ meta("itemprop", "name", settings["title"]);
1512
+ meta("itemprop", "description", settings["description"]);
1513
+ meta("itemprop", "image", `${settings["public_url"]}/favicon.png`);
1514
+ meta("property", "og:type", "website");
1515
+ meta("property", "og:url", settings["public_url"]);
1516
+ meta("property", "og:image", `${settings["public_url"]}/favicon.png`);
1517
+ meta("property", "og:title", settings["title"]);
1518
+ meta("property", "og:description", settings["description"]);
1519
+ meta("name", "twitter:card", "summary_large_image");
1520
+ meta("name", "twitter:title", settings["title"]);
1521
+ meta("name", "twitter:description", settings["description"]);
1522
+ meta("name", "twitter:image", `${settings["public_url"]}/favicon.png`);
1523
+ meta("name", "mobile-web-app-capable", "yes");
1524
+ meta("name", "apple-mobile-web-app-status-bar-style", "default");
1525
+ meta("name", "apple-mobile-web-app-title", settings["title"]);
1526
+ meta("name", "apple-touch-icon", `${settings["public_url"]}/favicon.png`);
1527
+ meta("name", "apple-touch-startup-image", `${settings["public_url"]}/favicon.png`);
1462
1528
  if (settings["pwa"]) {
1463
- const link = document.createElement("link");
1464
- link.setAttribute("rel", "manifest");
1465
- link.setAttribute("href", this.settings.api.url + "/manifest.json");
1466
- document.head.append(link);
1529
+ if (!document.querySelector('link[rel="manifest"]')) {
1530
+ const link = document.createElement("link");
1531
+ link.setAttribute("rel", "manifest");
1532
+ link.setAttribute("href", this.settings.api.url + "/manifest.json");
1533
+ document.head.append(link);
1534
+ }
1467
1535
  setTimeout(() => {
1468
1536
  const dismissed = !!localStorage.getItem("momentum:install-prompt");
1469
1537
  if (!dismissed && !this.pwa && this.mobile) this.pwaPrompt();
1470
- }, 500);
1538
+ }, 6e4);
1471
1539
  }
1540
+ meta("name", "theme-color", (_a = settings["theme"]) == null ? void 0 : _a.background);
1541
+ document.body.classList.add(((_b = settings["theme"]) == null ? void 0 : _b.darkMode) ? "theme-dark" : "theme-light");
1542
+ document.body.classList.remove(((_c = settings["theme"]) == null ? void 0 : _c.darkMode) ? "theme-light" : "theme-dark");
1543
+ const style = document.querySelector("style.momentum-theme") || document.createElement("style");
1544
+ style.classList.add("momentum-theme");
1545
+ style.innerHTML = `
1546
+ :root {
1547
+ --theme-backdrop: ${(_d = settings["theme"]) == null ? void 0 : _d.background} !important;
1548
+ --theme-primary: ${(_e = settings["theme"]) == null ? void 0 : _e.primary} !important;
1549
+ --theme-accent: ${(_f = settings["theme"]) == null ? void 0 : _f.accent} !important;
1550
+ --theme-contrast: ${blackOrWhite((_g = settings["theme"]) == null ? void 0 : _g.background)} !important;
1551
+ }
1552
+ `;
1553
+ if (!style.parentElement) document.head.append(style);
1472
1554
  this.emit(PES`client/inject:c`, this.platform);
1473
1555
  }
1556
+ /**
1557
+ * Create UI prompt for user to install as PWA
1558
+ * @param platform Platform prompt, leave blank to auto-detect
1559
+ */
1474
1560
  pwaPrompt(platform) {
1561
+ if (document.querySelector(".momentum-pwa-prompt")) return;
1475
1562
  const url = this.settings.api.url;
1476
1563
  const settings = this.settings.cache;
1477
1564
  const android = (platform || this.platform) == "android";
@@ -1578,7 +1665,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1578
1665
  </div>`;
1579
1666
  const close = document.createElement("button");
1580
1667
  close.classList.add("momentum-pwa-prompt-close");
1581
- close.innerText = "X";
1668
+ close.innerText = "x";
1582
1669
  close.onclick = () => {
1583
1670
  prompt.classList.add("exit");
1584
1671
  backdrop.classList.add("exit");
@@ -1594,6 +1681,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1594
1681
  document.body.append(backdrop);
1595
1682
  this.emit(PES`client/pwa:c`, platform);
1596
1683
  }
1684
+ /**
1685
+ * Enable device notifications
1686
+ * @return {Promise<null>} Resolves on success
1687
+ */
1597
1688
  async enableNotifications() {
1598
1689
  const granted = await Notification.requestPermission();
1599
1690
  if (!granted) return null;
@@ -1604,6 +1695,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1604
1695
  })).toJSON();
1605
1696
  return this.api.request({ url: "/api/notifications", body: subscription }).then(() => this.notifications = true);
1606
1697
  }
1698
+ /**
1699
+ * Disable device notifications
1700
+ * @return {Promise<void>} Resolves on success
1701
+ */
1607
1702
  async disableNotifications() {
1608
1703
  var _a;
1609
1704
  const subscription = await this.pushSubscription;
@@ -1613,11 +1708,36 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1613
1708
  } }).then(() => this.notifications = false);
1614
1709
  }
1615
1710
  }
1711
+ class Schemas extends PathEventEmitter {
1712
+ constructor(api) {
1713
+ super();
1714
+ this.api = api;
1715
+ }
1716
+ delete(path) {
1717
+ if (!path) throw new Error("Cannot delete schema, missing collection path");
1718
+ return this.api.request({ url: `/api/` + PES`schema/${path}`, method: "DELETE" }).then(() => this.emit(PES`schema/${path}:d`, path));
1719
+ }
1720
+ read(pathOrTree) {
1721
+ return this.api.request({ url: "/api/" + PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}` + (pathOrTree === true ? `?tree=${pathOrTree}` : "") }).then((resp) => {
1722
+ this.emit(PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}:r`, resp);
1723
+ return resp;
1724
+ });
1725
+ }
1726
+ update(schema) {
1727
+ if (!schema.path) throw new Error("Cannot update schema, missing collection path");
1728
+ return this.api.request({ url: "/api/" + PES`schema/${schema.path}`, body: schema }).then((resp) => {
1729
+ this.emit(PES`schema/${schema.path}:${schema._id ? "u" : "c"}`, resp);
1730
+ return resp;
1731
+ });
1732
+ }
1733
+ }
1616
1734
  class Data extends PathEventEmitter {
1617
1735
  constructor(api) {
1618
1736
  super();
1619
1737
  __publicField(this, "api");
1738
+ __publicField(this, "schema");
1620
1739
  this.api = typeof api == "string" ? new Api(api) : api;
1740
+ this.schema = new Schemas(this.api);
1621
1741
  }
1622
1742
  create(collection, document2) {
1623
1743
  if (!collection || !document2) throw new Error("Cannot create document, missing collection or document");
@@ -1663,24 +1783,6 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1663
1783
  return resp;
1664
1784
  });
1665
1785
  }
1666
- // Schema ==========================================================================================================
1667
- deleteSchema(path) {
1668
- if (!path) throw new Error("Cannot delete schema, missing collection path");
1669
- return this.api.request({ url: `/api/` + PES`schema/${path}`, method: "DELETE" }).then(() => this.emit(PES`schema/${path}:d`, path));
1670
- }
1671
- readSchema(pathOrTree) {
1672
- return this.api.request({ url: "/api/" + PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}` + (pathOrTree === true ? `?tree=${pathOrTree}` : "") }).then((resp) => {
1673
- this.emit(PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}:r`, resp);
1674
- return resp;
1675
- });
1676
- }
1677
- updateSchema(schema) {
1678
- if (!schema.path) throw new Error("Cannot update schema, missing collection path");
1679
- return this.api.request({ url: "/api/" + PES`schema/${schema.path}`, body: schema }).then((resp) => {
1680
- this.emit(PES`schema/${schema.path}:${schema._id ? "u" : "c"}`, resp);
1681
- return resp;
1682
- });
1683
- }
1684
1786
  }
1685
1787
  class Email extends PathEventEmitter {
1686
1788
  constructor(api) {
@@ -1688,6 +1790,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1688
1790
  __publicField(this, "api");
1689
1791
  this.api = typeof api == "string" ? new Api(api) : api;
1690
1792
  }
1793
+ /**
1794
+ * Send Email
1795
+ * @param {Mail} email Email to send
1796
+ * @return {Promise<any>} Response
1797
+ */
1691
1798
  send(email) {
1692
1799
  if (!email.to && !email.bcc || !email.body) throw new Error("Cannot send email, missing address or body");
1693
1800
  return this.api.request({ url: "/api/email", body: email }).then((response) => {
@@ -1898,46 +2005,103 @@ ${log}`;
1898
2005
  }
1899
2006
  }
1900
2007
  class Payments extends PathEventEmitter {
1901
- constructor(api, secret) {
2008
+ constructor(api, opts = {}) {
1902
2009
  super();
1903
2010
  __publicField(this, "api");
2011
+ __publicField(this, "loaded", false);
1904
2012
  __publicField(this, "stripe");
2013
+ __publicField(this, "token");
2014
+ this.opts = opts;
1905
2015
  this.api = typeof api == "string" ? new Api(api) : api;
1906
- const setup = (retry = 1) => {
1907
- try {
1908
- if (!Stripe) throw new Error("Stripe not added");
1909
- this.stripe = Stripe(secret);
1910
- } catch (err) {
1911
- if (retry > 0) setTimeout(() => setup(retry--), 250);
1912
- else console.warn("Stripe failed, did you add the library & setup your API key?", err);
1913
- }
2016
+ this.opts = {
2017
+ paymentUrl: this.api.url + "/ui/#/payment",
2018
+ ...this.opts
1914
2019
  };
1915
- setup();
1916
2020
  }
1917
- async create(amount, custom = {}) {
1918
- if (!amount) throw new Error("Please specify a valid amount`");
2021
+ async checkout(cart, opts = {}) {
2022
+ if (!(cart == null ? void 0 : cart.length)) throw new Error("Please add items to cart`");
1919
2023
  const request = await this.api.request({ url: "/api/payments", body: {
1920
- amount,
1921
- custom
2024
+ cart,
2025
+ recipient: opts == null ? void 0 : opts.recipient,
2026
+ invoice: opts == null ? void 0 : opts.invoice,
2027
+ extra: opts == null ? void 0 : opts.extra
1922
2028
  } });
1923
- this.emit(PES`payments:c`, request.data.clientSecret);
1924
- return request.data.clientSecret;
2029
+ this.emit(PES`payments:c`, request.token);
2030
+ return request.token;
1925
2031
  }
1926
- async createForm(element, amount, custom) {
1927
- const token = await this.create(amount, custom);
1928
- const form = this.stripe.elements({ clientSecret: token });
1929
- form.create("payment").mount(element);
1930
- return () => this.stripe.confirmPayment({
1931
- elements: form,
1932
- redirect: "if_required",
1933
- confirmParams: { return_url: location.origin }
2032
+ async init() {
2033
+ if (!this.stripe) this.stripe = await new Promise((res) => {
2034
+ if (this.loaded || this.stripe) return res(window["Stripe"]);
2035
+ const script = document.createElement("script");
2036
+ script.src = "https://js.stripe.com/v3/";
2037
+ script.setAttribute("crossorigin", "anonymous");
2038
+ script.onload = () => res(window["Stripe"]);
2039
+ document.head.appendChild(script);
1934
2040
  });
2041
+ return this.stripe(this.token);
1935
2042
  }
1936
2043
  async history(username) {
1937
- const history = await this.api.request({ url: `/api/` + PES`payments/${username}` });
2044
+ const history = await this.api.request({ url: `/api/payments/${username || ""}` });
1938
2045
  this.emit(PES`payments/${username}:r`, history);
1939
2046
  return history;
1940
2047
  }
2048
+ async products(id) {
2049
+ return this.api.request({ url: `/api/payments/products/${id || ""}` }).then((resp) => {
2050
+ this.emit(PES`products/${id}:r`, resp);
2051
+ return resp;
2052
+ });
2053
+ }
2054
+ async payment(card, token) {
2055
+ const client = await this.init();
2056
+ return client.confirmPayment({
2057
+ clientSecret: token,
2058
+ confirmParams: {
2059
+ payment_method: {
2060
+ card: { ...card, details: void 0 },
2061
+ billing_details: card.details || {}
2062
+ }
2063
+ }
2064
+ });
2065
+ }
2066
+ async paymentForm(element, token) {
2067
+ const client = await this.init();
2068
+ const form = client.elements({ clientSecret: token });
2069
+ form.create("payment").mount(element);
2070
+ return (redirect = location.href) => client.confirmPayment({
2071
+ elements: form,
2072
+ redirect: "if_required",
2073
+ confirmParams: { return_url: redirect + "?payment_status=success" }
2074
+ });
2075
+ }
2076
+ /**
2077
+ * Complete payment via Momentum's payment page
2078
+ * @param {string} token Stripe payment intent secret
2079
+ * @param {string} host Host origin attempting to login
2080
+ * @return {Promise<string>} Token on success
2081
+ */
2082
+ paymentRedirect(token, host = location.origin) {
2083
+ return new Promise(async (res, rej) => {
2084
+ var _a;
2085
+ let origin = new URL(this.opts.paymentUrl).origin, done = false, listener, win;
2086
+ window.addEventListener("message", listener = (event) => {
2087
+ const data = (event == null ? void 0 : event.data) || {};
2088
+ if (event.origin != origin || data.sender != origin) return;
2089
+ if (!data.response) return rej("Unknown response from payment page");
2090
+ done = true;
2091
+ window.removeEventListener("message", listener);
2092
+ win.close();
2093
+ res(data.response);
2094
+ });
2095
+ win = window.open(encodeURI(`${(_a = this.opts) == null ? void 0 : _a.paymentUrl}?token=${token}&host=${host}`), "_blank");
2096
+ if (!win) {
2097
+ window.removeEventListener("message", listener);
2098
+ return rej("Unable to open payment page");
2099
+ }
2100
+ win.addEventListener("close", () => {
2101
+ if (!done) rej("Window closed before payment was complete");
2102
+ });
2103
+ });
2104
+ }
1941
2105
  }
1942
2106
  class Pdf extends PathEventEmitter {
1943
2107
  constructor(api) {
@@ -2258,7 +2422,7 @@ ${log}`;
2258
2422
  this.forms = new Forms(this.api);
2259
2423
  this.groups = new Groups(this.api);
2260
2424
  this.logger = new Logger(this.api, (opts == null ? void 0 : opts.app) || "client", opts == null ? void 0 : opts.logLevel);
2261
- if (opts == null ? void 0 : opts.stripeSecret) this.payments = new Payments(this.api, opts.stripeSecret);
2425
+ this.payments = new Payments(this.api);
2262
2426
  this.pdf = new Pdf(this.api);
2263
2427
  this.phone = new Phone(this.api);
2264
2428
  this.settings = new Settings(this.api);
@@ -2277,7 +2441,7 @@ ${log}`;
2277
2441
  this.relayEvents(this.email);
2278
2442
  this.relayEvents(this.groups);
2279
2443
  this.relayEvents(this.logger);
2280
- if (this.payments) this.relayEvents(this.payments);
2444
+ this.relayEvents(this.payments);
2281
2445
  this.relayEvents(this.pdf);
2282
2446
  this.relayEvents(this.phone);
2283
2447
  this.relayEvents(this.settings);
@@ -2289,8 +2453,12 @@ ${log}`;
2289
2453
  const cached = this.users.cache.get(this.auth.user.username);
2290
2454
  if (cached) this.auth.user = cached;
2291
2455
  });
2292
- if (!(opts == null ? void 0 : opts.disableWorker) && "serviceWorker" in navigator) {
2293
- navigator.serviceWorker.register((opts == null ? void 0 : opts.workerUrl) || "/momentum.worker.js").catch(() => console.warn("Unable to load momentum worker, some features may be limited."));
2456
+ this.settings.on("settings:r", (event, value) => {
2457
+ var _a;
2458
+ this.payments.token = ((_a = value["stripe_token"]) == null ? void 0 : _a.value) || value["stripe_token"];
2459
+ });
2460
+ if ((opts == null ? void 0 : opts.worker) !== false && "serviceWorker" in navigator) {
2461
+ navigator.serviceWorker.register((opts == null ? void 0 : opts.worker) || "/momentum.worker.js").catch(() => console.warn("Unable to load momentum worker, some features may be limited."));
2294
2462
  }
2295
2463
  }
2296
2464
  }