dce-reactkit 3.1.20 → 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.
Files changed (59) hide show
  1. package/.vscode/settings.json +6 -0
  2. package/dist/cjs/index.js +517 -21
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
  5. package/dist/cjs/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
  6. package/dist/cjs/types/helpers/genRouteHandler.d.ts +2 -0
  7. package/dist/cjs/types/helpers/logClientEvent.d.ts +7 -0
  8. package/dist/cjs/types/helpers/parseUserAgent.d.ts +17 -0
  9. package/dist/cjs/types/index.d.ts +8 -1
  10. package/dist/cjs/types/server/initLogCollection.d.ts +8 -0
  11. package/dist/cjs/types/server/initServer.d.ts +13 -0
  12. package/dist/cjs/types/types/Log/LogMainInfo.d.ts +37 -0
  13. package/dist/cjs/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
  14. package/dist/cjs/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
  15. package/dist/cjs/types/types/Log/index.d.ts +10 -0
  16. package/dist/cjs/types/types/LogAction.d.ts +23 -0
  17. package/dist/cjs/types/types/LogBuiltInMetadata.d.ts +15 -0
  18. package/dist/cjs/types/types/LogFunction.d.ts +24 -0
  19. package/dist/cjs/types/types/LogSource.d.ts +9 -0
  20. package/dist/cjs/types/types/LogType.d.ts +9 -0
  21. package/dist/esm/index.js +512 -22
  22. package/dist/esm/index.js.map +1 -1
  23. package/dist/esm/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
  24. package/dist/esm/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
  25. package/dist/esm/types/helpers/genRouteHandler.d.ts +2 -0
  26. package/dist/esm/types/helpers/logClientEvent.d.ts +7 -0
  27. package/dist/esm/types/helpers/parseUserAgent.d.ts +17 -0
  28. package/dist/esm/types/index.d.ts +8 -1
  29. package/dist/esm/types/server/initLogCollection.d.ts +8 -0
  30. package/dist/esm/types/server/initServer.d.ts +13 -0
  31. package/dist/esm/types/types/Log/LogMainInfo.d.ts +37 -0
  32. package/dist/esm/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
  33. package/dist/esm/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
  34. package/dist/esm/types/types/Log/index.d.ts +10 -0
  35. package/dist/esm/types/types/LogAction.d.ts +23 -0
  36. package/dist/esm/types/types/LogBuiltInMetadata.d.ts +15 -0
  37. package/dist/esm/types/types/LogFunction.d.ts +24 -0
  38. package/dist/esm/types/types/LogSource.d.ts +9 -0
  39. package/dist/esm/types/types/LogType.d.ts +9 -0
  40. package/dist/index.d.ts +171 -1
  41. package/package.json +1 -1
  42. package/src/components/AppWrapper.tsx +1 -1
  43. package/src/constants/LOG_ROUTE_PATH.ts +9 -0
  44. package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
  45. package/src/helpers/genRouteHandler.ts +233 -2
  46. package/src/helpers/logClientEvent.tsx +68 -0
  47. package/src/helpers/parseUserAgent.ts +108 -0
  48. package/src/index.ts +14 -0
  49. package/src/server/initLogCollection.ts +22 -0
  50. package/src/server/initServer.ts +90 -0
  51. package/src/types/Log/LogMainInfo.ts +64 -0
  52. package/src/types/Log/LogSourceSpecificInfo.ts +25 -0
  53. package/src/types/Log/LogTypeSpecificInfo.ts +33 -0
  54. package/src/types/Log/index.ts +17 -0
  55. package/src/types/LogAction.ts +40 -0
  56. package/src/types/LogBuiltInMetadata.ts +18 -0
  57. package/src/types/LogFunction.ts +43 -0
  58. package/src/types/LogSource.ts +12 -0
  59. package/src/types/LogType.ts +12 -0
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Perform a rudimentary parsing of the user's browser agent string
3
+ * @author Gabe Abrams
4
+ * @param userAgent the user's browser agent
5
+ * @returns user info
6
+ */
7
+ const parseUserAgent = (userAgent: string) => {
8
+ /* ------------- Browser ------------ */
9
+
10
+ let browser: { name: string, version: string } = {
11
+ name: 'Unknown',
12
+ version: 'Unknown',
13
+ };
14
+
15
+ // Parse user agent
16
+ let verOffset: number;
17
+ let nameOffset: number;
18
+ if ((verOffset = userAgent.indexOf('Opera')) !== -1) {
19
+ // In Opera, the true version is after 'Opera' or after 'Version'
20
+ browser = {
21
+ name: 'Opera',
22
+ version: userAgent.substring(verOffset + 6),
23
+ };
24
+ if ((verOffset = userAgent.indexOf('Version')) !== -1) {
25
+ browser.version = userAgent.substring(verOffset + 8);
26
+ }
27
+ } else if ((verOffset = userAgent.indexOf('MSIE')) !== -1) {
28
+ // In MSIE, the true version is after 'MSIE' in userAgent
29
+ browser = {
30
+ name: 'Internet Explorer',
31
+ version: userAgent.substring(verOffset + 5),
32
+ };
33
+ } else if ((verOffset = userAgent.indexOf('Chrome')) !== -1) {
34
+ // In Chrome, the true version is after 'Chrome'
35
+ browser = {
36
+ name: 'Chrome',
37
+ version: userAgent.substring(verOffset + 7),
38
+ };
39
+ } else if ((verOffset = userAgent.indexOf('Safari')) !== -1) {
40
+ // In Safari, the true version is after 'Safari' or after 'Version'
41
+ browser = {
42
+ name: 'Safari',
43
+ version: userAgent.substring(verOffset + 7),
44
+ };
45
+ if ((verOffset = userAgent.indexOf('Version')) !== -1) {
46
+ browser.version = userAgent.substring(verOffset + 8);
47
+ }
48
+ } else if ((verOffset = userAgent.indexOf('Firefox')) != -1) {
49
+ // In Firefox, the true version is after 'Firefox'
50
+ browser = {
51
+ name: 'Firefox',
52
+ version: userAgent.substring(verOffset + 8),
53
+ };
54
+ } else if (
55
+ (nameOffset = userAgent.lastIndexOf(' ') + 1)
56
+ < (verOffset = userAgent.lastIndexOf('/'))
57
+ ) {
58
+ browser = {
59
+ name: userAgent.substring(nameOffset, verOffset),
60
+ version: userAgent.substring(verOffset + 1),
61
+ };
62
+ }
63
+
64
+ // Postprocess version
65
+ // trim the fullVersion string at semicolon/space if present
66
+ let ix: number;
67
+ if ((ix = browser.version.indexOf(';')) !== -1) {
68
+ browser.version = browser.version.substring(0, ix);
69
+ }
70
+ if ((ix = browser.version.indexOf(' ')) !== -1) {
71
+ browser.version = browser.version.substring(0, ix);
72
+ }
73
+
74
+ /* ------------- Device ------------- */
75
+
76
+ // Detect os
77
+ let os = 'Unknown';
78
+ if (userAgent.includes('Linux')) {
79
+ os = 'Linux';
80
+ } else if (userAgent.includes('like Mac')) {
81
+ os = 'iOS';
82
+ } else if (userAgent.includes('Mac')) {
83
+ os = 'Mac';
84
+ } else if (userAgent.includes('Android')) {
85
+ os = 'Android';
86
+ } else if (userAgent.includes('Win')) {
87
+ os = 'Win';
88
+ }
89
+
90
+ // Check if mobile
91
+ const isMobile = !!userAgent.match(/(iPhone|iPod|iPad|Android|BlackBerry)/);
92
+
93
+ // Device
94
+ const device = {
95
+ isMobile,
96
+ os,
97
+ };
98
+
99
+ /* ------------- Finish ------------- */
100
+
101
+ // Return info
102
+ return {
103
+ browser,
104
+ device,
105
+ };
106
+ };
107
+
108
+ export default parseUserAgent;
package/src/index.ts CHANGED
@@ -48,6 +48,8 @@ import getPartOfDay from './helpers/getPartOfDay';
48
48
  import stringsToHumanReadableList from './helpers/stringsToHumanReadableList';
