web_plsql 0.13.2 → 0.14.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/README.md CHANGED
@@ -50,11 +50,6 @@ The `startServer` api uses the following configuration object:
50
50
  /**
51
51
  * @typedef {'basic' | 'debug'} errorStyleType
52
52
  */
53
- export const z$errorStyleType = z.enum(['basic', 'debug']);
54
-
55
- /**
56
- * @typedef {{alias: string, procedure: string}} pathAliasType
57
- */
58
53
 
59
54
  /**
60
55
  * @typedef {object} configStaticType
@@ -63,14 +58,42 @@ export const z$errorStyleType = z.enum(['basic', 'debug']);
63
58
  */
64
59
 
65
60
  /**
66
- * @typedef {object} configPlSqlType
61
+ * @callback transactionCallbackType
62
+ * @param {Request} req - Incoming request object.
63
+ * @param {import('oracledb').Connection} connection - Active database connection pool.
64
+ * @returns {void | Promise<void>}
65
+ */
66
+
67
+ /**
68
+ * @typedef {'commit' | 'rollback' | transactionCallbackType | undefined | null} transactionModeType
69
+ */
70
+
71
+ /**
72
+ * @typedef {object} configPlSqlHandlerType
73
+ * @property {string} defaultPage - The default page.
74
+ * @property {string} [pathAlias] - The path alias.
75
+ * @property {string} [pathAliasProcedure] - The path alias.
76
+ * @property {string} documentTable - The document table.
77
+ * @property {string[]} [exclusionList] - The exclusion list.
78
+ * @property {string} [requestValidationFunction] - The request validation function.
79
+ * @property {Record<string, string>} [cgi] - The additional CGI.
80
+ * @property {transactionModeType} [transactionMode='commit'] - Specifies an optional transaction mode.
81
+ * "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
82
+ * "rollback" this automatically rolles back any open transaction after each request.
83
+ * "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
84
+ * @property {errorStyleType} errorStyle - The error style.
85
+ */
86
+
87
+ /**
88
+ * @typedef {object} configPlSqlConfigType
67
89
  * @property {string} route - The PL/SQL route path.
68
90
  * @property {string} user - The Oracle username.
69
91
  * @property {string} password - The Oracle password.
70
92
  * @property {string} connectString - The Oracle connect string.
71
- * @property {string} defaultPage - The default page.
72
- * @property {pathAliasType} [pathAlias] - The path alias.
73
- * @property {string} documentTable - The document table.
93
+ */
94
+
95
+ /**
96
+ * @typedef {configPlSqlHandlerType & configPlSqlConfigType} configPlSqlType
74
97
  */
75
98
 
76
99
  /**
@@ -78,7 +101,6 @@ export const z$errorStyleType = z.enum(['basic', 'debug']);
78
101
  * @property {number} port - The server port number.
79
102
  * @property {configStaticType[]} routeStatic - The static routes.
80
103
  * @property {configPlSqlType[]} routePlSql - The PL/SQL routes.
81
- * @property {errorStyleType} errorStyle - The error style.
82
104
  * @property {string} loggerFilename - name of the request logger filename or '' if not required.
83
105
  */
