@ztimson/momentum 0.51.0 → 0.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -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";
@@ -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,34 +1332,55 @@ 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;
1252
- const win = window.open(encodeURI(`${(_a = this.opts) == null ? void 0 : _a.loginUrl}?redirect=postmessage&host=${host}`), "_blank");
1253
- if (!win) return rej("Unable to open login");
1254
- const origin = new URL(this.opts.loginUrl).origin;
1255
- win.addEventListener("message", (event) => {
1343
+ let origin = new URL(this.opts.loginUrl).origin, done = false, listener, win;
1344
+ window.addEventListener("message", listener = (event) => {
1256
1345
  const data = (event == null ? void 0 : event.data) || {};
1257
- if (event.origin != data.sender || data.sender != origin) return;
1346
+ if (event.origin != origin || data.sender != origin) return;
1258
1347
  if (!data.token) return rej("Unknown response from login");
1259
- this.api.token = event.data.token;
1260
- res(event.data.token);
1348
+ done = true;
1349
+ this.api.token = data.token;
1350
+ window.removeEventListener("message", listener);
1261
1351
  win.close();
1352
+ res(data.token);
1353
+ });
1354
+ win = window.open(encodeURI(`${(_a = this.opts) == null ? void 0 : _a.loginUrl}?redirect=postmessage&host=${host}`), "_blank");
1355
+ if (!win) {
1356
+ window.removeEventListener("message", listener);
1357
+ return rej("Unable to open login");
1358
+ }
1359
+ win.addEventListener("close", () => {
1360
+ if (!done) rej("Window closed before logging in");
1262
1361
  });
1263
1362
  });
1264
1363
  }
1364
+ /**
1365
+ * Logout current user
1366
+ */
1265
1367
  logout() {
1266
1368
  this.emit(PES`auth/logout:d`, this.user);
1267
1369
  this.api.token = null;
1268
1370
  this.user = null;
1269
1371
  }
1270
- 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) {
1271
1378
  var _a;
1272
- if (!u.username || !u.password) throw new Error("Cannot register user, missing username or password");
1273
- const user = await this.api.request({ url: "/api/auth/register", body: { ...u } });
1274
- if ((_a = user == null ? void 0 : user.image) == null ? void 0 : _a.startsWith("/")) user.image = `${this.api.url}${user.image}?token=${this.api.token}`;
1275
- this.emit(PES`auth/register:c`, user);
1276
- 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;
1277
1384
  }
1278
1385
  reset(emailOrPass, token) {
1279
1386
  if (!emailOrPass) throw new Error("Cannot reset password, missing email or token");
@@ -1288,6 +1395,12 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1288
1395
  this.emit(PES`auth/reset:${token ? "u" : "c"}`, token || emailOrPass);
1289
1396
  });
