@prairielearn/flash 1.0.6 → 1.1.0
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/CHANGELOG.md +6 -0
- package/README.md +2 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +52 -9
- package/dist/index.test.js.map +1 -1
- package/package.json +4 -1
- package/src/index.test.ts +58 -9
- package/src/index.ts +9 -5
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ app.get('/set-flash', (req, res) => {
|
|
|
34
34
|
flash('notice', 'Your preferences have been updated.');
|
|
35
35
|
flash('success', 'Course created successfully.');
|
|
36
36
|
flash('warning', 'Syncing completed with 5 warnings.');
|
|
37
|
-
flash('error',
|
|
37
|
+
flash('error', html`Group must have <em>fewer than 10 members</em>.`);
|
|
38
38
|
|
|
39
39
|
res.redirect('/display-flash');
|
|
40
40
|
});
|
|
@@ -47,7 +47,7 @@ app.get('/display-flash', (req, res) => {
|
|
|
47
47
|
|
|
48
48
|
The `flash()` function has three behaviors:
|
|
49
49
|
|
|
50
|
-
- `flash(type: string, message: string)`: Set a message with the given type.
|
|
50
|
+
- `flash(type: string, message: string | HtmlSafeString)`: Set a message with the given type.
|
|
51
51
|
- `flash(type: string): FlashMessage[]`: Get all messages with the given type.
|
|
52
52
|
- `flash(types: string[]): FlashMessage[]`: Get all messages with any of the given types.
|
|
53
53
|
- `flash(): FlashMessage[]`: Get all messages.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { HtmlSafeString } from '@prairielearn/html';
|
|
2
3
|
export type FlashMessageType = 'notice' | 'success' | 'warning' | 'error';
|
|
3
4
|
export interface FlashMessage {
|
|
4
5
|
type: FlashMessageType;
|
|
@@ -6,4 +7,4 @@ export interface FlashMessage {
|
|
|
6
7
|
}
|
|
7
8
|
export declare function flashMiddleware(): (req: Request, _res: Response, next: NextFunction) => void;
|
|
8
9
|
export declare function flash(type?: FlashMessageType | FlashMessageType[]): FlashMessage[];
|
|
9
|
-
export declare function flash(type: FlashMessageType, message: string): void;
|
|
10
|
+
export declare function flash(type: FlashMessageType, message: string | HtmlSafeString): void;
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.flash = exports.flashMiddleware = void 0;
|
|
4
4
|
const node_async_hooks_1 = require("node:async_hooks");
|
|
5
|
+
const html_1 = require("@prairielearn/html");
|
|
5
6
|
const als = new node_async_hooks_1.AsyncLocalStorage();
|
|
6
7
|
function flashMiddleware() {
|
|
7
8
|
return (req, _res, next) => {
|
|
@@ -42,7 +43,7 @@ function makeFlashStorage(req) {
|
|
|
42
43
|
return {
|
|
43
44
|
add(type, message) {
|
|
44
45
|
session.flash ??= [];
|
|
45
|
-
session.flash.push({ type, message });
|
|
46
|
+
session.flash.push({ type, message: (0, html_1.html) `${message}`.toString() });
|
|
46
47
|
},
|
|
47
48
|
get(type) {
|
|
48
49
|
const messages = session.flash ?? [];
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,uDAAqD;AAErD,6CAA0D;AAE1D,MAAM,GAAG,GAAG,IAAI,oCAAiB,EAAgB,CAAC;AASlD,SAAgB,eAAe;IAC7B,OAAO,CAAC,GAAY,EAAE,IAAc,EAAE,IAAkB,EAAE,EAAE;QAC1D,MAAM,YAAY,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC3C,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC;AACJ,CAAC;AALD,0CAKC;AAID,SAAgB,KAAK,CACnB,IAA4C,EAC5C,OAAiC;IAEjC,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IACpC,IAAI,CAAC,YAAY,EAAE;QACjB,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;KAC5D;IAED,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QACvB,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QAChE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC3C,OAAO,QAAQ,CAAC;KACjB;IAED,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,EAAE;QACnC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAChC,OAAO;KACR;IAED,IAAI,IAAI,IAAI,IAAI,EAAE;QAChB,MAAM,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACvC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzB,OAAO,OAAO,CAAC;KAChB;IAED,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC;IACvC,YAAY,CAAC,QAAQ,EAAE,CAAC;IACxB,OAAO,QAAQ,CAAC;AAClB,CAAC;AA7BD,sBA6BC;AAUD,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE;QAChB,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;KACjE;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,OAAc,CAAC;IAEnC,OAAO;QACL,GAAG,CAAC,IAAsB,EAAE,OAAgC;YAC1D,OAAO,CAAC,KAAK,KAAK,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAA,WAAI,EAAA,GAAG,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACrE,CAAC;QACD,GAAG,CAAC,IAAsB;YACxB,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;YACrC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAqB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAC3E,CAAC;QACD,MAAM;YACJ,OAAO,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC;QAC7B,CAAC;QACD,KAAK,CAAC,IAAsB;YAC1B,OAAO,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAqB,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;QAChG,CAAC;QACD,QAAQ;YACN,OAAO,CAAC,KAAK,GAAG,EAAE,CAAC;QACrB,CAAC;KACqB,CAAC;AAC3B,CAAC"}
|
package/dist/index.test.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const chai_1 = require("chai");
|
|
4
4
|
const index_1 = require("./index");
|
|
5
|
+
const html_1 = require("@prairielearn/html");
|
|
5
6
|
describe('flash', () => {
|
|
6
7
|
it('throws an error if no session present', () => {
|
|
7
8
|
chai_1.assert.throw(() => {
|
|
@@ -12,8 +13,11 @@ describe('flash', () => {
|
|
|
12
13
|
chai_1.assert.throw(() => {
|
|
13
14
|
(0, index_1.flash)('notice', 'Hello world');
|
|
14
15
|
});
|
|
16
|
+
chai_1.assert.throw(() => {
|
|
17
|
+
(0, index_1.flash)('notice', (0, html_1.html) `<p>hello ${'&'} world</p>`);
|
|
18
|
+
});
|
|
15
19
|
});
|
|
16
|
-
it('adds a flash', () => {
|
|
20
|
+
it('adds a flash using string message', () => {
|
|
17
21
|
const req = {
|
|
18
22
|
session: {},
|
|
19
23
|
};
|
|
@@ -23,6 +27,28 @@ describe('flash', () => {
|
|
|
23
27
|
chai_1.assert.sameDeepMembers((0, index_1.flash)(), [{ type: 'notice', message: 'hello world' }]);
|
|
24
28
|
});
|
|
25
29
|
});
|
|
30
|
+
it('adds a flash and escapes unsafe HTML', () => {
|
|
31
|
+
const req = {
|
|
32
|
+
session: {},
|
|
33
|
+
};
|
|
34
|
+
const res = {};
|
|
35
|
+
(0, index_1.flashMiddleware)()(req, res, () => {
|
|
36
|
+
(0, index_1.flash)('notice', '<b>hello world</b>');
|
|
37
|
+
chai_1.assert.sameDeepMembers((0, index_1.flash)(), [
|
|
38
|
+
{ type: 'notice', message: '<b>hello world</b>' },
|
|
39
|
+
]);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
it('adds a flash with HTML-safe message', () => {
|
|
43
|
+
const req = {
|
|
44
|
+
session: {},
|
|
45
|
+
};
|
|
46
|
+
const res = {};
|
|
47
|
+
(0, index_1.flashMiddleware)()(req, res, () => {
|
|
48
|
+
(0, index_1.flash)('notice', (0, html_1.html) `<p>hello ${'&'} world</p>`);
|
|
49
|
+
chai_1.assert.sameDeepMembers((0, index_1.flash)(), [{ type: 'notice', message: '<p>hello & world</p>' }]);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
26
52
|
it('stores multiples flashes with the same type', () => {
|
|
27
53
|
const req = {
|
|
28
54
|
session: {},
|
|
@@ -30,10 +56,12 @@ describe('flash', () => {
|
|
|
30
56
|
const res = {};
|
|
31
57
|
(0, index_1.flashMiddleware)()(req, res, () => {
|
|
32
58
|
(0, index_1.flash)('notice', 'hello world');
|
|
33
|
-
(0, index_1.flash)('notice', 'goodbye world');
|
|
59
|
+
(0, index_1.flash)('notice', '<< goodbye world');
|
|
60
|
+
(0, index_1.flash)('notice', (0, html_1.html) `<p>hello ${'&'} world</p>`);
|
|
34
61
|
chai_1.assert.sameDeepMembers((0, index_1.flash)(), [
|
|
35
62
|
{ type: 'notice', message: 'hello world' },
|
|
36
|
-
{ type: 'notice', message: 'goodbye world' },
|
|
63
|
+
{ type: 'notice', message: '<< goodbye world' },
|
|
64
|
+
{ type: 'notice', message: '<p>hello & world</p>' },
|
|
37
65
|
]);
|
|
38
66
|
});
|
|
39
67
|
});
|
|
@@ -44,9 +72,15 @@ describe('flash', () => {
|
|
|
44
72
|
const res = {};
|
|
45
73
|
(0, index_1.flashMiddleware)()(req, res, () => {
|
|
46
74
|
(0, index_1.flash)('notice', 'hello world');
|
|
47
|
-
(0, index_1.flash)('error', 'goodbye world');
|
|
75
|
+
(0, index_1.flash)('error', '<< goodbye world');
|
|
76
|
+
(0, index_1.flash)('success', (0, html_1.html) `<p>hello ${'&'} world</p>`);
|
|
48
77
|
chai_1.assert.sameDeepMembers((0, index_1.flash)('notice'), [{ type: 'notice', message: 'hello world' }]);
|
|
49
|
-
chai_1.assert.sameDeepMembers((0, index_1.flash)('error'), [
|
|
78
|
+
chai_1.assert.sameDeepMembers((0, index_1.flash)('error'), [
|
|
79
|
+
{ type: 'error', message: '<< goodbye world' },
|
|
80
|
+
]);
|
|
81
|
+
chai_1.assert.sameDeepMembers((0, index_1.flash)('success'), [
|
|
82
|
+
{ type: 'success', message: '<p>hello & world</p>' },
|
|
83
|
+
]);
|
|
50
84
|
});
|
|
51
85
|
});
|
|
52
86
|
it('returns all flashes', () => {
|
|
@@ -56,10 +90,12 @@ describe('flash', () => {
|
|
|
56
90
|
const res = {};
|
|
57
91
|
(0, index_1.flashMiddleware)()(req, res, () => {
|
|
58
92
|
(0, index_1.flash)('notice', 'hello world');
|
|
59
|
-
(0, index_1.flash)('error', 'goodbye world');
|
|
93
|
+
(0, index_1.flash)('error', '<< goodbye world');
|
|
94
|
+
(0, index_1.flash)('success', (0, html_1.html) `<p>hello ${'&'} world</p>`);
|
|
60
95
|
chai_1.assert.sameDeepMembers((0, index_1.flash)(), [
|
|
61
96
|
{ type: 'notice', message: 'hello world' },
|
|
62
|
-
{ type: 'error', message: 'goodbye world' },
|
|
97
|
+
{ type: 'error', message: '<< goodbye world' },
|
|
98
|
+
{ type: 'success', message: '<p>hello & world</p>' },
|
|
63
99
|
]);
|
|
64
100
|
});
|
|
65
101
|
});
|
|
@@ -70,11 +106,18 @@ describe('flash', () => {
|
|
|
70
106
|
const res = {};
|
|
71
107
|
(0, index_1.flashMiddleware)()(req, res, () => {
|
|
72
108
|
(0, index_1.flash)('notice', 'hello world');
|
|
73
|
-
(0, index_1.flash)('error', 'goodbye world');
|
|
109
|
+
(0, index_1.flash)('error', '<< goodbye world');
|
|
110
|
+
(0, index_1.flash)('success', (0, html_1.html) `<p>hello ${'&'} world</p>`);
|
|
74
111
|
chai_1.assert.sameDeepMembers((0, index_1.flash)('notice'), [{ type: 'notice', message: 'hello world' }]);
|
|
75
|
-
chai_1.assert.sameDeepMembers((0, index_1.flash)('error'), [
|
|
112
|
+
chai_1.assert.sameDeepMembers((0, index_1.flash)('error'), [
|
|
113
|
+
{ type: 'error', message: '<< goodbye world' },
|
|
114
|
+
]);
|
|
115
|
+
chai_1.assert.sameDeepMembers((0, index_1.flash)('success'), [
|
|
116
|
+
{ type: 'success', message: '<p>hello & world</p>' },
|
|
117
|
+
]);
|
|
76
118
|
chai_1.assert.deepEqual((0, index_1.flash)('notice'), []);
|
|
77
119
|
chai_1.assert.deepEqual((0, index_1.flash)('error'), []);
|
|
120
|
+
chai_1.assert.deepEqual((0, index_1.flash)('success'), []);
|
|
78
121
|
chai_1.assert.isEmpty((0, index_1.flash)());
|
|
79
122
|
});
|
|
80
123
|
});
|
package/dist/index.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":";;AAAA,+BAA8B;AAE9B,mCAAiD;
|
|
1
|
+
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/flash",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -12,6 +12,9 @@
|
|
|
12
12
|
"dev": "tsc --watch --preserveWatchOutput",
|
|
13
13
|
"test": "mocha --no-config --require ts-node/register src/index.test.ts"
|
|
14
14
|
},
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@prairielearn/html": "^3.0.8"
|
|
17
|
+
},
|
|
15
18
|
"devDependencies": {
|
|
16
19
|
"@prairielearn/tsconfig": "^0.0.0",
|
|
17
20
|
"@types/express": "^4.17.18",
|
package/src/index.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assert } from 'chai';
|
|
2
2
|
|
|
3
3
|
import { flashMiddleware, flash } from './index';
|
|
4
|
+
import { html } from '@prairielearn/html';
|
|
4
5
|
|
|
5
6
|
describe('flash', () => {
|
|
6
7
|
it('throws an error if no session present', () => {
|
|
@@ -13,9 +14,12 @@ describe('flash', () => {
|
|
|
13
14
|
assert.throw(() => {
|
|
14
15
|
flash('notice', 'Hello world');
|
|
15
16
|
});
|
|
17
|
+
assert.throw(() => {
|
|
18
|
+
flash('notice', html`<p>hello ${'&'} world</p>`);
|
|
19
|
+
});
|
|
16
20
|
});
|
|
17
21
|
|
|
18
|
-
it('adds a flash', () => {
|
|
22
|
+
it('adds a flash using string message', () => {
|
|
19
23
|
const req = {
|
|
20
24
|
session: {},
|
|
21
25
|
} as any;
|
|
@@ -28,6 +32,34 @@ describe('flash', () => {
|
|
|
28
32
|
});
|
|
29
33
|
});
|
|
30
34
|
|
|
35
|
+
it('adds a flash and escapes unsafe HTML', () => {
|
|
36
|
+
const req = {
|
|
37
|
+
session: {},
|
|
38
|
+
} as any;
|
|
39
|
+
const res = {} as any;
|
|
40
|
+
|
|
41
|
+
flashMiddleware()(req, res, () => {
|
|
42
|
+
flash('notice', '<b>hello world</b>');
|
|
43
|
+
|
|
44
|
+
assert.sameDeepMembers(flash(), [
|
|
45
|
+
{ type: 'notice', message: '<b>hello world</b>' },
|
|
46
|
+
]);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('adds a flash with HTML-safe message', () => {
|
|
51
|
+
const req = {
|
|
52
|
+
session: {},
|
|
53
|
+
} as any;
|
|
54
|
+
const res = {} as any;
|
|
55
|
+
|
|
56
|
+
flashMiddleware()(req, res, () => {
|
|
57
|
+
flash('notice', html`<p>hello ${'&'} world</p>`);
|
|
58
|
+
|
|
59
|
+
assert.sameDeepMembers(flash(), [{ type: 'notice', message: '<p>hello & world</p>' }]);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
|
|
31
63
|
it('stores multiples flashes with the same type', () => {
|
|
32
64
|
const req = {
|
|
33
65
|
session: {},
|
|
@@ -36,11 +68,13 @@ describe('flash', () => {
|
|
|
36
68
|
|
|
37
69
|
flashMiddleware()(req, res, () => {
|
|
38
70
|
flash('notice', 'hello world');
|
|
39
|
-
flash('notice', 'goodbye world');
|
|
71
|
+
flash('notice', '<< goodbye world');
|
|
72
|
+
flash('notice', html`<p>hello ${'&'} world</p>`);
|
|
40
73
|
|
|
41
74
|
assert.sameDeepMembers(flash(), [
|
|
42
75
|
{ type: 'notice', message: 'hello world' },
|
|
43
|
-
{ type: 'notice', message: 'goodbye world' },
|
|
76
|
+
{ type: 'notice', message: '<< goodbye world' },
|
|
77
|
+
{ type: 'notice', message: '<p>hello & world</p>' },
|
|
44
78
|
]);
|
|
45
79
|
});
|
|
46
80
|
});
|
|
@@ -53,10 +87,16 @@ describe('flash', () => {
|
|
|
53
87
|
|
|
54
88
|
flashMiddleware()(req, res, () => {
|
|
55
89
|
flash('notice', 'hello world');
|
|
56
|
-
flash('error', 'goodbye world');
|
|
90
|
+
flash('error', '<< goodbye world');
|
|
91
|
+
flash('success', html`<p>hello ${'&'} world</p>`);
|
|
57
92
|
|
|
58
93
|
assert.sameDeepMembers(flash('notice'), [{ type: 'notice', message: 'hello world' }]);
|
|
59
|
-
assert.sameDeepMembers(flash('error'), [
|
|
94
|
+
assert.sameDeepMembers(flash('error'), [
|
|
95
|
+
{ type: 'error', message: '<< goodbye world' },
|
|
96
|
+
]);
|
|
97
|
+
assert.sameDeepMembers(flash('success'), [
|
|
98
|
+
{ type: 'success', message: '<p>hello & world</p>' },
|
|
99
|
+
]);
|
|
60
100
|
});
|
|
61
101
|
});
|
|
62
102
|
|
|
@@ -68,11 +108,13 @@ describe('flash', () => {
|
|
|
68
108
|
|
|
69
109
|
flashMiddleware()(req, res, () => {
|
|
70
110
|
flash('notice', 'hello world');
|
|
71
|
-
flash('error', 'goodbye world');
|
|
111
|
+
flash('error', '<< goodbye world');
|
|
112
|
+
flash('success', html`<p>hello ${'&'} world</p>`);
|
|
72
113
|
|
|
73
114
|
assert.sameDeepMembers(flash(), [
|
|
74
115
|
{ type: 'notice', message: 'hello world' },
|
|
75
|
-
{ type: 'error', message: 'goodbye world' },
|
|
116
|
+
{ type: 'error', message: '<< goodbye world' },
|
|
117
|
+
{ type: 'success', message: '<p>hello & world</p>' },
|
|
76
118
|
]);
|
|
77
119
|
});
|
|
78
120
|
});
|
|
@@ -85,13 +127,20 @@ describe('flash', () => {
|
|
|
85
127
|
|
|
86
128
|
flashMiddleware()(req, res, () => {
|
|
87
129
|
flash('notice', 'hello world');
|
|
88
|
-
flash('error', 'goodbye world');
|
|
130
|
+
flash('error', '<< goodbye world');
|
|
131
|
+
flash('success', html`<p>hello ${'&'} world</p>`);
|
|
89
132
|
|
|
90
133
|
assert.sameDeepMembers(flash('notice'), [{ type: 'notice', message: 'hello world' }]);
|
|
91
|
-
assert.sameDeepMembers(flash('error'), [
|
|
134
|
+
assert.sameDeepMembers(flash('error'), [
|
|
135
|
+
{ type: 'error', message: '<< goodbye world' },
|
|
136
|
+
]);
|
|
137
|
+
assert.sameDeepMembers(flash('success'), [
|
|
138
|
+
{ type: 'success', message: '<p>hello & world</p>' },
|
|
139
|
+
]);
|
|
92
140
|
|
|
93
141
|
assert.deepEqual(flash('notice'), []);
|
|
94
142
|
assert.deepEqual(flash('error'), []);
|
|
143
|
+
assert.deepEqual(flash('success'), []);
|
|
95
144
|
assert.isEmpty(flash());
|
|
96
145
|
});
|
|
97
146
|
});
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
2
2
|
import { Request, Response, NextFunction } from 'express';
|
|
3
|
+
import { HtmlSafeString, html } from '@prairielearn/html';
|
|
3
4
|
|
|
4
5
|
const als = new AsyncLocalStorage<FlashStorage>();
|
|
5
6
|
|
|
@@ -18,8 +19,11 @@ export function flashMiddleware() {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
export function flash(type?: FlashMessageType | FlashMessageType[]): FlashMessage[];
|
|
21
|
-
export function flash(type: FlashMessageType, message: string): void;
|
|
22
|
-
export function flash(
|
|
22
|
+
export function flash(type: FlashMessageType, message: string | HtmlSafeString): void;
|
|
23
|
+
export function flash(
|
|
24
|
+
type?: FlashMessageType | FlashMessageType[],
|
|
25
|
+
message?: string | HtmlSafeString,
|
|
26
|
+
) {
|
|
23
27
|
const flashStorage = als.getStore();
|
|
24
28
|
if (!flashStorage) {
|
|
25
29
|
throw new Error('flash() must be called within a request');
|
|
@@ -48,7 +52,7 @@ export function flash(type?: FlashMessageType | FlashMessageType[], message?: st
|
|
|
48
52
|
}
|
|
49
53
|
|
|
50
54
|
interface FlashStorage {
|
|
51
|
-
add(type: FlashMessageType, message: string): void;
|
|
55
|
+
add(type: FlashMessageType, message: string | HtmlSafeString): void;
|
|
52
56
|
get(type: FlashMessageType): FlashMessage[] | null;
|
|
53
57
|
getAll(): FlashMessage[];
|
|
54
58
|
clear(type: FlashMessageType): void;
|
|
@@ -63,9 +67,9 @@ function makeFlashStorage(req: Request): FlashStorage {
|
|
|
63
67
|
const session = req.session as any;
|
|
64
68
|
|
|
65
69
|
return {
|
|
66
|
-
add(type: FlashMessageType, message: string) {
|
|
70
|
+
add(type: FlashMessageType, message: string | HtmlSafeString) {
|
|
67
71
|
session.flash ??= [];
|
|
68
|
-
session.flash.push({ type, message });
|
|
72
|
+
session.flash.push({ type, message: html`${message}`.toString() });
|
|
69
73
|
},
|
|
70
74
|
get(type: FlashMessageType) {
|
|
71
75
|
const messages = session.flash ?? [];
|