dce-reactkit 3.2.0 → 3.2.2-beta.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/esm/index.js CHANGED
@@ -491,6 +491,184 @@ class ErrorWithCode extends Error {
491
491
  }
492
492
  }
493
493
 
494
+ /**
495
+ * Path that all routes start with
496
+ * @author Gabe Abrams
497
+ */
498
+ const ROUTE_PATH_PREFIX = '/dce-reactkit';
499
+
500
+ /**
501
+ * Path of the route for storing client-side logs
502
+ * @author Gabe Abrams
503
+ */
504
+ const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
505
+
506
+ /**
507
+ * Built-in metadata for logs
508
+ * @author Gabe Abrams
509
+ */
510
+ const LogBuiltInMetadata = {
511
+ // Contexts
512
+ Context: {
513
+ Uncategorized: 'n/a',
514
+ ServerRenderedErrorPage: '_server-rendered-error-page',
515
+ ServerEndpointError: '_server-endpoint-error',
516
+ ClientFatalError: '_client-fatal-error',
517
+ },
518
+ // Targets
519
+ Target: {
520
+ NoSpecificTarget: 'n/a',
521
+ },
522
+ };
523
+
524
+ // Keep track of whether or not session expiry has already been handled
525
+ let sessionAlreadyExpired = false;
526
+ /*------------------------------------------------------------------------*/
527
+ /* Stub Logic */
528
+ /*------------------------------------------------------------------------*/
529
+ // Stored stub responses
530
+ const stubResponses = {};
531
+ /**
532
+ * Add a stub response
533
+ * @author Gabe Abrams
534
+ * @param opts object containing all arguments
535
+ * @param [opts.method=GET] http request method
536
+ * @param opts.path pathname of the request
537
+ * @param [opts.body] body of the response if successful
538
+ * @param [opts.errorMessage] error message if not successful
539
+ * @param [opts.errorCode] error code if not successful
540
+ */
541
+ const _setStubResponse = (opts) => {
542
+ var _a, _b, _c;
543
+ const { path, body, } = opts;
544
+ const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET').toUpperCase();
545
+ const errorMessage = ((_b = opts.errorMessage) !== null && _b !== void 0 ? _b : 'An unknown error has occurred.');
546
+ const errorCode = ((_c = opts.errorCode) !== null && _c !== void 0 ? _c : ReactKitErrorCode$1.NoCode);
547
+ // Store to stub responses
548
+ if (!stubResponses[method]) {
549
+ stubResponses[method] = {};
550
+ }
551
+ stubResponses[method][path] = ((opts.errorMessage || opts.errorCode)
552
+ ? {
553
+ success: false,
554
+ errorMessage,
555
+ errorCode,
556
+ }
557
+ : {
558
+ success: true,
559
+ body: body !== null && body !== void 0 ? body : undefined,
560
+ });
561
+ };
562
+ /*------------------------------------------------------------------------*/
563
+ /* Main */
564
+ /*------------------------------------------------------------------------*/
565
+ /**
566
+ * Visit an endpoint on the server [for client only]
567
+ * @author Gabe Abrams
568
+ * @param opts object containing all arguments
569
+ * @param opts.path - the path of the server endpoint
570
+ * @param [opts.method=GET] - the method of the endpoint
571
+ * @param [opts.params] - query/body parameters to include
572
+ * @returns response from server
573
+ */
574
+ const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
575
+ var _a, _b, _c;
576
+ const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
577
+ // Handle stubs
578
+ const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
579
+ if (stubResponse) {
580
+ // Remove from list
581
+ try {
582
+ stubResponses[method][opts.path] = undefined;
583
+ }
584
+ catch (err) {
585
+ // Ignore
586
+ }
587
+ // Success
588
+ if (stubResponse.success) {
589
+ return stubResponse.body;
590
+ }
591
+ // Error
592
+ throw new ErrorWithCode(stubResponse.errorMessage, stubResponse.errorCode);
593
+ }
594
+ // Send the request
595
+ const response = yield cacclSendRequest({
596
+ path: opts.path,
597
+ method: (_c = opts.method) !== null && _c !== void 0 ? _c : 'GET',
598
+ params: opts.params,
599
+ });
600
+ // Check for failure
601
+ if (!response || !response.body) {
602
+ throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
603
+ }
604
+ if (!response.body.success) {
605
+ // Session expired
606
+ if (response.body.code === ReactKitErrorCode$1.SessionExpired) {
607
+ // Skip notice if session was already expired
608
+ if (sessionAlreadyExpired) {
609
+ // Never return (browser is already reloading)
610
+ yield new Promise(() => {
611
+ // Promise that never returns
612
+ });
613
+ }
614
+ sessionAlreadyExpired = true;
615
+ // Show session expiration message
616
+ {
617
+ // Fallback to alert
618
+ // eslint-disable-next-line no-alert
619
+ alert('Your session has expired. Please start over.');
620
+ }
621
+ // Never return (don't continue execution)
622
+ yield new Promise(() => {
623
+ // Promise that never returns
624
+ });
625
+ }
626
+ // Other errors
627
+ throw new ErrorWithCode((response.body.message
628
+ || 'An unknown error occurred. Please contact an admin.'), (response.body.code
629
+ || ReactKitErrorCode$1.NoCode));
630
+ }
631
+ // Success! Extract the body
632
+ const { body } = response.body;
633
+ // Return
634
+ return body;
635
+ });
636
+
637
+ /**
638
+ * Log a user action on the client (cannot be used on the server)
639
+ * @author Gabe Abrams
640
+ */
641
+ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
642
+ var _a, _b, _c, _d, _e, _f;
643
+ return visitServerEndpoint({
644
+ path: LOG_ROUTE_PATH,
645
+ method: 'POST',
646
+ params: {
647
+ context: (typeof opts.context === 'string'
648
+ ? opts.context
649
+ : ((_b = ((_a = opts.context) !== null && _a !== void 0 ? _a : {})._) !== null && _b !== void 0 ? _b : LogBuiltInMetadata.Context.Uncategorized)),
650
+ subcontext: ((_c = opts.subcontext) !== null && _c !== void 0 ? _c : LogBuiltInMetadata.Context.Uncategorized),
651
+ tags: JSON.stringify((_d = opts.tags) !== null && _d !== void 0 ? _d : []),
652
+ metadata: JSON.stringify((_e = opts.metadata) !== null && _e !== void 0 ? _e : {}),
653
+ errorMessage: (opts.error
654
+ ? opts.error.message
655
+ : undefined),
656
+ errorCode: (opts.error
657
+ ? opts.error.code
658
+ : undefined),
659
+ errorStack: (opts.error
660
+ ? opts.error.stack
661
+ : undefined),
662
+ target: (opts.action
663
+ ? ((_f = opts.target) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Target.NoSpecificTarget)
664
+ : undefined),
665
+ action: (opts.action
666
+ ? opts.action
667
+ : undefined),
668
+ },
669
+ });
670
+ });
671
+
494
672
  /**
495
673
  * A wrapper for the entire React app that adds global functionality like
496
674
  * handling for fatal error messages, adds bootstrap support
@@ -610,6 +788,14 @@ const showFatalError = (error, errorTitle = 'An Error Occurred') => {
610
788
  const code = (typeof error === 'string'
611
789
  ? ReactKitErrorCode$1.NoCode
612
790
  : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
791
+ // Add log
792
+ logClientEvent({
793
+ context: LogBuiltInMetadata.Context.ClientFatalError,
794
+ error,
795
+ metadata: {
796
+ errorTitle,
797
+ },
798
+ });
613
799
  // Handle case where app hasn't loaded
614
800
  if (!setFatalErrorMessage || !setFatalErrorCode) {
615
801
  alert$1(errorTitle, `${message} (code: ${code}). Please contact support.`);
@@ -2003,131 +2189,6 @@ const roundToNumDecimals = (num, numDecimals) => {
2003
2189
  return (Math.round(num * rounder) / rounder);
2004
2190
  };
2005
2191
 
2006
- // Keep track of whether or not session expiry has already been handled
2007
- let sessionAlreadyExpired = false;
2008
- /*------------------------------------------------------------------------*/
2009
- /* Stub Logic */
2010
- /*------------------------------------------------------------------------*/
2011
- // Stored stub responses
2012
- const stubResponses = {};
2013
- /**
2014
- * Add a stub response
2015
- * @author Gabe Abrams
2016
- * @param opts object containing all arguments
2017
- * @param [opts.method=GET] http request method
2018
- * @param opts.path pathname of the request
2019
- * @param [opts.body] body of the response if successful
2020
- * @param [opts.errorMessage] error message if not successful
2021
- * @param [opts.errorCode] error code if not successful
2022
- */
2023
- const _setStubResponse = (opts) => {
2024
- var _a, _b, _c;
2025
- const { path, body, } = opts;
2026
- const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET').toUpperCase();
2027
- const errorMessage = ((_b = opts.errorMessage) !== null && _b !== void 0 ? _b : 'An unknown error has occurred.');
2028
- const errorCode = ((_c = opts.errorCode) !== null && _c !== void 0 ? _c : ReactKitErrorCode$1.NoCode);
2029
- // Store to stub responses
2030
- if (!stubResponses[method]) {
2031
- stubResponses[method] = {};
2032
- }
2033
- stubResponses[method][path] = ((opts.errorMessage || opts.errorCode)
2034
- ? {
2035
- success: false,
2036
- errorMessage,
2037
- errorCode,
2038
- }
2039
- : {
2040
- success: true,
2041
- body: body !== null && body !== void 0 ? body : undefined,
2042
- });
2043
- };
2044
- /*------------------------------------------------------------------------*/
2045
- /* Main */
2046
- /*------------------------------------------------------------------------*/
2047
- /**
2048
- * Visit an endpoint on the server [for client only]
2049
- * @author Gabe Abrams
2050
- * @param opts object containing all arguments
2051
- * @param opts.path - the path of the server endpoint
2052
- * @param [opts.method=GET] - the method of the endpoint
2053
- * @param [opts.params] - query/body parameters to include
2054
- * @returns response from server
2055
- */
2056
- const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
2057
- var _a, _b, _c;
2058
- const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
2059
- // Handle stubs
2060
- const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
2061
- if (stubResponse) {
2062
- // Remove from list
2063
- try {
2064
- stubResponses[method][opts.path] = undefined;
2065
- }
2066
- catch (err) {
2067
- // Ignore
2068
- }
2069
- // Success
2070
- if (stubResponse.success) {
2071
- return stubResponse.body;
2072
- }
2073
- // Error
2074
- throw new ErrorWithCode(stubResponse.errorMessage, stubResponse.errorCode);
2075
- }
2076
- // Send the request
2077
- const response = yield cacclSendRequest({
2078
- path: opts.path,
2079
- method: (_c = opts.method) !== null && _c !== void 0 ? _c : 'GET',
2080
- params: opts.params,
2081
- });
2082
- // Check for failure
2083
- if (!response || !response.body) {
2084
- throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
2085
- }
2086
- if (!response.body.success) {
2087
- // Session expired
2088
- if (response.body.code === ReactKitErrorCode$1.SessionExpired) {
2089
- // Skip notice if session was already expired
2090
- if (sessionAlreadyExpired) {
2091
- // Never return (browser is already reloading)
2092
- yield new Promise(() => {
2093
- // Promise that never returns
2094
- });
2095
- }
2096
- sessionAlreadyExpired = true;
2097
- // Show session expiration message
2098
- {
2099
- // Fallback to alert
2100
- // eslint-disable-next-line no-alert
2101
- alert('Your session has expired. Please start over.');
2102
- }
2103
- // Never return (don't continue execution)
2104
- yield new Promise(() => {
2105
- // Promise that never returns
2106
- });
2107
- }
2108
- // Other errors
2109
- throw new ErrorWithCode((response.body.message
2110
- || 'An unknown error occurred. Please contact an admin.'), (response.body.code
2111
- || ReactKitErrorCode$1.NoCode));
2112
- }
2113
- // Success! Extract the body
2114
- const { body } = response.body;
2115
- // Return
2116
- return body;
2117
- });
2118
-
2119
- /**
2120
- * Path that all routes start with
2121
- * @author Gabe Abrams
2122
- */
2123
- const ROUTE_PATH_PREFIX = '/dce-reactkit';
2124
-
2125
- /**
2126
- * Path of the route for storing client-side logs
2127
- * @author Gabe Abrams
2128
- */
2129
- const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
2130
-
2131
2192
  /**
2132
2193
  * Server-side API param types
2133
2194
  * @author Gabe Abrams
@@ -2202,6 +2263,7 @@ const initServer = (opts) => {
2202
2263
  * @param {string} tags stringified list of tags that apply to this action
2203
2264
  * (each app determines tag usage)
2204
2265
  * @param {string} metadata stringified object containing optional custom metadata
2266
+ * @param {string} level log level
2205
2267
  * @param {string} [errorMessage] error message if type is an error
2206
2268
  * @param {string} [errorCode] error code if type is an error
2207
2269
  * @param {string} [errorStack] error stack if type is an error
@@ -2215,6 +2277,7 @@ const initServer = (opts) => {
2215
2277
  context: ParamType$1.String,
2216
2278
  subcontext: ParamType$1.String,
2217
2279
  tags: ParamType$1.JSON,
2280
+ level: ParamType$1.String,
2218
2281
  metadata: ParamType$1.JSON,
2219
2282
  errorMessage: ParamType$1.StringOptional,
2220
2283
  errorCode: ParamType$1.StringOptional,
@@ -2223,12 +2286,14 @@ const initServer = (opts) => {
2223
2286
  action: ParamType$1.StringOptional,
2224
2287
  },
2225
2288
  handler: ({ params, logServerEvent }) => {
2226
- const log = logServerEvent((params.errorMessage || params.errorCode || params.errorStack)
2289
+ // Create log info
2290
+ const logInfo = ((params.errorMessage || params.errorCode || params.errorStack)
2227
2291
  // Error
2228
2292
  ? {
2229
2293
  context: params.context,
2230
2294
  subcontext: params.subcontext,
2231
2295
  tags: params.tags,
2296
+ level: params.level,
2232
2297
  metadata: params.metadata,
2233
2298
  error: {
2234
2299
  message: params.errorMessage,
@@ -2241,10 +2306,16 @@ const initServer = (opts) => {
2241
2306
  context: params.context,
2242
2307
  subcontext: params.subcontext,
2243
2308
  tags: params.tags,
2309
+ level: params.level,
2244
2310
  metadata: params.metadata,
2245
2311
  target: params.target,
2246
2312
  action: params.action,
2247
2313
  });
2314
+ // Add hidden boolean to change source to "client"
2315
+ const logInfoForcedFromClient = Object.assign(Object.assign({}, logInfo), { overrideAsClientEvent: true });
2316
+ // Write the log
2317
+ const log = logServerEvent(logInfoForcedFromClient);
2318
+ // Return
2248
2319
  return log;
2249
2320
  },
2250
2321
  }));
@@ -2573,23 +2644,6 @@ var LogSource;
2573
2644
  })(LogSource || (LogSource = {}));
2574
2645
  var LogSource$1 = LogSource;
2575
2646
 
2576
- /**
2577
- * Built-in metadata for logs
2578
- * @author Gabe Abrams
2579
- */
2580
- const LogBuiltInMetadata = {
2581
- // Contexts
2582
- Context: {
2583
- Uncategorized: 'n/a',
2584
- ServerRenderedErrorPage: '_server-rendered-error-page',
2585
- ServerEndpointError: '_server-endpoint-error',
2586
- },
2587
- // Targets
2588
- Target: {
2589
- NoSpecificTarget: 'n/a',
2590
- },
2591
- };
2592
-
2593
2647
  /**
2594
2648
  * Types of actions
2595
2649
  * @author Gabe Abrams
@@ -2631,6 +2685,18 @@ var LogAction;
2631
2685
  })(LogAction || (LogAction = {}));
2632
2686
  var LogAction$1 = LogAction;
2633
2687
 
2688
+ /**
2689
+ * Allowed log levels
2690
+ * @author Gabe Abrams
2691
+ */
2692
+ var LogLevel;
2693
+ (function (LogLevel) {
2694
+ LogLevel["Warn"] = "Warn";
2695
+ LogLevel["Info"] = "Info";
2696
+ LogLevel["Debug"] = "Debug";
2697
+ })(LogLevel || (LogLevel = {}));
2698
+ var LogLevel$1 = LogLevel;
2699
+
2634
2700
  /**
2635
2701
  * Generate an express API route handler
2636
2702
  * @author Gabe Abrams
@@ -2955,7 +3021,7 @@ const genRouteHandler = (opts) => {
2955
3021
  * @author Gabe Abrams
2956
3022
  */