1290
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
+ */
1291
1404
  async session(token, set = false) {
1292
1405
  if (!token) token = this.api.token;
1293
1406
  const session = await this.api.request({
@@ -1304,6 +1417,13 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1304
1417
  }
1305
1418
  return session;
1306
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
+ */
1307
1427
  async updatePassword(username, password, oldPassword) {
1308
1428
  if (!username || !password) throw new Error("Cannot update password, missing username or password");
1309
1429
  return this.api.request({
@@ -1325,9 +1445,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1325
1445
  this.settings = settings;
1326
1446
  this.pushSubscription.then((resp) => this.notifications = !!resp);
1327
1447
  }
1448
+ /** Check if mobile device */
1328
1449
  get mobile() {
1329
1450
  return ["android", "ios"].includes(this.platform);
1330
1451
  }
1452
+ /** Are notifications enabled */
1331
1453
  get notifications() {
1332
1454
  return this._notifications;
1333
1455
  }
@@ -1335,6 +1457,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1335
1457
  this._notifications = enabled;
1336
1458
  this.emit(PES`client/notifications:${enabled ? "c" : "d"}`, enabled);
1337
1459
  }
1460
+ /** Get the current platform type */
1338
1461
  get platform() {
1339
1462
  if (!this._platform) {
1340
1463
  const userAgent = navigator.userAgent || navigator.vendor;
@@ -1347,6 +1470,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1347
1470
  }
1348
1471
  return this._platform;
1349
1472
  }
1473
+ /** Get Push Subscription info */
1350
1474
  get pushSubscription() {
1351
1475
  var _a;
1352
1476
  if (!((_a = navigator.serviceWorker) == null ? void 0 : _a.controller)) return Promise.resolve(null);
@@ -1355,115 +1479,77 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1355
1479
  return (_a2 = sw == null ? void 0 : sw.pushManager) == null ? void 0 : _a2.getSubscription();
1356
1480
  });
1357
1481
  }
1482
+ /** Check if running as a PWA */
1358
1483
  get pwa() {
1359
1484
  if (this._pwa == null)
1360
1485
  this._pwa = matchMedia("(display-mode: standalone)").matches || (navigator == null ? void 0 : navigator.standalone) || document.referrer.includes("android-app://");
1361
1486
  return this._pwa;
1362
1487
  }
1363
- async inject(reload = false, firstLoad = true) {
1364
- var _a, _b, _c, _d, _e, _f;
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
+ };
1365
1503
  let settings = this.settings.cache;
1366
- if (firstLoad) this.settings.all(false, reload).then(() => this.inject(false, false)).catch(() => {
1367
- });
1368
- if (settings["title"] && !document.head.querySelector('meta[property="og:title"]')) {
1369
- const meta = document.createElement("meta");
1370
- meta.setAttribute("property", "og:title");
1371
- meta.content = settings["title"];
1372
- document.head.append(meta);
1373
- }
1374
- if (!document.querySelector('meta[property="og:type"]')) {
1375
- const meta = document.createElement("meta");
1376
- meta.setAttribute("property", "og:type");
1377
- meta.content = "article";
1378
- document.head.append(meta);
1379
- }
1380
- if (settings["public_url"] && !document.querySelector('meta[property="og:url"]')) {
1381
- const meta = document.createElement("meta");
1382
- meta.setAttribute("property", "og:url");
1383
- meta.content = settings["public_url"];
1384
- document.head.append(meta);
1385
- }
1386
- if (settings["description"]) {
1387
- if (!document.querySelector('meta[property="og:description"]')) {
1388
- const meta = document.createElement("meta");
1389
- meta.setAttribute("property", "og:description");
1390
- meta.content = settings["description"];
1391
- document.head.append(meta);
1392
- }
1393
- if (!document.querySelector('meta[property="description"]')) {
1394
- const meta = document.createElement("meta");
1395
- meta.setAttribute("property", "description");
1396
- meta.content = settings["description"];
1397
- document.head.append(meta);
1398
- }
1399
- if (!document.querySelector('meta[property="twitter:card"]')) {
1400
- const meta = document.createElement("meta");
1401
- meta.setAttribute("property", "twitter:card");
1402
- meta.content = settings["description"];
1403
- document.head.append(meta);
1404
- }
1405
- }
1406
- if (settings["public_url"] && !document.querySelector('meta[property="og:image"]')) {
1407
- const meta = document.createElement("meta");
1408
- meta.setAttribute("property", "og:image");
1409
- meta.content = settings["public_url"] + "/favicon.ico";
1410
- document.head.append(meta);
1411
- }
1412
- if (!document.querySelector('meta[name="mobile-web-app-capable"]')) {
1413
- const meta = document.createElement("meta");
1414
- meta.name = "mobile-web-app-capable";
1415
- meta.content = "yes";
1416
- document.head.append(meta);
1417
- }
1418
- if (!document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]')) {
1419
- const meta = document.createElement("meta");
1420
- meta.name = "apple-mobile-web-app-status-bar-style";
1421
- meta.content = "default";
1422
- document.head.append(meta);
1423
- }
1424
- if (settings["title"] && !document.querySelector('meta[name="apple-mobile-web-app-title"]')) {
1425
- const meta = document.createElement("meta");
1426
- meta.name = "apple-mobile-web-app-title";
1427
- meta.content = settings["title"];
1428
- document.head.append(meta);
1429
- }
1430
- if (settings["public_url"] && !document.querySelector('link[rel="apple-touch-icon"]')) {
1431
- const meta = document.createElement("link");
1432
- meta.rel = "apple-touch-icon";
1433
- meta.href = settings["public_url"] + "/favicon.ico";
1434
- document.head.append(meta);
1435
- }
1436
- if (settings["public_url"] && !document.querySelector('link[rel="apple-touch-startup-image"]')) {
1437
- const meta = document.createElement("link");
1438
- meta.rel = "apple-touch-startup-image";
1439
- meta.href = settings["public_url"] + "/favicon.ico";
1440
- document.head.append(meta);
1441
- }
1442
1504
  if (settings["title"]) document.querySelectorAll(".momentum-title").forEach((el) => el.innerText = settings["title"]);
