web_plsql 1.3.1 → 1.4.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/dist/index.mjs CHANGED
@@ -111,11 +111,14 @@ const transactionModeSchema = z$1.union([
111
111
  /**
112
112
  * Authentication configuration for a PL/SQL route
113
113
  */
114
- const z$authSchema = z$1.strictObject({
114
+ const z$authSchema = z$1.union([z$1.strictObject({
115
115
  type: z$1.literal("basic"),
116
116
  callback: z$1.custom((val) => typeof val === "function", { message: "Invalid auth callback" }),
117
117
  realm: z$1.string().optional()
118
- });
118
+ }), z$1.strictObject({
119
+ type: z$1.literal("custom"),
120
+ callback: z$1.custom((val) => typeof val === "function", { message: "Invalid auth callback" })
121
+ })]);
119
122
  /**
120
123
  * PL/SQL handler behavior configuration
121
124
  */
@@ -211,7 +214,7 @@ globalThis.__VERSION__ ??= "**development**";
211
214
  * Returns the current library version
212
215
  * @returns {string} - Version.
213
216
  */
214
- const getVersion = () => "1.3.1";
217
+ const getVersion = () => "1.4.0";
215
218
 
216
219
  //#endregion
217
220
  //#region src/backend/server/config.ts
@@ -2969,33 +2972,36 @@ const processRequest = async (req, res, options, connectionPool, procedureNameCa
2969
2972
  debug$2("processRequest: ENTER");
2970
2973
  if (typeof req.params.name !== "string") console.warn(`processRequest: WARNING: the req.params.name is not a string but an array of string: ${req.params.name}`);
2971
2974
  const connection = await connectionPool.getConnection();
2972
- const cgiObj = getCGI(req, options.documentTable, options.cgi ?? {}, authenticatedUser);
2973
- debug$2("processRequest: cgiObj=", cgiObj);
2974
- const filesToUpload = getFiles(req);
2975
- debug$2("processRequest: filesToUpload=", filesToUpload);
2976
- const argObj = {};
2977
- Object.assign(argObj, req.query);
2978
- filesToUpload.reduce((aggregator, file) => {
2979
- aggregator[file.fieldname] = file.filename;
2980
- return aggregator;
2981
- }, argObj);
2982
- Object.assign(argObj, normalizeBody(req));
2983
- debug$2("processRequest: argObj=", argObj);
2984
- await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection, procedureNameCache, argumentCache);
2985
- if (options.transactionMode === "rollback") {
2986
- debug$2("transactionMode: rollback");
2987
- await connection.rollback();
2988
- } else if (typeof options.transactionMode === "function") {
2989
- debug$2("transactionMode: callback");
2990
- const procName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;
2991
- const result = options.transactionMode(connection, procName ?? "");
2992
- debug$2("transactionMode: callback restult", result);
2993
- if (result && typeof result.then === "function") await result;
2994
- } else {
2995
- debug$2("transactionMode: commit");
2996
- await connection.commit();
2997
- }
2998
- await connection.release();
2975
+ try {
2976
+ const cgiObj = getCGI(req, options.documentTable, options.cgi ?? {}, authenticatedUser);
2977
+ debug$2("processRequest: cgiObj=", cgiObj);
2978
+ const filesToUpload = getFiles(req);
2979
+ debug$2("processRequest: filesToUpload=", filesToUpload);
2980
+ const argObj = {};
2981
+ Object.assign(argObj, req.query);
2982
+ filesToUpload.reduce((aggregator, file) => {
2983
+ aggregator[file.fieldname] = file.filename;
2984
+ return aggregator;
2985
+ }, argObj);
2986
+ Object.assign(argObj, normalizeBody(req));
2987
+ debug$2("processRequest: argObj=", argObj);
2988
+ await invokeProcedure(req, res, argObj, cgiObj, filesToUpload, options, connection, procedureNameCache, argumentCache);
2989
+ if (options.transactionMode === "rollback") {
2990
+ debug$2("transactionMode: rollback");
2991
+ await connection.rollback();
2992
+ } else if (typeof options.transactionMode === "function") {
2993
+ debug$2("transactionMode: callback");
2994
+ const procName = Array.isArray(req.params.name) ? req.params.name[0] : req.params.name;
2995
+ const result = options.transactionMode(connection, procName ?? "");
2996
+ debug$2("transactionMode: callback restult", result);
2997
+ if (result && typeof result.then === "function") await result;
2998
+ } else {
2999
+ debug$2("transactionMode: commit");
3000
+ await connection.commit();
3001
+ }
3002
+ } finally {
3003
+ await connection.release();
3004
+ }
2999
3005
  debug$2("processRequest: EXIT");
3000
3006
  };
3001
3007
 
@@ -3099,6 +3105,10 @@ const errorPage = (req, res, options, error) => {
3099
3105
  }
3100
3106
  });
