{"version":3,"file":"express.mjs","names":[],"sources":["../src/adapter/express/middleware.ts"],"sourcesContent":["import type { ClientContract } from '@zenstackhq/orm';\nimport type { SchemaDef } from '@zenstackhq/orm/schema';\nimport type { Handler, Request, Response } from 'express';\nimport { logInternalError, type CommonAdapterOptions } from '../common';\n\n/**\n * Express middleware options\n */\nexport interface ExpressMiddlewareOptions<Schema extends SchemaDef> extends CommonAdapterOptions<Schema> {\n    /**\n     * Callback for getting a ZenStackClient for the given request\n     */\n    getClient: (req: Request, res: Response) => ClientContract<Schema> | Promise<ClientContract<Schema>>;\n\n    /**\n     * Controls if the middleware directly sends a response. If set to false,\n     * the response is stored in the `res.locals` object and then the middleware\n     * calls the `next()` function to pass the control to the next middleware.\n     * Subsequent middleware or request handlers need to make sure to send\n     * a response.\n     *\n     * Defaults to true;\n     */\n    sendResponse?: boolean;\n}\n\n/**\n * Creates an Express middleware for handling CRUD requests.\n */\nconst factory = <Schema extends SchemaDef>(options: ExpressMiddlewareOptions<Schema>): Handler => {\n    const requestHandler = options.apiHandler;\n\n    return async (request, response, next) => {\n        const client = await options.getClient(request, response);\n        const { sendResponse } = options;\n\n        if (sendResponse === false && !client) {\n            throw new Error('unable to get ZenStackClient from request context');\n        }\n\n        if (!client) {\n            return response.status(500).json({ message: 'unable to get ZenStackClient from request context' });\n        }\n\n        // express converts query parameters with square brackets into object\n        // e.g.: filter[foo]=bar is parsed to { filter: { foo: 'bar' } }\n        // we need to revert this behavior and reconstruct params from original URL\n        const url = request.protocol + '://' + request.get('host') + request.originalUrl;\n        const searchParams = new URL(url).searchParams;\n        const query = Object.fromEntries(searchParams);\n\n        try {\n            const r = await requestHandler.handleRequest({\n                method: request.method,\n                path: request.path,\n                query,\n                requestBody: request.body,\n                client,\n            });\n            if (sendResponse === false) {\n                // attach response and pass control to the next middleware\n                response.locals['zenstack'] = {\n                    status: r.status,\n                    body: r.body,\n                };\n                return next();\n            }\n            return response.status(r.status).json(r.body);\n        } catch (err) {\n            if (sendResponse === false) {\n                throw err;\n            }\n            logInternalError(options.apiHandler.log, err);\n            return response.status(500).json({ message: `An internal server error occurred` });\n        }\n    };\n};\n\nexport default factory;\n\nexport { factory as ZenStackMiddleware };\n"],"mappings":";;;;;AA6BA,MAAM,WAAqC,YAAuD;CAC9F,MAAM,iBAAiB,QAAQ;AAE/B,QAAO,OAAO,SAAS,UAAU,SAAS;EACtC,MAAM,SAAS,MAAM,QAAQ,UAAU,SAAS,SAAS;EACzD,MAAM,EAAE,iBAAiB;AAEzB,MAAI,iBAAiB,SAAS,CAAC,OAC3B,OAAM,IAAI,MAAM,oDAAoD;AAGxE,MAAI,CAAC,OACD,QAAO,SAAS,OAAO,IAAI,CAAC,KAAK,EAAE,SAAS,qDAAqD,CAAC;EAMtG,MAAM,MAAM,QAAQ,WAAW,QAAQ,QAAQ,IAAI,OAAO,GAAG,QAAQ;EACrE,MAAM,eAAe,IAAI,IAAI,IAAI,CAAC;EAClC,MAAM,QAAQ,OAAO,YAAY,aAAa;AAE9C,MAAI;GACA,MAAM,IAAI,MAAM,eAAe,cAAc;IACzC,QAAQ,QAAQ;IAChB,MAAM,QAAQ;IACd;IACA,aAAa,QAAQ;IACrB;IACH,CAAC;AACF,OAAI,iBAAiB,OAAO;AAExB,aAAS,OAAO,cAAc;KAC1B,QAAQ,EAAE;KACV,MAAM,EAAE;KACX;AACD,WAAO,MAAM;;AAEjB,UAAO,SAAS,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE,KAAK;WACxC,KAAK;AACV,OAAI,iBAAiB,MACjB,OAAM;AAEV,oBAAiB,QAAQ,WAAW,KAAK,IAAI;AAC7C,UAAO,SAAS,OAAO,IAAI,CAAC,KAAK,EAAE,SAAS,qCAAqC,CAAC"}