{"version":3,"sources":["jsdelivr-header.js","/npm/hapi-csv@6.0.0/lib/index.js"],"names":[],"mappings":"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","file":"/npm/hapi-csv@6.0.0/lib/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/hapi-csv@6.0.0/lib/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","'use strict';\n\nconst Joi = require('joi');\nconst Hoek = require('@hapi/hoek');\nconst ExcelJs = require('exceljs');\nconst FastCsv = require('fast-csv');\n\n\nconst internals = {\n    routeMap: new Map(),\n    riskyCharacters: ['=', '+', '-', '@']\n};\n\nexports.plugin = {\n    pkg: require('../package.json'),\n    register: (server, options) => {\n\n        internals.separator = options.separator || ',';\n        internals.maximumElementsInArray = options.maximumElementsInArray || 5;\n        internals.enableExcel = !!options.enableExcel;\n\n        /**\n         * Build up routeMap for all routes on all connections\n         */\n        server.ext('onPreStart', (srv) => {\n\n            return srv.table().forEach((route) => {\n\n                if (route.settings.response.schema) {\n                    internals.routeMap.set(internals.createRouteMethodString(route.path, route.method), route.settings.response.schema);\n                }\n            });\n        });\n\n        /**\n         * Allow .csv or .xlsx requests\n         */\n        server.ext('onRequest', (request, h) => {\n\n            const path = request.path;\n\n            if (path.endsWith('.csv')) {\n                request.setUrl(`${path.substring(0, path.length - 4)}${request.url.search ? request.url.search : ''}`);\n                request.headers.accept = 'text/csv';\n            }\n\n            if (internals.enableExcel && path.endsWith('.xlsx')) {\n                request.setUrl(`${path.substring(0, path.length - 5)}${request.url.search ? request.url.search : ''}`);\n                request.headers.accept = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';\n            }\n\n            return h.continue;\n        });\n\n        // Check for header and route mapping and convert and reply with csv if needed\n        const allowedTypesRegex = /(text\\/csv)|(application\\/csv)|(application\\/vnd.openxmlformats-officedocument.spreadsheetml.sheet)/i;\n\n        server.ext('onPreResponse', async (request, h) => {\n\n            //todo\n            if (request.response.isBoom) {\n                return h.continue;\n            }\n\n            if (!request.headers.accept) {\n                return h.continue;\n            }\n\n            const result = allowedTypesRegex.exec(request.headers.accept);\n            const preferredType = result && result[0];\n\n            if (!internals.enableExcel && preferredType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {\n                return h.continue;\n            }\n\n            if (!(preferredType && internals.routeMap.has(internals.createRouteMethodString(request.route.path, request.route.method)))) {\n                return h.continue;\n            }\n\n            const dynamicHandlerObject = request.route.settings.plugins['hapi-csv'] || {};\n\n            const resolvedSchemasObject = {};\n\n            for (const [path, handler] of Object.entries(dynamicHandlerObject)) {\n                resolvedSchemasObject[path] = await handler(request);\n            }\n\n            let schema = internals.routeMap.get(internals.createRouteMethodString(request.route.path, request.route.method));\n\n            if (typeof schema === 'function') {\n                schema = schema(request.response.source, request, true);\n            }\n\n            const { headerQueryMap, dataset } = internals.processSchema(schema, resolvedSchemasObject, request.response.source, options.resultKey);\n\n            // If the dataset isn't an array or object, we have to return it, as it is\n            if (!(Array.isArray(dataset)) && !(dataset === Object(dataset))) {\n                const response = h.response(dataset);\n                return response.type(`${preferredType}; charset=${response.settings.charset}; header=present;`).header('content-disposition', 'attachment;');\n            }\n\n            const res = request.raw.res;\n            if (internals.enableExcel && preferredType === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {\n                const wbOptions = {\n                    stream: res, // write to server response\n                    useStyles: false,\n                    useSharedStrings: false\n                };\n\n                const workbook = new ExcelJs.stream.xlsx.WorkbookWriter(wbOptions);\n\n                res.setHeader('Content-disposition', 'attachment;');\n                res.setHeader('Content-type', `${preferredType}; charset=utf-8; header=present;`);\n\n                internals.headerQueryMapToExcel(headerQueryMap, dataset, workbook);\n\n                return h.abandon;\n            }\n\n            const csvStream = FastCsv.format({\n                headers: true,\n                delimiter: internals.separator\n            });\n\n            csvStream.pipe(res);\n\n            res.setHeader('Content-disposition', 'attachment;');\n            res.setHeader('Content-type', `${preferredType}; charset=utf-8; header=present;`);\n\n            internals.headerQueryMapToCsv(headerQueryMap, dataset, csvStream);\n\n            return h.abandon;\n        });\n    }\n};\n\n/**\n * Take header schema and dataset and prep it for use in csv/xlsx creation\n * @param {joiSchema} schema\n * @param {} dynamicSchema\n * @param {[]} dataset\n * @param {} resultKey\n * @returns {{headerQueryMap?, dataset}}\n */\ninternals.processSchema = (schema, dynamicSchemas, dataset, resultKey) => {\n\n    // We return the dataset if the dataset is not an array or an object, just a primitive type\n    if (!(Array.isArray(dataset)) && !(dataset === Object(dataset))) {\n        return { dataset: internals.escapeQuotesInString(dataset) };\n    }\n\n    let schemaDescription = Joi.compile(schema).describe();\n\n    if (schemaDescription.keys && schemaDescription.keys[resultKey]) {\n        schemaDescription = schemaDescription.keys[resultKey];\n        dataset = dataset[resultKey];\n    }\n\n    const headerQueryArray = internals.parseSchema(schemaDescription, dynamicSchemas);\n    const headerQueryMap = internals.arrayToMap(headerQueryArray);\n\n    return { headerQueryMap, dataset };\n};\n\n/*\n * Recursive function which parses a Joi schema to an array of header, query entries\n * `parentIsArray` is true when the previous call to the function handled a joi schema of type array\n */\ninternals.parseSchema = (joiSchema, dynamicSchemas, keyName, path, parentIsArray) => {\n\n    if (dynamicSchemas[path]) {\n        joiSchema = Joi.compile(dynamicSchemas[path]).describe();\n    }\n\n    if (joiSchema.type === 'object') {\n        if (!joiSchema.keys) {\n            return false;\n        }\n\n        const childrenKeys = Object.keys(joiSchema.keys);\n        let children = childrenKeys.map((key) => internals.parseSchema(joiSchema.keys[key], dynamicSchemas, key, path !== undefined ? `${path}.${key}` : key));\n\n        if (!keyName) {\n            return children;\n        }\n\n        children = Hoek.flatten(children);\n\n        return children.map((child) => {\n\n            const key = Object.keys(child)[0];\n            child[key].unshift(keyName);\n\n            const label = joiSchema.keys[key]?.flags?.label;\n            // If the previous call was not for a joi schema of type array, we alter the keys to prefix them with the keyName\n            if (!parentIsArray) {\n                const name = label || `${keyName}.${key}`;\n\n                if (name !== key) {\n                    child[name] = child[key];\n                    delete child[key];\n                }\n            }\n            else if (label) {\n                const name =  label;\n\n                if (name !== key) {\n                    child[name] = child[key];\n                    delete child[key];\n                }\n            }\n\n            return child;\n        });\n    }\n\n    if (joiSchema.type === 'array') {\n\n        // FUTURE: Make it work for arrays with multiple item definitions/alternatives\n        const item = joiSchema.items[0];\n        const label = item.flags?.label;\n        const parsedItem = internals.parseSchema(item, dynamicSchemas, keyName, path, true);\n\n        if (keyName && parsedItem) {\n            const prefixedItemArray = [];\n\n            for (let i = 0; i < internals.maximumElementsInArray; ++i) {\n                prefixedItemArray.push(parsedItem.map((headerQuery) => {\n\n                    const key = Object.keys(headerQuery)[0];\n                    let name;\n                    if (label === '_') {\n                        name = parsedItem.length === 1 ? `${keyName}${i === 0 ? '' : ` ${i + 1}`}` : `${key}${i === 0 ? '' : ` ${i + 1}`}`;\n                    }\n                    else if (label) {\n                        name = parsedItem.length === 1 ? `${label}${i === 0 ? '' : ` ${i + 1}`}` : `${label}${i === 0 ? '' : ` ${i + 1}`} ${key}`;\n                    }\n                    else {\n                        name = parsedItem.length === 1 ? `${keyName} ${i + 1}` : `${keyName} ${i + 1} ${key}`;\n                    }\n\n                    const sliced = headerQuery[key].slice();\n\n                    // We splice the index after the array key\n                    sliced.splice(1, 0, i);\n\n                    return { [name]: sliced };\n                }));\n            }\n\n            return prefixedItemArray;\n        }\n\n        return parsedItem;\n    }\n\n    // Check if the key is in the root by checking if the path is composed\n    if (!path.includes('.')) {\n        return [{ [joiSchema.flags?.label || keyName]: [keyName] }];\n    }\n\n    // First square brackets are used to convert to String, second brackets are used to convert to an array\n    return [{ [keyName]: [keyName] }];\n};\n\n/**\n * Utility function to convert an array to a Map\n * @param {[]} array\n * @returns {Map}\n */\ninternals.arrayToMap = (array) => {\n\n    const resultMap = new Map();\n\n    Hoek.flatten(array).forEach((item) => {\n\n        const key = Object.keys(item)[0];\n\n        if (key !== undefined) {\n            resultMap.set(key, item[key]);\n        }\n    });\n\n    return resultMap;\n};\n\n/**\n * Push data to a csv stream\n * @param {Map} headerQueryMap headers\n * @param {[{}]} dataset data\n * @param {Stream} csvStream\n */\ninternals.headerQueryMapToCsv = (headerQueryMap, dataset, csvStream) => {\n\n    // Generate and set headers row\n    const headerRow = [];\n    for (const header of headerQueryMap.keys()) {\n        headerRow.push(`${internals.csvInjectionProtector(header)}`);\n    }\n\n    csvStream.write(headerRow);\n\n    // Push data to stream\n    internals.generateDataRows(headerQueryMap, dataset, (dataRow) => {\n\n        dataRow = dataRow.map((value) => {\n\n            return `${value}`;\n        });\n        csvStream.write(dataRow);\n    });\n\n    // End stream\n    csvStream.end();\n};\n\n/**\n * Push data to a excel workbook stream\n * @param {Map} headerQueryMap headers\n * @param {[{}]} dataset data\n * @param {Stream} csvStream\n */\ninternals.headerQueryMapToExcel = (headerQueryMap, dataset, workbook) => {\n\n    const sheet = workbook.addWorksheet('sheet');\n\n    // Generate and set headers row\n    const headerRow = [];\n    for (const header of headerQueryMap.keys()) {\n        const sanitizedHeader = internals.csvInjectionProtector(header);\n        headerRow.push({\n            name: sanitizedHeader,\n            header: sanitizedHeader\n        });\n    }\n\n    sheet.columns = headerRow;\n\n    // Push data to stream\n    internals.generateDataRows(headerQueryMap, dataset, (dataRow) => {\n\n        sheet.addRow(dataRow).commit();\n    });\n\n    // End stream\n    sheet.commit();\n    workbook.commit();\n};\n\n/**\n * Generic function that loops through data and generates rows\n * These rows gets processed by the writeRowFunction\n * @param {Map} headerQueryMap\n * @param {[{}]} dataset\n * @param {function (dataRow)} writeRowFunction\n */\ninternals.generateDataRows = (headerQueryMap, dataset, writeRowFunction) => {\n\n    if (!Array.isArray(dataset)) {\n        dataset = [dataset];\n    }\n\n    let valueFound = true;\n    for (let i = 0; i < dataset.length; ++i) {\n        const dataRow = [];\n        for (const key of headerQueryMap.keys()) {\n\n            const query = headerQueryMap.get(key);\n            let temp = dataset[i];\n\n            for (const queryPart of query) {\n                if (temp[queryPart] === null || temp[queryPart] === undefined) {\n                    valueFound = false;\n\n                    // We break out of the for loop because there is no need to dig deeper into the object, the object is already undefined or null\n                    break;\n                }\n                else {\n                    temp = temp[queryPart];\n                }\n            }\n\n            if (!valueFound) {\n                valueFound = true;\n                temp = '';\n            }\n\n            temp = internals.dateToISOString(temp);\n            // Sanitize values against CSV injection\n            temp = internals.csvInjectionProtector(temp);\n\n            dataRow.push(temp);\n        }\n\n        writeRowFunction(dataRow);\n    }\n};\n\n/**\n * @param {String} str\n * @returns {String}\n */\ninternals.escapeQuotesInString = (str) => {\n\n    if (typeof str === 'string' || str instanceof String) {\n        return str.replace(/\"/g, '\"\"');\n    }\n\n    return str;\n};\n\n/**\n * @param { Date | String} str\n * @returns {String}\n */\ninternals.dateToISOString = (str) => {\n\n    if (str instanceof Date) {\n        const dateParts = str.toISOString().split('T');\n        return `${dateParts[0]} ${dateParts[1].split('.')[0]}`;\n    }\n\n    return str;\n};\n\n/**\n * @param {} route\n * @param {} method\n * @returns {String}\n */\ninternals.createRouteMethodString = (route, method) => {\n\n    return `${route}_${method}`;\n};\n\ninternals.csvInjectionProtector = (string) => {\n\n    if (!string) {\n        return '';\n    }\n\n    if (typeof string !== 'string' && !(string instanceof String)) {\n        return string;\n    }\n\n    const firstCharacter = string.charAt(0);\n    const isInjected = internals.riskyCharacters.includes(firstCharacter);\n\n    if (!isInjected) {\n        return string;\n    }\n\n    const protectedString = `'${string}`;\n\n    return internals.csvInjectionProtector(protectedString);\n};\n"]}