1443
1505
  if (settings["description"]) document.querySelectorAll(".momentum-description").forEach((el) => el.innerText = settings["description"]);
1444
1506
  if (settings["version"]) document.querySelectorAll(".momentum-version").forEach((el) => el.innerText = settings["version"]);
1445
1507
  if (settings["logo"]) document.querySelectorAll(".momentum-logo").forEach((el) => el.src = settings["logo"]);
1446
- if (settings["theme"]) {
1447
- document.body.classList.add(((_a = settings["theme"]) == null ? void 0 : _a.darkMode) ? "theme-dark" : "theme-light");
1448
- document.body.classList.remove(((_b = settings["theme"]) == null ? void 0 : _b.darkMode) ? "theme-light" : "theme-dark");
1449
- document.body.style.setProperty("--theme-backdrop", (_c = settings["theme"]) == null ? void 0 : _c.background);
1450
- document.body.style.setProperty("--theme-primary", (_d = settings["theme"]) == null ? void 0 : _d.primary);
1451
- document.body.style.setProperty("--theme-accent", (_e = settings["theme"]) == null ? void 0 : _e.accent);
1452
- document.body.style.setProperty("--theme-contrast", blackOrWhite((_f = settings["theme"]) == null ? void 0 : _f.background));
1453
- }
1508
+ meta("name", "theme-color", (_a = settings["theme"]) == null ? void 0 : _a.background);
1509
+ document.body.classList.add(((_b = settings["theme"]) == null ? void 0 : _b.darkMode) ? "theme-dark" : "theme-light");
1510
+ document.body.classList.remove(((_c = settings["theme"]) == null ? void 0 : _c.darkMode) ? "theme-light" : "theme-dark");
1511
+ document.body.style.setProperty("--theme-backdrop", (_d = settings["theme"]) == null ? void 0 : _d.background);
1512
+ document.body.style.setProperty("--theme-primary", (_e = settings["theme"]) == null ? void 0 : _e.primary);
1513
+ document.body.style.setProperty("--theme-accent", (_f = settings["theme"]) == null ? void 0 : _f.accent);
1514
+ document.body.style.setProperty("--theme-contrast", blackOrWhite((_g = settings["theme"]) == null ? void 0 : _g.background));
1515
+ meta("name", "description", settings["description"]);
1516
+ meta("itemprop", "name", settings["title"]);
1517
+ meta("itemprop", "description", settings["description"]);
1518
+ meta("itemprop", "image", `${settings["public_url"]}/favicon.png`);
1519
+ meta("property", "og:type", "website");
1520
+ meta("property", "og:url", settings["public_url"]);
1521
+ meta("property", "og:image", `${settings["public_url"]}/favicon.png`);
1522
+ meta("property", "og:title", settings["title"]);
1523
+ meta("property", "og:description", settings["description"]);
1524
+ meta("name", "twitter:card", "summary_large_image");
1525
+ meta("name", "twitter:title", settings["title"]);
1526
+ meta("name", "twitter:description", settings["description"]);
1527
+ meta("name", "twitter:image", `${settings["public_url"]}/favicon.png`);
1528
+ meta("name", "mobile-web-app-capable", "yes");
1529
+ meta("name", "apple-mobile-web-app-status-bar-style", "default");
1530
+ meta("name", "apple-mobile-web-app-title", settings["title"]);
1531
+ meta("name", "apple-touch-icon", `${settings["public_url"]}/favicon.png`);
1532
+ meta("name", "apple-touch-startup-image", `${settings["public_url"]}/favicon.png`);
1454
1533
  if (settings["pwa"]) {
1455
- const link = document.createElement("link");
1456
- link.setAttribute("rel", "manifest");
1457
- link.setAttribute("href", this.settings.api.url + "/manifest.json");
1458
- document.head.append(link);
1534
+ if (!document.querySelector('link[rel="manifest"]')) {
1535
+ const link = document.createElement("link");
1536
+ link.setAttribute("rel", "manifest");
1537
+ link.setAttribute("href", this.settings.api.url + "/manifest.json");
1538
+ document.head.append(link);
1539
+ }
1459
1540
  setTimeout(() => {
1460
1541
  const dismissed = !!localStorage.getItem("momentum:install-prompt");
1461
1542
  if (!dismissed && !this.pwa && this.mobile) this.pwaPrompt();
1462
- }, 500);
1543
+ }, 6e4);
1463
1544
  }
