dce-reactkit 3.1.13 → 3.1.15

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.
@@ -7,10 +7,15 @@ import ParamType from '../types/ParamType';
7
7
  * included in the request (map: param name => type)
8
8
  * @param opts.handler function that processes the request
9
9
  * @returns express route handler that takes the following arguments:
10
- * params (map: param name => value), handleSuccess (function for handling
11
- * successful requests), handleError (function for handling failed requests),
12
- * req (express request object), res (express response object),
13
- * next (express next function). Params also has userId, userFirstName,
10
+ * params (map: param name => value),
11
+ * req (express request object),
12
+ * next (express next function),
13
+ * send (a function that sends a string to the client),
14
+ * redirect (takes a url and redirects the user to that url),
15
+ * renderErrorPage (shows a static error page to the user),
16
+ * and returns the value to send to the client as a JSON API response, or
17
+ * calls next() or redirect(...) or send(...) or renderErrorPage(...).
18
+ * Note: params also has userId, userFirstName,
14
19
  * userLastName, isLearner, isTTM, isAdmin, and any other variables that
15
20
  * are directly added to the session
16
21
  */
@@ -23,8 +28,16 @@ declare const genRouteHandler: (opts: {
23
28
  [k: string]: any;
24
29
  };
25
30
  req: any;
26
- res: any;
27
31
  next: () => void;
32
+ redirect: (pathOrURL: string) => void;
33
+ send: (text: string, status?: number | undefined) => void;
34
+ renderErrorPage: (opts?: {
35
+ title?: string | undefined;
36
+ description?: string | undefined;
37
+ code?: string | undefined;
38
+ pageTitle?: string | undefined;
39
+ status?: number | undefined;
40
+ } | undefined) => void;
28
41
  }) => any;
29
42
  }) => (req: any, res: any, next: () => void) => Promise<undefined>;
30
43
  export default genRouteHandler;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Generate a static error page
3
+ * @author Gabe Abrams
4
+ * @param opts object containing all arguments
5
+ * @param [opts.title=An Error Occurred] title of the error box
6
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
7
+ * a human-readable description of the error
8
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
9
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
10
+ * the title of the error
11
+ * @returns html of the page
12
+ */
13
+ declare const genErrorPage: (opts?: {
14
+ title?: string;
15
+ description?: string;
16
+ code?: string;
17
+ pageTitle?: string;
18
+ }) => string;
19
+ export default genErrorPage;
package/dist/index.d.ts CHANGED
@@ -510,10 +510,15 @@ declare enum ParamType {
510
510
  * included in the request (map: param name => type)
511
511
  * @param opts.handler function that processes the request
512
512
  * @returns express route handler that takes the following arguments:
513
- * params (map: param name => value), handleSuccess (function for handling
514
- * successful requests), handleError (function for handling failed requests),
515
- * req (express request object), res (express response object),
516
- * next (express next function). Params also has userId, userFirstName,
513
+ * params (map: param name => value),
514
+ * req (express request object),
515
+ * next (express next function),
516
+ * send (a function that sends a string to the client),
517
+ * redirect (takes a url and redirects the user to that url),
518
+ * renderErrorPage (shows a static error page to the user),
519
+ * and returns the value to send to the client as a JSON API response, or
520
+ * calls next() or redirect(...) or send(...) or renderErrorPage(...).
521
+ * Note: params also has userId, userFirstName,
517
522
  * userLastName, isLearner, isTTM, isAdmin, and any other variables that
518
523
  * are directly added to the session
519
524
  */
@@ -526,8 +531,16 @@ declare const genRouteHandler: (opts: {
526
531
  [k: string]: any;
527
532
  };
528
533
  req: any;
529
- res: any;
530
534
  next: () => void;
535
+ redirect: (pathOrURL: string) => void;
536
+ send: (text: string, status?: number | undefined) => void;
537
+ renderErrorPage: (opts?: {
538
+ title?: string | undefined;
539
+ description?: string | undefined;
540
+ code?: string | undefined;
541
+ pageTitle?: string | undefined;
542
+ status?: number | undefined;
543
+ } | undefined) => void;
531
544
  }) => any;
532
545
  }) => (req: any, res: any, next: () => void) => Promise<undefined>;
533
546
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dce-reactkit",
3
- "version": "3.1.13",
3
+ "version": "3.1.15",
4
4
  "description": "Shared components for Harvard DCE apps",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -8,6 +8,15 @@ import ParamType from '../types/ParamType';
8
8
  // Import helpers
9
9
  import handleError from './handleError';
10
10
  import handleSuccess from './handleSuccess';
11
+ import genErrorPage from '../html/genErrorPage';
12
+
13
+ /*------------------------------------------------------------------------*/
14
+ /* Helpers */
15
+ /*------------------------------------------------------------------------*/
16
+
17
+ /*------------------------------------------------------------------------*/
18
+ /* Main */
19
+ /*------------------------------------------------------------------------*/
11
20
 