84
106
  ```
@@ -147,30 +169,36 @@ The following mod_plsql DAD configuration translates to the configuration option
147
169
  # Configuration options
148
170
 
149
171
  ## Supported mod_plsql configuration options
150
- - PlsqlDatabaseConnectString
151
- - PlsqlDatabaseUserName
152
- - PlsqlDatabasePassword
153
- - PlsqlDefaultPage
154
- - PlsqlDocumentTablename
155
- - PlsqlErrorStyle
156
- - PlsqlLogEnable
157
- - PlsqlLogDirectory
158
- - PlsqlPathAlias
159
- - PlsqlPathAliasProcedure
172
+ - PlsqlDatabaseConnectString -> routePlSql[].connectString
173
+ - PlsqlDatabaseUserName -> routePlSql[].user
174
+ - PlsqlDatabasePassword -> routePlSql[].password
175
+ - PlsqlDefaultPage -> routePlSql[].defaultPage
176
+ - PlsqlDocumentTablename -> routePlSql[].documentTable
177
+ - PlsqlErrorStyle -> routePlSql[].errorStyle
178
+ - PlsqlLogEnable -> loggerFilename
179
+ - PlsqlLogDirectory -> loggerFilename
180
+ - PlsqlPathAlias -> routePlSql[].pathAlias
181
+ - PlsqlPathAliasProcedure -> routePlSql[].pathAliasProcedure
160
182
  - Default exclusion list.
161
- - PlsqlRequestValidationFunction
183
+ - PlsqlRequestValidationFunction -> routePlSql[].pathAliasProcedure
162
184
  - PlsqlExclusionList
163
185
  - Basic and custom authentication methods, based on the OWA_SEC package and custom packages.
164
186
 
187
+ ## Features that are only available in web_plsql
188
+ - The option `transactionModeType` specifies an optional transaction mode.
189
+ "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
190
+ "rollback" this automatically rolles back any open transaction after each request.
191
+ "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
192
+
165
193
  ## Features that are planned to be available in web_plsql
166
194
  - Support for APEX 5 or greater.
167
195
 
168
196
  ## Configuration options that will not be supported:
169
- - PlsqlDocumentProcedure
170
- - PlsqlAfterProcedure
171
197
  - PlsqlAlwaysDescribeProcedure
198
+ - PlsqlAfterProcedure
172
199
  - PlsqlBeforeProcedure
173
200
  - PlsqlCGIEnvironmentList
201
+ - PlsqlDocumentProcedure
174
202
  - PlsqlDocumentPath
175
203
  - PlsqlIdleSessionCleanupInterval
176
204
  - PlsqlSessionCookieName
@@ -1,6 +1,39 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import {startServer} from '../src/index.js';
4
+ import oracledb from 'oracledb';
5
+
6
+ /**
7
+ * @param {import('oracledb').Connection} connection - Oracle DB connection.
8
+ * @returns {Promise<boolean>}
9
+ */
10
+ const hasOpenTransaction = async (connection) => {
11
+ const sql = `SELECT COUNT(*) FROM sys.v_$transaction t JOIN sys.v_$session s ON t.ses_addr = s.saddr WHERE s.audsid = USERENV('SESSIONID')`;
12
+ /** @type {import('oracledb').Result<number>} */
13
+ let result;
14
+
15
+ try {
16
+ result = await connection.execute(sql, [], {
17
+ outFormat: oracledb.OUT_FORMAT_ARRAY,
18
+ });
19
+ } catch (error) {
20
+ console.error('hasOpenTransaction: error', error, sql);
21
+ return false;
22
+ }
23
+
24
+ if (
25
+ !Array.isArray(result.rows) ||
26
+ result.rows.length !== 1 ||
27
+ !Array.isArray(result.rows[0]) ||
28
+ result.rows[0].length !== 1 ||
29
+ typeof result.rows[0][0] !== 'number'
30
+ ) {
31
+ console.error('hasOpenTransaction: invalid result', result, sql);
32
+ return false;
33
+ }
34
+
35
+ return result.rows[0] > 0;
36
+ };
4
37
 
5
38
  void startServer({
6
39
  port: 8888,
@@ -22,6 +55,14 @@ void startServer({
22
55
  requestValidationFunction: 'sample_pkg.request_validation_function', // PlsqlRequestValidationFunction
23
56
  pathAlias: 'myalias', // PlsqlPathAlias
24
57
  pathAliasProcedure: 'sample_pkg.page_path_alias', // PlsqlPathAliasProcedure
58
+ transactionMode: 'commit',
59
+ /*
60
+ transactionMode: async (req, connection) => {
61
+ if (await hasOpenTransaction(connection)) {
62
+ console.log(`We detected an open transaction in "${req.originalUrl}"`);
63
+ }
64
+ },
65
+ */
25
66
  errorStyle: 'debug', // PlsqlErrorStyle
26
67
  },
27
68
  ],
@@ -15,6 +15,10 @@ GRANT create sequence TO &&SAMPLE_USER;
15
15
  GRANT create procedure TO &&SAMPLE_USER;
16
16
  GRANT execute on dbms_lob TO &&SAMPLE_USER;
17
17
 
18
+ -- only required if you want to test for open connections
19
+ GRANT select on sys.v_$transaction TO &&SAMPLE_USER;
20
+ GRANT select on sys.v_$session TO &&SAMPLE_USER;
21
+
18
22
  -- change schema
19
23
  ALTER SESSION SET CURRENT_SCHEMA=&&SAMPLE_USER;
20
24
 
@@ -18,7 +18,7 @@ PROCEDURE page_file_upload;
18
18
  PROCEDURE page_file_uploaded(name_array IN owa.vc_arr, value_array IN owa.vc_arr);
19
19
  PROCEDURE page_redirect;
20
20
  PROCEDURE page_location;
21
- PROCEDURE page_other;
21
+ PROCEDURE page_open_transaction;
22
22
  PROCEDURE page_path_alias(p_path IN VARCHAR2);
23
23
  PROCEDURE page_exclusion_list;
24
24
  PROCEDURE page_request_validation_function;
@@ -32,6 +32,7 @@ BEGIN
32
32
  htp.p('<li><a href="sample_pkg.page_file_upload">File upload</a></li>');
33
33
  htp.p('<li><a href="sample_pkg.page_redirect">Redirect</a></li>');
34
34
  htp.p('<li><a href="sample_pkg.page_location">Change location</a></li>');
35
+ htp.p('<li><a href="sample_pkg.page_open_transaction">Transaction mode: custom handler</a></li>');
35
36
  htp.p('<li><a href="myalias">pathAlias configuration setting</a></li>');
36
37
  htp.p('<li><a href="sample_pkg.page_exclusion_list">Custom exclusion list</a></li>');
37
38
  htp.p('<li><a href="sample_pkg.page_request_validation_function">Request validation function</a></li>');
@@ -236,12 +237,17 @@ BEGIN
236
237
  close_page();
237
238
  END page_location;
238
239
 
239
- PROCEDURE page_other
240
+ PROCEDURE page_open_transaction
240
241
  IS
242
+ l_name CONSTANT docTable.name%TYPE := 'page_open_transaction';
241
243
  BEGIN
242
- open_page('web_plsql - Other page');
244
+ open_page('web_plsql - Transaction mode: custom handler');
245
+
246
+ DELETE docTable WHERE name = l_name;
247
+ INSERT INTO docTable (name) VALUES (l_name);
248
+
243
249
  close_page();
244
- END page_other;
250
+ END page_open_transaction;
245
251
 
246
252
  PROCEDURE page_path_alias(p_path IN VARCHAR2)
247
253
  IS
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web_plsql",
3
- "version": "0.13.2",
3
+ "version": "0.14.0",
4
4
  "author": "Dieter Oberkofler <dieter.oberkofler@gmail.com>",
5
5
  "license": "MIT",
6
6
  "description": "The Express Middleware for Oracle PL/SQL",
@@ -53,7 +53,7 @@
53
53
  "image-save": "docker save web_plsql | gzip > web_plsql.tar.gz"
54
54
  },
55
55
  "peerDependencies": {
56
- "oracledb": "6.9.0"
56
+ "oracledb": "6.10.0"
57
57
  },
58
58
  "dependencies": {
59
59
  "basic-auth": "2.0.1",
@@ -67,27 +67,27 @@
67
67
  "morgan": "1.10.1",
68
68
  "multer": "2.0.2",
69
69
  "rotating-file-stream": "3.2.7",
70
- "zod": "4.1.11"
70
+ "zod": "4.1.12"
71
71
  },
72
72
  "devDependencies": {
73
73
  "@types/basic-auth": "1.1.8",
74
74
  "@types/compression": "1.8.1",
75
- "@types/cookie-parser": "1.4.9",
75
+ "@types/cookie-parser": "1.4.10",
76
76
  "@types/debug": "4.1.12",
77
77
  "@types/escape-html": "1.0.4",
78
78
  "@types/fs-extra": "11.0.4",
79
79
  "@types/morgan": "1.9.10",
80
80
  "@types/multer": "2.0.0",
81
- "@types/node": "24.5.2",
82
- "@types/oracledb": "6.9.1",
81
+ "@types/node": "24.9.2",
82
+ "@types/oracledb": "6.10.0",
83
83
  "@types/supertest": "6.0.3",
84
- "eslint": "9.36.0",
85
- "eslint-plugin-jsdoc": "60.1.1",
84
+ "eslint": "9.39.0",
85
+ "eslint-plugin-jsdoc": "61.1.11",
86
86
  "prettier": "3.6.2",
87
- "rimraf": "6.0.1",
87
+ "rimraf": "6.1.0",
88
88
  "shx": "0.4.0",
89
89
  "supertest": "7.1.4",
90
- "typescript": "5.9.2",
91
- "typescript-eslint": "8.44.0"
90
+ "typescript": "5.9.3",
91
+ "typescript-eslint": "8.46.2"
92
92
  }
93
93
  }
@@ -1,4 +1,4 @@
1
- import path from 'node:path';
1
+ //import path from 'node:path';
2
2
  import multer from 'multer';
3
3
 
4
4
  /**
@@ -17,11 +17,13 @@ import multer from 'multer';
17
17
  export const handlerUpload = () => {
18
18
  const upload = multer({
19
19
  storage: multer.diskStorage({
20
- destination: '/tmp/uploads',
20
+ /*
21
+ destination: '/tmp/webplsql',
21
22
  filename: (req, file, cb) => {
22
23
  const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
23
24
  cb(null, `${file.fieldname}-${uniqueSuffix}${path.extname(file.originalname)}`);
24
25
  },
26
+ */
25
27
  }),
26
28
  limits: {
27
29
  fileSize: 50 * 1024 * 1024, // 50MB limit
@@ -41,6 +41,7 @@ const requestHandler = async (req, res, next, connectionPool, options) => {
41
41
  errorPage(req, res, options, new RequestError('No procedure name given and no default page has been specified'));
42
42
  }
43
43
  } else {
44
+ // request handler
44
45
  await processRequest(req, res, options, connectionPool);
45
46
  }
46
47
  } catch (err) {
@@ -49,7 +49,7 @@ export const processRequest = async (req, res, options, connectionPool) => {
49
49
  const argObj = {};
50
50
  Object.assign(argObj, req.query);
51
51
 
52
- // For add the files that must be uploaded, we now copy the actual filename to the appropriate prameter to the invoked procedure.
52
+ // For add the files that must be uploaded, we now copy the actual filename to the appropriate parameter to the invoked procedure.
53
53
  filesToUpload.reduce((aggregator, file) => {
54
54
  aggregator[file.fieldname] = file.filename;
55
55
  return aggregator;
@@ -57,10 +57,27 @@ export const processRequest = async (req, res, options, connectionPool) => {
57
57
 
58
58
  // Does the request contain a body
59
59
  Object.assign(argObj, normalizeBody(req));
60
+ debug('executeRequest: argObj=', argObj);
60
61
 
61
62
  // invoke the Oracle procedure and get the page contenst
62
63
  await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection);
63
64
 
65
+ // transaction mode
66
+ if (options.transactionMode === 'rollback') {
67
+ debug('transactionMode: rollback');
68
+ await connection.rollback();
69
+ } else if (typeof options.transactionMode === 'function') {
70
+ debug('transactionMode: callback');
71
+ const result = options.transactionMode(req, connection);
72
+ debug('transactionMode: callback restult', result);
73
+ if (result && typeof result.then === 'function') {
74
+ await result;
75
+ }
76
+ } else {
77
+ debug('transactionMode: commit');
78
+ await connection.commit();
79
+ }
80
+
64
81
  // close database connection
65
82
  await connection.release();
66
83
 
@@ -8,6 +8,7 @@ import debugModule from 'debug';
8
8
  const debug = debugModule('webplsql:fileUpload');
9
9
 
10
10
  import {readFile, removeFile} from '../../util/file.js';
11
+ import {errorToString} from '../../util/errorToString.js';
11
12
  import oracledb from 'oracledb';
12
13
  import z from 'zod';
13
14
 
@@ -37,20 +38,25 @@ const z$reqFiles = z.array(
37
38
  * @returns {fileUploadType[]} - Promise that resolves with an array of files to be uploaded.
38
39
  */
39
40
  export const getFiles = (req) => {
40
- /** @type {fileUploadType[]} */
41
- const files = [];
42
-
43
41
  if (!('files' in req)) {
44
- return files;
42
+ debug('getFiles: no files');
43
+ return [];
45
44
  }
46
45
 
47
46
  if (typeof req.files === 'object' && req.files !== null && Object.keys(req.files).length === 0) {
48
- return files;
47
+ debug('getFiles: no files');
48
+ return [];
49
+ }
50
+
51
+ const files = z$reqFiles.parse(req.files);
52
+
53
+ for (const file of files) {
54
+ file.filename += `/${file.originalname}`;
49
55
  }
50
56
 
51
- debug('req.files=', req.files);
57
+ debug('getFiles', files);
52
58
 
53
- return z$reqFiles.parse(req.files);
59
+ return files;
54
60
  };
55
61
 
56
62
  /**
@@ -62,12 +68,22 @@ export const getFiles = (req) => {
62
68
  * @returns {Promise<void>} - Promise that resolves when uploaded.
63
69
  */
64
70
  export const uploadFile = async (file, doctable, databaseConnection) => {
71
+ debug(`uploadFile`, file, doctable);
72
+
65
73
  /* istanbul ignore next */
66
74
  if (typeof doctable !== 'string' || doctable.length === 0) {
67
75
  throw new Error(`Unable to upload file "${file.filename}" because the option ""doctable" has not been defined`);
