diff --git a/backend/envsubst.ts b/backend/envsubst.ts new file mode 100644 index 0000000..99bcf87 --- /dev/null +++ b/backend/envsubst.ts @@ -0,0 +1,139 @@ +/* + * Original Source: https://github.com/inventage/envsubst/blob/main/src/utils.js + * MIT License + * Copyright (c) 2021 Inventage AG + * + * Copy this file, because + */ +import escapeStringRegexp from "escape-string-regexp"; +import { LooseObject } from "./util-common"; + +const toLowerKeys = (object : LooseObject) => { + return Object.keys(object).reduce((accumulator : LooseObject, key) => { + accumulator[key.toLowerCase()] = object[key]; + return accumulator; + }, {}); +}; + +/** + * Regex pattern with an optional prefix. + * + * @see https://regex101.com/r/M3dVAW/1 + * @param prefix + * @returns {string} + */ +const variableRegexPattern = (prefix = ""): string => { + return `\\\${(${prefix ? escapeStringRegexp(prefix) : ""}\\w+)(:-([^}]*))?}`; +}; + +/** + * Regex pattern that wraps the variable regex pattern with a window variable statement: + * + * window['${VAR}'] or window["${VAR}"] + * + * @see https://regex101.com/r/ND057d/1 + * @param prefix + * @returns {string} + */ +const windowVariableRegexPattern = (prefix = ""): string => { + return `(window\\[['"]{1})?${variableRegexPattern(prefix)}(['"]{1}\\])?`; +}; + +/** + * Replaces all variable placeholders in the given string with either variable values + * found in the variables parameter OR with the given default in the variable string. + * + * @param {string} string + * @param {object} variables + * @param {string} prefix + * @param {boolean} trimWindow + * @param {boolean} ignoreCase + * @returns {Promise} + */ +const replaceVariables = (string: string, variables: object = {}, prefix: string = "", trimWindow: boolean = false, ignoreCase: boolean = false): Promise => + new Promise(resolve => { + resolve(replaceVariablesSync(string, variables, prefix, trimWindow, ignoreCase)); + }); + +/** + * Replaces all variable placeholders in the given string with either variable values + * found in the variables parameter OR with the given default in the variable string. + * + * @param {string} string + * @param {object} variables + * @param {string} prefix + * @param {boolean} trimWindow + * @param {boolean} ignoreCase + * @returns {unknown[]} + */ +const replaceVariablesSync = (string : string, variables: LooseObject = {}, prefix: string = "", trimWindow: boolean = false, ignoreCase: boolean = false): unknown[] => { + const regex = new RegExp(trimWindow ? windowVariableRegexPattern(prefix) : variableRegexPattern(prefix), ignoreCase ? "gmi" : "gm"); + const matches = [ ...string.matchAll(regex) ]; + const lowercaseVariables = toLowerKeys(variables); + + let replaced = string; + const replacements : LooseObject[] = []; + for (const match of matches) { + if (trimWindow) { + const [ original, windowStart, name, , fallback, windowEnd ] = match; + + // Bail if the match does not contain `^window[` + if (!windowStart) { + continue; + } + + const valueStartQuote = windowStart.replace("window[", ""); + const valueEndQuote = windowEnd.replace("]", ""); + const withoutWindow = original.replace(windowStart, "").replace(windowEnd, ""); + + let value; + if (ignoreCase) { + value = Object.hasOwnProperty.call(lowercaseVariables || {}, name.toLowerCase()) ? lowercaseVariables[name.toLowerCase()] : fallback; + } else { + value = Object.hasOwnProperty.call(variables || {}, name) ? variables[name] : fallback; + } + + if (value !== undefined) { + const quotedValue = `${valueStartQuote}${value}${valueEndQuote}`; + const replacement = replacements.find(r => r.from === original && r.to === quotedValue); + if (replacement) { + replacement.count = replacement.count + 1; + } else { + replacements.push({ from: original, + to: quotedValue, + count: 1 }); + } + + replaced = replaced.split(original).join(withoutWindow.split(withoutWindow).join(quotedValue)); + } + } else { + const [ original, name, , fallback ] = match; + + let value : string; + if (ignoreCase) { + value = Object.hasOwnProperty.call(lowercaseVariables || {}, name.toLowerCase()) ? lowercaseVariables[name.toLowerCase()] : fallback; + } else { + value = Object.hasOwnProperty.call(variables || {}, name) ? variables[name] : fallback; + } + + if (value !== undefined) { + const replacement = replacements.find(r => r.from === original && r.to === value); + if (replacement) { + replacement.count = replacement.count + 1; + } else { + replacements.push({ from: original, + to: value, + count: 1 }); + } + + replaced = replaced.split(original).join(value); + } + } + } + + return [ replaced, replacements ]; +}; + + + +export { variableRegexPattern, replaceVariables, replaceVariablesSync }; diff --git a/backend/util-common.ts b/backend/util-common.ts index 47a9cf9..2c2f893 100644 --- a/backend/util-common.ts +++ b/backend/util-common.ts @@ -2,15 +2,15 @@ * Common utilities for backend and frontend */ import yaml, { Document, Pair, Scalar } from "yaml"; -import dotenv, { DotenvParseOutput } from "dotenv"; -// @ts-ignore -import envsub from "envsub"; +import { DotenvParseOutput } from "dotenv"; // Init dayjs import dayjs from "dayjs"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; import relativeTime from "dayjs/plugin/relativeTime"; +// @ts-ignore +import { replaceVariablesSync } from "@inventage/envsubst"; dayjs.extend(utc); dayjs.extend(timezone); @@ -345,13 +345,18 @@ export function parseDockerPort(input : string, defaultHostname : string = "loca }; } +export function envsubst(string : string, variables : LooseObject) : string { + return replaceVariablesSync(string, variables)[0]; +} + /** * Traverse all values in the yaml and for each value, if there are template variables, replace it environment variables + * Emulates the behavior of how docker-compose handles environment variables in yaml files * @param content Yaml string * @param env Environment variables * @returns string Yaml string with environment variables replaced */ -export function renderYAML(content : string, env : DotenvParseOutput) : string { +export function envsubstYAML(content : string, env : DotenvParseOutput) : string { const doc = yaml.parseDocument(content); if (doc.contents) { // @ts-ignore @@ -362,7 +367,12 @@ export function renderYAML(content : string, env : DotenvParseOutput) : string { return doc.toString(); } -export function traverseYAML(pair : Pair, env : DotenvParseOutput) : void { +/** + * Used for envsubstYAML(...) + * @param pair + * @param env + */ +function traverseYAML(pair : Pair, env : DotenvParseOutput) : void { // @ts-ignore if (pair.value && pair.value.items) { // @ts-ignore @@ -370,45 +380,12 @@ export function traverseYAML(pair : Pair, env : DotenvParseOutput) : void { if (item instanceof Pair) { traverseYAML(item, env); } else if (item instanceof Scalar) { - item.value = "CAN_READ!"; + item.value = envsubst(item.value, env); } } // @ts-ignore } else if (pair.value && typeof(pair.value.value) === "string") { // @ts-ignore - pair.value.value = "CAN_READ!"; + pair.value.value = envsubst(pair.value.value, env); } } - -const config = dotenv.parse(`TEST=123 -`); - -let test = renderYAML(` -x-dockge: - icon: null - author: null - repo: "" - a: 1\${C} - urls: - - https://louislam.net:3000/test.php?aaa=232&bbb=23 - - http://uptime.kuma.pet - - "" -version: "3.8" -services: - nginx: - image: nginx:latest - restart: unless-stopped - ports: - - 8080\${C:-:}80 - environment: [] - networks: [] - depends_on: [] - nginx2: - image: nginx:latest - restart: unless-stopped -networks: - asdsd: {} -`, config); - -console.log(test); - diff --git a/frontend/src/components/Container.vue b/frontend/src/components/Container.vue index 604e068..0a20182 100644 --- a/frontend/src/components/Container.vue +++ b/frontend/src/components/Container.vue @@ -9,7 +9,7 @@
{{ status }} - + {{ parsePort(port).display }}
@@ -213,16 +213,29 @@ export default defineComponent({ jsonObject() { return this.$parent.$parent.jsonConfig; }, + + envsubstJSONConfig() { + return this.$parent.$parent.envsubstJSONConfig; + }, + + envsubstService() { + if (!this.envsubstJSONConfig.services[this.name]) { + return {}; + } + return this.envsubstJSONConfig.services[this.name]; + }, + imageName() { - if (this.service.image) { - return this.service.image.split(":")[0]; + if (this.envsubstService.image) { + return this.envsubstService.image.split(":")[0]; } else { return ""; } }, + imageTag() { - if (this.service.image) { - let tag = this.service.image.split(":")[1]; + if (this.envsubstService.image) { + let tag = this.envsubstService.image.split(":")[1]; if (tag) { return tag; diff --git a/frontend/src/pages/Compose.vue b/frontend/src/pages/Compose.vue index 29250a4..11caa2d 100644 --- a/frontend/src/pages/Compose.vue +++ b/frontend/src/pages/Compose.vue @@ -231,7 +231,7 @@ import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; import { COMBINED_TERMINAL_COLS, COMBINED_TERMINAL_ROWS, - copyYAMLComments, + copyYAMLComments, envsubstYAML, getCombinedTerminalName, getComposeTerminalName, PROGRESS_TERMINAL_ROWS, @@ -239,6 +239,7 @@ import { } from "../../../backend/util-common"; import { BModal } from "bootstrap-vue-next"; import NetworkInput from "../components/NetworkInput.vue"; +import dotenv from "dotenv"; const template = `version: "3.8" services: @@ -277,6 +278,7 @@ export default { return { editorFocus: false, jsonConfig: {}, + envsubstJSONConfig: {}, yamlError: "", processing: true, showProgressTerminal: false, @@ -645,28 +647,41 @@ export default { return highlight(code, languages.docker_env); }, + yamlToJSON(yaml) { + let doc = parseDocument(yaml); + if (doc.errors.length > 0) { + throw doc.errors[0]; + } + + const config = doc.toJS() ?? {}; + + // Check data types + // "services" must be an object + if (!config.services) { + config.services = {}; + } + + if (Array.isArray(config.services) || typeof config.services !== "object") { + throw new Error("Services must be an object"); + } + + return { + config, + doc, + }; + }, + yamlCodeChange() { try { - let doc = parseDocument(this.stack.composeYAML); - if (doc.errors.length > 0) { - throw doc.errors[0]; - } - - const config = doc.toJS() ?? {}; - - // Check data types - // "services" must be an object - if (!config.services) { - config.services = {}; - } - - if (Array.isArray(config.services) || typeof config.services !== "object") { - throw new Error("Services must be an object"); - } + let { config, doc } = this.yamlToJSON(this.stack.composeYAML); this.yamlDoc = doc; this.jsonConfig = config; + let env = dotenv.parse(this.stack.composeENV); + let envYAML = envsubstYAML(this.stack.composeYAML, env); + this.envsubstJSONConfig = this.yamlToJSON(envYAML).config; + clearTimeout(yamlErrorTimeout); this.yamlError = ""; } catch (e) { diff --git a/package.json b/package.json index e5fa1f8..f509f56 100644 --- a/package.json +++ b/package.json @@ -25,8 +25,8 @@ }, "dependencies": { "@homebridge/node-pty-prebuilt-multiarch": "~0.11.11", + "@inventage/envsubst": "^0.16.0", "@louislam/sqlite3": "~15.1.6", - "@tuplo/envsubst": "^1.15.2", "bcryptjs": "~2.4.3", "check-password-strength": "~2.0.7", "command-exists": "~1.2.9", @@ -35,7 +35,6 @@ "croner": "~7.0.5", "dayjs": "~1.11.10", "dotenv": "~16.3.1", - "envsub": "~4.1.0", "express": "~4.18.2", "express-static-gzip": "~2.1.7", "http-graceful-shutdown": "~3.1.13", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 52cdc35..fc8e9b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,12 +8,12 @@ dependencies: '@homebridge/node-pty-prebuilt-multiarch': specifier: ~0.11.11 version: 0.11.11 + '@inventage/envsubst': + specifier: ^0.16.0 + version: 0.16.0 '@louislam/sqlite3': specifier: ~15.1.6 version: 15.1.6 - '@tuplo/envsubst': - specifier: ^1.15.2 - version: 1.15.2 bcryptjs: specifier: ~2.4.3 version: 2.4.3 @@ -38,9 +38,6 @@ dependencies: dotenv: specifier: ~16.3.1 version: 16.3.1 - envsub: - specifier: ~4.1.0 - version: 4.1.0 express: specifier: ~4.18.2 version: 4.18.2 @@ -254,6 +251,13 @@ packages: to-fast-properties: 2.0.0 dev: true + /@colors/colors@1.5.0: + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + requiresBuild: true + dev: false + optional: true + /@es-joy/jsdoccomment@0.40.1: resolution: {integrity: sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg==} engines: {node: '>=16'} @@ -827,6 +831,18 @@ packages: engines: {node: '>= 16'} dev: true + /@inventage/envsubst@0.16.0: + resolution: {integrity: sha512-l3hc0nzMpREpcjDqxGjeGNH+N7wD45BQGg2CvLDdTvuEUxhacmRnlqRtRlIyfyW3XQjrlkcDXhWJlgImmLK+CA==} + engines: {node: '>=16.17.0'} + hasBin: true + dependencies: + cli-table3: 0.6.3 + escape-string-regexp: 5.0.0 + globby: 13.2.2 + meow: 12.1.1 + string.prototype.matchall: 4.0.10 + dev: false + /@isaacs/cliui@8.0.2: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -885,12 +901,10 @@ packages: dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - dev: true /@nodelib/fs.stat@2.0.5: resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - dev: true /@nodelib/fs.walk@1.2.8: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} @@ -898,7 +912,6 @@ packages: dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - dev: true /@npmcli/fs@1.1.1: resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} @@ -1138,11 +1151,6 @@ packages: dev: false optional: true - /@tuplo/envsubst@1.15.2: - resolution: {integrity: sha512-sz+Tj0V+t4RfihoBMa4fw3OfKSpbTKy8EEvCxirjr+G1+xlaQROqbavCabZA8DVAK75CvxAG9mwWpjsDx693Ew==} - engines: {node: '>=12'} - dev: false - /@types/bcryptjs@2.4.6: resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==} dev: true @@ -1736,10 +1744,6 @@ packages: readable-stream: 3.6.2 dev: false - /bluebird@3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - dev: false - /body-parser@1.20.1: resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -1800,7 +1804,6 @@ packages: engines: {node: '>=8'} dependencies: fill-range: 7.0.1 - dev: true /buffer-equal-constant-time@1.0.1: resolution: {integrity: sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=} @@ -1877,14 +1880,6 @@ packages: supports-color: 5.5.0 dev: false - /chalk@3.0.0: - resolution: {integrity: sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==} - engines: {node: '>=8'} - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - dev: false - /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1927,6 +1922,15 @@ packages: dev: false optional: true + /cli-table3@0.6.3: + resolution: {integrity: sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==} + engines: {node: 10.* || >= 12.*} + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + dev: false + /cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} dependencies: @@ -1992,11 +1996,6 @@ packages: engines: {node: '>=14'} dev: false - /commander@4.1.1: - resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} - engines: {node: '>= 6'} - dev: false - /commander@9.5.0: resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==} engines: {node: ^12.20.0 || >=14} @@ -2197,11 +2196,6 @@ packages: engines: {node: '>=8'} dev: false - /diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - dev: false - /dijkstrajs@1.0.3: resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} dev: true @@ -2211,7 +2205,6 @@ packages: engines: {node: '>=8'} dependencies: path-type: 4.0.0 - dev: true /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} @@ -2316,20 +2309,6 @@ packages: dev: false optional: true - /envsub@4.1.0: - resolution: {integrity: sha512-B44hta3xNFu6+zDhOha1TIrZkQHGDO3G5K8D2sJIkm/s3XyQjxWBGp1B+b/Y74Go1PqMP+cp8moPR4JullnD9Q==} - hasBin: true - dependencies: - bluebird: 3.7.2 - chalk: 3.0.0 - commander: 4.1.1 - diff: 4.0.2 - handlebars: 4.7.8 - lodash: 4.17.21 - replace-last: 1.2.6 - string.prototype.matchall: 4.0.10 - dev: false - /err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} requiresBuild: true @@ -2478,6 +2457,11 @@ packages: engines: {node: '>=10'} dev: true + /escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + dev: false + /eslint-plugin-jsdoc@46.8.2(eslint@8.50.0): resolution: {integrity: sha512-5TSnD018f3tUJNne4s4gDWQflbsgOycIKEUBoCLn6XtBMgNHxQFmV8vVxUtiPxAQq8lrX85OaSG/2gnctxw9uQ==} engines: {node: '>=16'} @@ -2687,7 +2671,6 @@ packages: glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.5 - dev: true /fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -2701,7 +2684,6 @@ packages: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: reusify: 1.0.4 - dev: true /file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} @@ -2715,7 +2697,6 @@ packages: engines: {node: '>=8'} dependencies: to-regex-range: 5.0.1 - dev: true /finalhandler@1.2.0: resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} @@ -2923,7 +2904,6 @@ packages: engines: {node: '>= 6'} dependencies: is-glob: 4.0.3 - dev: true /glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} @@ -2981,6 +2961,17 @@ packages: slash: 3.0.0 dev: true + /globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.3.0 + merge2: 1.4.1 + slash: 4.0.0 + dev: false + /gopd@1.0.1: resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} dependencies: @@ -2994,19 +2985,6 @@ packages: resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} dev: true - /handlebars@4.7.8: - resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} - engines: {node: '>=0.4.7'} - hasBin: true - dependencies: - minimist: 1.2.8 - neo-async: 2.6.2 - source-map: 0.6.1 - wordwrap: 1.0.0 - optionalDependencies: - uglify-js: 3.17.4 - dev: false - /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: false @@ -3132,7 +3110,6 @@ packages: /ignore@5.3.0: resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==} engines: {node: '>= 4'} - dev: true /immutable@4.3.4: resolution: {integrity: sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==} @@ -3265,7 +3242,6 @@ packages: /is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - dev: true /is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} @@ -3277,7 +3253,6 @@ packages: engines: {node: '>=0.10.0'} dependencies: is-extglob: 2.1.1 - dev: true /is-lambda@1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} @@ -3300,7 +3275,6 @@ packages: /is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - dev: true /is-path-inside@3.0.3: resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} @@ -3701,6 +3675,11 @@ packages: engines: {node: '>= 0.6'} dev: false + /meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + dev: false + /merge-descriptors@1.0.1: resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=} dev: false @@ -3708,7 +3687,6 @@ packages: /merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - dev: true /methods@1.1.2: resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} @@ -3721,7 +3699,6 @@ packages: dependencies: braces: 3.0.2 picomatch: 2.3.1 - dev: true /mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} @@ -3901,10 +3878,6 @@ packages: requiresBuild: true dev: false - /neo-async@2.6.2: - resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - dev: false - /node-abi@3.51.0: resolution: {integrity: sha512-SQkEP4hmNWjlniS5zdnfIXTk1x7Ome85RDzHlTbBtzE97Gfwz/Ipw4v/Ryk20DWIy3yCNVLVlGKApCnmvYoJbA==} engines: {node: '>=10'} @@ -4125,7 +4098,6 @@ packages: /path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - dev: true /pg-connection-string@2.5.0: resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} @@ -4142,7 +4114,6 @@ packages: /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - dev: true /pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} @@ -4261,7 +4232,6 @@ packages: /queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - dev: true /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} @@ -4345,11 +4315,6 @@ packages: set-function-name: 2.0.1 dev: false - /replace-last@1.2.6: - resolution: {integrity: sha512-Cj+MK38VtNu1S5J73mEZY3ciQb9dJajNq1Q8inP4dn/MhJMjHwoAF3Z3FjspwAEV9pfABl565MQucmrjOkty4g==} - engines: {node: '>= 4.0.0'} - dev: false - /require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4391,7 +4356,6 @@ packages: /reusify@1.0.4: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - dev: true /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} @@ -4423,7 +4387,6 @@ packages: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} dependencies: queue-microtask: 1.2.3 - dev: true /safe-array-concat@1.0.1: resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} @@ -4580,6 +4543,11 @@ packages: engines: {node: '>=8'} dev: true + /slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + dev: false + /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -4665,11 +4633,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - dev: false - /spdx-exceptions@2.3.0: resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} dev: true @@ -4886,7 +4849,6 @@ packages: engines: {node: '>=8.0'} dependencies: is-number: 7.0.0 - dev: true /toidentifier@1.0.1: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} @@ -5022,14 +4984,6 @@ packages: engines: {node: '>=8'} dev: false - /uglify-js@3.17.4: - resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} - engines: {node: '>=0.8.0'} - hasBin: true - requiresBuild: true - dev: false - optional: true - /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -5341,10 +5295,6 @@ packages: string-width: 4.2.3 dev: false - /wordwrap@1.0.0: - resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - dev: false - /wordwrapjs@4.0.1: resolution: {integrity: sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==} engines: {node: '>=8.0.0'}