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/cjs/index.js CHANGED
@@ -499,6 +499,184 @@ class ErrorWithCode extends Error {
499
499
  }
500
500
  }
501
501
 
502
+ /**
503
+ * Path that all routes start with
504
+ * @author Gabe Abrams
505
+ */
506
+ const ROUTE_PATH_PREFIX = '/dce-reactkit';
507
+
508
+ /**
509
+ * Path of the route for storing client-side logs
510
+ * @author Gabe Abrams
511
+ */
512
+ const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
513
+
514
+ /**
515
+ * Built-in metadata for logs
516
+ * @author Gabe Abrams
517
+ */
518
+ const LogBuiltInMetadata = {
519
+ // Contexts
520
+ Context: {
521
+ Uncategorized: 'n/a',
522
+ ServerRenderedErrorPage: '_server-rendered-error-page',
523
+ ServerEndpointError: '_server-endpoint-error',
524
+ ClientFatalError: '_client-fatal-error',
525
+ },
526
+ // Targets
527
+ Target: {
528
+ NoSpecificTarget: 'n/a',
529
+ },
530
+ };
531
+
532
+ // Keep track of whether or not session expiry has already been handled
533
+ let sessionAlreadyExpired = false;
534
+ /*------------------------------------------------------------------------*/
535
+ /* Stub Logic */
536
+ /*------------------------------------------------------------------------*/
537
+ // Stored stub responses
538
+ const stubResponses = {};
539
+ /**
540
+ * Add a stub response
541
+ * @author Gabe Abrams
542
+ * @param opts object containing all arguments
543
+ * @param [opts.method=GET] http request method
544
+ * @param opts.path pathname of the request
545
+ * @param [opts.body] body of the response if successful
546
+ * @param [opts.errorMessage] error message if not successful
547
+ * @param [opts.errorCode] error code if not successful
548
+ */
549
+ const _setStubResponse = (opts) => {
550
+ var _a, _b, _c;
551
+ const { path, body, } = opts;
552
+ const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET').toUpperCase();
553
+ const errorMessage = ((_b = opts.errorMessage) !== null && _b !== void 0 ? _b : 'An unknown error has occurred.');
554
+ const errorCode = ((_c = opts.errorCode) !== null && _c !== void 0 ? _c : ReactKitErrorCode$1.NoCode);
555
+ // Store to stub responses
556
+ if (!stubResponses[method]) {
557
+ stubResponses[method] = {};
558
+ }
559
+ stubResponses[method][path] = ((opts.errorMessage || opts.errorCode)
560
+ ? {
561
+ success: false,
562
+ errorMessage,
563
+ errorCode,
564
+ }
565
+ : {
566
+ success: true,
567
+ body: body !== null && body !== void 0 ? body : undefined,
568
+ });
569
+ };
570
+ /*------------------------------------------------------------------------*/
571
+ /* Main */
572
+ /*------------------------------------------------------------------------*/
573
+ /**
574
+ * Visit an endpoint on the server [for client only]
575
+ * @author Gabe Abrams
576
+ * @param opts object containing all arguments
577
+ * @param opts.path - the path of the server endpoint
578
+ * @param [opts.method=GET] - the method of the endpoint
579
+ * @param [opts.params] - query/body parameters to include
580
+ * @returns response from server
581
+ */
582
+ const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
583
+ var _a, _b, _c;
584
+ const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
585
+ // Handle stubs
586
+ const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
587
+ if (stubResponse) {
588
+ // Remove from list
589
+ try {
590
+ stubResponses[method][opts.path] = undefined;
591
+ }
592
+ catch (err) {
593
+ // Ignore
594
+ }
595
+ // Success
596
+ if (stubResponse.success) {
597
+ return stubResponse.body;
598
+ }
599
+ // Error
600
+ throw new ErrorWithCode(stubResponse.errorMessage, stubResponse.errorCode);
601
+ }
602
+ // Send the request
603
+ const response = yield cacclSendRequest({
604
+ path: opts.path,
605
+ method: (_c = opts.method) !== null && _c !== void 0 ? _c : 'GET',
606
+ params: opts.params,
607
+ });
608
+ // Check for failure
609
+ if (!response || !response.body) {
610
+ throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
611
+ }
612
+ if (!response.body.success) {
613
+ // Session expired
614
+ if (response.body.code === ReactKitErrorCode$1.SessionExpired) {
615
+ // Skip notice if session was already expired
616
+ if (sessionAlreadyExpired) {
617
+ // Never return (browser is already reloading)
618
+ yield new Promise(() => {
619
+ // Promise that never returns
620
+ });
621
+ }
622
+ sessionAlreadyExpired = true;
623
+ // Show session expiration message
624
+ {
625
+ // Fallback to alert
626
+ // eslint-disable-next-line no-alert
627
+ alert('Your session has expired. Please start over.');
628
+ }
629
+ // Never return (don't continue execution)
630
+ yield new Promise(() => {
631
+ // Promise that never returns
632
+ });
633
+ }
634
+ // Other errors
635
+ throw new ErrorWithCode((response.body.message
636
+ || 'An unknown error occurred. Please contact an admin.'), (response.body.code
637
+ || ReactKitErrorCode$1.NoCode));
638
+ }
639
+ // Success! Extract the body
640
+ const { body } = response.body;
641
+ // Return
642
+ return body;
643
+ });
644
+
645
+ /**
646
+ * Log a user action on the client (cannot be used on the server)
647
+ * @author Gabe Abrams
648
+ */
649
+ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
650
+ var _a, _b, _c, _d, _e, _f;
651
+ return visitServerEndpoint({
652
+ path: LOG_ROUTE_PATH,
653
+ method: 'POST',
654
+ params: {
655
+ context: (typeof opts.context === 'string'
656
+ ? opts.context
657
+ : ((_b = ((_a = opts.context) !== null && _a !== void 0 ? _a : {})._) !== null && _b !== void 0 ? _b : LogBuiltInMetadata.Context.Uncategorized)),
658
+ subcontext: ((_c = opts.subcontext) !== null && _c !== void 0 ? _c : LogBuiltInMetadata.Context.Uncategorized),
659
+ tags: JSON.stringify((_d = opts.tags) !== null && _d !== void 0 ? _d : []),
660
+ metadata: JSON.stringify((_e = opts.metadata) !== null && _e !== void 0 ? _e : {}),
661
+ errorMessage: (opts.error
662
+ ? opts.error.message
663
+ : undefined),
664
+ errorCode: (opts.error
665
+ ? opts.error.code
666
+ : undefined),
667
+ errorStack: (opts.error
668
+ ? opts.error.stack
669
+ : undefined),
670
+ target: (opts.action
671
+ ? ((_f = opts.target) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Target.NoSpecificTarget)
672
+ : undefined),
673
+ action: (opts.action
674
+ ? opts.action
675
+ : undefined),
676
+ },
677
+ });
678
+ });
679
+
502
680
  /**
503
681
  * A wrapper for the entire React app that adds global functionality like
504
682
  * handling for fatal error messages, adds bootstrap support
@@ -618,6 +796,14 @@ const showFatalError = (error, errorTitle = 'An Error Occurred') => {
618
796
  const code = (typeof error === 'string'
619
797
  ? ReactKitErrorCode$1.NoCode
620
798
  : String((_b = error.code) !== null && _b !== void 0 ? _b : ReactKitErrorCode$1.NoCode));
799
+ // Add log
800
+ logClientEvent({
801
+ context: LogBuiltInMetadata.Context.ClientFatalError,
802
+ error,
803
+ metadata: {
804
+ errorTitle,
805
+ },
806
+ });
621
807
  // Handle case where app hasn't loaded
622
808
  if (!setFatalErrorMessage || !setFatalErrorCode) {
623
809
  alert$1(errorTitle, `${message} (code: ${code}). Please contact support.`);
@@ -2011,131 +2197,6 @@ const roundToNumDecimals = (num, numDecimals) => {
2011
2197
  return (Math.round(num * rounder) / rounder);
2012
2198
  };
2013
2199
 
2014
- // Keep track of whether or not session expiry has already been handled
2015
- let sessionAlreadyExpired = false;
2016
- /*------------------------------------------------------------------------*/
2017
- /* Stub Logic */
2018
- /*------------------------------------------------------------------------*/
2019
- // Stored stub responses
2020
- const stubResponses = {};
2021
- /**
2022
- * Add a stub response
2023
- * @author Gabe Abrams
2024
- * @param opts object containing all arguments
2025
- * @param [opts.method=GET] http request method
2026
- * @param opts.path pathname of the request
2027
- * @param [opts.body] body of the response if successful
2028
- * @param [opts.errorMessage] error message if not successful
2029
- * @param [opts.errorCode] error code if not successful
2030
- */
2031
- const _setStubResponse = (opts) => {
2032
- var _a, _b, _c;
2033
- const { path, body, } = opts;
2034
- const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET').toUpperCase();
2035
- const errorMessage = ((_b = opts.errorMessage) !== null && _b !== void 0 ? _b : 'An unknown error has occurred.');
2036
- const errorCode = ((_c = opts.errorCode) !== null && _c !== void 0 ? _c : ReactKitErrorCode$1.NoCode);
2037
- // Store to stub responses
2038
- if (!stubResponses[method]) {
2039
- stubResponses[method] = {};
2040
- }
2041
- stubResponses[method][path] = ((opts.errorMessage || opts.errorCode)
2042
- ? {
2043
- success: false,
2044
- errorMessage,
2045
- errorCode,
2046
- }
2047
- : {
2048
- success: true,
2049
- body: body !== null && body !== void 0 ? body : undefined,
2050
- });
2051
- };
2052
- /*------------------------------------------------------------------------*/
2053
- /* Main */
2054
- /*------------------------------------------------------------------------*/
2055
- /**
2056
- * Visit an endpoint on the server [for client only]
2057
- * @author Gabe Abrams
2058
- * @param opts object containing all arguments
2059
- * @param opts.path - the path of the server endpoint
2060
- * @param [opts.method=GET] - the method of the endpoint
2061
- * @param [opts.params] - query/body parameters to include
2062
- * @returns response from server
2063
- */
2064
- const visitServerEndpoint = (opts) => __awaiter(void 0, void 0, void 0, function* () {
2065
- var _a, _b, _c;
2066
- const method = ((_a = opts.method) !== null && _a !== void 0 ? _a : 'GET');
2067
- // Handle stubs
2068
- const stubResponse = (_b = stubResponses[method]) === null || _b === void 0 ? void 0 : _b[opts.path];
2069
- if (stubResponse) {
2070
- // Remove from list
2071
- try {
2072
- stubResponses[method][opts.path] = undefined;
2073
- }
2074
- catch (err) {
2075
- // Ignore
2076
- }
2077
- // Success
2078
- if (stubResponse.success) {
2079
- return stubResponse.body;
2080
- }
2081
- // Error
2082
- throw new ErrorWithCode(stubResponse.errorMessage, stubResponse.errorCode);
2083
- }
2084
- // Send the request
2085
- const response = yield cacclSendRequest({
2086
- path: opts.path,
2087
- method: (_c = opts.method) !== null && _c !== void 0 ? _c : 'GET',
2088
- params: opts.params,
2089
- });
2090
- // Check for failure
2091
- if (!response || !response.body) {
2092
- throw new ErrorWithCode('We didn\'t get a response from the server. Please check your internet connection.', ReactKitErrorCode$1.NoResponse);
2093
- }
2094
- if (!response.body.success) {
2095
- // Session expired
2096
- if (response.body.code === ReactKitErrorCode$1.SessionExpired) {
2097
- // Skip notice if session was already expired
2098
- if (sessionAlreadyExpired) {
2099
- // Never return (browser is already reloading)
2100
- yield new Promise(() => {
2101
- // Promise that never returns
2102
- });
2103
- }
2104
- sessionAlreadyExpired = true;
2105
- // Show session expiration message
2106
- {
2107
- // Fallback to alert
2108
- // eslint-disable-next-line no-alert
2109
- alert('Your session has expired. Please start over.');
2110
- }
2111
- // Never return (don't continue execution)
2112
- yield new Promise(() => {
2113
- // Promise that never returns
2114
- });
2115
- }
2116
- // Other errors
2117
- throw new ErrorWithCode((response.body.message
2118
- || 'An unknown error occurred. Please contact an admin.'), (response.body.code
2119
- || ReactKitErrorCode$1.NoCode));
2120
- }
2121
- // Success! Extract the body
2122
- const { body } = response.body;
2123
- // Return
2124
- return body;
2125
- });
2126
-
2127
- /**
2128
- * Path that all routes start with
2129
- * @author Gabe Abrams
2130
- */
2131
- const ROUTE_PATH_PREFIX = '/dce-reactkit';
2132
-
2133
- /**
2134
- * Path of the route for storing client-side logs
2135
- * @author Gabe Abrams
2136
- */
2137
- const LOG_ROUTE_PATH = `${ROUTE_PATH_PREFIX}/log`;
2138
-
2139
2200
  /**
2140
2201
  * Server-side API param types
2141
2202
  * @author Gabe Abrams
@@ -2210,6 +2271,7 @@ const initServer = (opts) => {
2210
2271
  * @param {string} tags stringified list of tags that apply to this action
2211
2272
  * (each app determines tag usage)
2212
2273
  * @param {string} metadata stringified object containing optional custom metadata
2274
+ * @param {string} level log level
2213
2275
  * @param {string} [errorMessage] error message if type is an error
2214
2276
  * @param {string} [errorCode] error code if type is an error
2215
2277
  * @param {string} [errorStack] error stack if type is an error
@@ -2223,6 +2285,7 @@ const initServer = (opts) => {
2223
2285
  context: ParamType$1.String,
2224
2286
  subcontext: ParamType$1.String,
2225
2287
  tags: ParamType$1.JSON,
2288
+ level: ParamType$1.String,
2226
2289
  metadata: ParamType$1.JSON,
2227
2290
  errorMessage: ParamType$1.StringOptional,
2228
2291
  errorCode: ParamType$1.StringOptional,
@@ -2231,12 +2294,14 @@ const initServer = (opts) => {
2231
2294
  action: ParamType$1.StringOptional,
2232
2295
  },
2233
2296
  handler: ({ params, logServerEvent }) => {
2234
- const log = logServerEvent((params.errorMessage || params.errorCode || params.errorStack)
2297
+ // Create log info
2298
+ const logInfo = ((params.errorMessage || params.errorCode || params.errorStack)
2235
2299
  // Error
2236
2300
  ? {
2237
2301
  context: params.context,
2238
2302
  subcontext: params.subcontext,
2239
2303
  tags: params.tags,
2304
+ level: params.level,
2240
2305
  metadata: params.metadata,
2241
2306
  error: {
2242
2307
  message: params.errorMessage,
@@ -2249,10 +2314,16 @@ const initServer = (opts) => {
2249
2314
  context: params.context,
2250
2315
  subcontext: params.subcontext,
2251
2316
  tags: params.tags,
2317
+ level: params.level,
2252
2318
  metadata: params.metadata,
2253
2319
  target: params.target,
2254
2320
  action: params.action,
2255
2321
  });
2322
+ // Add hidden boolean to change source to "client"
2323
+ const logInfoForcedFromClient = Object.assign(Object.assign({}, logInfo), { overrideAsClientEvent: true });
2324
+ // Write the log
2325
+ const log = logServerEvent(logInfoForcedFromClient);
2326
+ // Return
2256
2327
  return log;
2257
2328
  },
2258
2329
  }));
@@ -2581,23 +2652,6 @@ var LogSource;
2581
2652
  })(LogSource || (LogSource = {}));
2582
2653
  var LogSource$1 = LogSource;
2583
2654
 
2584
- /**
2585
- * Built-in metadata for logs
2586
- * @author Gabe Abrams
2587
- */
2588
- const LogBuiltInMetadata = {
2589
- // Contexts
2590
- Context: {
2591
- Uncategorized: 'n/a',
2592
- ServerRenderedErrorPage: '_server-rendered-error-page',
2593
- ServerEndpointError: '_server-endpoint-error',
2594
- },
2595
- // Targets
2596
- Target: {
2597
- NoSpecificTarget: 'n/a',
2598
- },
2599
- };
2600
-
2601
2655
  /**
2602
2656
  * Types of actions
2603
2657
  * @author Gabe Abrams
@@ -2639,6 +2693,18 @@ var LogAction;
2639
2693
  })(LogAction || (LogAction = {}));
2640
2694
  var LogAction$1 = LogAction;
2641
2695
 
2696
+ /**
2697
+ * Allowed log levels
2698
+ * @author Gabe Abrams
2699
+ */
2700
+ var LogLevel;
2701
+ (function (LogLevel) {
2702
+ LogLevel["Warn"] = "Warn";
2703
+ LogLevel["Info"] = "Info";
2704
+ LogLevel["Debug"] = "Debug";
2705
+ })(LogLevel || (LogLevel = {}));
2706
+ var LogLevel$1 = LogLevel;
2707
+
2642
2708
  /**
2643
2709
  * Generate an express API route handler
2644
2710
  * @author Gabe Abrams
@@ -2963,7 +3029,7 @@ const genRouteHandler = (opts) => {
2963
3029
  * @author Gabe Abrams
2964
3030
  */