3101
3107
  console.error(text);
3108
+ if (res.headersSent) {
3109
+ console.warn("errorPage: Response headers already sent. Cannot show error page.");
3110
+ return;
3111
+ }
3102
3112
  if (options.errorStyle === "basic") res.status(404).send("Page not found");
3103
3113
  else res.status(404).send(getHtmlPage(html));
3104
3114
  };
@@ -3122,16 +3132,22 @@ const requestHandler = async (req, res, _next, connectionPool, options, procedur
3122
3132
  if (options.auth?.type === "basic") {
3123
3133
  const b64auth = (req.headers.authorization ?? "").split(" ")[1] ?? "";
3124
3134
  const [login, password] = Buffer.from(b64auth, "base64").toString().split(":");
3125
- if (login) authenticatedUser = await options.auth.callback(connectionPool, {
3135
+ if (login) authenticatedUser = await options.auth.callback({
3126
3136
  username: login,
3127
3137
  password
3128
- });
3138
+ }, connectionPool);
3129
3139
  if (authenticatedUser === null) {
3130
3140
  const realm = options.auth.realm ?? "PL/SQL Gateway";
3131
3141
  res.set("WWW-Authenticate", `Basic realm="${realm}"`);
3132
3142
  res.status(401).send("Authentication required.");
3133
3143
  return;
3134
3144
  }
3145
+ } else if (options.auth?.type === "custom") {
3146
+ authenticatedUser = await options.auth.callback(req, connectionPool);
3147
+ if (authenticatedUser === null) {
3148
+ res.status(401).send("Authentication required.");
3149
+ return;
3150
+ }
3135
3151
  }
3136
3152
  if (typeof req.params.name !== "string" || req.params.name.length === 0) if (typeof options.defaultPage === "string" && options.defaultPage.length > 0) {
3137
3153
  const newUrl = url.resolve(`${req.originalUrl}/${options.defaultPage}`, "");
@@ -3237,8 +3253,8 @@ const createAdminRouter = (adminContext) => {
3237
3253
  const name = cache?.poolName ?? `pool-${index}`;
3238
3254
  const p = pool;
3239
3255
  const stats = typeof p.getStatistics === "function" ? p.getStatistics() : null;
3240
- const procStats = cache?.procedureNameCache.getStats();
3241
- const argStats = cache?.argumentCache.getStats();
3256
+ const procStats = cache?.procedureNameCache?.getStats();
3257
+ const argStats = cache?.argumentCache?.getStats();
3242
3258
  return {
3243
3259
  name,
3244
3260
  stats,
@@ -3246,12 +3262,12 @@ const createAdminRouter = (adminContext) => {
3246
3262
  connectionsInUse: pool.connectionsInUse,
3247
3263
  cache: {
3248
3264
  procedureName: {
3249
- size: cache?.procedureNameCache.keys().length ?? 0,
3265
+ size: cache?.procedureNameCache?.keys().length ?? 0,
3250
3266
  hits: procStats?.hits ?? 0,
3251
3267
  misses: procStats?.misses ?? 0
3252
3268
  },
3253
3269
  argument: {
3254
- size: cache?.argumentCache.keys().length ?? 0,
3270
+ size: cache?.argumentCache?.keys().length ?? 0,
3255
3271
  hits: argStats?.hits ?? 0,
3256
3272
  misses: argStats?.misses ?? 0
3257
3273
  }
@@ -3458,32 +3474,45 @@ const handlerAdminConsole = (config, adminContext) => {
3458
3474
  const resolvedStaticDir = config.staticDir ?? resolveAdminStaticDir();
3459
3475
  if (adminRoute && !adminRoute.startsWith("/")) throw new Error("adminRoute must start with /");
3460
3476
  if (!config.devMode && !existsSync(resolvedStaticDir)) throw new Error(`Admin console not built. Run 'npm run build:frontend' first.\nExpected: ${resolvedStaticDir}`);
3461
- const originalRotate = adminContext.statsManager.rotateBucket.bind(adminContext.statsManager);
3462
- adminContext.statsManager.rotateBucket = () => {
3463
- originalRotate(adminContext.pools.map((pool, index) => {
3464
- const cache = adminContext.caches[index];
3465
- const name = cache?.poolName ?? `pool-${index}`;
3466
- const procStats = cache?.procedureNameCache.getStats();
3467
- const argStats = cache?.argumentCache.getStats();
3468
- return {
3469
- name,
3470
- connectionsOpen: pool.connectionsOpen,
3471
- connectionsInUse: pool.connectionsInUse,
3472
- cache: {
3473
- procedureName: {
3474
- size: cache?.procedureNameCache.keys().length ?? 0,
3475
- hits: procStats?.hits ?? 0,
3476
- misses: procStats?.misses ?? 0
3477
- },
3478
- argument: {
3479
- size: cache?.argumentCache.keys().length ?? 0,
3480
- hits: argStats?.hits ?? 0,
3481
- misses: argStats?.misses ?? 0
3477
+ const statsManager = adminContext.statsManager;
3478
+ const currentRotate = statsManager.rotateBucket;
3479
+ if (!Object.prototype.hasOwnProperty.call(currentRotate, "_isWrapped")) {
3480
+ const originalRotate = statsManager.rotateBucket;
3481
+ const wrappedRotate = (poolSnapshots = []) => {
3482
+ const mergedSnapshots = [...adminContext.pools.map((pool, index) => {
3483
+ const cache = adminContext.caches[index];
3484
+ const name = cache?.poolName ?? `pool-${index}`;
3485
+ const procStats = cache?.procedureNameCache?.getStats();
3486
+ const argStats = cache?.argumentCache?.getStats();
3487
+ return {
3488
+ name,
3489
+ connectionsOpen: pool.connectionsOpen,
3490
+ connectionsInUse: pool.connectionsInUse,
3491
+ cache: {
3492
+ procedureName: {
3493
+ size: cache?.procedureNameCache?.keys().length ?? 0,
3494
+ hits: procStats?.hits ?? 0,
3495
+ misses: procStats?.misses ?? 0
3496
+ },
3497
+ argument: {
3498
+ size: cache?.argumentCache?.keys().length ?? 0,
3499
+ hits: argStats?.hits ?? 0,
3500
+ misses: argStats?.misses ?? 0
3501
+ }
3482
3502
  }
3483
- }
3484
- };
3485
- }));
3486
- };
3503
+ };
3504
+ })];
3505
+ if (Array.isArray(poolSnapshots)) {
3506
+ for (const ps of poolSnapshots) if (!mergedSnapshots.some((s) => s.name === ps.name)) mergedSnapshots.push(ps);
3507
+ }
3508
+ originalRotate.call(statsManager, mergedSnapshots);
3509
+ };
3510
+ Object.defineProperty(wrappedRotate, "_isWrapped", {
3511
+ value: true,
3512
+ writable: false
3513
+ });
3514
+ statsManager.rotateBucket = wrappedRotate;
3515
+ }
3487
3516
  const router = Router();
3488
3517
  router.use((req, res, next) => {
3489
3518
  if (adminContext.paused && !req.path.startsWith(adminRoute)) {