dce-reactkit 3.0.0-beta.23 → 3.0.0-beta.27

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.
Files changed (34) hide show
  1. package/dist/cjs/index.js +438 -7
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/types/components/AppWrapper.d.ts +25 -0
  4. package/dist/cjs/types/helpers/genRouteHandler.d.ts +26 -0
  5. package/dist/cjs/types/helpers/handleError.d.ts +18 -0
  6. package/dist/cjs/types/helpers/handleSuccess.d.ts +8 -0
  7. package/dist/cjs/types/helpers/visitServerEndpoint.d.ts +23 -0
  8. package/dist/cjs/types/index.d.ts +6 -1
  9. package/dist/cjs/types/server/initServer.d.ts +21 -0
  10. package/dist/cjs/types/types/ParamType.d.ts +17 -0
  11. package/dist/cjs/types/types/ReactKitErrorCode.d.ts +3 -1
  12. package/dist/esm/index.js +433 -7
  13. package/dist/esm/index.js.map +1 -1
  14. package/dist/esm/types/components/AppWrapper.d.ts +25 -0
  15. package/dist/esm/types/helpers/genRouteHandler.d.ts +26 -0
  16. package/dist/esm/types/helpers/handleError.d.ts +18 -0
  17. package/dist/esm/types/helpers/handleSuccess.d.ts +8 -0
  18. package/dist/esm/types/helpers/visitServerEndpoint.d.ts +23 -0
  19. package/dist/esm/types/index.d.ts +6 -1
  20. package/dist/esm/types/server/initServer.d.ts +21 -0
  21. package/dist/esm/types/types/ParamType.d.ts +17 -0
  22. package/dist/esm/types/types/ReactKitErrorCode.d.ts +3 -1
  23. package/dist/index.d.ts +105 -2
  24. package/package.json +2 -1
  25. package/rollup.config.js +0 -1
  26. package/src/components/AppWrapper.tsx +69 -10
  27. package/src/helpers/genRouteHandler.ts +323 -0
  28. package/src/helpers/handleError.ts +65 -0
  29. package/src/helpers/handleSuccess.ts +18 -0
  30. package/src/helpers/visitServerEndpoint.tsx +110 -0
  31. package/src/index.ts +13 -0
  32. package/src/server/initServer.ts +53 -0
  33. package/src/types/ParamType.tsx +18 -0
  34. package/src/types/ReactKitErrorCode.tsx +3 -1
package/dist/cjs/index.js CHANGED
@@ -35,7 +35,7 @@ function __awaiter(thisArg, _arguments, P, generator) {
35
35
  });
36
36
  }
37
37
 
38
- // Highest error code = DRK2
38
+ // Highest error code = DRK8
39
39
  /**
40
40
  * List of error codes built into the react kit
41
41
  * @author Gabe Abrams
@@ -48,6 +48,8 @@ var ReactKitErrorCode;
48
48
  ReactKitErrorCode["MissingParameter"] = "DRK4";
49
49
  ReactKitErrorCode["InvalidParameter"] = "DRK5";
50
50
  ReactKitErrorCode["WrongCourse"] = "DRK6";
51
+ ReactKitErrorCode["NoCACCLSendRequestFunction"] = "DRK7";
52
+ ReactKitErrorCode["NoCACCLGetLaunchInfoFunction"] = "DRK8";
51
53
  })(ReactKitErrorCode || (ReactKitErrorCode = {}));
52
54
  var ReactKitErrorCode$1 = ReactKitErrorCode;
53
55
 
@@ -559,6 +561,24 @@ class ErrorWithCode extends Error {
559
561
  /* Static Helpers */
560
562
  /*------------------------------------------------------------------------*/
561
563
  /*----------------------------------------*/
564
+ /* Send Request */
565
+ /*----------------------------------------*/
566
+ // Store copy of caccl send request
567
+ let _cacclSendRequest;
568
+ /**
569
+ * Send a request using caccl's send request feature
570
+ * @author Gabe Abrams
571
+ * @param opts send request options
572
+ * @returns send request response
573
+ */
574
+ const cacclSendRequest = (opts) => __awaiter(void 0, void 0, void 0, function* () {
575
+ // Make sure send request has been passed in
576
+ if (!_cacclSendRequest) {
577
+ throw new ErrorWithCode('The request could not be sent because the AppWrapper component does not have a copy of sendRequest from CACCL.', ReactKitErrorCode$1.NoCACCLSendRequestFunction);
578
+ }
579
+ return _cacclSendRequest(opts);
580
+ });
581
+ /*----------------------------------------*/
562
582
  /* Alert */