2965
3031
  const logServerEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
2966
- var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
3032
+ var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
2967
3033
  // NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
2968
3034
  // that indicates that this is actually a client event, but we don't
2969
3035
  // include that in the LogFunction type because this is internal and
@@ -2997,21 +3063,22 @@ const genRouteHandler = (opts) => {
2997
3063
  ? opts.context
2998
3064
  : ((_d = ((_c = opts.context) !== null && _c !== void 0 ? _c : {})._) !== null && _d !== void 0 ? _d : LogBuiltInMetadata.Context.Uncategorized)),
2999
3065
  subcontext: ((_e = opts.subcontext) !== null && _e !== void 0 ? _e : LogBuiltInMetadata.Context.Uncategorized),
3000
- tags: (_f = opts.tags) !== null && _f !== void 0 ? _f : [],
3001
- metadata: (_g = opts.metadata) !== null && _g !== void 0 ? _g : {},
3066
+ tags: ((_f = opts.tags) !== null && _f !== void 0 ? _f : []),
3067
+ level: ((_g = opts.level) !== null && _g !== void 0 ? _g : LogLevel$1.Info),
3068
+ metadata: ((_h = opts.metadata) !== null && _h !== void 0 ? _h : {}),
3002
3069
  };
3003
3070
  // Type-specific info
