dce-reactkit 3.1.20 → 3.2.0-beta.2
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 +405 -19
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/dist/cjs/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/cjs/types/helpers/genRouteHandler.d.ts +2 -0
- package/dist/cjs/types/helpers/logClientEvent.d.ts +7 -0
- package/dist/cjs/types/helpers/parseUserAgent.d.ts +17 -0
- package/dist/cjs/types/index.d.ts +6 -1
- package/dist/cjs/types/server/initServer.d.ts +13 -0
- package/dist/cjs/types/types/Log/LogMainInfo.d.ts +37 -0
- package/dist/cjs/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
- package/dist/cjs/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
- package/dist/cjs/types/types/Log/index.d.ts +10 -0
- package/dist/cjs/types/types/LogAction.d.ts +22 -0
- package/dist/cjs/types/types/LogFunction.d.ts +24 -0
- package/dist/cjs/types/types/LogSource.d.ts +9 -0
- package/dist/cjs/types/types/LogType.d.ts +9 -0
- package/dist/esm/index.js +402 -20
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/constants/LOG_ROUTE_PATH.d.ts +6 -0
- package/dist/esm/types/constants/ROUTE_PATH_PREFIX.d.ts +6 -0
- package/dist/esm/types/helpers/genRouteHandler.d.ts +2 -0
- package/dist/esm/types/helpers/logClientEvent.d.ts +7 -0
- package/dist/esm/types/helpers/parseUserAgent.d.ts +17 -0
- package/dist/esm/types/index.d.ts +6 -1
- package/dist/esm/types/server/initServer.d.ts +13 -0
- package/dist/esm/types/types/Log/LogMainInfo.d.ts +37 -0
- package/dist/esm/types/types/Log/LogSourceSpecificInfo.d.ts +13 -0
- package/dist/esm/types/types/Log/LogTypeSpecificInfo.d.ts +17 -0
- package/dist/esm/types/types/Log/index.d.ts +10 -0
- package/dist/esm/types/types/LogAction.d.ts +22 -0
- package/dist/esm/types/types/LogFunction.d.ts +24 -0
- package/dist/esm/types/types/LogSource.d.ts +9 -0
- package/dist/esm/types/types/LogType.d.ts +9 -0
- package/dist/index.d.ts +147 -1
- package/package.json +1 -1
- package/src/components/AppWrapper.tsx +1 -1
- package/src/constants/LOG_ROUTE_PATH.ts +9 -0
- package/src/constants/ROUTE_PATH_PREFIX.ts +7 -0
- package/src/helpers/genRouteHandler.ts +137 -1
- package/src/helpers/logClientEvent.tsx +58 -0
- package/src/helpers/parseUserAgent.ts +108 -0
- package/src/index.ts +10 -0
- package/src/server/initServer.ts +90 -0
- package/src/types/Log/LogMainInfo.ts +64 -0
- package/src/types/Log/LogSourceSpecificInfo.ts +25 -0
- package/src/types/Log/LogTypeSpecificInfo.ts +33 -0
- package/src/types/Log/index.ts +17 -0
- package/src/types/LogAction.ts +38 -0
- package/src/types/LogFunction.ts +41 -0
- package/src/types/LogSource.ts +12 -0
- package/src/types/LogType.ts +12 -0
package/dist/index.d.ts
CHANGED
|
@@ -515,6 +515,139 @@ declare enum ParamType {
|
|
|
515
515
|
StringOptional = "string-optional"
|
|
516
516
|
}
|
|
517
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Main information in a log event
|
|
520
|
+
* @author Gabe Abrams
|
|
521
|
+
*/
|
|
522
|
+
declare type LogMainInfo = {
|
|
523
|
+
id: string;
|
|
524
|
+
userFirstName: string;
|
|
525
|
+
userLastName: string;
|
|
526
|
+
userEmail: string;
|
|
527
|
+
userId: number;
|
|
528
|
+
isLearner: boolean;
|
|
529
|
+
isAdmin: boolean;
|
|
530
|
+
isTTM: boolean;
|
|
531
|
+
courseId: number;
|
|
532
|
+
courseName: string;
|
|
533
|
+
browser: {
|
|
534
|
+
name: string;
|
|
535
|
+
version: string;
|
|
536
|
+
};
|
|
537
|
+
device: {
|
|
538
|
+
os: string;
|
|
539
|
+
isMobile: boolean;
|
|
540
|
+
};
|
|
541
|
+
year: number;
|
|
542
|
+
month: number;
|
|
543
|
+
day: number;
|
|
544
|
+
hour: number;
|
|
545
|
+
minute: number;
|
|
546
|
+
timestamp: number;
|
|
547
|
+
category: string;
|
|
548
|
+
subcategory: string;
|
|
549
|
+
tags: string[];
|
|
550
|
+
metadata?: {
|
|
551
|
+
[k: string]: any;
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
|
|
555
|
+
/**
|
|
556
|
+
* Source of a log event
|
|
557
|
+
* @author Gabe Abrams
|
|
558
|
+
*/
|
|
559
|
+
declare enum LogSource {
|
|
560
|
+
Client = "client",
|
|
561
|
+
Server = "server"
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Log info that is specific to the type of source
|
|
566
|
+
* @author Gabe Abrams
|
|
567
|
+
*/
|
|
568
|
+
declare type LogSourceSpecificInfo = ({
|
|
569
|
+
source: LogSource.Client;
|
|
570
|
+
} | {
|
|
571
|
+
source: LogSource.Server;
|
|
572
|
+
routePath: string;
|
|
573
|
+
routeTemplate: string;
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Types of actions
|
|
578
|
+
* @author Gabe Abrams
|
|
579
|
+
*/
|
|
580
|
+
declare enum LogAction {
|
|
581
|
+
Open = "open",
|
|
582
|
+
Close = "close",
|
|
583
|
+
Expand = "expand",
|
|
584
|
+
Collapse = "collapse",
|
|
585
|
+
View = "view",
|
|
586
|
+
Interrupt = "interrupt",
|
|
587
|
+
Create = "create",
|
|
588
|
+
Edit = "edit",
|
|
589
|
+
Delete = "delete",
|
|
590
|
+
Add = "add",
|
|
591
|
+
Remove = "remove",
|
|
592
|
+
Activate = "activate",
|
|
593
|
+
Deactivate = "deactivate",
|
|
594
|
+
Peek = "peek",
|
|
595
|
+
Unknown = "unknown"
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Type of a log event
|
|
600
|
+
* @author Gabe Abrams
|
|
601
|
+
*/
|
|
602
|
+
declare enum LogType {
|
|
603
|
+
Action = "action",
|
|
604
|
+
Error = "error"
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Log info that is specific to the type of log
|
|
609
|
+
* @author Gabe Abrams
|
|
610
|
+
*/
|
|
611
|
+
declare type LogTypeSpecificInfo = ({
|
|
612
|
+
type: LogType.Error;
|
|
613
|
+
errorMessage: string;
|
|
614
|
+
errorCode: string;
|
|
615
|
+
errorStack: string;
|
|
616
|
+
} | {
|
|
617
|
+
type: LogType.Action;
|
|
618
|
+
target: string;
|
|
619
|
+
action: LogAction;
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
/**
|
|
623
|
+
* A single log event corresponding to an action performed by a user or an
|
|
624
|
+
* error encountered by a user
|
|
625
|
+
* @author Gabe Abrams
|
|
626
|
+
*/
|
|
627
|
+
declare type Log = (LogMainInfo & LogSourceSpecificInfo & LogTypeSpecificInfo);
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Type of a log action function
|
|
631
|
+
* @author Gabe Abrams
|
|
632
|
+
*/
|
|
633
|
+
declare type LogFunction = (opts: ({
|
|
634
|
+
category: string | {
|
|
635
|
+
name: string;
|
|
636
|
+
};
|
|
637
|
+
subcategory?: string | {
|
|
638
|
+
name: string;
|
|
639
|
+
};
|
|
640
|
+
tags?: string[];
|
|
641
|
+
metadata?: {
|
|
642
|
+
[k: string]: any;
|
|
643
|
+
};
|
|
644
|
+
} & ({
|
|
645
|
+
error: any;
|
|
646
|
+
} | {
|
|
647
|
+
target: string;
|
|
648
|
+
action: LogAction;
|
|
649
|
+
}))) => Promise<Log>;
|
|
650
|
+
|
|
518
651
|
/**
|
|
519
652
|
* Generate an express API route handler
|
|
520
653
|
* @author Gabe Abrams
|
|
@@ -556,6 +689,7 @@ declare const genRouteHandler: (opts: {
|
|
|
556
689
|
pageTitle?: string | undefined;
|
|
557
690
|
status?: number | undefined;
|
|
558
691
|
} | undefined) => void;
|
|
692
|
+
logServerEvent: LogFunction;
|
|
559
693
|
}) => any;
|
|
560
694
|
skipSessionCheck?: boolean | undefined;
|
|
561
695
|
}) => (req: any, res: any, next: () => void) => Promise<undefined>;
|
|
@@ -594,10 +728,16 @@ declare type GetLaunchInfoFunction = (req: any) => {
|
|
|
594
728
|
* Prepare dce-reactkit to run on the server
|
|
595
729
|
* @author Gabe Abrams
|
|
596
730
|
* @param opts object containing all arguments
|
|
731
|
+
* @param opts.app express app from inside of the postprocessor function that
|
|
732
|
+
* we will add routes to
|
|
597
733
|
* @param opts.getLaunchInfo CACCL LTI's get launch info function
|
|
734
|
+
* @param [opts.logCollection] mongo collection from dce-mango to use for
|
|
735
|
+
* storing logs. If none is included, logs are written to the console
|
|
598
736
|
*/
|
|
599
737
|
declare const initServer: (opts: {
|
|
738
|
+
app: any;
|
|
600
739
|
getLaunchInfo: GetLaunchInfoFunction;
|
|
740
|
+
logCollection?: any;
|
|
601
741
|
}) => void;
|
|
602
742
|
|
|
603
743
|
/**
|
|
@@ -696,6 +836,12 @@ declare const onlyKeepLetters: (str: string) => string;
|
|
|
696
836
|
*/
|
|
697
837
|
declare const parallelLimit: (taskFunctions: (() => Promise<any>)[], limit?: number | undefined) => Promise<any[]>;
|
|
698
838
|
|
|
839
|
+
/**
|
|
840
|
+
* Log a user action on the client (cannot be used on the server)
|
|
841
|
+
* @author Gabe Abrams
|
|
842
|
+
*/
|
|
843
|
+
declare const logClientEvent: LogFunction;
|
|
844
|
+
|
|
699
845
|
/**
|
|
700
846
|
* List of error codes built into the react kit
|
|
701
847
|
* @author Gabe Abrams
|
|
@@ -727,4 +873,4 @@ declare enum DayOfWeek {
|
|
|
727
873
|
Sunday = "u"
|
|
728
874
|
}
|
|
729
875
|
|
|
730
|
-
export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, 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 };
|
|
876
|
+
export { AppWrapper, ButtonInputGroup, CheckboxButton, CopiableBox, DAY_IN_MS, DayOfWeek, Drawer, ErrorBox, ErrorWithCode, HOUR_IN_MS, ItemPicker, LoadingSpinner, Log, LogAction, LogSource, LogType, MINUTE_IN_MS, Modal, ModalButtonType, ModalSize, ModalType, ParamType, PickableItem, PopFailureMark, PopPendingMark, PopSuccessMark, RadioButton, ReactKitErrorCode, SimpleDateChooser, TabBox, Variant, abbreviate, alert, avg, ceilToNumDecimals, confirm, floorToNumDecimals, forceNumIntoBounds, genRouteHandler, getHumanReadableDate, getOrdinal, getPartOfDay, getTimeInfoInET, handleError, handleSuccess, initServer, logClientEvent, onlyKeepLetters, padDecimalZeros, padZerosLeft, parallelLimit, roundToNumDecimals, showFatalError, startMinWait, stringsToHumanReadableList, stubServerEndpoint, sum, visitServerEndpoint, waitMs };
|
package/package.json
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// Import caccl functions
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
cacclGetLaunchInfo,
|
|
4
|
+
internalGetLogCollection,
|
|
5
|
+
} from '../server/initServer';
|
|
3
6
|
|
|
4
7
|
// Import shared types
|
|
5
8
|
import ReactKitErrorCode from '../types/ReactKitErrorCode';
|
|
@@ -9,6 +12,17 @@ import ParamType from '../types/ParamType';
|
|
|
9
12
|
import handleError from './handleError';
|
|
10
13
|
import handleSuccess from './handleSuccess';
|
|
11
14
|
import genErrorPage from '../html/genErrorPage';
|
|
15
|
+
import parseUserAgent from './parseUserAgent';
|
|
16
|
+
import getTimeInfoInET from './getTimeInfoInET';
|
|
17
|
+
|
|
18
|
+
// Import shared types
|
|
19
|
+
import LogFunction from '../types/LogFunction';
|
|
20
|
+
import Log from '../types/Log';
|
|
21
|
+
import LogType from '../types/LogType';
|
|
22
|
+
import LogSource from '../types/LogSource';
|
|
23
|
+
import LogTypeSpecificInfo from '../types/Log/LogTypeSpecificInfo';
|
|
24
|
+
import LogMainInfo from '../types/Log/LogMainInfo';
|
|
25
|
+
import LogSourceSpecificInfo from '../types/Log/LogSourceSpecificInfo';
|
|
12
26
|
|
|
13
27
|
/**
|
|
14
28
|
* Generate an express API route handler
|
|
@@ -55,6 +69,7 @@ const genRouteHandler = (
|
|
|
55
69
|
status?: number,
|
|
56
70
|
},
|
|
57
71
|
) => void,
|
|
72
|
+
logServerEvent: LogFunction,
|
|
58
73
|
},
|
|
59
74
|
) => any,
|
|
60
75
|
skipSessionCheck?: boolean,
|
|
@@ -439,6 +454,126 @@ const genRouteHandler = (
|
|
|
439
454
|
);
|
|
440
455
|
}
|
|
441
456
|
|
|
457
|
+
/*----------------------------------------*/
|
|
458
|
+
/* Log Handler */
|
|
459
|
+
/*----------------------------------------*/
|
|
460
|
+
|
|
461
|
+
// Create a log handler function
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Log an event on the server
|
|
465
|
+
* @author Gabe Abrams
|
|
466
|
+
*/
|
|
467
|
+
const logServerEvent: LogFunction = async (opts) => {
|
|
468
|
+
// NOTE: internally, we slip through an opts.overrideAsClientEvent boolean
|
|
469
|
+
// that indicates that this is actually a client event, but we don't
|
|
470
|
+
// include that in the LogFunction type because this is internal and
|
|
471
|
+
// hidden from users
|
|
472
|
+
|
|
473
|
+
// Parse user agent
|
|
474
|
+
const {
|
|
475
|
+
browser,
|
|
476
|
+
device,
|
|
477
|
+
} = parseUserAgent(req.headers['user-agent']);
|
|
478
|
+
|
|
479
|
+
// Get time info in ET
|
|
480
|
+
const {
|
|
481
|
+
timestamp,
|
|
482
|
+
year,
|
|
483
|
+
month,
|
|
484
|
+
day,
|
|
485
|
+
hour,
|
|
486
|
+
minute,
|
|
487
|
+
} = getTimeInfoInET();
|
|
488
|
+
|
|
489
|
+
// Main log info
|
|
490
|
+
const mainLogInfo: LogMainInfo = {
|
|
491
|
+
id: `${launchInfo.userId}-${Date.now()}-${Math.floor(Math.random() * 100000)}-${Math.floor(Math.random() * 100000)}`,
|
|
492
|
+
userFirstName: launchInfo.userFirstName,
|
|
493
|
+
userLastName: launchInfo.userLastName,
|
|
494
|
+
userEmail: launchInfo.userEmail,
|
|
495
|
+
userId: launchInfo.userId,
|
|
496
|
+
isLearner: !!launchInfo.isLearner,
|
|
497
|
+
isAdmin: !!launchInfo.isAdmin,
|
|
498
|
+
isTTM: !!launchInfo.isTTM,
|
|
499
|
+
courseId: launchInfo.courseId,
|
|
500
|
+
courseName: launchInfo.courseName,
|
|
501
|
+
browser,
|
|
502
|
+
device,
|
|
503
|
+
year,
|
|
504
|
+
month,
|
|
505
|
+
day,
|
|
506
|
+
hour,
|
|
507
|
+
minute,
|
|
508
|
+
timestamp,
|
|
509
|
+
category: (
|
|
510
|
+
typeof opts.category === 'string'
|
|
511
|
+
? opts.category
|
|
512
|
+
: opts.category.name
|
|
513
|
+
),
|
|
514
|
+
subcategory: (
|
|
515
|
+
typeof opts.category === 'string'
|
|
516
|
+
? opts.subcategory
|
|
517
|
+
: ((opts.subcategory as any) ?? { name: 'none' }).name
|
|
518
|
+
),
|
|
519
|
+
tags: opts.tags ?? [],
|
|
520
|
+
metadata: opts.metadata ?? {},
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
// Type-specific info
|
|
524
|
+
const typeSpecificInfo: LogTypeSpecificInfo = (
|
|
525
|
+
('error' in opts && opts.error)
|
|
526
|
+
? {
|
|
527
|
+
type: LogType.Error,
|
|
528
|
+
errorMessage: opts.error.message ?? 'Unknown message',
|
|
529
|
+
errorCode: opts.error.code ?? ReactKitErrorCode.NoCode,
|
|
530
|
+
errorStack: opts.error.stack ?? 'No stack',
|
|
531
|
+
}
|
|
532
|
+
: {
|
|
533
|
+
type: LogType.Action,
|
|
534
|
+
target: (opts as any).target ?? 'Unknown',
|
|
535
|
+
action: (opts as any).action ?? 'Unknown'
|
|
536
|
+
}
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
// Source-specific info
|
|
540
|
+
const sourceSpecificInfo: LogSourceSpecificInfo = (
|
|
541
|
+
(opts as any).overrideAsClientEvent
|
|
542
|
+
? {
|
|
543
|
+
source: LogSource.Client,
|
|
544
|
+
}
|
|
545
|
+
: {
|
|
546
|
+
source: LogSource.Server,
|
|
547
|
+
routePath: req.path,
|
|
548
|
+
routeTemplate: req.route.path,
|
|
549
|
+
}
|
|
550
|
+
);
|
|
551
|
+
|
|
552
|
+
// Build log event
|
|
553
|
+
const log: Log = {
|
|
554
|
+
...mainLogInfo,
|
|
555
|
+
...typeSpecificInfo,
|
|
556
|
+
...sourceSpecificInfo,
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
// Either print to console or save to db
|
|
560
|
+
const logCollection = internalGetLogCollection();
|
|
561
|
+
if (logCollection) {
|
|
562
|
+
// Store to the log collection
|
|
563
|
+
await logCollection.insert(log);
|
|
564
|
+
} else {
|
|
565
|
+
// Print to console
|
|
566
|
+
if (log.type === LogType.Error) {
|
|
567
|
+
console.error('dce-reactkit error log:', log);
|
|
568
|
+
} else {
|
|
569
|
+
console.log('dce-reactkit action log:', log);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// Return log entry
|
|
574
|
+
return log;
|
|
575
|
+
};
|
|
576
|
+
|
|
442
577
|
/*------------------------------------------------------------------------*/
|
|
443
578
|
/* Call handler */
|
|
444
579
|
/*------------------------------------------------------------------------*/
|
|
@@ -504,6 +639,7 @@ const genRouteHandler = (
|
|
|
504
639
|
},
|
|
505
640
|
redirect,
|
|
506
641
|
renderErrorPage,
|
|
642
|
+
logServerEvent,
|
|
507
643
|
});
|
|
508
644
|
|
|
509
645
|
// Send results to client (only if next wasn't called)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Import shared types
|
|
2
|
+
import LOG_ROUTE_PATH from '../constants/LOG_ROUTE_PATH';
|
|
3
|
+
import LogFunction from '../types/LogFunction';
|
|
4
|
+
|
|
5
|
+
// Import shared functions
|
|
6
|
+
import visitServerEndpoint from './visitServerEndpoint';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Log a user action on the client (cannot be used on the server)
|
|
10
|
+
* @author Gabe Abrams
|
|
11
|
+
*/
|
|
12
|
+
const logClientEvent: LogFunction = async (opts) => {
|
|
13
|
+
return visitServerEndpoint({
|
|
14
|
+
path: LOG_ROUTE_PATH,
|
|
15
|
+
method: 'POST',
|
|
16
|
+
params: {
|
|
17
|
+
category: (
|
|
18
|
+
typeof opts.category === 'string'
|
|
19
|
+
? opts.category
|
|
20
|
+
: opts.category.name
|
|
21
|
+
),
|
|
22
|
+
subcategory: (
|
|
23
|
+
typeof opts.category === 'string'
|
|
24
|
+
? opts.subcategory
|
|
25
|
+
: ((opts.subcategory as any) ?? { name: 'none' }).name
|
|
26
|
+
),
|
|
27
|
+
tags: JSON.stringify(opts.tags),
|
|
28
|
+
metadata: JSON.stringify(opts.metadata ?? {}),
|
|
29
|
+
errorMessage: (
|
|
30
|
+
(opts as any).error
|
|
31
|
+
? (opts as any).error.message
|
|
32
|
+
: undefined
|
|
33
|
+
),
|
|
34
|
+
errorCode: (
|
|
35
|
+
(opts as any).error
|
|
36
|
+
? (opts as any).error.code
|
|
37
|
+
: undefined
|
|
38
|
+
),
|
|
39
|
+
errorStack: (
|
|
40
|
+
(opts as any).error
|
|
41
|
+
? (opts as any).error.stack
|
|
42
|
+
: undefined
|
|
43
|
+
),
|
|
44
|
+
target: (
|
|
45
|
+
(opts as any).target
|
|
46
|
+
? (opts as any).target
|
|
47
|
+
: undefined
|
|
48
|
+
),
|
|
49
|
+
action: (
|
|
50
|
+
(opts as any).action
|
|
51
|
+
? (opts as any).action
|
|
52
|
+
: undefined
|
|
53
|
+
),
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default logClientEvent;
|
|
@@ -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,7 @@ 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';
|
|
51
52
|
|
|
52
53
|
// Import types
|
|
53
54
|
import ModalButtonType from './types/ModalButtonType';
|
|
@@ -57,6 +58,10 @@ import ReactKitErrorCode from './types/ReactKitErrorCode';
|
|
|
57
58
|
import Variant from './types/Variant';
|
|
58
59
|
import ParamType from './types/ParamType';
|
|
59
60
|
import DayOfWeek from './types/DayOfWeek';
|
|
61
|
+
import Log from './types/Log';
|
|
62
|
+
import LogType from './types/LogType';
|
|
63
|
+
import LogSource from './types/LogSource';
|
|
64
|
+
import LogAction from './types/LogAction';
|
|
60
65
|
|
|
61
66
|
// Component-specific-types
|
|
62
67
|
import PickableItem from './components/ItemPicker/types/PickableItem';
|
|
@@ -111,6 +116,7 @@ export {
|
|
|
111
116
|
parallelLimit,
|
|
112
117
|
// Client helpers
|
|
113
118
|
visitServerEndpoint,
|
|
119
|
+
logClientEvent,
|
|
114
120
|
// Server helpers
|
|
115
121
|
initServer,
|
|
116
122
|
genRouteHandler,
|
|
@@ -123,6 +129,10 @@ export {
|
|
|
123
129
|
ReactKitErrorCode,
|
|
124
130
|
Variant,
|
|
125
131
|
DayOfWeek,
|
|
132
|
+
Log,
|
|
133
|
+
LogType,
|
|
134
|
+
LogSource,
|
|
135
|
+
LogAction,
|
|
126
136
|
// Component-specific-types
|
|
127
137
|
PickableItem,
|
|
128
138
|
// Server types
|