563
583
  /*----------------------------------------*/
564
584
  // Stored copies of setters
@@ -570,7 +590,7 @@ let onAlertClosed;
570
590
  * @param title the title text to display at the top of the alert
571
591
  * @param text the text to display in the alert
572
592
  */
573
- const alert = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
593
+ const alert$1 = (title, text) => __awaiter(void 0, void 0, void 0, function* () {
574
594
  // Fallback if alert not available
575
595
  if (!setAlertInfo) {
576
596
  window.alert(`${title}\n\n${text}`);
@@ -644,7 +664,7 @@ const showFatalError = (error, errorTitle = 'An Error Occurred') => {
644
664
  : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
645
665
  // Handle case where app hasn't loaded
646
666
  if (!setFatalErrorMessage || !setFatalErrorCode) {
647
- alert(errorTitle, `${message} (code: ${code}). Please contact support.`);
667
+ alert$1(errorTitle, `${message} (code: ${code}). Please contact support.`);
648
668
  return undefined;
649
669
  }
650
670
  // Use setters
@@ -661,7 +681,9 @@ const AppWrapper = (props) => {
661
681
  /* Setup */
662
682
  /*------------------------------------------------------------------------*/
663
683
  /* -------------- Props ------------- */
664
- const { children, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
684
+ const { children, sendRequest, dark, sessionExpiredMessage = 'Your session has expired. Please go back to Canvas and start over.', } = props;
685
+ // Store copy of send request
686
+ _cacclSendRequest = sendRequest;
665
687
  /* -------------- State ------------- */
666
688
  // Fatal error
667
689
  const [fatalErrorMessage, setFatalErrorMessageInner,] = React.useState();
@@ -671,10 +693,10 @@ const AppWrapper = (props) => {
671
693
  const [fatalErrorTitle, setFatalErrorTitleInner,] = React.useState();
672
694
  setFatalErrorTitle = setFatalErrorTitleInner;
673
695
  // Alert
674
- const [alertInfo, setAlertInfoInner,] = React.useState();
696
+ const [alertInfo, setAlertInfoInner,] = React.useState(undefined);
675
697
  setAlertInfo = setAlertInfoInner;
676
698
  // Confirm
677
- const [confirmInfo, setConfirmInfoInner,] = React.useState();
699
+ const [confirmInfo, setConfirmInfoInner,] = React.useState(undefined);
678
700
  setConfirmInfo = setConfirmInfoInner;
679
701
  // Session expired
680
702
  const [sessionHasExpired, setSessionHasExpiredInner,] = React.useState(false);
@@ -701,6 +723,7 @@ const AppWrapper = (props) => {
701
723
  if (onConfirmClosed) {
702
724
  onConfirmClosed(buttonType === ModalButtonType$1.Okay);
703
725
  }
726
+ setConfirmInfo(undefined);
704
727
  }, dontAllowBackdropExit: true }, confirmInfo.text));
705
728
  }
706
729
  /*----------------------------------------*/
@@ -1052,6 +1075,409 @@ const roundToNumDecimals = (num, numDecimals) => {
1052
1075
  return (Math.round(num * rounder) / rounder);
1053
1076
  };
1054
1077
 
1078
+ // Keep track of whether or not session expiry has already been handled
1079
+ let sessionAlreadyExpired = false;
1080
+ /*------------------------------------------------------------------------*/
1081
+ /* Main */
1082
+ /*------------------------------------------------------------------------*/
1083
+ /**
1084
+ * Visit an endpoint on the server [for client only]
1085
+ * @author Gabe Abrams
1086
+ * @param opts object containing all arguments
1087
+ * @param opts.path - the path of the server endpoint
1088
+ * @param [opts.method=GET] - the method of the endpoint
1089
+ * @param [opts.params] - query/body parameters to include
1090
+ * @returns response from server
1091
+ */
1092
+ const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
1093
+ var _a;
1094
+ // Send the request
1095
+ const response = yield cacclSendRequest({
1096
+ path: opts.path,
1097
+ method: (_a = opts.method) !== null && _a !== void 0 ? _a : 'GET',
1098
+ params: opts.params,
1099
+ });
1100
+ // Check for failure
1101
+ if (!response || !response.body) {
1102
+ throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
1103
+ }
1104
+ if (!response.body.success) {
1105
+ // Session expired
1106
+ if (response.body.code === ReactKitErrorCode$1.SessionExpired) {
1107
+ // Skip notice if session was already expired
1108
+ if (sessionAlreadyExpired) {
1109
+ // Never return (browser is already reloading)
1110
+ yield new Promise(() => {
1111
+ // Promise that never returns
1112
+ });
1113
+ }
1114
+ sessionAlreadyExpired = true;
1115
+ // Show session expiration message
1116
+ {
1117
+ // Fallback to alert
1118
+ // eslint-disable-next-line no-alert
1119
+ alert('Your session has expired. Please start over.');
1120
+ }
1121
+ // Never return (don't continue execution)
1122
+ yield new Promise(() => {
1123
+ // Promise that never returns
1124
+ });
1125
+ }
1126
+ // Other errors
1127
+ throw new ErrorWithCode((response.body.message
1128
+ || 'An unknown error occurred. Please contact an admin.'), (response.body.code
1129
+ || ReactKitErrorCode$1.NoCode));
1130
+ }
1131
+ // Success! Extract the body
1132
+ const { body } = response.body;
1133
+ // Return
1134
+ return body;
1135
+ });
1136
+
1137
+ // Import custom error
1138
+ // Stored copy of caccl functions
1139
+ let _cacclGetLaunchInfo;
1140
+ /*------------------------------------------------------------------------*/
1141
+ /* Helpers */
1142
+ /*------------------------------------------------------------------------*/
1143
+ /**
1144
+ * Get launch info via CACCL
1145
+ * @author Gabe Abrams
1146
+ * @param req express request object
1147
+ * @returns object { launched, launchInfo }
1148
+ */
1149
+ const cacclGetLaunchInfo = (req) => {
1150
+ {
1151
+ throw new ErrorWithCode('Could not get launch info because server was not initialized with dce-reactkit\'s initServer function', ReactKitErrorCode$1.NoCACCLGetLaunchInfoFunction);
1152
+ }
1153
+ };
1154
+
1155
+ /**
1156
+ * Server-side API param types
1157
+ * @author Gabe Abrams
1158
+ */
1159
+ var ParamType;
1160
+ (function (ParamType) {
1161
+ ParamType["Boolean"] = "boolean";
1162
+ ParamType["BooleanOptional"] = "boolean-optional";
1163
+ ParamType["Float"] = "float";
1164
+ ParamType["FloatOptional"] = "float-optional";
1165
+ ParamType["Int"] = "int";
1166
+ ParamType["IntOptional"] = "int-optional";
1167
+ ParamType["JSON"] = "json";
1168
+ ParamType["JSONOptional"] = "json-optional";
1169
+ ParamType["String"] = "string";
1170
+ ParamType["StringOptional"] = "string-optional";
1171
+ })(ParamType || (ParamType = {}));
1172
+ var ParamType$1 = ParamType;
1173
+
1174
+ // Import shared types
1175
+ /**
1176
+ * Handle an error and respond to the client
1177
+ * @author Gabe Abrams
1178
+ * @param res express response
1179
+ * @param error error info
1180
+ * @param opts.err the error to send to the client
1181
+ * or the error message
1182
+ * @param [opts.code] an error code (only used if err.code is not
1183
+ * included)
1184
+ * @param [opts.status=500] the https status code to use
1185
+ * defined)
1186
+ */
1187
+ const handleError = (res, error) => {
1188
+ // Get the error message
1189
+ let message;
1190
+ if (error && error.message) {
1191
+ message = (error.message || 'An unknown error occurred.');
1192
+ }
1193
+ else if (typeof error === 'string') {
1194
+ message = (error.trim().length > 0
1195
+ ? error
1196
+ : 'An unknown error occurred.');
1197
+ }
1198
+ else {
1199
+ message = 'An unknown error occurred.';
1200
+ }
1201
+ // Get the error code
1202
+ const code = (error.code || ReactKitErrorCode$1.NoCode);
1203
+ // Get the status code
1204
+ const status = (error.status || 500);
1205
+ // Respond to user
1206
+ res
1207
+ // Set the http status code
1208
+ .status(status)
1209
+ // Send a JSON response
1210
+ .json({
1211
+ // Error message
1212
+ message,
1213
+ // Error code
1214
+ code,
1215
+ // Success = false flag so client can detect server-side errors
1216
+ success: false,
1217
+ });
1218
+ return undefined;
1219
+ };
1220
+
1221
+ /**
1222
+ * Send successful API response
1223
+ * @author Gabe Abrams
1224
+ * @param res express response
1225
+ * @param body the body of the response to send to the client
1226
+ */
1227
+ const handleSuccess = (res, body) => {
1228
+ // Send a http 200 json response
1229
+ res.json({
1230
+ // Include the body as a parameter
1231
+ body,
1232
+ // Success = true flag so client can detect successful responses
1233
+ success: true,
1234
+ });
1235
+ return undefined;
1236
+ };
1237
+
1238
+ /**
1239
+ * Generate an express API route handler
1240
+ * @author Gabe Abrams
1241
+ * @param params map containing parameters that are included in the request
1242
+ * (map: param name => type)
1243
+ * @param handler function that processes the request
1244
+ * @returns express route handler that takes the following arguments:
1245
+ * params (map: param name => value), handleSuccess (function for handling
1246
+ * successful requests), handleError (function for handling failed requests),
1247
+ * req (express request object), res (express response object)
1248
+ */
1249
+ const genRouteHandler = (params, handler) => {
1250
+ // Return a route handler
1251
+ return (req, res) => __awaiter(void 0, void 0, void 0, function* () {
1252
+ // Output params
1253
+ const output = {};
1254
+ /*----------------------------------------*/
1255
+ /* Parse Params */
1256
+ /*----------------------------------------*/
1257
+ // Process items one by one
1258
+ const paramList = Object.entries(params);
1259
+ for (let i = 0; i < paramList.length; i++) {
1260
+ const [name, type] = paramList[i];
1261
+ // Find the value as a string
1262
+ const value = (req.params[name]
1263
+ || req.query[name]
1264
+ || req.body[name]);
1265
+ // Parse
1266
+ if (type === ParamType$1.Boolean || type === ParamType$1.BooleanOptional) {
1267
+ // Boolean
1268
+ // Handle case where value doesn't exist
1269
+ if (value === undefined) {
1270
+ if (type === ParamType$1.BooleanOptional) {
1271
+ output[name] = undefined;
1272
+ }
1273
+ else {
1274
+ return handleError(res, {
1275
+ message: `Parameter ${name} is required, but it was not included.`,
1276
+ code: ReactKitErrorCode$1.MissingParameter,
1277
+ status: 422,
1278
+ });
1279
+ }
1280
+ }
1281
+ else {
1282
+ // Value exists
1283
+ // Simplify value
1284
+ const simpleVal = (String(value)
1285
+ .trim()
1286
+ .toLowerCase());
1287
+ // Parse
1288
+ output[name] = ([
1289
+ 'true',
1290
+ 'yes',
1291
+ 'y',
1292
+ '1',
1293
+ 't',
1294
+ ].indexOf(simpleVal) >= 0);
1295
+ }
1296
+ }
1297
+ else if (type === ParamType$1.Float || type === ParamType$1.FloatOptional) {
1298
+ // Float
1299
+ // Handle case where value doesn't exist
1300
+ if (value === undefined) {
1301
+ if (type === ParamType$1.FloatOptional) {
1302
+ output[name] = undefined;
1303
+ }
1304
+ else {
1305
+ return handleError(res, {
1306
+ message: `Parameter ${name} is required, but it was not included.`,
1307
+ code: ReactKitErrorCode$1.MissingParameter,
1308
+ status: 422,
1309
+ });
1310
+ }
1311
+ }
1312
+ else if (!Number.isNaN(Number.parseFloat(String(value)))) {
1313
+ // Value is a number
1314
+ output[name] = Number.parseFloat(String(value));
1315
+ }
1316
+ else {
1317
+ // Issue!
1318
+ return handleError(res, {
1319
+ message: `Request data was malformed: ${name} was not a valid float.`,
1320
+ code: ReactKitErrorCode$1.InvalidParameter,
1321
+ status: 422,
1322
+ });
1323
+ }
1324
+ }
1325
+ else if (type === ParamType$1.Int || type === ParamType$1.IntOptional) {
1326
+ // Int
1327
+ // Handle case where value doesn't exist
1328
+ if (value === undefined) {
1329
+ if (type === ParamType$1.IntOptional) {
1330
+ output[name] = undefined;
1331
+ }
1332
+ else {
1333
+ return handleError(res, {
1334
+ message: `Parameter ${name} is required, but it was not included.`,
1335
+ code: ReactKitErrorCode$1.MissingParameter,
1336
+ status: 422,
1337
+ });
1338
+ }
1339
+ }
1340
+ else if (!Number.isNaN(Number.parseInt(String(value), 10))) {
1341
+ // Value is a number
1342
+ output[name] = Number.parseInt(String(value), 10);
1343
+ }
1344
+ else {
1345
+ // Issue!
1346
+ return handleError(res, {
1347
+ message: `Request data was malformed: ${name} was not a valid int.`,
1348
+ code: ReactKitErrorCode$1.InvalidParameter,
1349
+ status: 422,
1350
+ });
1351
+ }
1352
+ }
1353
+ else if (type === ParamType$1.JSON || type === ParamType$1.JSONOptional) {
1354
+ // Stringified JSON
1355
+ // Handle case where value doesn't exist
1356
+ if (value === undefined) {
1357
+ if (type === ParamType$1.JSONOptional) {
1358
+ output[name] = undefined;
1359
+ }
1360
+ else {
1361
+ return handleError(res, {
1362
+ message: `Parameter ${name} is required, but it was not included.`,
1363
+ code: ReactKitErrorCode$1.MissingParameter,
1364
+ status: 422,
1365
+ });
1366
+ }
1367
+ }
1368
+ else {
1369
+ // Value exists
1370
+ // Parse
1371
+ try {
1372
+ output[name] = JSON.parse(String(value));
1373
+ }
1374
+ catch (err) {
1375
+ return handleError(res, {
1376
+ message: `Request data was malformed: ${name} was not a valid JSON payload.`,
1377
+ code: ReactKitErrorCode$1.InvalidParameter,
1378
+ status: 422,
1379
+ });
1380
+ }
1381
+ }
1382
+ }
1383
+ else if (type === ParamType$1.String || type === ParamType$1.StringOptional) {
1384
+ // String
1385
+ // Handle case where value doesn't exist
1386
+ if (value === undefined) {
1387
+ if (type === ParamType$1.StringOptional) {
1388
+ output[name] = undefined;
1389
+ }
1390
+ else {
1391
+ return handleError(res, {
1392
+ message: `Parameter ${name} is required, but it was not included.`,
1393
+ code: ReactKitErrorCode$1.MissingParameter,
1394
+ status: 422,
1395
+ });
1396
+ }
1397
+ }
1398
+ else {
1399
+ // Value exists
1400
+ // Leave as is
1401
+ output[name] = value;
1402
+ }
1403
+ }
1404
+ else {
1405
+ // No valid data type
1406
+ return handleError(res, {
1407
+ message: `An internal error occurred: we could not determine the type of ${name}.`,
1408
+ code: ReactKitErrorCode$1.InvalidParameter,
1409
+ status: 422,
1410
+ });
1411
+ }
1412
+ }
1413
+ /*----------------------------------------*/
1414
+ /* Launch Info */
1415
+ /*----------------------------------------*/
1416
+ // Get launch info
1417
+ const { launched, launchInfo } = cacclGetLaunchInfo(req);
1418
+ if (!launched || !launchInfo) {
1419
+ return handleError(res, {
1420
+ message: 'Your session has expired. Please refresh the page and try again.',
1421
+ code: ReactKitErrorCode$1.SessionExpired,
1422
+ status: 440,
1423
+ });
1424
+ }
1425
+ // Error if user info cannot be found
1426
+ if (!launchInfo.userId
1427
+ || !launchInfo.userFirstName
1428
+ || !launchInfo.userLastName
1429
+ || (launchInfo.notInCourse
1430
+ && !launchInfo.isAdmin)
1431
+ || (!launchInfo.isTTM
1432
+ && !launchInfo.isLearner
1433
+ && !launchInfo.isAdmin)) {
1434
+ return handleError(res, {
1435
+ message: 'Your session was invalid. Please refresh the page and try again.',
1436
+ code: ReactKitErrorCode$1.SessionExpired,
1437
+ status: 440,
1438
+ });
1439
+ }
1440
+ // Add launch info to output
1441
+ output.userId = launchInfo.userId;
1442
+ output.userFirstName = launchInfo.userFirstName;
1443
+ output.userLastName = launchInfo.userLastName;
1444
+ output.isLearner = !!launchInfo.isLearner;
1445
+ output.isTTM = !!launchInfo.isTTM;
1446
+ output.isAdmin = !!launchInfo.isAdmin;
1447
+ output.isWatchingInPrivate = !!(req.session.isWatchingInPrivate);
1448
+ /*----------------------------------------*/
1449
+ /* Require Course Consistency */
1450
+ /*----------------------------------------*/
1451
+ // Make sure the user actually launched from the appropriate course
1452
+ if (output.courseId
1453
+ && launchInfo.courseId
1454
+ && output.courseId !== launchInfo.courseId
1455
+ && !output.isTTM
1456
+ && !output.isAdmin) {
1457
+ // Course of interest is not the launch course
1458
+ return handleError(res, {
1459
+ message: 'You switched sessions by opening Immersive Classroom in another tab. Please refresh the page and try again.',
1460
+ code: ReactKitErrorCode$1.WrongCourse,
1461
+ status: 401,
1462
+ });
1463
+ }
1464
+ /*------------------------------------------------------------------------*/
1465
+ /* Call handler */
1466
+ /*------------------------------------------------------------------------*/
1467
+ handler({
1468
+ params: output,
1469
+ handleSuccess: (body) => {
1470
+ return handleSuccess(res, body);
1471
+ },
1472
+ handleError: (error) => {
1473
+ return handleError(res, error);
1474
+ },
1475
+ req,
1476
+ res,
1477
+ });
1478
+ });
1479
+ };
1480
+
1055
1481
  exports.AppWrapper = AppWrapper;
1056
1482
  exports.ErrorBox = ErrorBox;
1057
1483
  exports.ErrorWithCode = ErrorWithCode;
@@ -1060,20 +1486,25 @@ exports.Modal = Modal;
1060
1486
  exports.ModalButtonType = ModalButtonType$1;
1061
1487
  exports.ModalSize = ModalSize$1;
1062
1488
  exports.ModalType = ModalType$1;
1489
+ exports.ParamType = ParamType$1;
1063
1490
  exports.ReactKitErrorCode = ReactKitErrorCode$1;
1064
1491
  exports.TabBox = TabBox;
1065
1492
  exports.Variant = Variant$1;
1066
1493
  exports.abbreviate = abbreviate;
1067
- exports.alert = alert;
1494
+ exports.alert = alert$1;
1068
1495
  exports.avg = avg;
1069
1496
  exports.ceilToNumDecimals = ceilToNumDecimals;
1070
1497
  exports.confirm = confirm;
1071
1498
  exports.floorToNumDecimals = floorToNumDecimals;
1072
1499
  exports.forceNumIntoBounds = forceNumIntoBounds;
1500
+ exports.genRouteHandler = genRouteHandler;
1501
+ exports.handleError = handleError;
1502
+ exports.handleSuccess = handleSuccess;
1073
1503
  exports.padDecimalZeros = padDecimalZeros;
1074
1504
  exports.padZerosLeft = padZerosLeft;
1075
1505
  exports.roundToNumDecimals = roundToNumDecimals;
1076
1506
  exports.showFatalError = showFatalError;
1077
1507
  exports.sum = sum;
1508
+ exports.visitServerEndpoint = visitServerEndpoint;
1078
1509
  exports.waitMs = waitMs;
1079
1510
  //# sourceMappingURL=index.js.map