3004
3071
  const typeSpecificInfo = (('error' in opts && opts.error)
3005
3072
  ? {
3006
3073
  type: LogType$1.Error,
3007
- errorMessage: (_h = opts.error.message) !== null && _h !== void 0 ? _h : 'Unknown message',
3008
- errorCode: (_j = opts.error.code) !== null && _j !== void 0 ? _j : ReactKitErrorCode$1.NoCode,
3009
- errorStack: (_k = opts.error.stack) !== null && _k !== void 0 ? _k : 'No stack',
3074
+ errorMessage: (_j = opts.error.message) !== null && _j !== void 0 ? _j : 'Unknown message',
3075
+ errorCode: (_k = opts.error.code) !== null && _k !== void 0 ? _k : ReactKitErrorCode$1.NoCode,
3076
+ errorStack: (_l = opts.error.stack) !== null && _l !== void 0 ? _l : 'No stack',
3010
3077
  }
3011
3078
  : {
3012
3079
  type: LogType$1.Action,
3013
- target: ((_l = opts.target) !== null && _l !== void 0 ? _l : LogBuiltInMetadata.Target.NoSpecificTarget),
3014
- action: ((_m = opts.action) !== null && _m !== void 0 ? _m : LogAction$1.Unknown),
3080
+ target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.NoSpecificTarget),
3081
+ action: ((_o = opts.action) !== null && _o !== void 0 ? _o : LogAction$1.Unknown),
3015
3082
  });
