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.
Files changed (44) hide show
  1. package/.editorconfig +8 -8
  2. package/.eslintignore +3 -3
  3. package/.eslintrc.js +347 -273
  4. package/CHANGELOG.md +127 -106
  5. package/LICENSE +21 -21
  6. package/README.md +165 -164
  7. package/examples/apex.js +70 -70
  8. package/examples/credentials.js +22 -22
  9. package/examples/oracledb_example.js +30 -30
  10. package/examples/sample.js +101 -84
  11. package/examples/sql/doc_table.sql +13 -13
  12. package/examples/sql/install.sql +31 -31
  13. package/examples/sql/sample.pkb +223 -223
  14. package/examples/sql/sample.pks +24 -24
  15. package/examples/sql/uninstall.sql +5 -5
  16. package/examples/static/sample.css +25 -25
  17. package/jest.config.js +204 -0
  18. package/package.json +85 -109
  19. package/src/cgi.ts +95 -95
  20. package/src/config.ts +97 -97
  21. package/src/errorPage.ts +286 -286
  22. package/src/fileUpload.ts +126 -132
  23. package/src/index.ts +65 -66
  24. package/src/page.ts +275 -277
  25. package/src/procedure.ts +360 -359
  26. package/src/procedureError.ts +27 -27
  27. package/src/request.ts +139 -139
  28. package/src/requestError.ts +19 -19
  29. package/src/stream.ts +26 -26
  30. package/src/trace.ts +194 -193
  31. package/test/.eslintrc.json +5 -5
  32. package/test/{cgi.ts → __tests__/cgi.ts} +96 -95
  33. package/test/{config.ts → __tests__/config.ts} +41 -41
  34. package/test/__tests__/errorPage.ts +101 -0
  35. package/test/{oracledb_mock.ts → __tests__/oracledb_mock.ts} +98 -98
  36. package/test/{server.ts → __tests__/server.ts} +495 -498
  37. package/test/{stream.ts → __tests__/stream.ts} +21 -21
  38. package/test/mock/oracledb.ts +85 -85
  39. package/test/static/static.html +1 -1
  40. package/tsconfig.json +24 -23
  41. package/tsconfig.src.json +23 -22
  42. package/test/errorPage.ts +0 -101
  43. package/test/mocha.opts +0 -7
  44. package/tsconfig.test.json +0 -19
