web_plsql 1.0.0 → 1.3.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 (108) hide show
  1. package/README.md +108 -13
  2. package/dist/chunk-DQk6qfdC.mjs +18 -0
  3. package/dist/frontend/assets/main-0oxRTIkL.js +147 -0
  4. package/dist/frontend/assets/main-0oxRTIkL.js.br +0 -0
  5. package/dist/frontend/assets/main-0oxRTIkL.js.gz +0 -0
  6. package/dist/frontend/assets/main-TYgLak6O.css +1 -0
  7. package/dist/frontend/assets/main-TYgLak6O.css.br +0 -0
  8. package/dist/frontend/assets/main-TYgLak6O.css.gz +0 -0
  9. package/dist/frontend/index.html +516 -0
  10. package/dist/frontend/index.html.br +0 -0
  11. package/dist/frontend/index.html.gz +0 -0
  12. package/dist/index.d.mts +646 -0
  13. package/dist/index.mjs +3548 -0
  14. package/dist/index.mjs.map +1 -0
  15. package/dist/oracledb-mock-Dn8aHtd7.mjs +68 -0
  16. package/dist/oracledb-mock-Dn8aHtd7.mjs.map +1 -0
  17. package/examples/config-apex.js +1 -1
  18. package/examples/config-docker.js +1 -2
  19. package/package.json +45 -32
  20. package/examples/config-native.js +0 -68
  21. package/src/admin/client/charts.ts +0 -299
  22. package/src/admin/client/main.js +0 -3
  23. package/src/admin/client/tailwind.css +0 -41
  24. package/src/admin/index.html +0 -315
  25. package/src/admin/js/api.ts +0 -95
  26. package/src/admin/js/app.ts +0 -306
  27. package/src/admin/js/eslint.config.js +0 -74
  28. package/src/admin/js/schemas.ts +0 -153
  29. package/src/admin/js/templates/config.ts +0 -146
  30. package/src/admin/js/templates/errorRow.ts +0 -18
  31. package/src/admin/js/templates/index.ts +0 -3
  32. package/src/admin/js/templates/poolCard.ts +0 -61
  33. package/src/admin/js/tsconfig.json +0 -24
  34. package/src/admin/js/types.ts +0 -223
  35. package/src/admin/js/ui/theme.ts +0 -93
  36. package/src/admin/js/ui/views.ts +0 -164
  37. package/src/admin/js/util/format.ts +0 -27
  38. package/src/admin/lib/assets/main-zpdhQ1gD.css +0 -1
  39. package/src/admin/lib/chart.bundle.js +0 -139
  40. package/src/admin/style.css +0 -1321
  41. package/src/bin/load-test.js +0 -202
  42. package/src/handler/handlerAdmin.js +0 -198
  43. package/src/handler/handlerLogger.js +0 -21
  44. package/src/handler/handlerMetrics.js +0 -68
  45. package/src/handler/handlerUpload.js +0 -26
  46. package/src/handler/plsql/cgi.js +0 -131
  47. package/src/handler/plsql/errorPage.js +0 -117
  48. package/src/handler/plsql/handlerPlSql.js +0 -90
  49. package/src/handler/plsql/parsePage.js +0 -213
  50. package/src/handler/plsql/procedure.js +0 -356
  51. package/src/handler/plsql/procedureError.js +0 -40
  52. package/src/handler/plsql/procedureNamed.js +0 -268
  53. package/src/handler/plsql/procedureSanitize.js +0 -262
  54. package/src/handler/plsql/procedureVariable.js +0 -54
  55. package/src/handler/plsql/request.js +0 -124
  56. package/src/handler/plsql/requestError.js +0 -23
  57. package/src/handler/plsql/sendResponse.js +0 -136
  58. package/src/handler/plsql/stream.js +0 -28
  59. package/src/handler/plsql/upload.js +0 -109
  60. package/src/index.js +0 -20
  61. package/src/server/config.js +0 -57
  62. package/src/server/connections.js +0 -40
  63. package/src/server/server.js +0 -289
  64. package/src/types.js +0 -150
  65. package/src/util/cache.js +0 -123
  66. package/src/util/errorToString.js +0 -24
  67. package/src/util/file.js +0 -91
  68. package/src/util/html.js +0 -53
  69. package/src/util/jsonLogger.js +0 -47
  70. package/src/util/oracle.js +0 -80
  71. package/src/util/shutdown.js +0 -44
  72. package/src/util/trace.js +0 -325
  73. package/src/util/type.js +0 -6
  74. package/src/util/util.js +0 -66
  75. package/src/version.js +0 -5
  76. package/types/handler/handlerAdmin.d.ts +0 -9
  77. package/types/handler/handlerLogger.d.ts +0 -2
  78. package/types/handler/handlerMetrics.d.ts +0 -25
  79. package/types/handler/handlerUpload.d.ts +0 -7
  80. package/types/handler/plsql/cgi.d.ts +0 -4
  81. package/types/handler/plsql/errorPage.d.ts +0 -11
  82. package/types/handler/plsql/handlerPlSql.d.ts +0 -13
  83. package/types/handler/plsql/parsePage.d.ts +0 -3
  84. package/types/handler/plsql/procedure.d.ts +0 -12
  85. package/types/handler/plsql/procedureError.d.ts +0 -23
  86. package/types/handler/plsql/procedureNamed.d.ts +0 -14
  87. package/types/handler/plsql/procedureSanitize.d.ts +0 -11
  88. package/types/handler/plsql/procedureVariable.d.ts +0 -10
  89. package/types/handler/plsql/request.d.ts +0 -9
  90. package/types/handler/plsql/requestError.d.ts +0 -8
  91. package/types/handler/plsql/sendResponse.d.ts +0 -6
  92. package/types/handler/plsql/upload.d.ts +0 -5
  93. package/types/index.d.ts +0 -11
  94. package/types/server/config.d.ts +0 -2
  95. package/types/server/server.d.ts +0 -74
  96. package/types/types.d.ts +0 -328
  97. package/types/util/cache.d.ts +0 -69
  98. package/types/util/errorToString.d.ts +0 -1
  99. package/types/util/file.d.ts +0 -6
  100. package/types/util/html.d.ts +0 -3
  101. package/types/util/jsonLogger.d.ts +0 -45
  102. package/types/util/oracle.d.ts +0 -5
  103. package/types/util/shutdown.d.ts +0 -2
  104. package/types/util/trace.d.ts +0 -22
  105. package/types/util/type.d.ts +0 -1
  106. package/types/util/util.d.ts +0 -3
  107. package/types/version.d.ts +0 -1
  108. /package/{src/admin/favicon.svg → dist/frontend/assets/favicon-mQAM4tVu.svg} +0 -0
