@fontoxml/fontoxml-development-tools
Version:
Development tools for Fonto.
52 lines (49 loc) • 1.67 kB
JavaScript
/** @typedef {import('express').Request} Request */
/** @typedef {import('express').Response} Response */
/** @typedef {import('express').NextFunction} NextFunction */
/** @typedef {import('./connectors-cms-standard/stores/DevelopmentCms').DevCmsFileLock} DevCmsFileLock */
/**
* Wrapper which cleans up locks on completion of the request, and allows async handlers.
*
* @description
* Express does not handle rejected promises, so this also explicitly catches and handles them.
*
* @param {(acquireLock: (filePath) => Promise<DevCmsFileLock>, req: Request, res: Response, next: NextFunction) => Promise<void>} asyncHandler
*
* @return {(req: Request, res: Response, next: NextFunction) => void}
*/
export default function asyncRouteWithLockCleanupHandler(asyncHandler) {
return async (req, res, next) => {
let hasCalledNext = false;
/** @type {Promise<DevCmsFileLock>[]} */
const lockPromises = [];
try {
const acquireLock = async (filePath) => {
const lockPromise = req.cms.acquireLock(filePath);
lockPromises.push(lockPromise);
return lockPromise;
};
await asyncHandler(acquireLock, req, res, next);
} catch (error) {
// Pass any error to the next middleware / default error handler.
hasCalledNext = true;
next(error);
} finally {
// Release all locks. Note that locks might already have been released.
for (const lockPromise of lockPromises) {
let firstError;
try {
(await lockPromise).release();
} catch (error) {
if (!firstError) {
firstError = error;
}
}
if (firstError && !hasCalledNext) {
hasCalledNext = true;
next(firstError);
}
}
}
};
}