49
49
  import onlyKeepLetters from './helpers/onlyKeepLetters';
50
50
  import parallelLimit from './helpers/parallelLimit';
51
+ import logClientEvent from './helpers/logClientEvent';
52
+ import initLogCollection from './server/initLogCollection';
51
53
 
52
54
  // Import types
53
55
  import ModalButtonType from './types/ModalButtonType';
@@ -57,6 +59,11 @@ import ReactKitErrorCode from './types/ReactKitErrorCode';
57
59
  import Variant from './types/Variant';
58
60
  import ParamType from './types/ParamType';
59
61
  import DayOfWeek from './types/DayOfWeek';
62
+ import Log from './types/Log';
63
+ import LogType from './types/LogType';
64
+ import LogSource from './types/LogSource';
65
+ import LogAction from './types/LogAction';
66
+ import LogBuiltInMetadata from './types/LogBuiltInMetadata';
60
67
 
61
68
  // Component-specific-types
62
69
  import PickableItem from './components/ItemPicker/types/PickableItem';
@@ -111,11 +118,13 @@ export {
111
118
  parallelLimit,
112
119
  // Client helpers
113
120
  visitServerEndpoint,
121
+ logClientEvent,
114
122
  // Server helpers
115
123
  initServer,
116
124
  genRouteHandler,
117
125
  handleError,
118
126
  handleSuccess,
127
+ initLogCollection,
119
128
  // Types
120
129
  ModalButtonType,
121
130
  ModalSize,
@@ -123,6 +132,11 @@ export {
123
132
  ReactKitErrorCode,
124
133
  Variant,
125
134
  DayOfWeek,
135
+ Log,
136
+ LogType,
137
+ LogSource,
138
+ LogAction,
139
+ LogBuiltInMetadata,
126
140
  // Component-specific-types
127
141
  PickableItem,
128
142
  // Server types
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Initialize a log collection given the dce-mango Collection class
3
+ * @author Gabe Abrams
4
+ * @param Collection the Collection class from dce-mango
5
+ * @returns initialized logCollection
6
+ */
7
+ const initLogCollection = (Collection: any) => {
8
+ return new Collection(
9
+ 'Log',
10
+ {
11
+ uniqueIndexKey: 'id',
12
+ indexKeys: [
13
+ 'courseId',
14
+ 'context',
15
+ 'subcontext',
16
+ 'tags',
17
+ ],
18
+ },
19
+ );
20
+ };
21
+
22
+ export default initLogCollection;
@@ -1,5 +1,12 @@
1
1
  // Import custom error
2
+ import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
2
3
  import ErrorWithCode from '../errors/ErrorWithCode';
4
+
5
+ // Import shared helpers
6
+ import genRouteHandler from '../helpers/genRouteHandler';
7
+ import ParamType from '../types/ParamType';
8
+
9
+ // Import shared types
3
10
  import ReactKitErrorCode from '../types/ReactKitErrorCode';
4
11
 
5
12
  // Types
@@ -11,6 +18,9 @@ type GetLaunchInfoFunction = (req: any) => {
11
18
  // Stored copy of caccl functions
12
19
  let _cacclGetLaunchInfo: GetLaunchInfoFunction;
13
20
 
21
+ // Stored copy of dce-mango log collection
22
+ let _logCollection: any;
23
+
14
24
  /*------------------------------------------------------------------------*/
15
25
  /* Helpers */
16
26
  /*------------------------------------------------------------------------*/
@@ -32,6 +42,16 @@ export const cacclGetLaunchInfo: GetLaunchInfoFunction = (req: any) => {
32
42
  return _cacclGetLaunchInfo(req);
33
43
  };
34
44
 
45
+ /**
46
+ * Get log collection
47
+ * @author Gabe Abrams
48
+ * @returns log collection if one was included during launch or null if we don't
49
+ * have a log collection (yet)
50
+ */
51
+ export const internalGetLogCollection = () => {
52
+ return _logCollection ?? null;
53
+ };
54
+
35
55
  /*------------------------------------------------------------------------*/
36
56
  /* Main */
37
57
  /*------------------------------------------------------------------------*/
@@ -40,14 +60,84 @@ export const cacclGetLaunchInfo: GetLaunchInfoFunction = (req: any) => {
40
60
  * Prepare dce-reactkit to run on the server
41
61
  * @author Gabe Abrams
42
62
  * @param opts object containing all arguments
63
+ * @param opts.app express app from inside of the postprocessor function that
64
+ * we will add routes to
43
65
  * @param opts.getLaunchInfo CACCL LTI's get launch info function
66
+ * @param [opts.logCollection] mongo collection from dce-mango to use for
67
+ * storing logs. If none is included, logs are written to the console
44
68
  */
45
69
  const initServer = (
46
70
  opts: {
71
+ app: any,
47
72
  getLaunchInfo: GetLaunchInfoFunction,
73
+ logCollection?: any,
48
74
  },
49
75
  ) => {
50
76
  _cacclGetLaunchInfo = opts.getLaunchInfo;
77
+ _logCollection = opts.logCollection;
78
+
79
+ /**
80
+ * Log an event
81
+ * @author Gabe Abrams
82
+ * @param {string} context Context of the event (each app determines how to
83
+ * organize its contexts)
84
+ * @param {string} subcontext Subcontext of the event (each app determines
85
+ * how to organize its subcontexts)
86
+ * @param {string} tags stringified list of tags that apply to this action
87
+ * (each app determines tag usage)
88
+ * @param {string} metadata stringified object containing optional custom metadata
89
+ * @param {string} [errorMessage] error message if type is an error
90
+ * @param {string} [errorCode] error code if type is an error
91
+ * @param {string} [errorStack] error stack if type is an error
92
+ * @param {string} [target] Target of the action (each app determines the list
93
+ * of targets) These are usually buttons, panels, elements, etc.
94
+ * @param {LogAction} [action] the type of action performed on the target
95
+ * @returns {Log}
96
+ */
97
+ opts.app.post(
98
+ LOG_ROUTE_PATH,
99
+ genRouteHandler({
100
+ paramTypes: {
101
+ context: ParamType.String,
102
+ subcontext: ParamType.String,
103
+ tags: ParamType.JSON,
104
+ metadata: ParamType.JSON,
105
+ errorMessage: ParamType.StringOptional,
106
+ errorCode: ParamType.StringOptional,
107
+ errorStack: ParamType.StringOptional,
108
+ target: ParamType.StringOptional,
109
+ action: ParamType.StringOptional,
110
+ },
111
+ handler: ({ params, logServerEvent }) => {
112
+ const log = logServerEvent(
113
+ (params.errorMessage || params.errorCode || params.errorStack)
114
+ // Error
115
+ ? {
116
+ context: params.context,
117
+ subcontext: params.subcontext,
118
+ tags: params.tags,
119
+ metadata: params.metadata,
120
+ error: {
121
+ message: params.errorMessage,
122
+ code: params.errorCode,
123
+ stack: params.errorStack,
124
+ },
125
+ }
126
+ // Action
127
+ : {
128
+ context: params.context,
129
+ subcontext: params.subcontext,
130
+ tags: params.tags,
131
+ metadata: params.metadata,
132
+ target: params.target,
133
+ action: params.action,
134
+ }
135
+ );
136
+
137
+ return log;
138
+ },
139
+ }),
140
+ );
51
141
  };
52
142
 
53
143
  export default initServer;
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Main information in a log event
3
+ * @author Gabe Abrams
4
+ */
5
+ type LogMainInfo = {
6
+ // Unique id of the log event
7
+ id: string,
8
+ // First name of the user
9
+ userFirstName: string,
10
+ // Last name of the user
11
+ userLastName: string,
12
+ // User email
13
+ userEmail: string,
14
+ // User Canvas Id
15
+ userId: number,
16
+ // If true, the user is a learner
17
+ isLearner: boolean,
18
+ // If true, the user is an admin
19
+ isAdmin: boolean,
20
+ // If true, the user is a ttm
21
+ isTTM: boolean,
22
+ // The id of the Canvas course that the user launched from
23
+ courseId: number,
24
+ // The name of the Canvas course
25
+ courseName: string,
26
+ // Browser info
27
+ browser: {
28
+ // Name of the browser
29
+ name: string,
30
+ // Version of the browser
31
+ version: string,
32
+ },
33
+ // Device info
34
+ device: {
35
+ // Name of the operating system
36
+ os: string,
37
+ // If true, device is a mobile device
38
+ isMobile: boolean,
39
+ },
40
+ // Calendar year that the event is from
41
+ year: number,
42
+ // Month that the event is from (1 = Jan, 12 = Dec)
43
+ month: number,
44
+ // Day of the month that the event is from
45
+ day: number,
46
+ // Hour of the day (24hr) when the event occurred
47
+ hour: number,
48
+ // Minute of the day when the event occurred
49
+ minute: number,
50
+ // Timestamp of event (ms since epoch)
51
+ timestamp: number,
52
+ // Context of the event (each app determines how to organize contexts)
53
+ context: string,
54
+ // Subcontext of the event (each app determines how to organize subcontexts)
55
+ subcontext: string,
56
+ // List of tags that apply to this action (each app determines tag usage)
57
+ tags: string[],
58
+ // Additional optional custom metadata
59
+ metadata?: {
60
+ [k: string]: any,
61
+ },
62
+ };
63
+
64
+ export default LogMainInfo;
@@ -0,0 +1,25 @@
1
+ // Import shared types
2
+ import LogSource from '../LogSource';
3
+
4
+ /**
5
+ * Log info that is specific to the type of source
6
+ * @author Gabe Abrams
7
+ */
8
+ type LogSourceSpecificInfo = (
9
+ // Client
10
+ | {
11
+ // Source of the event
12
+ source: LogSource.Client,
13
+ }
14
+ // Server
15
+ | {
16
+ // Source of the event
17
+ source: LogSource.Server,
18
+ // Route path (e.g. /api/admin/courses/53450/blocks)
19
+ routePath: string,
20
+ // Route template (e.g. /api/admin/courses/:courseId/blocks)
21
+ routeTemplate: string,
22
+ }
23
+ );
24
+
25
+ export default LogSourceSpecificInfo;
@@ -0,0 +1,33 @@
1
+ // Import shared types
2
+ import LogAction from '../LogAction';
3
+ import LogType from '../LogType';
4
+
5
+ /**
6
+ * Log info that is specific to the type of log
7
+ * @author Gabe Abrams
8
+ */
9
+ type LogTypeSpecificInfo = (
10
+ // Error
11
+ | {
12
+ // Type of the event
13
+ type: LogType.Error,
14
+ // The error message
15
+ errorMessage: string,
16
+ // The error code
17
+ errorCode: string,
18
+ // Error stack trace
19
+ errorStack: string,
20
+ }
21
+ // Action
22
+ | {
23
+ // Type of the event
24
+ type: LogType.Action,
25
+ // Target of the action (each app determines the list of targets)
26
+ // These are usually buttons, panels, elements, etc.
27
+ target: string,
28
+ // The type of action performed on the target
29
+ action: LogAction,
30
+ }
31
+ );
32
+
33
+ export default LogTypeSpecificInfo;
@@ -0,0 +1,17 @@
1
+ // Import shared types
2
+ import LogMainInfo from './LogMainInfo';
3
+ import LogSourceSpecificInfo from './LogSourceSpecificInfo';
4
+ import LogTypeSpecificInfo from './LogTypeSpecificInfo';
5
+
6
+ /**
7
+ * A single log event corresponding to an action performed by a user or an
8
+ * error encountered by a user
9
+ * @author Gabe Abrams
10
+ */
11
+ type Log = (
12
+ LogMainInfo
13
+ & LogSourceSpecificInfo
14
+ & LogTypeSpecificInfo
15
+ );
16
+
17
+ export default Log;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Types of actions
3
+ * @author Gabe Abrams
4
+ */
5
+ enum LogAction {
6
+ // Target was opened by the user (it was not on screen, but now it is)
7
+ Open = 'open',
8
+ // Target was closed by the user (it was on screen, but now it is not)
9
+ Close = 'close',
10
+ // Target was cancelled by the user (it was on closed without saving)
11
+ Cancel = 'cancel',
12
+ // Target was expanded by the user (it always remains on screen, but size was changed)
13
+ Expand = 'expand',
14
+ // Target was collapsed by the user (it always remains on screen, but size was changed)
15
+ Collapse = 'collapse',
16
+ // Target was viewed by the user (only for items that are not opened or closed, those must use Open/Close actions)
17
+ View = 'view',
18
+ // Target interrupted the user (popup, dialog, validation message, etc. appeared without user prompting)
19
+ Interrupt = 'interrupt',
20
+ // Target was created by the user (it did not exist before)
21
+ Create = 'create',
22
+ // Target was edited by the user (it existed and was changed)
23
+ Edit = 'edit',
24
+ // Target was deleted by the user (it existed and now it doesn't)
25
+ Delete = 'delete',
26
+ // Target was added by the user (it already existed and was added to another place)
27
+ Add = 'add',
28
+ // Target was removed by the user (it was removed from something but still exists)
29
+ Remove = 'remove',
30
+ // Target was activated by the user (click, check, tap, keypress, etc.)
31
+ Activate = 'activate',
32
+ // Target was deactivated by the user (click away, uncheck, tap outside of, tab away, etc.)
33
+ Deactivate = 'deactivate',
34
+ // User showed interest in a target (hover, peek, etc.)
35
+ Peek = 'peek',
36
+ // Unknown action
37
+ Unknown = 'unknown',
38
+ };
39
+
40
+ export default LogAction;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Built-in metadata for logs
3
+ * @author Gabe Abrams
4
+ */
5
+ const LogBuiltInMetadata = {
6
+ // Contexts
7
+ Context: {
8
+ Uncategorized: 'n/a',
9
+ ServerRenderedErrorPage: '_server-rendered-error-page',
10
+ ServerEndpointError: '_server-endpoint-error',
11
+ },
12
+ // Targets
13
+ Target: {
14
+ NoSpecificTarget: 'n/a',
15
+ },
16
+ };
17
+
18
+ export default LogBuiltInMetadata;
@@ -0,0 +1,43 @@
1
+ // Import shared types
2
+ import Log from './Log';
3
+ import LogAction from './LogAction';
4
+
5
+ /**
6
+ * Type of a log action function
7
+ * @author Gabe Abrams
8
+ */
9
+ type LogFunction = (
10
+ opts: (
11
+ // Shared info
12
+ {
13
+ // Context of the event (each app determines how to organize contexts)
14
+ context: string | { _: string },
15
+ // Subcontext of the event (each app determines how to organize subcontexts)
16
+ subcontext?: string | { _: string },
17
+ // List of tags that apply to this action (each app determines tag usage)
18
+ tags?: string[],
19
+ // Additional optional custom metadata
20
+ metadata?: {
21
+ [k: string]: any,
22
+ },
23
+ } & (
24
+ // Error
25
+ | {
26
+ // The error object to log
27
+ error: any,
28
+ }
29
+ // Action
30
+ | {
31
+ // The type of action performed
32
+ action: LogAction,
33
+ // Target of the action (each app determines the list of targets)
34
+ // These are usually buttons, panels, elements, etc.
35
+ // If no target is included, this should indicate that this is an action
36
+ // being performed on the whole feature (open/close/etc)
37
+ target?: string,
38
+ }
39
+ )
40
+ ),
41
+ ) => Promise<Log>;
42
+
43
+ export default LogFunction;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Source of a log event
3
+ * @author Gabe Abrams
4
+ */
5
+ enum LogSource {
6
+ // Client
7
+ Client = 'client',
8
+ // Server
9
+ Server = 'server',
10
+ };
11
+
12
+ export default LogSource;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Type of a log event
3
+ * @author Gabe Abrams
4
+ */
5
+ enum LogType {
6
+ // User action
7
+ Action = 'action',
8
+ // Error
9
+ Error = 'error',
10
+ };
11
+
12
+ export default LogType;