2957
3023
  const logServerEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
2958
- var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
3024
+ var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
2959
3025
  // NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
2960
3026
  // that indicates that this is actually a client event, but we don't
2961
3027
  // include that in the LogFunction type because this is internal and
@@ -2989,21 +3055,22 @@ const genRouteHandler = (opts) => {
2989
3055
  ? opts.context
2990
3056
  : ((_d = ((_c = opts.context) !== null && _c !== void 0 ? _c : {})._) !== null && _d !== void 0 ? _d : LogBuiltInMetadata.Context.Uncategorized)),
2991
3057
  subcontext: ((_e = opts.subcontext) !== null && _e !== void 0 ? _e : LogBuiltInMetadata.Context.Uncategorized),
2992
- tags: (_f = opts.tags) !== null && _f !== void 0 ? _f : [],
2993
- metadata: (_g = opts.metadata) !== null && _g !== void 0 ? _g : {},
3058
+ tags: ((_f = opts.tags) !== null && _f !== void 0 ? _f : []),
3059
+ level: ((_g = opts.level) !== null && _g !== void 0 ? _g : LogLevel$1.Info),
3060
+ metadata: ((_h = opts.metadata) !== null && _h !== void 0 ? _h : {}),
2994
3061
  };
2995
3062
  // Type-specific info