1464
1545
  this.emit(PES`client/inject:c`, this.platform);
1465
1546
  }
1547
+ /**
1548
+ * Create UI prompt for user to install as PWA
1549
+ * @param platform Platform prompt, leave blank to auto-detect
1550
+ */
1466
1551
  pwaPrompt(platform) {
1552
+ if (document.querySelector(".momentum-pwa-prompt")) return;
1467
1553
  const url = this.settings.api.url;
1468
1554
  const settings = this.settings.cache;
1469
1555
  const android = (platform || this.platform) == "android";
@@ -1570,7 +1656,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1570
1656
  </div>`;
1571
1657
  const close = document.createElement("button");
1572
1658
  close.classList.add("momentum-pwa-prompt-close");
1573
- close.innerText = "X";
1659
+ close.innerText = "x";
1574
1660
  close.onclick = () => {
1575
1661
  prompt.classList.add("exit");
1576
1662
  backdrop.classList.add("exit");
@@ -1586,6 +1672,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1586
1672
  document.body.append(backdrop);
1587
1673
  this.emit(PES`client/pwa:c`, platform);
1588
1674
  }
1675
+ /**
1676
+ * Enable device notifications
1677
+ * @return {Promise<null>} Resolves on success
1678
+ */
1589
1679
  async enableNotifications() {
1590
1680
  const granted = await Notification.requestPermission();
1591
1681
  if (!granted) return null;
@@ -1596,6 +1686,10 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1596
1686
  })).toJSON();
1597
1687
  return this.api.request({ url: "/api/notifications", body: subscription }).then(() => this.notifications = true);
1598
1688
  }
1689
+ /**
1690
+ * Disable device notifications
1691
+ * @return {Promise<void>} Resolves on success
1692
+ */
1599
1693
  async disableNotifications() {
1600
1694
  var _a;
1601
1695
  const subscription = await this.pushSubscription;
@@ -1605,11 +1699,36 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1605
1699
  } }).then(() => this.notifications = false);
1606
1700
  }
1607
1701
  }
1702
+ class Schemas extends PathEventEmitter {
1703
+ constructor(api) {
1704
+ super();
1705
+ this.api = api;
1706
+ }
1707
+ delete(path) {
1708
+ if (!path) throw new Error("Cannot delete schema, missing collection path");
1709
+ return this.api.request({ url: `/api/` + PES`schema/${path}`, method: "DELETE" }).then(() => this.emit(PES`schema/${path}:d`, path));
1710
+ }
1711
+ read(pathOrTree) {
1712
+ return this.api.request({ url: "/api/" + PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}` + (pathOrTree === true ? `?tree=${pathOrTree}` : "") }).then((resp) => {
1713
+ this.emit(PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}:r`, resp);
1714
+ return resp;
1715
+ });
1716
+ }
1717
+ update(schema) {
1718
+ if (!schema.path) throw new Error("Cannot update schema, missing collection path");
1719
+ return this.api.request({ url: "/api/" + PES`schema/${schema.path}`, body: schema }).then((resp) => {
1720
+ this.emit(PES`schema/${schema.path}:${schema._id ? "u" : "c"}`, resp);
1721
+ return resp;
1722
+ });
1723
+ }
1724
+ }
1608
1725
  class Data extends PathEventEmitter {
1609
1726
  constructor(api) {
1610
1727
  super();
1611
1728
  __publicField(this, "api");
1729
+ __publicField(this, "schema");
1612
1730
  this.api = typeof api == "string" ? new Api(api) : api;
1731
+ this.schema = new Schemas(this.api);
1613
1732
  }
1614
1733
  create(collection, document2) {
1615
1734
  if (!collection || !document2) throw new Error("Cannot create document, missing collection or document");
@@ -1655,24 +1774,6 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1655
1774
  return resp;
1656
1775
  });
1657
1776
  }
1658
- // Schema ==========================================================================================================
1659
- deleteSchema(path) {
1660
- if (!path) throw new Error("Cannot delete schema, missing collection path");
1661
- return this.api.request({ url: `/api/` + PES`schema/${path}`, method: "DELETE" }).then(() => this.emit(PES`schema/${path}:d`, path));
1662
- }
1663
- readSchema(pathOrTree) {
1664
- return this.api.request({ url: "/api/" + PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}` + (pathOrTree === true ? `?tree=${pathOrTree}` : "") }).then((resp) => {
1665
- this.emit(PES`schema/${typeof pathOrTree == "string" ? pathOrTree : ""}:r`, resp);
1666
- return resp;
1667
- });
1668
- }
1669
- updateSchema(schema) {
1670
- if (!schema.path) throw new Error("Cannot update schema, missing collection path");
1671
- return this.api.request({ url: "/api/" + PES`schema/${schema.path}`, body: schema }).then((resp) => {
1672
- this.emit(PES`schema/${schema.path}:${schema._id ? "u" : "c"}`, resp);
1673
- return resp;
1674
- });
1675
- }
1676
1777
  }