12
21
  /**
13
22
  * Generate an express API route handler
@@ -17,10 +26,15 @@ import handleSuccess from './handleSuccess';
17
26
  * included in the request (map: param name => type)
18
27
  * @param opts.handler function that processes the request
19
28
  * @returns express route handler that takes the following arguments:
20
- * params (map: param name => value), handleSuccess (function for handling
21
- * successful requests), handleError (function for handling failed requests),
22
- * req (express request object), res (express response object),
23
- * next (express next function). Params also has userId, userFirstName,
29
+ * params (map: param name => value),
30
+ * req (express request object),
31
+ * next (express next function),
32
+ * send (a function that sends a string to the client),
33
+ * redirect (takes a url and redirects the user to that url),
34
+ * renderErrorPage (shows a static error page to the user),
35
+ * and returns the value to send to the client as a JSON API response, or
36
+ * calls next() or redirect(...) or send(...) or renderErrorPage(...).
37
+ * Note: params also has userId, userFirstName,
24
38
  * userLastName, isLearner, isTTM, isAdmin, and any other variables that
25
39
  * are directly added to the session
26
40
  */
@@ -35,8 +49,18 @@ const genRouteHandler = (
35
49
  [k: string]: any
36
50
  },
37
51
  req: any,
38
- res: any,
39
52
  next: () => void,
53
+ redirect: (pathOrURL: string) => void,
54
+ send: (text: string, status?: number) => void,
55
+ renderErrorPage: (
56
+ opts?: {
57
+ title?: string,
58
+ description?: string,
59
+ code?: string,
60
+ pageTitle?: string,
61
+ status?: number,
62
+ },
63
+ ) => void,
40
64
  },
41
65
  ) => any,
42
66
  },