68
76
  }
69
77
 
70
- const blobContent = await readFile(file.path);
78
+ // read file
79
+ let blobContent;
80
+ try {
81
+ blobContent = await readFile(file.path);
82
+ } catch (err) {
83
+ throw new Error(`Unable to load file "${file.path}".\n${errorToString(err)}`);
84
+ }
85
+
86
+ // insert file in document table
71
87
  const sql = `INSERT INTO ${doctable} (name, mime_type, doc_size, dad_charset, last_updated, content_type, blob_content) VALUES (:name, :mime_type, :doc_size, 'ascii', SYSDATE, 'BLOB', :blob_content)`;
72
88
  const bind = {
73
89
  name: file.filename,
@@ -78,13 +94,16 @@ export const uploadFile = async (file, doctable, databaseConnection) => {
78
94
  type: oracledb.BUFFER,
79
95
  },
80
96
  };
81
-
82
97
  try {
83
98
  await databaseConnection.execute(sql, bind, {autoCommit: true});
84
99
  } catch (err) {
85
- throw new Error(`Unable to insert file "${file.filename}"}`);
100
+ throw new Error(`Unable to insert file "${file.filename}".\n${errorToString(err)}`);
86
101
  }
87
102
 
88
103
  // remove the file
89
- await removeFile(file.path);
104
+ try {
105
+ await removeFile(file.path);
106
+ } catch (err) {
107
+ throw new Error(`Unable to remove file "${file.filename}".\n${errorToString(err)}`);
108
+ }
90
109
  };
