dce-reactkit 3.2.0-beta.1 → 3.2.0-beta.10

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
@@ -2195,10 +2195,10 @@ const initServer = (opts) => {
2195
2195
  /**
2196
2196
  * Log an event
2197
2197
  * @author Gabe Abrams
2198
- * @param {string} category Category of the event (each app determines how to
2199
- * categorize its events)
2200
- * @param {string} subcategory Subcategory of the event (each app determines
2201
- * how to categorize its events)
2198
+ * @param {string} context Context of the event (each app determines how to
2199
+ * organize its contexts)
2200
+ * @param {string} subcontext Subcontext of the event (each app determines
2201
+ * how to organize its subcontexts)
2202
2202
  * @param {string} tags stringified list of tags that apply to this action
2203
2203
  * (each app determines tag usage)
2204
2204
  * @param {string} metadata stringified object containing optional custom metadata
@@ -2212,8 +2212,8 @@ const initServer = (opts) => {
2212
2212
  */
2213
2213
  opts.app.post(LOG_ROUTE_PATH, genRouteHandler({
2214
2214
  paramTypes: {
2215
- category: ParamType$1.String,
2216
- subcategory: ParamType$1.String,
2215
+ context: ParamType$1.String,
2216
+ subcontext: ParamType$1.String,
2217
2217
  tags: ParamType$1.JSON,
2218
2218
  metadata: ParamType$1.JSON,
2219
2219
  errorMessage: ParamType$1.StringOptional,
@@ -2226,8 +2226,8 @@ const initServer = (opts) => {
2226
2226
  const log = logServerEvent((params.errorMessage || params.errorCode || params.errorStack)
2227
2227
  // Error
2228
2228
  ? {
2229
- category: params.category,
2230
- subcategory: params.subcategory,
2229
+ context: params.context,
2230
+ subcontext: params.subcontext,
2231
2231
  tags: params.tags,
2232
2232
  metadata: params.metadata,
2233
2233
  error: {
@@ -2238,8 +2238,8 @@ const initServer = (opts) => {
2238
2238
  }
2239
2239
  // Action
2240
2240
  : {
2241
- category: params.category,
2242
- subcategory: params.subcategory,
2241
+ context: params.context,
2242
+ subcontext: params.subcontext,
2243
2243
  tags: params.tags,
2244
2244
  metadata: params.metadata,
2245
2245
  target: params.target,
@@ -2573,6 +2573,64 @@ var LogSource;
2573
2573
  })(LogSource || (LogSource = {}));
2574
2574
  var LogSource$1 = LogSource;
2575
2575
 
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
+ /**
2594
+ * Types of actions
2595
+ * @author Gabe Abrams
2596
+ */
2597
+ var LogAction;
2598
+ (function (LogAction) {
2599
+ // Target was opened by the user (it was not on screen, but now it is)
2600
+ LogAction["Open"] = "open";
2601
+ // Target was closed by the user (it was on screen, but now it is not)
2602
+ LogAction["Close"] = "close";
2603
+ // Target was cancelled by the user (it was on closed without saving)
2604
+ LogAction["Cancel"] = "cancel";
2605
+ // Target was expanded by the user (it always remains on screen, but size was changed)
2606
+ LogAction["Expand"] = "expand";
2607
+ // Target was collapsed by the user (it always remains on screen, but size was changed)
2608
+ LogAction["Collapse"] = "collapse";
2609
+ // Target was viewed by the user (only for items that are not opened or closed, those must use Open/Close actions)
2610
+ LogAction["View"] = "view";
2611
+ // Target interrupted the user (popup, dialog, validation message, etc. appeared without user prompting)
2612
+ LogAction["Interrupt"] = "interrupt";
2613
+ // Target was created by the user (it did not exist before)
2614
+ LogAction["Create"] = "create";
2615
+ // Target was edited by the user (it existed and was changed)
2616
+ LogAction["Edit"] = "edit";
2617
+ // Target was deleted by the user (it existed and now it doesn't)
2618
+ LogAction["Delete"] = "delete";
2619
+ // Target was added by the user (it already existed and was added to another place)
2620
+ LogAction["Add"] = "add";
2621
+ // Target was removed by the user (it was removed from something but still exists)
2622
+ LogAction["Remove"] = "remove";
2623
+ // Target was activated by the user (click, check, tap, keypress, etc.)
2624
+ LogAction["Activate"] = "activate";
2625
+ // Target was deactivated by the user (click away, uncheck, tap outside of, tab away, etc.)
2626
+ LogAction["Deactivate"] = "deactivate";
2627
+ // User showed interest in a target (hover, peek, etc.)
2628
+ LogAction["Peek"] = "peek";
2629
+ // Unknown action
2630
+ LogAction["Unknown"] = "unknown";
2631
+ })(LogAction || (LogAction = {}));
2632
+ var LogAction$1 = LogAction;
2633
+
2576
2634
  /**
2577
2635
  * Generate an express API route handler
2578
2636
  * @author Gabe Abrams
@@ -2897,82 +2955,136 @@ const genRouteHandler = (opts) => {
2897
2955
  * @author Gabe Abrams
2898
2956
  */
2899
2957
  const logServerEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
2958
+ var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
2900
2959
  // NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
2901
2960
  // that indicates that this is actually a client event, but we don't
2902
2961
  // include that in the LogFunction type because this is internal and
2903
2962
  // hidden from users
2904
- var _c, _d, _e, _f, _g, _h, _j, _k;
2905
- // Parse user agent
2906
- const { browser, device, } = parseUserAgent(req.headers['user-agent']);
2907
- // Get time info in ET
2908
- const { timestamp, year, month, day, hour, minute, } = getTimeInfoInET();
2909
- // Main log info
2910
- const mainLogInfo = {
2911
- id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
2912
- userFirstName: launchInfo.userFirstName,
2913
- userLastName: launchInfo.userLastName,
2914
- userEmail: launchInfo.userEmail,
2915
- userId: launchInfo.userId,
2916
- isLearner: !!launchInfo.isLearner,
2917
- isAdmin: !!launchInfo.isAdmin,
2918
- isTTM: !!launchInfo.isTTM,
2919
- courseId: launchInfo.courseId,
2920
- courseName: launchInfo.courseName,
2921
- browser,
2922
- device,
2923
- year,
2924
- month,
2925
- day,
2926
- hour,
2927
- minute,
2928
- timestamp,
2929
- category: opts.category,
2930
- subcategory: (_c = opts.subcategory) !== null && _c !== void 0 ? _c : 'none',
2931
- tags: (_d = opts.tags) !== null && _d !== void 0 ? _d : [],
2932
- metadata: (_e = opts.metadata) !== null && _e !== void 0 ? _e : {},
2933
- };
2934
- // Type-specific info
2935
- const typeSpecificInfo = (('error' in opts && opts.error)
2936
- ? {
2937
- type: LogType$1.Error,
2938
- errorMessage: (_f = opts.error.message) !== null && _f !== void 0 ? _f : 'Unknown message',
2939
- errorCode: (_g = opts.error.code) !== null && _g !== void 0 ? _g : ReactKitErrorCode$1.NoCode,
2940
- errorStack: (_h = opts.error.stack) !== null && _h !== void 0 ? _h : 'No stack',
2963
+ try {
2964
+ // Parse user agent
2965
+ const { browser, device, } = parseUserAgent(req.headers['user-agent']);
2966
+ // Get time info in ET
2967
+ const { timestamp, year, month, day, hour, minute, } = getTimeInfoInET();
2968
+ // Main log info
2969
+ const mainLogInfo = {
2970
+ id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
2971
+ userFirstName: launchInfo.userFirstName,
2972
+ userLastName: launchInfo.userLastName,
2973
+ userEmail: launchInfo.userEmail,
2974
+ userId: launchInfo.userId,
2975
+ isLearner: !!launchInfo.isLearner,
2976
+ isAdmin: !!launchInfo.isAdmin,
2977
+ isTTM: !!launchInfo.isTTM,
2978
+ courseId: launchInfo.courseId,
2979
+ courseName: launchInfo.courseName,
2980
+ browser,
2981
+ device,
2982
+ year,
2983
+ month,
2984
+ day,
2985
+ hour,
2986
+ minute,
2987
+ timestamp,
2988
+ context: (typeof opts.context === 'string'
2989
+ ? opts.context
2990
+ : ((_d = ((_c = opts.context) !== null && _c !== void 0 ? _c : {})._) !== null && _d !== void 0 ? _d : LogBuiltInMetadata.Context.Uncategorized)),
2991
+ subcontext: (typeof opts.context === 'string'
2992
+ ? opts.subcontext
2993
+ : ((_f = ((_e = opts.subcontext) !== null && _e !== void 0 ? _e : {})._) !== null && _f !== void 0 ? _f : LogBuiltInMetadata.Context.Uncategorized)),
2994
+ tags: (_g = opts.tags) !== null && _g !== void 0 ? _g : [],
2995
+ metadata: (_h = opts.metadata) !== null && _h !== void 0 ? _h : {},
2996
+ };
2997
+ // Type-specific info
2998
+ const typeSpecificInfo = (('error' in opts && opts.error)
2999
+ ? {
3000
+ type: LogType$1.Error,
3001
+ errorMessage: (_j = opts.error.message) !== null && _j !== void 0 ? _j : 'Unknown message',
3002
+ errorCode: (_k = opts.error.code) !== null && _k !== void 0 ? _k : ReactKitErrorCode$1.NoCode,
3003
+ errorStack: (_l = opts.error.stack) !== null && _l !== void 0 ? _l : 'No stack',
3004
+ }
3005
+ : {
3006
+ type: LogType$1.Action,
3007
+ target: ((_m = opts.target) !== null && _m !== void 0 ? _m : LogBuiltInMetadata.Target.NoSpecificTarget),
3008
+ action: ((_o = opts.action) !== null && _o !== void 0 ? _o : LogAction$1.Unknown),
3009
+ });
3010
+ // Source-specific info
3011
+ const sourceSpecificInfo = (opts.overrideAsClientEvent
3012
+ ? {
3013
+ source: LogSource$1.Client,
3014
+ }
3015
+ : {
3016
+ source: LogSource$1.Server,
3017
+ routePath: req.path,
3018
+ routeTemplate: req.route.path,
3019
+ });
3020
+ // Build log event
3021
+ const log = Object.assign(Object.assign(Object.assign({}, mainLogInfo), typeSpecificInfo), sourceSpecificInfo);
3022
+ // Either print to console or save to db
3023
+ const logCollection = internalGetLogCollection();
3024
+ if (logCollection) {
3025
+ // Store to the log collection
3026
+ yield logCollection.insert(log);
2941
3027
  }
2942
- : {
2943
- type: LogType$1.Action,
2944
- target: (_j = opts.target) !== null && _j !== void 0 ? _j : 'Unknown',
2945
- action: (_k = opts.action) !== null && _k !== void 0 ? _k : 'Unknown'
2946
- });
2947
- // Source-specific info
2948
- const sourceSpecificInfo = (opts.overrideAsClientEvent
2949
- ? {
2950
- source: LogSource$1.Client,
3028
+ else {
3029
+ // Print to console
3030
+ if (log.type === LogType$1.Error) {
3031
+ console.error('dce-reactkit error log:', log);
3032
+ }
3033
+ else {
3034
+ console.log('dce-reactkit action log:', log);
3035
+ }
2951
3036
  }
2952
- : {
3037
+ // Return log entry
3038
+ return log;
3039
+ }
3040
+ catch (err) {
3041
+ // Print because we cannot store the error
3042
+ console.error('Could not log the following:', opts);
3043
+ // Create a dummy log to return
3044
+ const dummyMainInfo = {
3045
+ id: '-1',
3046
+ userFirstName: 'Unknown',
3047
+ userLastName: 'Unknown',
3048
+ userEmail: 'unknown@harvard.edu',
3049
+ userId: 1,
3050
+ isLearner: false,
3051
+ isAdmin: false,
3052
+ isTTM: false,
3053
+ courseId: 1,
3054
+ courseName: 'Unknown',
3055
+ browser: {
3056
+ name: 'Unknown',
3057
+ version: 'Unknown',
3058
+ },
3059
+ device: {
3060
+ isMobile: false,
3061
+ os: 'Unknown',
3062
+ },
3063
+ year: 1,
3064
+ month: 1,
3065
+ day: 1,
3066
+ hour: 1,
3067
+ minute: 1,
3068
+ timestamp: Date.now(),
3069
+ tags: [],
3070
+ metadata: {},
3071
+ context: LogBuiltInMetadata.Context.Uncategorized,
3072
+ subcontext: LogBuiltInMetadata.Context.Uncategorized,
3073
+ };
3074
+ const dummyTypeSpecificInfo = {
3075
+ type: LogType$1.Error,
3076
+ errorMessage: 'Unknown',
3077
+ errorCode: 'Unknown',
3078
+ errorStack: 'No Stack',
3079
+ };
3080
+ const dummySourceSpecificInfo = {
2953
3081
  source: LogSource$1.Server,
2954
3082
  routePath: req.path,
2955
3083
  routeTemplate: req.route.path,
2956
- });
2957
- // Build log event
2958
- const log = Object.assign(Object.assign(Object.assign({}, mainLogInfo), typeSpecificInfo), sourceSpecificInfo);
2959
- // Either print to console or save to db
2960
- const logCollection = internalGetLogCollection();
2961
- if (logCollection) {
2962
- // Store to the log collection
2963
- yield logCollection.insert(log);
2964
- }
2965
- else {
2966
- // Print to console
2967
- if (log.type === LogType$1.Error) {
2968
- console.error('dce-reactkit error log:', log);
2969
- }
2970
- else {
2971
- console.log('dce-reactkit action log:', log);
2972
- }
3084
+ };
3085
+ const log = Object.assign(Object.assign(Object.assign({}, dummyMainInfo), dummyTypeSpecificInfo), dummySourceSpecificInfo);
3086
+ return log;
2973
3087
  }
2974
- // Return log entry
2975
- return log;
2976
3088
  });
2977
3089
  /*------------------------------------------------------------------------*/
2978
3090
  /* Call handler */
@@ -3011,9 +3123,24 @@ const genRouteHandler = (opts) => {
3011
3123
  * @param [opts.status=500] http status code
3012
3124
  */
3013
3125
  const renderErrorPage = (opts = {}) => {
3014
- var _a;
3126
+ var _a, _b;
3015
3127
  const html = genErrorPage(opts);
3016
3128
  send(html, (_a = opts.status) !== null && _a !== void 0 ? _a : 500);
3129
+ // Log
3130
+ logServerEvent({
3131
+ context: LogBuiltInMetadata.Context.ServerRenderedErrorPage,
3132
+ error: {
3133
+ message: `${opts.title}: ${opts.description}`,
3134
+ code: opts.code,
3135
+ },
3136
+ metadata: {
3137
+ title: opts.title,
3138
+ description: opts.description,
3139
+ code: opts.code,
3140
+ pageTitle: opts.pageTitle,
3141
+ status: (_b = opts.status) !== null && _b !== void 0 ? _b : 500,
3142
+ },
3143
+ });
3017
3144
  };
3018
3145
  // Call the handler
3019
3146
  try {
@@ -3037,7 +3164,13 @@ const genRouteHandler = (opts) => {
3037
3164
  catch (err) {
3038
3165
  // Send error to client (only if next wasn't called)
3039
3166
  if (!responseSent) {
3040
- return handleError(res, err);
3167
+ handleError(res, err);
3168
+ // Log server-side error
3169
+ logServerEvent({
3170
+ context: LogBuiltInMetadata.Context.ServerEndpointError,
3171
+ error: err,
3172
+ });
3173
+ return;
3041
3174
  }
3042
3175
  // Log error that was not responded with
3043
3176
  console.log('Error occurred but could not be sent to client because a response was already sent:', err);
@@ -3231,6 +3364,61 @@ const parallelLimit = (taskFunctions, limit) => __awaiter(void 0, void 0, void 0
3231
3364
  return results;
3232
3365
  });
3233
3366
 
3367
+ /**
3368
+ * Log a user action on the client (cannot be used on the server)
3369
+ * @author Gabe Abrams
3370
+ */
3371
+ const logClientEvent = (opts) => __awaiter(void 0, void 0, void 0, function* () {
3372
+ var _a, _b, _c, _d, _e, _f, _g;
3373
+ return visitServerEndpoint({
3374
+ path: LOG_ROUTE_PATH,
3375
+ method: 'POST',
3376
+ params: {
3377
+ context: (typeof opts.context === 'string'
3378
+ ? opts.context
3379
+ : ((_b = ((_a = opts.context) !== null && _a !== void 0 ? _a : {})._) !== null && _b !== void 0 ? _b : LogBuiltInMetadata.Context.Uncategorized)),
3380
+ subcontext: (typeof opts.context === 'string'
3381
+ ? opts.subcontext
3382
+ : ((_d = ((_c = opts.subcontext) !== null && _c !== void 0 ? _c : {})._) !== null && _d !== void 0 ? _d : LogBuiltInMetadata.Context.Uncategorized)),
3383
+ tags: JSON.stringify((_e = opts.tags) !== null && _e !== void 0 ? _e : []),
3384
+ metadata: JSON.stringify((_f = opts.metadata) !== null && _f !== void 0 ? _f : {}),
3385
+ errorMessage: (opts.error
3386
+ ? opts.error.message
3387
+ : undefined),
3388
+ errorCode: (opts.error
3389
+ ? opts.error.code
3390
+ : undefined),
3391
+ errorStack: (opts.error
3392
+ ? opts.error.stack
3393
+ : undefined),
3394
+ target: (opts.action
3395
+ ? ((_g = opts.target) !== null && _g !== void 0 ? _g : LogBuiltInMetadata.Target.NoSpecificTarget)
3396
+ : undefined),
3397
+ action: (opts.action
3398
+ ? opts.action
3399
+ : undefined),
3400
+ },
3401
+ });
3402
+ });
3403
+
3404
+ /**
3405
+ * Initialize a log collection given the dce-mango Collection class
3406
+ * @author Gabe Abrams
3407
+ * @param Collection the Collection class from dce-mango
3408
+ * @returns initialized logCollection
3409
+ */
3410
+ const initLogCollection = (Collection) => {
3411
+ return new Collection('Log', {
3412
+ uniqueIndexKey: 'id',
3413
+ indexKeys: [
3414
+ 'courseId',
3415
+ 'context',
3416
+ 'subcontext',
3417
+ 'tags',
3418
+ ],
3419
+ });
3420
+ };
3421
+
3234
3422
  /**
3235
3423
  * Days of the week
3236
3424
  * @author Gabe Abrams
@@ -3247,44 +3435,5 @@ var DayOfWeek;
3247
3435
  })(DayOfWeek || (DayOfWeek = {}));
3248
3436
  var DayOfWeek$1 = DayOfWeek;
3249
3437
 
3250
- /**
3251
- * Types of actions
3252
- * @author Gabe Abrams
3253
- */
3254
- var LogAction;
3255
- (function (LogAction) {
3256
- // Target was opened by the user (it was not on screen, but now it is)
3257
- LogAction["Open"] = "open";
3258
- // Target was closed by the user (it was on screen, but now it is not)
3259
- LogAction["Close"] = "close";
3260
- // Target was expanded by the user (it always remains on screen, but size was changed)
3261
- LogAction["Expand"] = "expand";
3262
- // Target was collapsed by the user (it always remains on screen, but size was changed)
3263
- LogAction["Collapse"] = "collapse";
3264
- // Target was viewed by the user (only for items that are not opened or closed, those must use Open/Close actions)
3265
- LogAction["View"] = "view";
3266
- // Target interrupted the user (popup, dialog, validation message, etc. appeared without user prompting)
3267
- LogAction["Interrupt"] = "interrupt";
3268
- // Target was created by the user (it did not exist before)
3269
- LogAction["Create"] = "create";
3270
- // Target was edited by the user (it existed and was changed)
3271
- LogAction["Edit"] = "edit";
3272
- // Target was deleted by the user (it existed and now it doesn't)
3273
- LogAction["Delete"] = "delete";
3274
- // Target was added by the user (it already existed and was added to another place)
3275
- LogAction["Add"] = "add";
3276
- // Target was removed by the user (it was removed from something but still exists)
3277
- LogAction["Remove"] = "remove";
3278
- // Target was activated by the user (click, check, tap, keypress, etc.)
3279
- LogAction["Activate"] = "activate";
3280
- // Target was deactivated by the user (click away, uncheck, tap outside of, tab away, etc.)
3281
- LogAction["Deactivate"] = "deactivate";
3282
- // User showed interest in a target (hover, peek, etc.)
3283
- LogAction["Peek"] = "peek";
3284
- // Unknown action
3285
- LogAction["Unknown"] = "unknown";
3286
- })(LogAction || (LogAction = {}));
3287
- var LogAction$1 = LogAction;
3288
-
3289
- export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initServer, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
3438
+ export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek$1 as DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, LogAction$1 as LogAction, LogBuiltInMetadata, LogSource$1 as LogSource, LogType$1 as LogType, MINUTE_IN_MS, Modal, ModalButtonType$1 as ModalButtonType, ModalSize$1 as ModalSize, ModalType$1 as ModalType, ParamType$1 as ParamType, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode$1 as ReactKitErrorCode, SimpleDateChooser, TabBox, Variant$1 as Variant, abbreviate, alert$1 as alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initLogCollection, initServer, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
3290
3439
  //# sourceMappingURL=index.js.map