web_plsql 0.3.2 → 0.5.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/.editorconfig +8 -8
- package/.eslintignore +3 -3
- package/.eslintrc.js +347 -273
- package/CHANGELOG.md +127 -106
- package/LICENSE +21 -21
- package/README.md +165 -164
- package/examples/apex.js +70 -70
- package/examples/credentials.js +22 -22
- package/examples/oracledb_example.js +30 -30
- package/examples/sample.js +101 -84
- package/examples/sql/doc_table.sql +13 -13
- package/examples/sql/install.sql +31 -31
- package/examples/sql/sample.pkb +223 -223
- package/examples/sql/sample.pks +24 -24
- package/examples/sql/uninstall.sql +5 -5
- package/examples/static/sample.css +25 -25
- package/jest.config.js +204 -0
- package/package.json +85 -109
- package/src/cgi.ts +95 -95
- package/src/config.ts +97 -97
- package/src/errorPage.ts +286 -286
- package/src/fileUpload.ts +126 -132
- package/src/index.ts +65 -66
- package/src/page.ts +275 -277
- package/src/procedure.ts +360 -359
- package/src/procedureError.ts +27 -27
- package/src/request.ts +139 -139
- package/src/requestError.ts +19 -19
- package/src/stream.ts +26 -26
- package/src/trace.ts +194 -193
- package/test/.eslintrc.json +5 -5
- package/test/{cgi.ts → __tests__/cgi.ts} +96 -95
- package/test/{config.ts → __tests__/config.ts} +41 -41
- package/test/__tests__/errorPage.ts +101 -0
- package/test/{oracledb_mock.ts → __tests__/oracledb_mock.ts} +98 -98
- package/test/{server.ts → __tests__/server.ts} +495 -498
- package/test/{stream.ts → __tests__/stream.ts} +21 -21
- package/test/mock/oracledb.ts +85 -85
- package/test/static/static.html +1 -1
- package/tsconfig.json +24 -23
- package/tsconfig.src.json +23 -22
- package/test/errorPage.ts +0 -101
- package/test/mocha.opts +0 -7
- package/tsconfig.test.json +0 -19
package/src/errorPage.ts
CHANGED
|
@@ -1,286 +1,286 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Error handling
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import util from 'util';
|
|
6
|
-
import escape from 'escape-html';
|
|
7
|
-
import {ProcedureError} from './procedureError';
|
|
8
|
-
import {RequestError} from './requestError';
|
|
9
|
-
import express from 'express';
|
|
10
|
-
import {Trace} from './trace';
|
|
11
|
-
import {oracleExpressMiddleware$options} from './config';
|
|
12
|
-
import {environmentType} from './cgi';
|
|
13
|
-
type outputType = {html: string; text: string};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Show an error page
|
|
17
|
-
*
|
|
18
|
-
* @param {express.Request} req - The req object represents the HTTP request.
|
|
19
|
-
* @param {express.Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
|
|
20
|
-
* @param {Object} options - The configuration options.
|
|
21
|
-
* @param {Trace} trace - Tracing object.
|
|
22
|
-
* @param {Error} error - The error.
|
|
23
|
-
*/
|
|
24
|
-
export function errorPage(req: express.Request, res: express.Response, options: oracleExpressMiddleware$options, trace: Trace, error:
|
|
25
|
-
let output = {
|
|
26
|
-
html: '',
|
|
27
|
-
text: ''
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// get the error description
|
|
31
|
-
try {
|
|
32
|
-
output = getError(req, error);
|
|
33
|
-
} catch (
|
|
34
|
-
/* istanbul ignore next */
|
|
35
|
-
const header = 'ERROR';
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
res.status(404).send(
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
let
|
|
66
|
-
let
|
|
67
|
-
let
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
environment = error.environment;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
/* istanbul ignore next */
|
|
85
|
-
message = error.stack || '';
|
|
86
|
-
} else
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
/* istanbul ignore next */
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
output.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
/* istanbul ignore next */
|
|
173
|
-
output.
|
|
174
|
-
/* istanbul ignore next */
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
html +=
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
/* istanbul ignore next */
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
html += '</table>';
|
|
208
|
-
|
|
209
|
-
output.html += html;
|
|
210
|
-
output.text += text;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/*
|
|
214
|
-
* get text header
|
|
215
|
-
*/
|
|
216
|
-
function getHeaderText(text: string): string {
|
|
217
|
-
return `\n${text}\n${'='.repeat(text.length)}\n`;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/*
|
|
221
|
-
* get html header
|
|
222
|
-
*/
|
|
223
|
-
function getHeaderHtml(text: string): string {
|
|
224
|
-
return `<h1>${text}</h1>`;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/*
|
|
228
|
-
* get text
|
|
229
|
-
*/
|
|
230
|
-
function getText(text: string): string {
|
|
231
|
-
return text + '\n';
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/*
|
|
235
|
-
* get html
|
|
236
|
-
*/
|
|
237
|
-
function getHtml(text: string): string {
|
|
238
|
-
return `<p>${convertToHtml(text)}</p>`;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/*
|
|
242
|
-
* convert value to string
|
|
243
|
-
*/
|
|
244
|
-
function inspect(value: any): string {
|
|
245
|
-
return util.inspect(value, {showHidden: false, depth: null, colors: false});
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/*
|
|
249
|
-
* convert LF and/or CR to <br>
|
|
250
|
-
*/
|
|
251
|
-
function convertToHtml(text: string): string {
|
|
252
|
-
let html = escape(text);
|
|
253
|
-
|
|
254
|
-
html = html.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
|
255
|
-
html = html.replace(/\t/g, ' ');
|
|
256
|
-
|
|
257
|
-
return html;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
/*
|
|
261
|
-
* getHtmlPage
|
|
262
|
-
*/
|
|
263
|
-
function getHtmlPage(body: string): string {
|
|
264
|
-
return `<!DOCTYPE html>
|
|
265
|
-
<html lang="en">
|
|
266
|
-
<head>
|
|
267
|
-
<meta charset="utf-8">
|
|
268
|
-
<title>web_plsql error page</title>
|
|
269
|
-
<style type="text/css">
|
|
270
|
-
html {
|
|
271
|
-
font-family: monospace, sans-serif;
|
|
272
|
-
font-size: 12px;
|
|
273
|
-
}
|
|
274
|
-
h1 {
|
|
275
|
-
font-size: 16px;
|
|
276
|
-
padding: 2px;
|
|
277
|
-
background-color: #cc0000;
|
|
278
|
-
}
|
|
279
|
-
</style>
|
|
280
|
-
</head>
|
|
281
|
-
<body>
|
|
282
|
-
` + body + `
|
|
283
|
-
</body>
|
|
284
|
-
</html>
|
|
285
|
-
`;
|
|
286
|
-
}
|
|
1
|
+
/*
|
|
2
|
+
* Error handling
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import util from 'util';
|
|
6
|
+
import escape from 'escape-html';
|
|
7
|
+
import {ProcedureError} from './procedureError';
|
|
8
|
+
import {RequestError} from './requestError';
|
|
9
|
+
import express from 'express';
|
|
10
|
+
import {Trace} from './trace';
|
|
11
|
+
import {oracleExpressMiddleware$options} from './config';
|
|
12
|
+
import {environmentType} from './cgi';
|
|
13
|
+
type outputType = {html: string; text: string};
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Show an error page
|
|
17
|
+
*
|
|
18
|
+
* @param {express.Request} req - The req object represents the HTTP request.
|
|
19
|
+
* @param {express.Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
|
|
20
|
+
* @param {Object} options - The configuration options.
|
|
21
|
+
* @param {Trace} trace - Tracing object.
|
|
22
|
+
* @param {Error} error - The error.
|
|
23
|
+
*/
|
|
24
|
+
export function errorPage(req: express.Request, res: express.Response, options: oracleExpressMiddleware$options, trace: Trace, error: unknown): void {
|
|
25
|
+
let output = {
|
|
26
|
+
html: '',
|
|
27
|
+
text: ''
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// get the error description
|
|
31
|
+
try {
|
|
32
|
+
output = getError(req, error);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
/* istanbul ignore next */
|
|
35
|
+
const header = 'ERROR';
|
|
36
|
+
|
|
37
|
+
/* istanbul ignore next */
|
|
38
|
+
const message = err instanceof Error ? `${err.message}\n${err.stack}` : JSON.stringify(err);
|
|
39
|
+
|
|
40
|
+
/* istanbul ignore next */
|
|
41
|
+
output.html += getHeaderHtml(header) + getHtml(message);
|
|
42
|
+
|
|
43
|
+
/* istanbul ignore next */
|
|
44
|
+
output.text += getHeaderHtml(header) + getHtml(message);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// trace to file
|
|
48
|
+
trace.write(output.text);
|
|
49
|
+
|
|
50
|
+
// console
|
|
51
|
+
console.error(output.text);
|
|
52
|
+
|
|
53
|
+
// show page
|
|
54
|
+
if (options.errorStyle === 'basic') {
|
|
55
|
+
res.status(404).send('Page not found');
|
|
56
|
+
} else {
|
|
57
|
+
res.status(404).send(getHtmlPage(output.html));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* Show an error page
|
|
63
|
+
*/
|
|
64
|
+
function getError(req: express.Request, error: unknown): outputType {
|
|
65
|
+
let timestamp: Date = new Date();
|
|
66
|
+
let message: string = '';
|
|
67
|
+
let environment: environmentType | null = null;
|
|
68
|
+
let sql: string | null = null;
|
|
69
|
+
let bind: any | null = null;
|
|
70
|
+
|
|
71
|
+
// what type of Error did we receive
|
|
72
|
+
if (error instanceof ProcedureError) {
|
|
73
|
+
timestamp = error.timestamp;
|
|
74
|
+
/* istanbul ignore next */
|
|
75
|
+
message = error.stack || '';
|
|
76
|
+
environment = error.environment;
|
|
77
|
+
sql = error.sql;
|
|
78
|
+
bind = error.bind;
|
|
79
|
+
} else if (error instanceof RequestError) {
|
|
80
|
+
timestamp = error.timestamp;
|
|
81
|
+
/* istanbul ignore next */
|
|
82
|
+
message = error.stack || '';
|
|
83
|
+
} else if (error instanceof Error) {
|
|
84
|
+
/* istanbul ignore next */
|
|
85
|
+
message = error.stack || '';
|
|
86
|
+
} else {
|
|
87
|
+
if (typeof error === 'string') {
|
|
88
|
+
/* istanbul ignore next */
|
|
89
|
+
message = error + '\n';
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
/* istanbul ignore next */
|
|
93
|
+
new Error(); // eslint-disable-line no-new
|
|
94
|
+
} catch (err) {
|
|
95
|
+
/* istanbul ignore next */
|
|
96
|
+
message += err instanceof Error ? err.stack : '';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const output: outputType = {
|
|
101
|
+
html: '',
|
|
102
|
+
text: ''
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// timestamp
|
|
106
|
+
let header = 'TIMESTAMP';
|
|
107
|
+
output.html += getHeaderHtml(header);
|
|
108
|
+
output.html += getHtml(timestamp.toUTCString());
|
|
109
|
+
|
|
110
|
+
// error
|
|
111
|
+
header = 'ERROR';
|
|
112
|
+
output.html += getHeaderHtml(header);
|
|
113
|
+
output.html += getHtml(message);
|
|
114
|
+
output.text += getHeaderText(header);
|
|
115
|
+
output.text += getText(message);
|
|
116
|
+
|
|
117
|
+
// request
|
|
118
|
+
header = 'REQUEST';
|
|
119
|
+
output.text += getHeaderText(header);
|
|
120
|
+
output.text += getText(Trace.inspectRequest(req));
|
|
121
|
+
|
|
122
|
+
// parameters
|
|
123
|
+
if (typeof sql === 'string' && bind) {
|
|
124
|
+
header = 'PROCEDURE';
|
|
125
|
+
output.html += getHeaderHtml(header);
|
|
126
|
+
output.text += getHeaderText(header);
|
|
127
|
+
getProcedure(output, sql, bind);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// environment
|
|
131
|
+
if (environment) {
|
|
132
|
+
header = 'ENVIRONMENT';
|
|
133
|
+
output.html += getHeaderHtml(header);
|
|
134
|
+
output.text += getHeaderText(header);
|
|
135
|
+
getEnvironment(output, environment);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return output;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/*
|
|
142
|
+
* get procedure
|
|
143
|
+
*/
|
|
144
|
+
function getProcedure(output: outputType, sql: string, bind: any) {
|
|
145
|
+
let html = '<table>';
|
|
146
|
+
let text = '';
|
|
147
|
+
|
|
148
|
+
text += 'PROCEDURE: ' + sql + '\n';
|
|
149
|
+
html += `<tr><td>PROCEDURE:</td><td>${sql}</td></tr>`;
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
/* istanbul ignore else */
|
|
153
|
+
if (sql.indexOf('(:argnames, :argvalues)') < 0) {
|
|
154
|
+
for (const key in bind) {
|
|
155
|
+
const value = inspect(bind[key].val);
|
|
156
|
+
|
|
157
|
+
html += `<tr><td>${key}:</td><td>${value}</td></tr>`;
|
|
158
|
+
text += key + ': ' + value + '\n';
|
|
159
|
+
}
|
|
160
|
+
} else {
|
|
161
|
+
bind.argnames.val.forEach((name: string, index: number) => {
|
|
162
|
+
const value = inspect(bind.argvalues.val[index]);
|
|
163
|
+
|
|
164
|
+
html += `<tr><td>${name}:</td><td>${value}</td></tr>`;
|
|
165
|
+
text += name + ': ' + value + '\n';
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
/* istanbul ignore next */
|
|
170
|
+
output.html += err instanceof Error ? err.toString() : 'ERROR';
|
|
171
|
+
|
|
172
|
+
/* istanbul ignore next */
|
|
173
|
+
output.text += err instanceof Error ? err.toString() : 'ERROR';
|
|
174
|
+
/* istanbul ignore next */
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
html += '</table>';
|
|
179
|
+
|
|
180
|
+
output.html += html;
|
|
181
|
+
output.text += text;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
* get evnironment
|
|
186
|
+
*/
|
|
187
|
+
function getEnvironment(output: outputType, environment: environmentType) {
|
|
188
|
+
let html = '<table>';
|
|
189
|
+
let text = '';
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
for (const key in environment) {
|
|
193
|
+
html += `<tr><td>${key}:</td><td>${environment[key]}</td></tr>`;
|
|
194
|
+
text += key + '=' + environment[key] + '\n';
|
|
195
|
+
}
|
|
196
|
+
} catch (err) {
|
|
197
|
+
/* istanbul ignore next */
|
|
198
|
+
output.html += err instanceof Error ? err.toString() : 'ERROR';
|
|
199
|
+
|
|
200
|
+
/* istanbul ignore next */
|
|
201
|
+
output.text += err instanceof Error ? err.toString() : 'ERROR';
|
|
202
|
+
|
|
203
|
+
/* istanbul ignore next */
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
html += '</table>';
|
|
208
|
+
|
|
209
|
+
output.html += html;
|
|
210
|
+
output.text += text;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* get text header
|
|
215
|
+
*/
|
|
216
|
+
function getHeaderText(text: string): string {
|
|
217
|
+
return `\n${text}\n${'='.repeat(text.length)}\n`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/*
|
|
221
|
+
* get html header
|
|
222
|
+
*/
|
|
223
|
+
function getHeaderHtml(text: string): string {
|
|
224
|
+
return `<h1>${text}</h1>`;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/*
|
|
228
|
+
* get text
|
|
229
|
+
*/
|
|
230
|
+
function getText(text: string): string {
|
|
231
|
+
return text + '\n';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/*
|
|
235
|
+
* get html
|
|
236
|
+
*/
|
|
237
|
+
function getHtml(text: string): string {
|
|
238
|
+
return `<p>${convertToHtml(text)}</p>`;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/*
|
|
242
|
+
* convert value to string
|
|
243
|
+
*/
|
|
244
|
+
function inspect(value: any): string {
|
|
245
|
+
return util.inspect(value, {showHidden: false, depth: null, colors: false});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/*
|
|
249
|
+
* convert LF and/or CR to <br>
|
|
250
|
+
*/
|
|
251
|
+
function convertToHtml(text: string): string {
|
|
252
|
+
let html = escape(text);
|
|
253
|
+
|
|
254
|
+
html = html.replace(/(?:\r\n|\r|\n)/g, '<br />');
|
|
255
|
+
html = html.replace(/\t/g, ' ');
|
|
256
|
+
|
|
257
|
+
return html;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/*
|
|
261
|
+
* getHtmlPage
|
|
262
|
+
*/
|
|
263
|
+
function getHtmlPage(body: string): string {
|
|
264
|
+
return `<!DOCTYPE html>
|
|
265
|
+
<html lang="en">
|
|
266
|
+
<head>
|
|
267
|
+
<meta charset="utf-8">
|
|
268
|
+
<title>web_plsql error page</title>
|
|
269
|
+
<style type="text/css">
|
|
270
|
+
html {
|
|
271
|
+
font-family: monospace, sans-serif;
|
|
272
|
+
font-size: 12px;
|
|
273
|
+
}
|
|
274
|
+
h1 {
|
|
275
|
+
font-size: 16px;
|
|
276
|
+
padding: 2px;
|
|
277
|
+
background-color: #cc0000;
|
|
278
|
+
}
|
|
279
|
+
</style>
|
|
280
|
+
</head>
|
|
281
|
+
<body>
|
|
282
|
+
` + body + `
|
|
283
|
+
</body>
|
|
284
|
+
</html>
|
|
285
|
+
`;
|
|
286
|
+
}
|