@@ -1,262 +0,0 @@
1
- import debugModule from 'debug';
2
- const debug = debugModule('webplsql:procedureSanitize');
3
-
4
- import oracledb from 'oracledb';
5
- import z from 'zod';
6
- import {RequestError} from './requestError.js';
7
- import {errorToString} from '../../util/errorToString.js';
8
-
9
- /**
10
- * @typedef {import('express').Request} Request
11
- * @typedef {import('express').Response} Response
12
- * @typedef {import('oracledb').Connection} Connection
13
- * @typedef {import('oracledb').Result<unknown>} Result
14
- * @typedef {import('../../types.js').argObjType} argObjType
15
- * @typedef {import('../../types.js').fileUploadType} fileUploadType
16
- * @typedef {import('../../types.js').environmentType} environmentType
17
- * @typedef {import('../../types.js').configPlSqlHandlerType} configPlSqlHandlerType
18
- * @typedef {import('../../types.js').BindParameterConfig} BindParameterConfig
19
- * @typedef {import('../../util/cache.js').Cache<string>} ProcedureNameCache
20
- */
21
-
22
- const DEFAULT_EXCLUSION_LIST = ['sys.', 'dbms_', 'utl_', 'owa_', 'htp.', 'htf.', 'wpg_docload.', 'ctxsys.', 'mdsys.'];
23
-
24
- import {Cache} from '../../util/cache.js';
25
- /** @type {Cache<{valid: boolean}>} */
26
- const validationFunctionCache = new Cache();
27
-
28
- /**
29
- * Resolve the procedure name using dbms_utility.name_resolve.
30
- *
31
- * @param {string} procName - The procedure name to resolve.
32
- * @param {Connection} databaseConnection - The database connection.
33
- * @param {ProcedureNameCache} procedureNameCache - The procedure name cache.
34
- * @returns {Promise<string>} - The resolved canonical procedure name (SCHEMA.NAME).
35
- */
36
- const resolveProcedureName = async (procName, databaseConnection, procedureNameCache) => {
37
- // Check cache
38
- const cachedName = procedureNameCache.get(procName);
39
- if (cachedName) {
40
- debug(`resolveProcedureName: Cache hit for "${procName}" -> "${cachedName}"`);
41
- return cachedName;
42
- }
43
-
44
- debug(`resolveProcedureName: Cache miss for "${procName}". Resolving in DB...`);
45
-
46
- const sql = `
47
- DECLARE
48
- l_schema VARCHAR2(128);
49
- l_part1 VARCHAR2(128);
50
- l_part2 VARCHAR2(128);
51
- l_dblink VARCHAR2(128);
52
- l_part1_type NUMBER;
53
- l_object_number NUMBER;
54
- BEGIN
55
- dbms_utility.name_resolve(
56
- name => :name,
57
- context => 1,
58
- schema => l_schema,
59
- part1 => l_part1,
60
- part2 => l_part2,
61
- dblink => l_dblink,
62
- part1_type => l_part1_type,
63
- object_number => l_object_number
64
- );
65
-
66
- -- Reconstruct the canonical name
67
- -- If it's a package procedure: schema.package.procedure
68
- -- If it's a standalone procedure: schema.procedure
69
-
70
- IF l_part1 IS NOT NULL THEN
71
- :resolved := l_schema || '.' || l_part1 || '.' || l_part2;
72
- ELSE
73
- :resolved := l_schema || '.' || l_part2;
74
- END IF;
75
- END;
76
- `;
77
-
78
- const bind = {
79
- name: {dir: oracledb.BIND_IN, type: oracledb.STRING, val: procName},
80
- resolved: {dir: oracledb.BIND_OUT, type: oracledb.STRING, maxSize: 400},
81
- };
82
-
83
- try {
84
- const result = await databaseConnection.execute(sql, bind);
85
- const {resolved} = z.object({resolved: z.string()}).parse(result.outBinds);
86
-
87
- if (!resolved) {
88
- throw new RequestError(`Could not resolve procedure name "${procName}"`);
89
- }
90
-
91
- debug(`resolveProcedureName: Resolved "${procName}" -> "${resolved}"`);
92
-
93
- // Update cache
94
- procedureNameCache.set(procName, resolved);
95
-
96
- return resolved;
97
- } catch (err) {
98
- debug(`resolveProcedureName: Error resolving "${procName}"`, err);
99
- // Rethrow as RequestError to indicate 404/403
100
- throw new RequestError(`Procedure "${procName}" not found or not accessible.\n${errorToString(err)}`);
101
- }
102
- };
103
-
104
- /**
105
- * Sanitize the procedure name.
106
- *
107
- * @param {string} procName - The procedure name.
108
- * @param {Connection} databaseConnection - The database connection
109
- * @param {configPlSqlHandlerType} options - the options for the middleware.
110
- * @param {ProcedureNameCache} procedureNameCache - The procedure name cache.
111
- * @returns {Promise<string>} Promise resolving to final procedure name.
112
- */
113
- export const sanitizeProcName = async (procName, databaseConnection, options, procedureNameCache) => {
114
- debug('sanitizeProcName', procName);
115
-
116
- // make lowercase and trim
117
- let finalProcName = procName.toLowerCase().trim();
118
-
119
- // remove special characters (basic sanity check before DB call)
120
- finalProcName = removeSpecialCharacters(finalProcName);
121
-
122
- // check for default exclusions
123
- for (const i of DEFAULT_EXCLUSION_LIST) {
124
- if (finalProcName.startsWith(i)) {
125
- const error = `Procedure name "${procName}" is in default exclusion list "${DEFAULT_EXCLUSION_LIST.join(',')}"`;
126
- debug(error);
127
- throw new RequestError(error);
128
- }
129
- }
130
-
131
- // check for custom exclusions
132
- if (options.exclusionList && options.exclusionList.length > 0) {
133
- for (const i of options.exclusionList) {
134
- if (finalProcName.startsWith(i)) {
135
- const error = `Procedure name "${procName}" is in custom exclusion list "${options.exclusionList.join(',')}"`;
136
- debug(error);
137
- throw new RequestError(error);
138
- }
139
- }
140
- }
141
-
142
- // Check request validation function
143
- if (options.requestValidationFunction && options.requestValidationFunction.length > 0) {
144
- // Note: We might want to scope this cache too, but for now let's focus on the procedure name cache.
145
- // The original code used a global cache for this.
146
- // For strict correctness, we should probably verify the *resolved* name,
147
- // but legacy behavior checks the input name.
148
- const valid = await requestValidationFunction(finalProcName, options.requestValidationFunction, databaseConnection);
149
- if (!valid) {
150
- const error = `Procedure name "${procName}" is not valid according to the request validation function "${options.requestValidationFunction}"`;
151
- debug(error);
152
- throw new RequestError(error);
153
- }
154
- }
155
-
156
- // NEW: Resolve the procedure name against the database
157
- // This prevents SQL injection and ensures the procedure exists
158
- const resolvedName = await resolveProcedureName(finalProcName, databaseConnection, procedureNameCache);
159
-
160
- return resolvedName;
161
- };
162
-
163
- /**
164
- * @param {string} str - string
165
- * @returns {string} - string
166
- */
167
- const removeSpecialCharacters = (str) => {
168
- if (str === null || str === undefined) {
169
- return '';
170
- }
171
-
172
- const chars = [];
173
-
174
- for (const c of str) {
175
- if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c === '.' || c === '_' || c === '#' || c === '$') {
176
- chars.push(c);
177
- }
178
- }
179
-
180
- return chars.join('');
181
- };
182
-
183
- /**
184
- * @param {string} procName - The procedure name.
185
- * @param {string} requestValidationFunction - The request validation function.
186
- * @param {Connection} databaseConnection - The database connection
187
- * @returns {Promise<boolean>} Promise resolving to final procedure name.
188
- */
189
- const loadRequestValid = async (procName, requestValidationFunction, databaseConnection) => {
190
- /** @type {BindParameterConfig} */
191
- const bind = {
192
- proc: {dir: oracledb.BIND_IN, type: oracledb.STRING, val: procName},
193
- valid: {dir: oracledb.BIND_OUT, type: oracledb.NUMBER},
194
- };
195
-
196
- const SQL = [
197
- 'DECLARE',
198
- ' l_valid NUMBER := 0;',
199
- 'BEGIN',
200
- ` IF (${requestValidationFunction}(:proc)) THEN`,
201
- ' l_valid := 1;',
202
- ' END IF;',
203
- ' :valid := l_valid;',
204
- 'END;',
205
- ].join('\n');
206
-
207
- /** @type {Result} */
208
- let result = {};
209
- try {
210
- result = await databaseConnection.execute(SQL, bind);
211
- } catch (err) {
212
- debug('result', result);
213
- const message = `Error when validating procedure name "${procName}"\n${SQL}\n${errorToString(err)}`;
214
- throw new RequestError(message);
215
- }
216
-
217
- try {
218
- const data = z.strictObject({valid: z.number()}).parse(result.outBinds);
219
- return data.valid === 1;
220
- } catch (err) {
221
- debug('result', result.outBinds);
222
- const message = `Internal error when parsing ${result.outBinds}\n${errorToString(err)}`;
223
- throw new Error(message);
224
- }
225
- };
226
-
227
- /**
228
- * Request validation function.
229
- *
230
- * @param {string} procName - The procedure name.
231
- * @param {string} requestValidationFunction - The request validation function.
232
- * @param {Connection} databaseConnection - The database connection
233
- * @returns {Promise<boolean>} Promise resolving to final procedure name.
234
- */
235
- const requestValidationFunction = async (procName, requestValidationFunction, databaseConnection) => {
236
- debug('requestValidationFunction', procName, requestValidationFunction);
237
-
238
- // calculate the key
239
- const key = procName.toLowerCase();
240
-
241
- // lookup in the cache
242
- const cacheEntry = validationFunctionCache.get(key);
243
-
244
- // if we found the procedure in the cache, we return it (hitCount already incremented by get)
245
- if (cacheEntry !== undefined) {
246
- if (debug.enabled) {
247
- debug(`requestValidationFunction: procedure "${procName}" found in cache`);
248
- }
249
- return cacheEntry.valid;
250
- }
251
-
252
- // load from database
253
- if (debug.enabled) {
254
- debug(`requestValidationFunction: procedure "${procName}" not found in cache and must be loaded`);
255
- }
256
- const valid = await loadRequestValid(procName, requestValidationFunction, databaseConnection);
257
-
258
- // add to the cache
259
- validationFunctionCache.set(key, {valid});
260
-
261
- return valid;
262
- };
@@ -1,54 +0,0 @@
1
- /*
2
- * Invoke the Oracle procedure and return the raw content of the page
3
- */
4
-
5
- import debugModule from 'debug';
6
- const debug = debugModule('webplsql:procedureVariable');
7
-
8
- import oracledb from 'oracledb';
9
-
10
- /**
11
- * @typedef {import('express').Request} Request
12
- * @typedef {import('oracledb').Connection} Connection
13
- * @typedef {import('oracledb').Result<unknown>} Result
14
- * @typedef {import('../../types.js').configPlSqlHandlerType} configPlSqlHandlerType
15
- * @typedef {import('../../types.js').argObjType} argObjType
16
- * @typedef {import('../../types.js').BindParameterConfig} BindParameterConfig
17
- */
18
-
19
- /**
20
- * Get the sql statement and bindings for the procedure to execute for a variable number of arguments
21
- * @param {Request} _req - The req object represents the HTTP request. (only used for debugging)
22
- * @param {string} procName - The procedure to execute
23
- * @param {argObjType} argObj - The arguments to pass to the procedure
24
- * @returns {{sql: string; bind: BindParameterConfig}} - The SQL statement and bindings for the procedure to execute
25
- */
26
- export const getProcedureVariable = (_req, procName, argObj) => {
27
- if (debug.enabled) {
28
- debug(`getProcedureVariable: ${procName} arguments=`, argObj);
29
- }
30
-
31
- const names = [];
32
- const values = [];
33
-
34
- for (const key in argObj) {
35
- const value = argObj[key];
36
- if (typeof value === 'string') {
37
- names.push(key);
38
- values.push(value);
39
- } else if (Array.isArray(value)) {
40
- value.forEach((item) => {
41
- names.push(key);
42
- values.push(item);
43
- });
44
- }
45
- }
46
-
47
- return {
48
- sql: `${procName}(:argnames, :argvalues)`,
49
- bind: {
50
- argnames: {dir: oracledb.BIND_IN, type: oracledb.STRING, val: names},
51
- argvalues: {dir: oracledb.BIND_IN, type: oracledb.STRING, val: values},
52
- },
53
- };
54
- };
@@ -1,124 +0,0 @@
1
- /*
2
- * Process the http request
3
- */
4
-
5
- import debugModule from 'debug';
6
- const debug = debugModule('webplsql:request');
7
-
8
- import util from 'node:util';
9
- import {invokeProcedure} from './procedure.js';
10
- import {getCGI} from './cgi.js';
11
- import {getFiles} from './upload.js';
12
- import {RequestError} from './requestError.js';
13
- import {isStringOrArrayOfString} from '../../util/type.js';
14
-
15
- /**
16
- * @typedef {import('express').Request} Request
17
- * @typedef {import('express').Response} Response
18
- * @typedef {import('oracledb').Pool} Pool
19
- * @typedef {import('oracledb').Connection} Connection
20
- * @typedef {import('../../types.js').argObjType} argObjType
21
- * @typedef {import('../../types.js').configPlSqlHandlerType} configPlSqlHandlerType
22
- * @typedef {import('../../util/cache.js').Cache<string>} ProcedureNameCache
23
- * @typedef {import('../../util/cache.js').Cache<import('./procedureNamed.js').argsType>} ArgumentCache
24
- */
25
-
26
- /**
27
- * Execute the request
28
- *
29
- * @param {Request} req - The req object represents the HTTP request.
30
- * @param {Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
31
- * @param {configPlSqlHandlerType} options - the options for the middleware.
32
- * @param {Pool} connectionPool - The connection pool.
33
- * @param {ProcedureNameCache} procedureNameCache - The procedure name cache.
34
- * @param {ArgumentCache} argumentCache - The argument cache.
35
- * @returns {Promise<void>} - Promise resolving to th page
36
- */
37
- export const processRequest = async (req, res, options, connectionPool, procedureNameCache, argumentCache) => {
38
- debug('processRequest: ENTER');
39
-
40
- //
41
- if (Array.isArray(req.params.name)) {
42
- console.warn(`processRequest: WARNING: the req.params.name is not a string but an array of string: ${req.params.name.join(',')}`);
43
- }
44
-
45
- // open database connection
46
- const connection = await connectionPool.getConnection();
47
-
48
- // Get the CGI
49
- const cgiObj = getCGI(req, options.documentTable ?? '', options.cgi ?? {});
50
- debug('processRequest: cgiObj=', cgiObj);
51
-
52
- // Does the request contain any files
53
- const filesToUpload = getFiles(req);
54
- debug('processRequest: filesToUpload=', filesToUpload);
55
-
56
- // Add the query properties
57
- /** @type {argObjType} */
58
- const argObj = {};
59
- Object.assign(argObj, req.query);
60
-
61
- // For add the files that must be uploaded, we now copy the actual filename to the appropriate parameter to the invoked procedure.
62
- filesToUpload.reduce((aggregator, file) => {
63
- aggregator[file.fieldname] = file.filename;
64
- return aggregator;
65
- }, argObj);
66
-
67
- // Does the request contain a body
68
- Object.assign(argObj, normalizeBody(req));
69
- debug('processRequest: argObj=', argObj);
70
-
71
- // invoke the Oracle procedure and get the page contenst
72
- await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection, procedureNameCache, argumentCache);
73
-
74
- // transaction mode
75
- if (options.transactionMode === 'rollback') {
76
- debug('transactionMode: rollback');
77
- await connection.rollback();
78
- } else if (typeof options.transactionMode === 'function') {
79
- debug('transactionMode: callback');
80
- const procName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;
81
- const result = options.transactionMode(connection, procName ?? '');
82
- debug('transactionMode: callback restult', result);
83
- if (result && typeof result.then === 'function') {
84
- await result;
85
- }
86
- } else {
87
- debug('transactionMode: commit');
88
- await connection.commit();
89
- }
90
-
91
- // close database connection
92
- await connection.release();
93
-
94
- debug('processRequest: EXIT');
95
- };
96
-
97
- /**
98
- * Normalize the body by making sure that only "simple" parameters and no nested objects are submitted
99
- * @param {Request} req - The req object represents the HTTP request.
100
- * @returns {Record<string, string | string[]>} - The normalized body.
101
- */
102
- const normalizeBody = (req) => {
103
- /** @type {Record<string, string | string[]>} */
104
- const args = {};
105
-
106
- /* v8 ignore else - body validation */
107
- if (typeof req.body === 'object' && req.body !== null) {
108
- for (const key in req.body) {
109
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
110
- const value = req.body[key];
111
- /* v8 ignore else - type validation */
112
- if (isStringOrArrayOfString(value)) {
113
- args[key] = value;
114
- } else {
115
- /* v8 ignore next - invalid body type */
116
- throw new RequestError(
117
- `The element "${key}" in the body is not a string or an array of strings!\n${util.inspect(req.body, {showHidden: false, depth: null, colors: false})}`,
118
- );
119
- }
120
- }
121
- }
122
-
123
- return args;
124
- };
@@ -1,23 +0,0 @@
1
- /*
2
- * RequestError
3
- */
4
-
5
- export class RequestError extends Error {
6
- /** @type {Date} */
7
- timestamp;
8
-
9
- /**
10
- * @param {string} message - The error message.
11
- */
12
- constructor(message) {
13
- super(message);
14
-
15
- // Maintains proper stack trace for where our error was thrown (only available on V8)
16
- if (Error.captureStackTrace) {
17
- Error.captureStackTrace(this, RequestError);
18
- }
19
-
20
- // Custom debugging information
21
- this.timestamp = new Date();
22
- }
23
- }
@@ -1,136 +0,0 @@
1
- /*
2
- * Page the raw page content and return the content to the client
3
- */
4
-
5
- import debugModule from 'debug';
6
- const debug = debugModule('webplsql:sendResponse');
7
-
8
- import stream from 'node:stream';
9
- import {getBlock} from '../../util/trace.js';
10
-
11
- /**
12
- * @typedef {import('express').Request} Request
13
- * @typedef {import('express').Response} Response
14
- * @typedef {import('express').CookieOptions} CookieOptions
15
- * @typedef {import('../../types.js').pageType} pageType
16
- * @typedef {import('../../types.js').cookieType} cookieType
17
- */
18
-
19
- /**
20
- * Send "default" response to the browser
21
- * @param {Request} _req - The req object represents the HTTP request.
22
- * @param {Response} res - The res object represents the HTTP response that an Express app sends when it gets an HTTP request.
23
- * @param {pageType} page - The page to send.
24
- * @returns {Promise<void>}
25
- */
26
- export const sendResponse = async (_req, res, page) => {
27
- /** @type {string[]} */
28
- const debugText = [];
29
-
30
- // Send the "cookies"
31
- page.head.cookies.forEach((cookie) => {
32
- if (debug.enabled) {
33
- debugText.push(`res.cookie: name="${cookie.name}" value="${cookie.value}" options=${JSON.stringify(cookie.options)}`);
34
- }
35
-
36
- res.cookie(cookie.name, cookie.value, cookie.options);
37
- });
38
-
39
- // If there is a "redirectLocation" header, we immediately redirect and return
40
- if (typeof page.head.redirectLocation === 'string' && page.head.redirectLocation.length > 0) {
41
- if (debug.enabled) {
42
- debugText.push(`res.redirect(302, "${page.head.redirectLocation}")`);
43
- debug(getBlock('RESPONSE', debugText.join('\n')));
44
- }
45
-
46
- res.redirect(302, page.head.redirectLocation);
47
- return;
48
- }
49
-
50
- // Send all the "otherHeaders"
51
- for (const key in page.head.otherHeaders) {
52
- if (debug.enabled) {
53
- debugText.push(`res.set("${key}", "${page.head.otherHeaders[key]}")`);
54
- }
55
-
56
- res.set(key, page.head.otherHeaders[key]);
57
- }
58
-
59
- // If this is a file download, we eventually set the "Content-Type" and the file content and then return.
60
- if (page.file.fileType === 'B' || page.file.fileType === 'F') {
61
- /** @type {Record<string, string>} */
62
- const headers = {};
63
-
64
- if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
65
- headers['Content-Type'] = page.head.contentType;
66
- }
67
-
68
- if (typeof page.file.fileSize === 'number' && page.file.fileSize > 0) {
69
- headers['Content-Length'] = page.file.fileSize.toString();
70
- }
71
-
72
- if (Object.keys(headers).length > 0) {
73
- if (debug.enabled) {
74
- debugText.push(`res.writeHead(200, ${JSON.stringify(headers)})`);
75
- }
76
- res.writeHead(200, headers);
77
- }
78
-
79
- // Check if fileBlob is a stream
80
- if (page.file.fileBlob instanceof stream.Readable) {
81
- if (debug.enabled) {
82
- debugText.push(`res.pipe("${page.file.fileType}") - streaming`);
83
- debug(getBlock('RESPONSE', debugText.join('\n')));
84
- }
85
-
86
- /** @type {Promise<void>} */
87
- const streamComplete = new Promise((resolve, reject) => {
88
- if (page.file.fileBlob instanceof stream.Readable) {
89
- page.file.fileBlob.pipe(res);
90
- page.file.fileBlob.on('end', () => resolve());
91
- /* v8 ignore next - error handler */
92
- page.file.fileBlob.on('error', (/** @type {Error} */ err) => reject(err));
93
- /* v8 ignore next - error handler */
94
- res.on('close', () => resolve());
95
- }
96
- });
97
- await streamComplete;
98
- } else {
99
- if (debug.enabled) {
100
- debugText.push(`res.end("${page.file.fileType}") - buffer`);
101
- debug(getBlock('RESPONSE', debugText.join('\n')));
102
- }
103
-
104
- res.end(page.file.fileBlob, 'binary');
105
- }
106
- return;
107
- }
108
-
109
- // Is the a "contentType" header
110
- if (typeof page.head.contentType === 'string' && page.head.contentType.length > 0) {
111
- if (debug.enabled) {
112
- debugText.push(`res.set("Content-Type", "${page.head.contentType}")`);
113
- }
114
-
115
- res.set('Content-Type', page.head.contentType);
116
- }
117
-
118
- // If we have a "Status" header, we send the header and then return.
119
- if (typeof page.head.statusCode === 'number') {
120
- if (debug.enabled) {
121
- debugText.push(`res.status(page.head.statusCode).send("${page.head.statusDescription}")`);
122
- debug(getBlock('RESPONSE', debugText.join('\n')));
123
- }
124
-
125
- res.status(page.head.statusCode).send(page.head.statusDescription);
126
- return;
127
- }
128
-
129
- // Send the body
130
- if (debug.enabled) {
131
- debugText.push(`${'-'.repeat(60)}\n${page.body}`);
132
- debug(getBlock('RESPONSE', debugText.join('\n')));
133
- }
134
-
135
- res.send(page.body);
136
- };
@@ -1,28 +0,0 @@
1
- import stream from 'node:stream';
2
-
3
- /**
4
- * Convert a readable stream to a buffer.
5
- *
6
- * @param {stream.Readable} readable - The readable stream.
7
- * @returns {Promise<Buffer>} The buffer.
8
- */
9
- export const streamToBuffer = async (readable) => {
10
- return new Promise((resolve, reject) => {
11
- /** @type {Buffer[]} */
12
- const buffers = [];
13
-
14
- readable.on('data', (buffer) => {
15
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
16
- buffers.push(buffer);
17
- });
18
-
19
- readable.on('end', () => {
20
- resolve(Buffer.concat(buffers));
21
- });
22
-
23
- readable.on('error', (err) => {
24
- /* v8 ignore next - error handler */
25
- reject(err);
26
- });
27
- });
28
- };