3016
3083
  // Source-specific info
3017
3084
  const sourceSpecificInfo = (opts.overrideAsClientEvent
@@ -3073,6 +3140,7 @@ const genRouteHandler = (opts) => {
3073
3140
  minute: 1,
3074
3141
  timestamp: Date.now(),
3075
3142
  tags: [],
3143
+ level: LogLevel$1.Warn,
3076
3144
  metadata: {},
3077
3145
  context: LogBuiltInMetadata.Context.Uncategorized,
3078
3146
  subcontext: LogBuiltInMetadata.Context.Uncategorized,
@@ -3370,41 +3438,6 @@ const parallelLimit = (taskFunctions, limit) => __awaiter(void 0, void 0, void 0
3370
3438
  return results;
3371
3439
  });
3372
3440
 
3373
- /**
3374
- * Log a user action on the client (cannot be used on the server)
3375
- * @author Gabe Abrams
3376
- */
3377
- const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
3378
- var _a, _b, _c, _d, _e, _f;
3379
- return visitServerEndpoint({
3380
- path: LOG_ROUTE_PATH,
3381
- method: 'POST',
3382
- params: {
3383
- context: (typeof opts.context === 'string'
3384
- ? opts.context
3385
- : ((_b = ((_a = opts.context) !== null && _a !== void 0 ? _a : {})._) !== null && _b !== void 0 ? _b : LogBuiltInMetadata.Context.Uncategorized)),
3386
- subcontext: ((_c = opts.subcontext) !== null && _c !== void 0 ? _c : LogBuiltInMetadata.Context.Uncategorized),
3387
- tags: JSON.stringify((_d = opts.tags) !== null && _d !== void 0 ? _d : []),
3388
- metadata: JSON.stringify((_e = opts.metadata) !== null && _e !== void 0 ? _e : {}),
3389
- errorMessage: (opts.error
3390
- ? opts.error.message
3391
- : undefined),
3392
- errorCode: (opts.error
3393
- ? opts.error.code
3394
- : undefined),
3395
- errorStack: (opts.error
3396
- ? opts.error.stack
3397
- : undefined),
3398
- target: (opts.action
3399
- ? ((_f = opts.target) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Target.NoSpecificTarget)
3400
- : undefined),
3401
- action: (opts.action
3402
- ? opts.action
3403
- : undefined),
3404
- },
3405
- });
3406
- });
3407
-
3408
3441
  /**
3409
3442
  * Initialize a log collection given the dce-mango Collection class
3410
3443
  * @author Gabe Abrams