package/src/page.ts CHANGED
@@ -1,277 +1,275 @@
1
- /*
2
- * Page the raw page content and return the content to the client
3
- */
4
-
5
- import {Trace} from './trace';
6
- import express from 'express';
7
-
8
- type cookieType = {
9
- name: string;
10
- value: string;
11
- path?: string;
12
- domain?: string;
13
- secure?: string;
14
- expires?: Date;
15
- httpOnly?: boolean;
16
- };
17
-
18
- type pageType = {
19
- body: string;
20
- head: {
21
- cookies: Array<cookieType>;
22
- contentType?: string;
23
- contentLength?: number;
24
- statusCode?: number;
25
- statusDescription?: string;
26
- redirectLocation?: string;
27
- otherHeaders: {};
28
- server?: string;
29
- };
30
- file: {
31
- fileType: any;
32
- fileSize: any;
33
- fileBlob: any;
34
- };
35
- };
36
-
37
- /**
38
- * Parse the header and split it up into the individual components
39
- *
40
- * @param {string} text - The text returned from the PL/SQL procedure.
41
- * @returns {Object} - The parsed page.
42
- */
43
- export function parse(text: string): pageType {
44
- const page: pageType = {
45
- body: '',
46
- head: {
47
- cookies: [],
48
- otherHeaders: {}
49
- },
50
- file: {
51
- fileType: null,
52
- fileSize: null,
53
- fileBlob: null
54
- }
55
- };
56
-
57
- //
58
- // 1) Split up the text in header and body
59
- //
60
-
61
- // Find the end of the header identified by \n\n
62
- let head = '';
63
- const headerEndPosition = text.indexOf('\n\n');
64
- if (headerEndPosition === -1) {
65
- head = text;
66
- } else {
67
- head = text.substring(0, headerEndPosition + 2);
68
- page.body = text.substring(headerEndPosition + 2);
69
- }
70
-
71
- //
72
- // 2) parse the headers
73
- //
74
-
75
- head.split('\n').forEach(line => {
76
- const header = getHeader(line);
77
-
78
- if (header) {
79
- switch (header.name.toLowerCase()) {
80
- case 'set-cookie':
81
- {
82
- const cookie = parseCookie(header.value);
83
- /* istanbul ignore else */
84
- if (cookie !== null) {
85
- page.head.cookies.push(cookie);
86
- }
87
- }
88
- break;
89
-
90
- case 'content-type':
91
- page.head.contentType = header.value;
92
- break;
93
-
94
- case 'x-db-content-length':
95
- {
96
- const contentLength = parseInt(header.value, 10);
97
- /* istanbul ignore else */
98
- if (!Number.isNaN(contentLength)) {
99
- page.head.contentLength = contentLength;
100
- }
101
- }
102
- break;
103
-
104
- case 'status':
105
- {
106
- const statusCode = parseInt(header.value, 10);
107
- /* istanbul ignore else */
108
- if (!Number.isNaN(statusCode)) {
109
- page.head.statusCode = statusCode;
110
- const index = header.value.indexOf(' ');
111
- /* istanbul ignore else */
112
- if (index !== -1) {
113
- page.head.statusDescription = header.value.substr(index + 1);
114
- }
115
- }
116
- }
117
- break;
118
-
119
- case 'location':
120
- page.head.redirectLocation = header.value;
121
- break;
122
-
123
- case 'x-oracle-ignore':
124
- break;
125
-
126
- default:
127
- //@ts-ignore
128
- page.head.otherHeaders[header.name] = header.value;
129
- break;
130
- }
131
- }
132
- });
133
-
134
- return page;
135
- }
136
-
137
- /*
138
- * get a header line
139
- */
140
- function getHeader(line: string): {name: string; value: string} | null {
141
- const index = line.indexOf(':');
142
-
143
- if (index !== -1) {
144
- return {
145
- name: line.substr(0, index).trim(),
146
- value: line.substr(index + 1).trim()
147
- };
148
- }
149
-
150
- return null;
151
- }
152
-
153
- /*
154
- * Send "default" response to the browser
155
- */
156
- export function send(req: express.Request, res: express.Response, page: {body: string; head: any; file: any}, trace: Trace): void {
157
- trace.write('send: ENTER');
158
-
159
- // Send the "cookies"
160
- //@ts-ignore
161
- page.head.cookies.forEach(cookie => {
162
- const name = cookie.name;
163
- const value = cookie.value;
164
-
165
- delete cookie.name;
166
- delete cookie.value;
167
-
168
- res.cookie(name, value, cookie);
169
- trace.append(`send: res.cookie("${name}", "${value}")\n`);
170
- });
171
-
172
- // If there is a "redirectLocation" header, we immediately redirect and return
173
- if (typeof page.head.redirectLocation === 'string' && page.head.redirectLocation.length > 0) {
174
- res.redirect(302, page.head.redirectLocation);
175
- trace.append(`send: res.redirect(302, "${page.head.redirectLocation}")\n`);
176
- return;
177
- }
178
-
179
- // Send all the "otherHeaders"
180
- for (const key in page.head.otherHeaders) {
181
- res.set(key, page.head.otherHeaders[key]);
182
- trace.append(`send: res.set("${key}", "${page.head.otherHeaders[key]}")\n`);
183
- }
184
-
185
- // If this is a file download, we eventually set the "Content-Type" and the file content and then return.
186
- if (page.file.fileType === 'B' || page.file.fileType === 'F') {
187
- if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
188
- res.writeHead(200, {'Content-Type': page.head.contentType});
189
- trace.append(`sendFile: res.writeHead("Content-Type", "${page.head.contentType}")\n`);
190
- }
191
- res.end(page.file.fileBlob, 'binary');
192
- trace.append('send: res.end()\n');
193
- return;
194
- }
195
-
196
- // Is the a "contentType" header
197
- if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
198
- res.set('Content-Type', page.head.contentType);
199
- trace.append(`send: res.set("Content-Type", "${page.head.contentType}")\n`);
200
- }
201
-
202
- // If we have a "Status" header, we send the header and then return.
203
- if (typeof page.head.statusCode === 'number') {
204
- res.status(page.head.statusCode).send(page.head.statusDescription);
205
- return;
206
- }
207
-
208
- // Send the body
209
- res.send(page.body);
210
- trace.append(`send: res.send\n${'-'.repeat(30)}${page.body}\n${'-'.repeat(30)}\n`);
211
-
212
- trace.write('send: EXIT');
213
- }
214
-
215
- /*
216
- * Parses a cookie string
217
- */
218
- function parseCookie(text: string): cookieType | null {
219
- // validate
220
- /* istanbul ignore next */
221
- if (typeof text !== 'string' || text.trim().length === 0) {
222
- return null;
223
- }
224
-
225
- // split the cookie into it's parts
226
- let cookieElements = text.split(';');
227
-
228
- // trim cookie elements
229
- cookieElements = cookieElements.map(element => element.trim());
230
-
231
- // get name and value
232
- const index = cookieElements[0].indexOf('=');
233
- /* istanbul ignore next */
234
- if (index <= 0) {
235
- // if the index is -1, there is no equal sign and if it's 0 the name is empty
236
- return null;
237
- }
238
- const cookie: any = {};
239
- cookie.name = cookieElements[0].substring(0, index).trim();
240
- cookie.value = cookieElements[0].substring(index + 1).trim();
241
-
242
- // remove the fisrt element
243
- cookieElements.shift();
244
-
245
- // get the other options
246
- cookieElements.forEach(element => {
247
- if (element.indexOf('path=') === 0) {
248
- cookie.path = element.substring(5);
249
- } else if (element.toLowerCase().indexOf('domain=') === 0) {
250
- cookie.domain = element.substring(7);
251
- } else if (element.toLowerCase().indexOf('secure=') === 0) {
252
- /* istanbul ignore next */
253
- cookie.secure = element.substring(7);
254
- } else if (element.toLowerCase().indexOf('expires=') === 0) {
255
- const date = tryDecodeDate(element.substring(8));
256
- if (date) {
257
- cookie.expires = date;
258
- }
259
- } else if (element.toLowerCase().indexOf('httponly') === 0) {
260
- cookie.httpOnly = true;
261
- }
262
- });
263
-
264
- return cookie;
265
- }
266
-
267
- /*
268
- * Decode a date
269
- */
270
- function tryDecodeDate(value: string): Date | null {
271
- try {
272
- return new Date(value);
273
- } catch (err) {
274
- /* istanbul ignore next */
275
- return null;
276
- }
277
- }
1
+ /*
2
+ * Page the raw page content and return the content to the client
3
+ */
4
+
5
+ import {Trace} from './trace';
6
+ import express from 'express';
7
+
8
+ type cookieType = {
9
+ name: string;
10
+ value: string;
11
+ path?: string;
12
+ domain?: string;
13
+ secure?: string;
14
+ expires?: Date;
15
+ httpOnly?: boolean;
16
+ };
17
+
18
+ type pageType = {
19
+ body: string;
20
+ head: {
21
+ cookies: Array<cookieType>;
22
+ contentType?: string;
23
+ contentLength?: number;
24
+ statusCode?: number;
25
+ statusDescription?: string;
26
+ redirectLocation?: string;
27
+ otherHeaders: Record<string, unknown>;
28
+ server?: string;
29
+ };
30
+ file: {
31
+ fileType: any;
32
+ fileSize: any;
33
+ fileBlob: any;
34
+ };
35
+ };
36
+
37
+ /**
38
+ * Parse the header and split it up into the individual components
39
+ *
40
+ * @param {string} text - The text returned from the PL/SQL procedure.
41
+ * @returns {Object} - The parsed page.
42
+ */
43
+ export function parse(text: string): pageType {
44
+ const page: pageType = {
45
+ body: '',
46
+ head: {
47
+ cookies: [],
48
+ otherHeaders: {}
49
+ },
50
+ file: {
51
+ fileType: null,
52
+ fileSize: null,
53
+ fileBlob: null
54
+ }
55
+ };
56
+
57
+ //
58
+ // 1) Split up the text in header and body
59
+ //
60
+
61
+ // Find the end of the header identified by \n\n
62
+ let head = '';
63
+ const headerEndPosition = text.indexOf('\n\n');
64
+ if (headerEndPosition === -1) {
65
+ head = text;
66
+ } else {
67
+ head = text.substring(0, headerEndPosition + 2);
68
+ page.body = text.substring(headerEndPosition + 2);
69
+ }
70
+
71
+ //
72
+ // 2) parse the headers
73
+ //
74
+
75
+ head.split('\n').forEach(line => {
76
+ const header = getHeader(line);
77
+
78
+ if (header) {
79
+ switch (header.name.toLowerCase()) {
80
+ case 'set-cookie':
81
+ {
82
+ const cookie = parseCookie(header.value);
83
+ /* istanbul ignore else */
84
+ if (cookie !== null) {
85
+ page.head.cookies.push(cookie);
86
+ }
87
+ }
88
+ break;
89
+
90
+ case 'content-type':
91
+ page.head.contentType = header.value;
92
+ break;
93
+
94
+ case 'x-db-content-length':
95
+ {
96
+ const contentLength = parseInt(header.value, 10);
97
+ /* istanbul ignore else */
98
+ if (!Number.isNaN(contentLength)) {
99
+ page.head.contentLength = contentLength;
100
+ }
101
+ }
102
+ break;
103
+
104
+ case 'status':
105
+ {
106
+ const statusCode = parseInt(header.value, 10);
107
+ /* istanbul ignore else */
108
+ if (!Number.isNaN(statusCode)) {
109
+ page.head.statusCode = statusCode;
110
+ const index = header.value.indexOf(' ');
111
+ /* istanbul ignore else */
112
+ if (index !== -1) {
113
+ page.head.statusDescription = header.value.substr(index + 1);
114
+ }
115
+ }
116
+ }
117
+ break;
118
+
119
+ case 'location':
120
+ page.head.redirectLocation = header.value;
121
+ break;
122
+
123
+ case 'x-oracle-ignore':
124
+ break;
125
+
126
+ default:
127
+ page.head.otherHeaders[header.name] = header.value;
128
+ break;
129
+ }
130
+ }
131
+ });
132
+
133
+ return page;
134
+ }
135
+
136
+ /*
137
+ * get a header line
138
+ */
139
+ function getHeader(line: string): {name: string; value: string} | null {
140
+ const index = line.indexOf(':');
141
+
142
+ if (index !== -1) {
143
+ return {
144
+ name: line.substr(0, index).trim(),
145
+ value: line.substr(index + 1).trim()
146
+ };
147
+ }
148
+
149
+ return null;
150
+ }
151
+
152
+ /*
153
+ * Send "default" response to the browser
154
+ */
155
+ export function send(req: express.Request, res: express.Response, page: {body: string; head: any; file: any}, trace: Trace): void {
156
+ trace.write('send: ENTER');
157
+
158
+ // Send the "cookies"
159
+ page.head.cookies.forEach((cookie: Record<string, unknown>) => {
160
+ const name = cookie.name as string;
161
+ const value = cookie.value as string;
162
+
163
+ delete cookie.name;
164
+ delete cookie.value;
165
+
166
+ res.cookie(name, value, cookie);
167
+ trace.append(`send: res.cookie("${name}", "${value}")\n`);
168
+ });
169
+
170
+ // If there is a "redirectLocation" header, we immediately redirect and return
171
+ if (typeof page.head.redirectLocation === 'string' && page.head.redirectLocation.length > 0) {
172
+ res.redirect(302, page.head.redirectLocation);
173
+ trace.append(`send: res.redirect(302, "${page.head.redirectLocation}")\n`);
174
+ return;
175
+ }
176
+
177
+ // Send all the "otherHeaders"
178
+ for (const key in page.head.otherHeaders) {
179
+ res.set(key, page.head.otherHeaders[key]);
180
+ trace.append(`send: res.set("${key}", "${page.head.otherHeaders[key]}")\n`);
181
+ }
182
+
183
+ // If this is a file download, we eventually set the "Content-Type" and the file content and then return.
184
+ if (page.file.fileType === 'B' || page.file.fileType === 'F') {
185
+ if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
186
+ res.writeHead(200, {'Content-Type': page.head.contentType});
187
+ trace.append(`sendFile: res.writeHead("Content-Type", "${page.head.contentType}")\n`);
188
+ }
189
+ res.end(page.file.fileBlob, 'binary');
190
+ trace.append('send: res.end()\n');
191
+ return;
192
+ }
193
+
194
+ // Is the a "contentType" header
195
+ if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
196
+ res.set('Content-Type', page.head.contentType);
197
+ trace.append(`send: res.set("Content-Type", "${page.head.contentType}")\n`);
198
+ }
199
+
200
+ // If we have a "Status" header, we send the header and then return.
201
+ if (typeof page.head.statusCode === 'number') {
202
+ res.status(page.head.statusCode).send(page.head.statusDescription);
203
+ return;
204
+ }
205
+
206
+ // Send the body
207
+ res.send(page.body);
208
+ trace.append(`send: res.send\n${'-'.repeat(30)}${page.body}\n${'-'.repeat(30)}\n`);
209
+
210
+ trace.write('send: EXIT');
211
+ }
212
+
213
+ /*
214
+ * Parses a cookie string
215
+ */
216
+ function parseCookie(text: string): cookieType | null {
217
+ // validate
218
+ /* istanbul ignore next */
219
+ if (typeof text !== 'string' || text.trim().length === 0) {
220
+ return null;
221
+ }
222
+
223
+ // split the cookie into it's parts
224
+ let cookieElements = text.split(';');
225
+
226
+ // trim cookie elements
227
+ cookieElements = cookieElements.map(element => element.trim());
228
+
229
+ // get name and value
230
+ const index = cookieElements[0].indexOf('=');
231
+ /* istanbul ignore next */
232
+ if (index <= 0) {
233
+ // if the index is -1, there is no equal sign and if it's 0 the name is empty
234
+ return null;
235
+ }
236
+ const cookie: any = {};
237
+ cookie.name = cookieElements[0].substring(0, index).trim();
238
+ cookie.value = cookieElements[0].substring(index + 1).trim();
239
+
240
+ // remove the fisrt element
241
+ cookieElements.shift();
242
+
243
+ // get the other options
244
+ cookieElements.forEach(element => {
245
+ if (element.indexOf('path=') === 0) {
246
+ cookie.path = element.substring(5);
247
+ } else if (element.toLowerCase().indexOf('domain=') === 0) {
248
+ cookie.domain = element.substring(7);
249
+ } else if (element.toLowerCase().indexOf('secure=') === 0) {
250
+ /* istanbul ignore next */
251
+ cookie.secure = element.substring(7);
252
+ } else if (element.toLowerCase().indexOf('expires=') === 0) {
253
+ const date = tryDecodeDate(element.substring(8));
254
+ if (date) {
255
+ cookie.expires = date;
256
+ }
257
+ } else if (element.toLowerCase().indexOf('httponly') === 0) {
258
+ cookie.httpOnly = true;
259
+ }
260
+ });
261
+
262
+ return cookie;
263
+ }
264
+
265
+ /*
266
+ * Decode a date
267
+ */
268
+ function tryDecodeDate(value: string): Date | null {
269
+ try {
270
+ return new Date(value);
271
+ } catch (err) {
272
+ /* istanbul ignore next */
273
+ return null;
274
+ }
275
+ }