1677
1778
  class Email extends PathEventEmitter {
1678
1779
  constructor(api) {
@@ -1680,6 +1781,11 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
1680
1781
  __publicField(this, "api");
1681
1782
  this.api = typeof api == "string" ? new Api(api) : api;
1682
1783
  }
1784
+ /**
1785
+ * Send Email
1786
+ * @param {Mail} email Email to send
1787
+ * @return {Promise<any>} Response
1788
+ */
1683
1789
  send(email) {
1684
1790
  if (!email.to && !email.bcc || !email.body) throw new Error("Cannot send email, missing address or body");
1685
1791
  return this.api.request({ url: "/api/email", body: email }).then((response) => {
@@ -1890,46 +1996,102 @@ ${log}`;
1890
1996
  }
1891
1997
  }
1892
1998
  class Payments extends PathEventEmitter {
1893
- constructor(api, secret) {
1999
+ constructor(api, opts = {}) {
1894
2000
  super();
1895
2001
  __publicField(this, "api");
2002
+ __publicField(this, "loaded", false);
1896
2003
  __publicField(this, "stripe");
2004
+ __publicField(this, "token");
2005
+ this.opts = opts;
1897
2006
  this.api = typeof api == "string" ? new Api(api) : api;
1898
- const setup = (retry = 1) => {
1899
- try {
1900
- if (!Stripe) throw new Error("Stripe not added");
1901
- this.stripe = Stripe(secret);
1902
- } catch (err) {
1903
- if (retry > 0) setTimeout(() => setup(retry--), 250);
1904
- else console.warn("Stripe failed, did you add the library & setup your API key?", err);
1905
- }
2007
+ this.opts = {
2008
+ paymentUrl: this.api.url + "/ui/#/payment",
2009
+ ...this.opts
1906
2010
  };
1907
- setup();
1908
2011
  }
1909
- async create(amount, custom = {}) {
1910
- if (!amount) throw new Error("Please specify a valid amount`");
2012
+ async checkout(cart, invoice, extra = {}) {
2013
+ if (!(cart == null ? void 0 : cart.length)) throw new Error("Please add items to cart`");
1911
2014
  const request = await this.api.request({ url: "/api/payments", body: {
1912
- amount,
1913
- custom
2015
+ cart,
2016
+ invoice,
2017
+ extra
1914
2018
  } });
1915
- this.emit(PES`payments:c`, request.data.clientSecret);
1916
- return request.data.clientSecret;
2019
+ this.emit(PES`payments:c`, request.token);
2020
+ return request.token;
1917
2021
  }
1918
- async createForm(element, amount, custom) {
1919
- const token = await this.create(amount, custom);
1920
- const form = this.stripe.elements({ clientSecret: token });
1921
- form.create("payment").mount(element);
1922
- return () => this.stripe.confirmPayment({
1923
- elements: form,
1924
- redirect: "if_required",
1925
- confirmParams: { return_url: location.origin }
2022
+ async init() {
2023
+ if (!this.stripe) this.stripe = await new Promise((res) => {
2024
+ if (this.loaded || this.stripe) return res(window["Stripe"]);
2025
+ const script = document.createElement("script");
2026
+ script.src = "https://js.stripe.com/v3/";
2027
+ script.setAttribute("crossorigin", "anonymous");
2028
+ script.onload = () => res(window["Stripe"]);
2029
+ document.head.appendChild(script);
1926
2030
  });
2031
+ return this.stripe(this.token);
1927
2032
  }
1928
2033
  async history(username) {
1929
- const history = await this.api.request({ url: `/api/` + PES`payments/${username}` });
2034
+ const history = await this.api.request({ url: `/api/payments/${username || ""}` });
1930
2035
  this.emit(PES`payments/${username}:r`, history);
1931
2036
  return history;
1932
2037
  }
2038
+ async products(id) {
2039
+ return this.api.request({ url: `/api/payments/products/${id || ""}` }).then((resp) => {
2040
+ this.emit(PES`products/${id}:r`, resp);
2041
+ return resp;
2042
+ });
2043
+ }
2044
+ async payment(card, token) {
2045
+ const client = await this.init();
2046
+ return client.confirmPayment({
2047
+ clientSecret: token,
2048
+ confirmParams: {
2049
+ payment_method: {
2050
+ card: { ...card, details: void 0 },
2051
+ billing_details: card.details || {}
2052
+ }
2053
+ }
2054
+ });
2055
+ }
2056
+ async paymentForm(element, token) {
2057
+ const client = await this.init();
2058
+ const form = client.elements({ clientSecret: token });
2059
+ form.create("payment").mount(element);
2060
+ return (redirect = location.href) => client.confirmPayment({
2061
+ elements: form,
2062
+ redirect: "if_required",
2063
+ confirmParams: { return_url: redirect + "?payment_status=success" }
2064
+ });
2065
+ }
2066
+ /**
2067
+ * Complete payment via Momentum's payment page
2068
+ * @param {string} token Stripe payment intent secret
2069
+ * @param {string} host Host origin attempting to login
2070
+ * @return {Promise<string>} Token on success
2071
+ */
2072
+ paymentRedirect(token, host = location.origin) {
2073
+ return new Promise(async (res, rej) => {
2074
+ var _a;
2075
+ let origin = new URL(this.opts.paymentUrl).origin, done = false, listener, win;
2076
+ window.addEventListener("message", listener = (event) => {
2077
+ const data = (event == null ? void 0 : event.data) || {};
2078
+ if (event.origin != origin || data.sender != origin) return;
2079
+ if (!data.response) return rej("Unknown response from payment page");
2080
+ done = true;
2081
+ window.removeEventListener("message", listener);
2082
+ win.close();
2083
+ res(data.response);
2084
+ });
2085
+ win = window.open(encodeURI(`${(_a = this.opts) == null ? void 0 : _a.paymentUrl}?token=${token}&host=${host}`), "_blank");
2086
+ if (!win) {
2087
+ window.removeEventListener("message", listener);
2088
+ return rej("Unable to open payment page");
2089
+ }
2090
+ win.addEventListener("close", () => {
2091
+ if (!done) rej("Window closed before payment was complete");
2092
+ });
2093
+ });
2094
+ }
1933
2095
  }
1934
2096
  class Pdf extends PathEventEmitter {
1935
2097
  constructor(api) {
@@ -2250,7 +2412,7 @@ ${log}`;
2250
2412
  this.forms = new Forms(this.api);
2251
2413
  this.groups = new Groups(this.api);
2252
2414
  this.logger = new Logger(this.api, (opts == null ? void 0 : opts.app) || "client", opts == null ? void 0 : opts.logLevel);
2253
- if (opts == null ? void 0 : opts.stripeSecret) this.payments = new Payments(this.api, opts.stripeSecret);
2415
+ this.payments = new Payments(this.api);
2254
2416
  this.pdf = new Pdf(this.api);
2255
2417
  this.phone = new Phone(this.api);
2256
2418
  this.settings = new Settings(this.api);
@@ -2269,7 +2431,7 @@ ${log}`;
2269
2431
  this.relayEvents(this.email);
2270
2432
  this.relayEvents(this.groups);
2271
2433
  this.relayEvents(this.logger);
2272
- if (this.payments) this.relayEvents(this.payments);
2434
+ this.relayEvents(this.payments);
2273
2435
  this.relayEvents(this.pdf);
2274
2436
  this.relayEvents(this.phone);
2275
2437
  this.relayEvents(this.settings);
@@ -2281,8 +2443,12 @@ ${log}`;
2281
2443
  const cached = this.users.cache.get(this.auth.user.username);
2282
2444
  if (cached) this.auth.user = cached;
2283
2445
  });
2284
- if (!(opts == null ? void 0 : opts.disableWorker) && "serviceWorker" in navigator) {
2285
- navigator.serviceWorker.register("/momentum-worker.js");
2446
+ this.settings.on("settings:r", (event, value) => {
2447
+ var _a;
2448
+ this.payments.token = ((_a = value["stripe_token"]) == null ? void 0 : _a.value) || value["stripe_token"];
2449
+ });
2450
+ if ((opts == null ? void 0 : opts.worker) !== false && "serviceWorker" in navigator) {
2451
+ 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."));
2286
2452
  }
2287
2453
  }
2288
2454
  }