2996
3063
  const typeSpecificInfo = (('error' in opts && opts.error)
2997
3064
  ? {
2998
3065
  type: LogType$1.Error,
2999
- errorMessage: (_h = opts.error.message) !== null && _h !== void 0 ? _h : 'Unknown message',
3000
- errorCode: (_j = opts.error.code) !== null && _j !== void 0 ? _j : ReactKitErrorCode$1.NoCode,
3001
- errorStack: (_k = opts.error.stack) !== null && _k !== void 0 ? _k : 'No stack',
3066
+ errorMessage: (_j = opts.error.message) !== null && _j !== void 0 ? _j : 'Unknown message',
3067
+ errorCode: (_k = opts.error.code) !== null && _k !== void 0 ? _k : ReactKitErrorCode$1.NoCode,
3068
+ errorStack: (_l = opts.error.stack) !== null && _l !== void 0 ? _l : 'No stack',
3002
3069
  }
3003
3070
  : {
3004
3071
  type: LogType$1.Action,
3005
- target: ((_l = opts.target) !== null && _l !== void 0 ? _l : LogBuiltInMetadata.Target.NoSpecificTarget),
3006
- action: ((_m = opts.action) !== null && _m !== void 0 ? _m : LogAction$1.Unknown),
3072
+ target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.NoSpecificTarget),
3073
+ action: ((_o = opts.action) !== null && _o !== void 0 ? _o : LogAction$1.Unknown),
3007
3074
  });