@@ -376,17 +400,53 @@ const genRouteHandler = (
376
400
  /* Call handler */
377
401
  /*------------------------------------------------------------------------*/
378
402
 
379
- // Keep track of whether "next" was called
380
- let nextCalled = false;
403
+ // Keep track of whether a response was already sent
404
+ let responseSent = false;
381
405
 
382
406
  /**
383
- * Call the "next" function and keep track of the call so responses
384
- * are not sent
407
+ * Redirect the user to another path or url
385
408
  * @author Gabe Abrams
409
+ * @param pathOrURL the path or url to redirect to
386
410
  */
387
- const nextWithTracking = () => {
388
- nextCalled = true;
389
- next();
411
+ const redirect = (pathOrURL: string) => {
412
+ responseSent = true;
413
+ res.redirect(pathOrURL);
414
+ };
415
+
416
+ /**
417
+ * Send text to the client (with an optional status code)
418
+ * @author Gabe Abrams
419
+ * @param text the text to send to the client
420
+ * @parm [status=200] the http status code to send
421
+ */
422
+ const send = (text: string, status: number = 200) => {
423
+ responseSent = true;
424
+ res.status(status).send(text);
425
+ };
426
+
427
+ /**
428
+ * Render an error page
429
+ * @author Gabe Abrams
430
+ * @param opts object containing all arguments
431
+ * @param [opts.title=An Error Occurred] title of the error box
432
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
433
+ * a human-readable description of the error
434
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
435
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
436
+ * the title of the error
437
+ * @param [opts.status=500] http status code
438
+ */
439
+ const renderErrorPage = (
440
+ opts: {
441
+ title?: string,
442
+ description?: string,
443
+ code?: string,
444
+ pageTitle?: string,
445
+ status?: number,
446
+ } = {},
447
+ ) => {
448
+ const html = genErrorPage(opts);
449
+ send(html, opts.status ?? 500);
390
450
  };
391
451
 
392
452
  // Call the handler
@@ -394,19 +454,27 @@ const genRouteHandler = (
394
454
  const results = await opts.handler({
395
455
  params: output,
396
456
  req,
397
- res,
398
- next: nextWithTracking,
457
+ send,
458
+ next: () => {
459
+ responseSent = true;
460
+ next();
461
+ },
462
+ redirect,
463
+ renderErrorPage,
399
464
  });
400
465
 
401
466
  // Send results to client (only if next wasn't called)
402
- if (!nextCalled) {
467
+ if (!responseSent) {
403
468
  return handleSuccess(res, results ?? undefined);
404
469
  }
405
470
  } catch (err) {
406
471
  // Send error to client (only if next wasn't called)
407
- if (!nextCalled) {
472
+ if (!responseSent) {
408
473
  return handleError(res, err);
409
474
  }
475
+
476
+ // Log error that was not responded with
477
+ console.log('Error occurred but could not be sent to client because a response was already sent:', err);
410
478
  }
411
479
  };
412
480
  };
@@ -0,0 +1,141 @@
1
+ // Import shared types
2
+ import ReactKitErrorCode from '../types/ReactKitErrorCode';
3
+
4
+ /**
5
+ * Generate a static error page
6
+ * @author Gabe Abrams
7
+ * @param opts object containing all arguments
8
+ * @param [opts.title=An Error Occurred] title of the error box
9
+ * @param [opts.description=An unknown server error occurred. Please contact support.]
10
+ * a human-readable description of the error
11
+ * @param [opts.code=ReactKitErrorCode.NoCode] error code to show
12
+ * @param [opts.pageTitle=opts.title] title of the page/tab if it differs from
13
+ * the title of the error
14
+ * @returns html of the page
15
+ */
16
+ const genErrorPage = (
17
+ opts: {
18
+ title?: string,
19
+ description?: string,
20
+ code?: string,
21
+ pageTitle?: string,
22
+ } = {},
23
+ ): string => {
24
+ const title = (opts.title ?? 'An Error Occurred');
25
+ const pageTitle = (opts.pageTitle ?? title);
26
+ const description = (
27
+ opts.description
28
+ ?? 'An unknown server error occurred. Please contact support.'
29
+ );
30
+ const code = (opts.code ?? ReactKitErrorCode.NoCode);
31
+
32
+ return `
33
+ <head>
34
+ <!-- Metadata -->
35
+ <meta
36
+ name="viewport"
37
+ content="width=device-width, height=device-height, initial-scale=1.0, minimum-scale=1.0"
38
+ >
39
+
40
+ <!-- Title -->
41
+ <title>${pageTitle}</title>
42
+
43
+ <!-- Bootstrap -->
44
+ <link
45
+ rel="stylesheet"
46
+ href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.1/css/bootstrap.min.css"
47
+ integrity="sha512-siwe/oXMhSjGCwLn+scraPOWrJxHlUgMBMZXdPe2Tnk3I0x3ESCoLz7WZ5NTH6SZrywMY+PB1cjyqJ5jAluCOg=="
48
+ crossorigin="anonymous"
49
+ referrerpolicy="no-referrer"
50
+ />
51
+ <script
52
+ src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.1/js/bootstrap.min.js"
53
+ integrity="sha512-vyRAVI0IEm6LI/fVSv/Wq/d0KUfrg3hJq2Qz5FlfER69sf3ZHlOrsLriNm49FxnpUGmhx+TaJKwJ+ByTLKT+Yg=="
54
+ crossorigin="anonymous"
55
+ referrerpolicy="no-referrer"
56
+ ></script>
57
+
58
+ <!-- FontAwesome -->
59
+ <link
60
+ rel="stylesheet"
61
+ href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
62
+ integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
63
+ crossorigin="anonymous"
64
+ referrerpolicy="no-referrer"
65
+ />
66
+
67
+ <!-- Style -->
68
+ <style>
69
+ .DCEReactKit-pop-in {
70
+ animation-name: DCEReactKit-pop-in;
71
+ animation-duration: 0.5s;
72
+ animation-iteration-count: 1;
73
+ animation-timing-function: ease-out;
74
+ animation-fill-mode: both;
75
+
76
+ transform-origin: center;
77
+ }
78
+
79
+ @keyframes DCEReactKit-pop-in {
80
+ 0% {
81
+ opacity: 0;
82
+ transform: scale(0.9);
83
+ }
84
+ 100% {
85
+ opacity: 1;
86
+ transform: scale(1);
87
+ }
88
+ }
89
+
90
+ .DCEReactKit-slide-in {
91
+ animation-name: DCEReactKit-slide-in;
92
+ animation-duration: 1s;
93
+ animation-iteration-count: 1;
94
+ animation-timing-function: ease-out;
95
+ animation-fill-mode: both;
96
+ animation-delay: 0.2s;
97
+ }
98
+
99
+ @keyframes DCEReactKit-slide-in {
100
+ 0% {
101
+ opacity: 0;
102
+ transform: translate(0, 0.3em);
103
+ }
104
+ 100% {
105
+ opacity: 1;
106
+ transform: translate(0, 0);
107
+ }
108
+ }
109
+ </style>
110
+ </head>
111
+
112
+ <!-- Body -->
113
+ <body class="bg-dark text-center pt-3 ps-3 pe-3">
114
+ <!-- Alert -->
115
+ <div
116
+ class="DCEReactKit-pop-in alert alert-warning d-inline-block"
117
+ style="width: 50em; max-width: 100%"
118
+ >
119
+ <!-- Title -->
120
+ <h2>
121
+ <i class="me-1 fa-solid fa-triangle-exclamation"></i>
122
+ ${title}
123
+ </h2>
124
+ <!-- Description -->
125
+ <div>
126
+ ${description}
127
+ </div>
128
+ </div>
129
+
130
+ <!-- Error Code -->
131
+ <div class="DCEReactKit-slide-in text-light">
132
+ <strong>
133
+ Error Code:
134
+ </strong>
135
+ ${code}
136
+ </div>
137
+ </body>
138
+ `;
139
+ };
140
+
141
+ export default genErrorPage;
package/tsconfig.json CHANGED
@@ -5,7 +5,6 @@
5
5
  "forceConsistentCasingInFileNames": true,
6
6
  "strict": true,
7
7
  "skipLibCheck": true,
8
-
9
8
  "jsx": "react",
10
9
  "module": "ESNext",
11
10
  "declaration": true,