@@ -14,30 +14,38 @@ export const showConfig = (config) => {
14
14
  console.log('NODE PL/SQL SERVER');
15
15
  console.log(LINE);
16
16
 
17
- console.log(`Server port: ${config.port}`);
18
- console.log(`Access log: ${config.loggerFilename.length > 0 ? config.loggerFilename : ''}`);
17
+ console.log(`Server port: ${config.port}`);
18
+ console.log(`Access log: ${config.loggerFilename.length > 0 ? config.loggerFilename : ''}`);
19
19
 
20
20
  if (config.routeStatic.length > 0) {
21
21
  console.log(LINE);
22
22
  config.routeStatic.forEach((e) => {
23
- console.log(`Static route: ${e.route}`);
24
- console.log(`Directory path: ${e.directoryPath}`);
23
+ console.log(`Static route: ${e.route}`);
24
+ console.log(`Directory path: ${e.directoryPath}`);
25
25
  });
26
26
  }
27
27
 
28
28
  if (config.routePlSql.length > 0) {
29
29
  console.log(LINE);
30
30
  config.routePlSql.forEach((e) => {
31
- console.log(`Route: http://localhost:${config.port}${e.route}`);
32
- console.log(`Oracle user: ${e.user}`);
33
- console.log(`Oracle server: ${e.connectString}`);
34
- console.log(`Oracle document table: ${e.documentTable}`);
35
- console.log(`Default page: ${e.defaultPage}`);
36
- console.log(`Path alias: ${e.pathAlias}`);
37
- console.log(`Path alias procedure: ${e.pathAliasProcedure}`);
38
- console.log(`Exclution list: ${e.exclusionList?.join(', ')}`);
39
- console.log(`Validation function: ${e.requestValidationFunction}`);
40
- console.log(`Error style: ${e.errorStyle}`);
31
+ let transactionMode = '';
32
+ if (typeof e.transactionMode === 'string') {
33
+ transactionMode = e.transactionMode;
34
+ } else if (typeof e.transactionMode === 'function') {
35
+ transactionMode = 'custom callback';
36
+ }
37
+
38
+ console.log(`Route: http://localhost:${config.port}${e.route}`);
39
+ console.log(`Oracle user: ${e.user}`);
40
+ console.log(`Oracle server: ${e.connectString}`);
41
+ console.log(`Oracle document table: ${e.documentTable}`);
42
+ console.log(`Default page: ${e.defaultPage}`);
43
+ console.log(`Path alias: ${e.pathAlias}`);
44
+ console.log(`Path alias procedure: ${e.pathAliasProcedure}`);
45
+ console.log(`Exclution list: ${e.exclusionList?.join(', ')}`);
46
+ console.log(`Validation function: ${e.requestValidationFunction}`);
47
+ console.log(`After request handler: ${transactionMode}`);
48
+ console.log(`Error style: ${e.errorStyle}`);
41
49
  });
42
50
  }
43
51
 
@@ -67,20 +67,20 @@ export const createServer = (app, ssl) => {
67
67
  export const startServer = async (config, ssl) => {
68
68
  debug('startServer: BEGIN', config, ssl);
69
69
 
70
- config = z$configType.parse(config);
70
+ const internalConfig = /** @type {configType} */ (z$configType.parse(config));
71
71
 
72
- showConfig(config);
72
+ showConfig(internalConfig);
73
73
 
74
74
  // Create express app
75
75
  const app = express();
76
76
 
77
77
  // Access log
78
- if (config.loggerFilename.length > 0) {
79
- app.use(handlerLogger(config.loggerFilename));
78
+ if (internalConfig.loggerFilename.length > 0) {
79
+ app.use(handlerLogger(internalConfig.loggerFilename));
80
80
  }
81
81
 
82
82
  // Serving static files
83
- for (const i of config.routeStatic) {
83
+ for (const i of internalConfig.routeStatic) {
84
84
  app.use(i.route, express.static(i.directoryPath));
85
85
  }
86
86
 
@@ -95,7 +95,7 @@ export const startServer = async (config, ssl) => {
95
95
  const connectionPools = [];
96
96
 
97
97
  // Oracle pl/sql express middleware
98
- for (const i of config.routePlSql) {
98
+ for (const i of internalConfig.routePlSql) {
99
99
  // Allocate the Oracle database pool
100
100
  const pool = await poolCreate(i.user, i.password, i.connectString);
101
101
  connectionPools.push(pool);
@@ -145,7 +145,7 @@ export const startServer = async (config, ssl) => {
145
145
  await /** @type {Promise<void>} */ (
146
146
  new Promise((resolve, reject) => {
147
147
  server
148
- .listen(config.port)
148
+ .listen(internalConfig.port)
149
149
  .on('listening', () => {
150
150
  debug('startServer: listener running');
151
151
  resolve();
@@ -160,7 +160,7 @@ export const startServer = async (config, ssl) => {
160
160
  debug('startServer: END');
161
161
 
162
162
  return {
163
- config,
163
+ config: internalConfig,
164
164
  connectionPools,
165
165
  app,
166
166
  server,
@@ -173,7 +173,7 @@ export const startServer = async (config, ssl) => {
173
173
  * @param {string} [filename] - The configuration filename.
174
174
  * @returns {configType} - Promise.
175
175
  */
176
- export const loadConfig = (filename = 'config.json') => z$configType.parse(getJsonFile(filename));
176
+ export const loadConfig = (filename = 'config.json') => /** @type {configType} */ (z$configType.parse(getJsonFile(filename)));
177
177
 
178
178
  /**
179
179
  * Start server from config file.
package/src/types.js CHANGED
@@ -3,6 +3,7 @@ import z from 'zod';
3
3
  /**
4
4
  * @typedef {import('oracledb').BindParameter} BindParameter
5
5
  * @typedef {import('express').CookieOptions} CookieOptions
6
+ * @typedef {import('express').Request} Request
6
7
  */
7
8
 
8
9
  /**
@@ -20,6 +21,17 @@ export const z$configStaticType = z.strictObject({
20
21
  directoryPath: z.string(),
21
22
  });
22
23
 
24
+ /**
25
+ * @callback transactionCallbackType
26
+ * @param {Request} req - Incoming request object.
27
+ * @param {import('oracledb').Connection} connection - Active database connection pool.
28
+ * @returns {void | Promise<void>}
29
+ */
30
+
31
+ /**
32
+ * @typedef {'commit' | 'rollback' | transactionCallbackType | undefined | null} transactionModeType
33
+ */
34
+
23
35
  /**
24
36
  * @typedef {object} configPlSqlHandlerType
25
37
  * @property {string} defaultPage - The default page.
@@ -29,6 +41,10 @@ export const z$configStaticType = z.strictObject({
29
41
  * @property {string[]} [exclusionList] - The exclusion list.
30
42
  * @property {string} [requestValidationFunction] - The request validation function.
31
43
  * @property {Record<string, string>} [cgi] - The additional CGI.
44
+ * @property {transactionModeType} [transactionMode='commit'] - Specifies an optional transaction mode.
45
+ * "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
46
+ * "rollback" this automatically rolles back any open transaction after each request.
47
+ * "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
32
48
  * @property {errorStyleType} errorStyle - The error style.
33
49
  */
34
50
  export const z$configPlSqlHandlerType = z.strictObject({
@@ -38,6 +54,7 @@ export const z$configPlSqlHandlerType = z.strictObject({
38
54
  documentTable: z.string(),
39
55
  exclusionList: z.array(z.string()).optional(),
40
56
  requestValidationFunction: z.string().optional(),
57
+ transactionMode: z.unknown().optional(),
41
58
  errorStyle: z$errorStyleType,
42
59
  });
43
60
 
package/src/util/type.js CHANGED
@@ -1,3 +1,5 @@
1
+ /* eslint-disable jsdoc/no-undefined-types */
2
+
1
3
  /**
2
4
  * Is the given value a string or an array of strings
3
5
  * @param {unknown} value - The value to check.
package/types/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * @typedef {import('oracledb').BindParameter} BindParameter
3
3
  * @typedef {import('express').CookieOptions} CookieOptions
4
+ * @typedef {import('express').Request} Request
4
5
  */
5
6
  /**
6
7
  * @typedef {'basic' | 'debug'} errorStyleType
@@ -18,6 +19,15 @@ export const z$configStaticType: z.ZodObject<{
18
19
  route: z.ZodString;
19
20
  directoryPath: z.ZodString;
20
21
  }, z.core.$strict>;
22
+ /**
23
+ * @callback transactionCallbackType
24
+ * @param {Request} req - Incoming request object.
25
+ * @param {import('oracledb').Connection} connection - Active database connection pool.
26
+ * @returns {void | Promise<void>}
27
+ */
28
+ /**
29
+ * @typedef {'commit' | 'rollback' | transactionCallbackType | undefined | null} transactionModeType
30
+ */
21
31
  /**
22
32
  * @typedef {object} configPlSqlHandlerType
23
33
  * @property {string} defaultPage - The default page.
@@ -27,6 +37,10 @@ export const z$configStaticType: z.ZodObject<{
27
37
  * @property {string[]} [exclusionList] - The exclusion list.
28
38
  * @property {string} [requestValidationFunction] - The request validation function.
29
39
  * @property {Record<string, string>} [cgi] - The additional CGI.
40
+ * @property {transactionModeType} [transactionMode='commit'] - Specifies an optional transaction mode.
41
+ * "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
42
+ * "rollback" this automatically rolles back any open transaction after each request.
43
+ * "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
30
44
  * @property {errorStyleType} errorStyle - The error style.
31
45
  */
32
46
  export const z$configPlSqlHandlerType: z.ZodObject<{
@@ -36,6 +50,7 @@ export const z$configPlSqlHandlerType: z.ZodObject<{
36
50
  documentTable: z.ZodString;
37
51
  exclusionList: z.ZodOptional<z.ZodArray<z.ZodString>>;
38
52
  requestValidationFunction: z.ZodOptional<z.ZodString>;
53
+ transactionMode: z.ZodOptional<z.ZodUnknown>;
39
54
  errorStyle: z.ZodEnum<{
40
55
  basic: "basic";
41
56
  debug: "debug";
@@ -68,6 +83,7 @@ export const z$configPlSqlType: z.ZodObject<{
68
83
  documentTable: z.ZodString;
69
84
  exclusionList: z.ZodOptional<z.ZodArray<z.ZodString>>;
70
85
  requestValidationFunction: z.ZodOptional<z.ZodString>;
86
+ transactionMode: z.ZodOptional<z.ZodUnknown>;
71
87
  errorStyle: z.ZodEnum<{
72
88
  basic: "basic";
73
89
  debug: "debug";
@@ -97,6 +113,7 @@ export const z$configType: z.ZodObject<{
97
113
  documentTable: z.ZodString;
98
114
  exclusionList: z.ZodOptional<z.ZodArray<z.ZodString>>;
99
115
  requestValidationFunction: z.ZodOptional<z.ZodString>;
116
+ transactionMode: z.ZodOptional<z.ZodUnknown>;
100
117
  errorStyle: z.ZodEnum<{
101
118
  basic: "basic";
102
119
  debug: "debug";
@@ -106,6 +123,7 @@ export const z$configType: z.ZodObject<{
106
123
  }, z.core.$strict>;
107
124
  export type BindParameter = import("oracledb").BindParameter;
108
125
  export type CookieOptions = import("express").CookieOptions;
126
+ export type Request = import("express").Request;
109
127
  export type errorStyleType = "basic" | "debug";
110
128
  export type configStaticType = {
111
129
  /**
@@ -117,6 +135,8 @@ export type configStaticType = {
117
135
  */
118
136
  directoryPath: string;
119
137
  };
138
+ export type transactionCallbackType = (req: Request, connection: import("oracledb").Connection) => void | Promise<void>;
139
+ export type transactionModeType = "commit" | "rollback" | transactionCallbackType | undefined | null;
120
140
  export type configPlSqlHandlerType = {
121
141
  /**
122
142
  * - The default page.
@@ -146,6 +166,13 @@ export type configPlSqlHandlerType = {
146
166
  * - The additional CGI.
147
167
  */
148
168
  cgi?: Record<string, string>;
169
+ /**
170
+ * - Specifies an optional transaction mode.
171
+ * "commit" this automatically commits any open transaction after each request. This is the defaults because this is what mod_plsql and ohs are doing.
172
+ * "rollback" this automatically rolles back any open transaction after each request.
173
+ * "transactionCallbackType" this allows to defined a custom handler as a JavaScript function.
174
+ */
175
+ transactionMode?: transactionModeType;
149
176
  /**
150
177
  * - The error style.
151
178
  */