3008
3075
  // Source-specific info
3009
3076
  const sourceSpecificInfo = (opts.overrideAsClientEvent
@@ -3065,6 +3132,7 @@ const genRouteHandler = (opts) => {
3065
3132
  minute: 1,
3066
3133
  timestamp: Date.now(),
3067
3134
  tags: [],
3135
+ level: LogLevel$1.Warn,
3068
3136
  metadata: {},
3069
3137
  context: LogBuiltInMetadata.Context.Uncategorized,
3070
3138
  subcontext: LogBuiltInMetadata.Context.Uncategorized,
@@ -3362,41 +3430,6 @@ const parallelLimit = (taskFunctions, limit) => __awaiter(void 0, void 0, void 0
3362
3430
  return results;
3363
3431
  });
3364
3432
 
3365
- /**
3366
- * Log a user action on the client (cannot be used on the server)
3367
- * @author Gabe Abrams
3368
- */
3369
- const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
3370
- var _a, _b, _c, _d, _e, _f;
3371
- return visitServerEndpoint({
3372
- path: LOG_ROUTE_PATH,
3373
- method: 'POST',
3374
- params: {
3375
- context: (typeof opts.context === 'string'
3376
- ? opts.context
3377
- : ((_b = ((_a = opts.context) !== null && _a !== void 0 ? _a : {})._) !== null && _b !== void 0 ? _b : LogBuiltInMetadata.Context.Uncategorized)),
3378
- subcontext: ((_c = opts.subcontext) !== null && _c !== void 0 ? _c : LogBuiltInMetadata.Context.Uncategorized),
3379
- tags: JSON.stringify((_d = opts.tags) !== null && _d !== void 0 ? _d : []),
3380
- metadata: JSON.stringify((_e = opts.metadata) !== null && _e !== void 0 ? _e : {}),
3381
- errorMessage: (opts.error
3382
- ? opts.error.message
3383
- : undefined),
3384
- errorCode: (opts.error
3385
- ? opts.error.code
3386
- : undefined),
3387
- errorStack: (opts.error
3388
- ? opts.error.stack
3389
- : undefined),
3390
- target: (opts.action
3391
- ? ((_f = opts.target) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Target.NoSpecificTarget)
3392
- : undefined),
3393
- action: (opts.action
3394
- ? opts.action
3395
- : undefined),
3396
- },
3397
- });
3398
- });
3399
-
3400
3433
  /**
3401
3434
  * Initialize a log collection given the dce-mango Collection class
3402
3435
  * @author Gabe Abrams