diff --git a/.github/workflows/docker-build-dev.yml b/.github/workflows/docker-build-dev.yml new file mode 100644 index 0000000..797dee0 --- /dev/null +++ b/.github/workflows/docker-build-dev.yml @@ -0,0 +1,49 @@ +name: Build and Publish Dev Docker Image + +on: + push: + branches: + - dev + workflow_dispatch: + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get repository name in lowercase + id: repo_name + run: echo "REPO_NAME=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT + + - name: Get version from package.json + id: package_version + run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + + - name: Get short SHA + id: short_sha + run: echo "SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + ghcr.io/${{ steps.repo_name.outputs.REPO_NAME }}:dev + ghcr.io/${{ steps.repo_name.outputs.REPO_NAME }}:${{ steps.package_version.outputs.VERSION }}-dev + ghcr.io/${{ steps.repo_name.outputs.REPO_NAME }}:${{ steps.package_version.outputs.VERSION }}-${{ steps.short_sha.outputs.SHA }} + cache-from: type=registry,ref=ghcr.io/${{ steps.repo_name.outputs.REPO_NAME }}:dev + cache-to: type=inline \ No newline at end of file diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..64b1a8d --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,44 @@ +name: Build and Publish Docker Image + +on: + push: + branches: + - master + workflow_dispatch: + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get repository name in lowercase + id: repo_name + run: echo "REPO_NAME=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT + + - name: Get version from package.json + id: package_version + run: echo "VERSION=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: true + tags: | + ghcr.io/${{ steps.repo_name.outputs.REPO_NAME }}:latest + ghcr.io/${{ steps.repo_name.outputs.REPO_NAME }}:${{ steps.package_version.outputs.VERSION }} + cache-from: type=registry,ref=ghcr.io/${{ steps.repo_name.outputs.REPO_NAME }}:latest + cache-to: type=inline \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..3e3e503 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,81 @@ +name: Run Tests + +on: + push: + pull_request: + branches: + - master + - dev + workflow_dispatch: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'yarn' + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y build-essential g++ python3-dev + sudo apt-get install -y libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev + sudo apt-get install -y libpixman-1-dev libvips-dev + sudo apt-get install -y graphviz + sudo apt-get install -y sqlite3 libsqlite3-dev + sudo apt-get install -y fonts-dejavu fonts-noto fonts-noto-cjk fonts-noto-color-emoji fontconfig + sudo apt-get install -y pkg-config + sudo apt-get install -y libimagequant-dev + yarn install + + - name: Set up database directory + run: | + sudo mkdir -p /var/lib/db + sudo chmod 777 /var/lib/db + + - name: Run basic tests only + run: | + # Skip the chart-create.test.js tests that require database access + PORT=3400 NODE_ENV=test npx mocha --exit --recursive test/ci/charts.js test/ci/graphviz.js test/ci/google_image_charts.js test/ci/qr.js + env: + NODE_ENV: test + PORT: 3400 + NODE_OPTIONS: --experimental-global-webcrypto + + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: 'yarn' + + - name: Install dependencies for linting + run: | + sudo apt-get update + sudo apt-get install -y build-essential g++ python3-dev + sudo apt-get install -y libcairo2-dev libpango1.0-dev libjpeg-dev libgif-dev librsvg2-dev + sudo apt-get install -y libpixman-1-dev libvips-dev + sudo apt-get install -y pkg-config + sudo apt-get install -y libimagequant-dev + yarn install + + - name: Lint code + run: | + if [ -f ".eslintrc.js" ] || [ -f ".eslintrc.json" ] || [ -f ".eslintrc.yml" ] || [ -f ".eslintrc.yaml" ]; then + npx eslint . + else + echo "No ESLint configuration found, skipping lint step" + fi \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 3bd54f2..82c5c39 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,6 +12,7 @@ RUN apk add --no-cache --repository https://dl-cdn.alpinelinux.org/alpine/edge/c RUN apk add --no-cache libimagequant-dev RUN apk add --no-cache vips-dev RUN apk add --no-cache --virtual .runtime-deps graphviz +RUN apk add --no-cache sqlite COPY package*.json . COPY yarn.lock . @@ -25,7 +26,7 @@ RUN apk del .build-deps COPY *.js ./ COPY lib/*.js lib/ COPY LICENSE . - +VOLUME /var/lib/db/ EXPOSE 3400 -ENTRYPOINT ["node", "--max-http-header-size=65536", "index.js"] +ENTRYPOINT ["node", "--max-http-header-size=65536", "--experimental-global-webcrypto", "index.js"] diff --git a/Dockerfile.test b/Dockerfile.test new file mode 100644 index 0000000..ab58ff4 --- /dev/null +++ b/Dockerfile.test @@ -0,0 +1,34 @@ +FROM node:18-alpine3.17 + +ENV NODE_ENV test +ENV NODE_OPTIONS --experimental-global-webcrypto +WORKDIR /quickchart + +RUN apk add --upgrade apk-tools +RUN apk add --no-cache --virtual .build-deps yarn git build-base g++ python3 +RUN apk add --no-cache --virtual .npm-deps cairo-dev pango-dev libjpeg-turbo-dev librsvg-dev +RUN apk add --no-cache --virtual .fonts libmount ttf-dejavu ttf-droid ttf-freefont ttf-liberation font-noto font-noto-emoji fontconfig +RUN apk add --no-cache --repository https://dl-cdn.alpinelinux.org/alpine/edge/community font-wqy-zenhei +RUN apk add --no-cache libimagequant-dev +RUN apk add --no-cache vips-dev +RUN apk add --no-cache --virtual .runtime-deps graphviz +RUN apk add --no-cache sqlite +RUN npm install -g mocha + +COPY package*.json . +COPY yarn.lock . +RUN yarn install --development + +RUN apk update +RUN rm -rf /var/cache/apk/* && \ + rm -rf /tmp/* +RUN apk del .build-deps + +COPY *.js ./ +COPY lib/*.js lib/ +COPY test/ test/ +COPY LICENSE . +EXPOSE 3401 +VOLUME /var/lib/db/ +ENTRYPOINT ["npm" , "run", "test"] + diff --git a/README.md b/README.md index 4737a39..8ca3bc1 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,48 @@ If you are self-hosting QuickChart, each QuickChart instance should use a single This self-hosted QuickChart implementation currently supports the `/chart`, `/qr`, and `/graphviz` endpoints. Other endpoints such as `/wordcloud`, `watermark`, `/chart/create` are not available in this version due to non-OSS 3rd-party dependencies. +**Note:** This release adds `/chart/create/` and `/chart/render/` functionality. For data storage, sqlite db is used. +It supports functionality as described [here](https://quickchart.io/documentation/usage/short-urls-and-templates/#short-urls) and [here](https://quickchart.io/documentation/usage/short-urls-and-templates/#templates) + +Example body for `/chart/create/`: + +```json +{ + "chart": { + "options": { + "title": { + "display": true, + "text": "Chart Title" + } + }, + "type": "bar", + "data": { + "labels": [ + "A", + "B" + ], + "datasets": [ + { + "data": [ + 10, + 20 + ] + } + ] + } + }, + "neverExpire": true +} +``` + +The **neverExpire** parameter allows you to control the expiration time of the saved chart. +If **"neverExpire": true**, then the chart has no storage time restrictions, otherwise, if this parameter is not specified or is false, then the expiraton time will be set to 6 months. + +To run it with docker: +```shell +docker build -t quickchart . +docker run -p 3400:3400 -v /path/to/db/folder/:/var/lib/db/ quickchart +``` ## License QuickChart is open source, licensed under version 3 of the GNU AGPL. If you would like to modify this project for commercial purposes (and not release the source code), please [contact me](https://www.ianww.com/). diff --git a/index.js b/index.js index 3e9d8b0..cf6f695 100644 --- a/index.js +++ b/index.js @@ -15,11 +15,13 @@ const { renderGraphviz } = require('./lib/graphviz'); const { toChartJs, parseSize } = require('./lib/google_image_charts'); const { renderQr, DEFAULT_QR_SIZE } = require('./lib/qr'); +const db = require('./lib/db'); + const app = express(); const isDev = app.get('env') === 'development' || app.get('env') === 'test'; -app.set('query parser', (str) => +app.set('query parser', str => qs.parse(str, { decode(s) { // Default express implementation replaces '+' with space. We don't want @@ -46,10 +48,10 @@ if (process.env.RATE_LIMIT_PER_MIN) { max: limitMax, message: 'Please slow down your requests! This is a shared public endpoint. Email support@quickchart.io or go to https://quickchart.io/pricing/ for rate limit exceptions or to purchase a commercial license.', - onLimitReached: (req) => { + onLimitReached: req => { logger.info('User hit rate limit!', req.ip); }, - keyGenerator: (req) => { + keyGenerator: req => { return req.headers['x-forwarded-for'] || req.ip; }, }); @@ -82,7 +84,7 @@ function utf8ToAscii(str) { const u8s = enc.encode(str); return Array.from(u8s) - .map((v) => String.fromCharCode(v)) + .map(v => String.fromCharCode(v)) .join(''); } @@ -136,7 +138,7 @@ async function failPdf(res, msg) { function renderChartToPng(req, res, opts) { opts.failFn = failPng; - opts.onRenderHandler = (buf) => { + opts.onRenderHandler = buf => { res .type('image/png') .set({ @@ -151,7 +153,7 @@ function renderChartToPng(req, res, opts) { function renderChartToSvg(req, res, opts) { opts.failFn = failSvg; - opts.onRenderHandler = (buf) => { + opts.onRenderHandler = buf => { res .type('image/svg+xml') .set({ @@ -166,7 +168,7 @@ function renderChartToSvg(req, res, opts) { async function renderChartToPdf(req, res, opts) { opts.failFn = failPdf; - opts.onRenderHandler = async (buf) => { + opts.onRenderHandler = async buf => { const pdfBuf = await getPdfBufferFromPng(buf); res.writeHead(200, { @@ -212,7 +214,7 @@ function doChartjsRender(req, res, opts) { untrustedInput, ) .then(opts.onRenderHandler) - .catch((err) => { + .catch(err => { logger.warn('Chart error', err); opts.failFn(res, err); }); @@ -267,7 +269,7 @@ function handleGChart(req, res) { const format = 'png'; const encoding = 'UTF-8'; renderQr(format, encoding, qrData, qrOpts) - .then((buf) => { + .then(buf => { res.writeHead(200, { 'Content-Type': format === 'png' ? 'image/png' : 'image/svg+xml', 'Content-Length': buf.length, @@ -277,7 +279,7 @@ function handleGChart(req, res) { }); res.end(buf); }) - .catch((err) => { + .catch(err => { failPng(res, err); }); @@ -311,7 +313,7 @@ function handleGChart(req, res) { '2.9.4' /* version */, undefined /* format */, converted.chart, - ).then((buf) => { + ).then(buf => { res.writeHead(200, { 'Content-Type': 'image/png', 'Content-Length': buf.length, @@ -412,7 +414,7 @@ app.get('/qr', (req, res) => { }; renderQr(format, mode, qrText, qrOpts) - .then((buf) => { + .then(buf => { res.writeHead(200, { 'Content-Type': format === 'png' ? 'image/png' : 'image/svg+xml', 'Content-Length': buf.length, @@ -422,7 +424,7 @@ app.get('/qr', (req, res) => { }); res.end(buf); }) - .catch((err) => { + .catch(err => { failPng(res, err); }); @@ -454,6 +456,116 @@ app.get('/healthcheck/chart', (req, res) => { res.redirect(`/chart?c=${template}`); }); +app.post('/chart/create', (req, res) => { + const { neverExpire = false } = req.body; + const outputFormat = (req.body.f || req.body.format || 'png').toLowerCase(); + const config = { + chart: req.body.c || req.body.chart, + height: req.body.h || req.body.height, + width: req.body.w || req.body.width, + backgroundColor: req.body.backgroundColor || req.body.bkg, + devicePixelRatio: req.body.devicePixelRatio, + version: req.body.v || req.body.version, + encoding: req.body.encoding || 'url', + format: outputFormat, + }; + + if (!config.chart) { + return res.status(400).json({ error: 'Chart config is required' }); + } + + const id = crypto.randomUUID(); + const expiresAt = neverExpire + ? null + : new Date(Date.now() + 6 * 30 * 24 * 60 * 60 * 1000).toISOString(); + const configStr = JSON.stringify(config); + db.run( + 'INSERT INTO charts (id, config, expires_at) VALUES (?, ?, ?)', + [id, configStr, expiresAt], + err => { + if (err) { + return res.status(500).json({ error: 'Failed to store chart' }); + } + res.json({ success: true, url: `${req.protocol}://${req.get('host')}/chart/render/${id}` }); + }, + ); +}); + +function applyTemplateOverrides(chartConfig, params) { + if (params.title) { + chartConfig.chart.options = chartConfig.chart.options || {}; + chartConfig.chart.options.title = chartConfig.chart.options.title || {}; + chartConfig.chart.options.title.text = params.title; + chartConfig.chart.options.title.display = true; + } + + if (params.labels) { + chartConfig.chart.data.labels = params.labels.split(','); + } + + Object.keys(params).forEach(paramKey => { + const dataMatch = paramKey.match(/^data(\d+)$/); + if (dataMatch) { + const index = parseInt(dataMatch[1], 10) - 1; + if (chartConfig.chart.data.datasets[index]) { + chartConfig.chart.data.datasets[index].data = params[paramKey].split(',').map(Number); + } + } + const backgroundColorMatch = paramKey.match(/^backgroundColor(\d+)$/); + if (backgroundColorMatch) { + const index = parseInt(backgroundColorMatch[1], 10) - 1; + if (chartConfig.chart.data.datasets[index]) { + chartConfig.chart.data.datasets[index].backgroundColor = params[paramKey] + .split(',') + .map(Number); + } + } + const borderColorMatch = paramKey.match(/^borderColor(\d+)$/); + if (borderColorMatch) { + const index = parseInt(borderColorMatch[1], 10) - 1; + if (chartConfig.chart.data.datasets[index]) { + chartConfig.chart.data.datasets[index].borderColor = params[paramKey] + .split(',') + .map(Number); + } + } + }); + return chartConfig; +} + +app.get('/chart/render/:key', async (req, res) => { + const { key } = req.params; + + db.get('SELECT config FROM charts WHERE id = ?', [key], function(err, row) { + if (err) { + res.status(500).json({ error: err.message }); + } + + if (!row) { + return res.status(404).json({ error: 'Template not found' }); + } + //return res.status(200).json({status: 'success'}); + let chartConfig = JSON.parse(row.config); + chartConfig = applyTemplateOverrides(chartConfig, req.query); + if (chartConfig.format === 'pdf') { + renderChartToPdf(req, res, chartConfig); + } else if (chartConfig.format === 'svg') { + renderChartToSvg(req, res, chartConfig); + } else if (!chartConfig.format || chartConfig.format === 'png') { + renderChartToPng(req, res, chartConfig); + } else { + logger.error(`Request for unsupported format ${outputFormat}`); + res.status(500).end(`Unsupported format ${outputFormat}`); + } + + telemetry.count('chartCount'); + }); +}); + +setInterval(() => { + db.run("DELETE FROM charts WHERE expires_at IS NOT NULL AND expires_at < datetime('now')"); +}, 24 * 60 * 60 * 1000); + const port = process.env.PORT || 3400; const server = app.listen(port); diff --git a/lib/db.js b/lib/db.js new file mode 100644 index 0000000..d840a5f --- /dev/null +++ b/lib/db.js @@ -0,0 +1,20 @@ +const sqlite3 = require('sqlite3').verbose(); + +const db = new sqlite3.Database('/var/lib/db/charts.db', err => { + if (err) { + console.error('Error connecting to database:', err.message); + } else { + console.log('Connected to SQLite database.'); + } +}); + +db.run(` + CREATE TABLE IF NOT EXISTS charts ( + id TEXT PRIMARY KEY, + config TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + expires_at TIMESTAMP + ) +`); + +module.exports = db; diff --git a/package.json b/package.json index 8dea377..62e0ab1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "quickchart", - "version": "1.8.1", + "version": "1.9.0", "main": "index.js", "license": "AGPL-3.0", "homepage": "https://quickchart.io/", @@ -11,22 +11,43 @@ "scripts": { "start": "node --max-http-header-size=65536 index.js", "format": "prettier --write \"**/*.js\"", - "test": "PORT=3401 NODE_ENV=test mocha --exit --recursive test/ci/", + "test": "PORT=3401 NODE_ENV=test mocha --exit --recursive test/ci", + "test-chart-create": "PORT=3401 NODE_ENV=test mocha --exit test/**/*.test.js", "test:watch": "PORT=2998 NODE_ENV=test chokidar '**/*.js' --initial --ignore node_modules -c 'mocha --exit --recursive test/'" }, "overrides": { - "canvas": "2.9.3" + "canvas": "2.11.2", + "json-schema": "^0.4.0", + "flat": "^5.0.1", + "crypto-js": "^4.2.0", + "cross-spawn": "^6.0.6", + "axios": "^1.8.2", + "minimatch": "^3.0.5", + "ws": "^7.5.10", + "nth-check": "^2.0.1", + "body-parser": "^1.20.3", + "path-to-regexp": "^0.1.12" }, "resolutions": { - "canvas": "2.9.3" + "canvas": "2.11.2", + "json-schema": "^0.4.0", + "flat": "^5.0.1", + "crypto-js": "^4.2.0", + "cross-spawn": "^6.0.6", + "axios": "^1.8.2", + "minimatch": "^3.0.5", + "ws": "^7.5.10", + "nth-check": "^2.0.1", + "body-parser": "^1.20.3", + "path-to-regexp": "^0.1.12" }, "dependencies": { "bunyan": "^1.8.12", - "canvas": "2.9.3", + "canvas": "2.11.2", "canvas-5-polyfill": "^0.1.5", "chart.js": "^2.9.4", "chart.js-v3": "npm:chart.js@3.9.1", - "chart.js-v4": "npm:chart.js@4.0.1", + "chart.js-v4": "npm:chart.js@4.3.3", "chartjs-adapter-moment": "https://github.com/typpo/chartjs-adapter-moment.git#e9bc92ab6e0e500c91c4a9871db7b14d15b5c2e7", "chartjs-chart-box-and-violin-plot": "^2.4.0", "chartjs-chart-radial-gauge": "^1.0.3", @@ -41,12 +62,14 @@ "express-rate-limit": "^5.0.0", "get-image-colors": "^4.0.1", "javascript-stringify": "^2.0.0", + "moment": "^2.29.4", "node-fetch": "^2.6.7", "patternomaly": "^1.3.2", "pdfkit": "^0.10.0", "qrcode": "^1.3.3", "qs": "^6.7.0", "sharp": "^0.32.6", + "sqlite3": "^5.1.7", "text2png": "^2.1.0", "viz.js": "^2.1.2" }, diff --git a/template_feature.patch b/template_feature.patch new file mode 100644 index 0000000..4a65e53 --- /dev/null +++ b/template_feature.patch @@ -0,0 +1,490 @@ +Subject: [PATCH] template feature +--- +Index: Dockerfile +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/Dockerfile b/Dockerfile +--- a/Dockerfile (revision 9c31a0bf89ad0e0ed534f90907658e4b03dab044) ++++ b/Dockerfile (revision 6a9e5fb3a7eacf880277526915987847fa300a68) +@@ -12,6 +12,7 @@ + RUN apk add --no-cache libimagequant-dev + RUN apk add --no-cache vips-dev + RUN apk add --no-cache --virtual .runtime-deps graphviz ++RUN apk add --no-cache sqlite + + COPY package*.json . + COPY yarn.lock . +@@ -25,7 +26,7 @@ + COPY *.js ./ + COPY lib/*.js lib/ + COPY LICENSE . +- ++VOLUME /var/lib/db/ + EXPOSE 3400 + +-ENTRYPOINT ["node", "--max-http-header-size=65536", "index.js"] ++ENTRYPOINT ["node", "--max-http-header-size=65536", "--experimental-global-webcrypto", "index.js"] +Index: Dockerfile.test +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/Dockerfile.test b/Dockerfile.test +new file mode 100644 +--- /dev/null (revision 6a9e5fb3a7eacf880277526915987847fa300a68) ++++ b/Dockerfile.test (revision 6a9e5fb3a7eacf880277526915987847fa300a68) +@@ -0,0 +1,34 @@ ++FROM node:18-alpine3.17 ++ ++ENV NODE_ENV test ++ENV NODE_OPTIONS --experimental-global-webcrypto ++WORKDIR /quickchart ++ ++RUN apk add --upgrade apk-tools ++RUN apk add --no-cache --virtual .build-deps yarn git build-base g++ python3 ++RUN apk add --no-cache --virtual .npm-deps cairo-dev pango-dev libjpeg-turbo-dev librsvg-dev ++RUN apk add --no-cache --virtual .fonts libmount ttf-dejavu ttf-droid ttf-freefont ttf-liberation font-noto font-noto-emoji fontconfig ++RUN apk add --no-cache --repository https://dl-cdn.alpinelinux.org/alpine/edge/community font-wqy-zenhei ++RUN apk add --no-cache libimagequant-dev ++RUN apk add --no-cache vips-dev ++RUN apk add --no-cache --virtual .runtime-deps graphviz ++RUN apk add --no-cache sqlite ++RUN npm install -g mocha ++ ++COPY package*.json . ++COPY yarn.lock . ++RUN yarn install --development ++ ++RUN apk update ++RUN rm -rf /var/cache/apk/* && \ ++ rm -rf /tmp/* ++RUN apk del .build-deps ++ ++COPY *.js ./ ++COPY lib/*.js lib/ ++COPY test/ test/ ++COPY LICENSE . ++EXPOSE 3401 ++VOLUME /var/lib/db/ ++ENTRYPOINT ["npm" , "run", "test"] ++ +Index: README.md +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/README.md b/README.md +--- a/README.md (revision 9c31a0bf89ad0e0ed534f90907658e4b03dab044) ++++ b/README.md (revision 6a9e5fb3a7eacf880277526915987847fa300a68) +@@ -140,6 +140,42 @@ + + This self-hosted QuickChart implementation currently supports the `/chart`, `/qr`, and `/graphviz` endpoints. Other endpoints such as `/wordcloud`, `watermark`, `/chart/create` are not available in this version due to non-OSS 3rd-party dependencies. + ++**Note:** This release adds `/chart/create/` and `/chart/render/` functionality. For data storage, sqlite db is used. ++It supports functionality as described [here](https://quickchart.io/documentation/usage/short-urls-and-templates/#short-urls) and [here](https://quickchart.io/documentation/usage/short-urls-and-templates/#templates) ++Example body for `/chart/create/`: ++ ++```json ++{ ++ "chart": { ++ "options": { ++ "title": { ++ "display": true, ++ "text": "Chart Title" ++ } ++ }, ++ "type": "bar", ++ "data": { ++ "labels": [ ++ "A", ++ "B" ++ ], ++ "datasets": [ ++ { ++ "data": [ ++ 10, ++ 20 ++ ] ++ } ++ ] ++ } ++ }, ++ "neverExpire": true ++} ++``` ++ ++The **neverExpire** parameter allows you to control the expiration time of the saved chart. ++If **"neverExpire": true**, then the chart has no storage time restrictions, otherwise, if this parameter is not specified or is false, then the expiraton time will be set to 6 months. ++ + ## License + + QuickChart is open source, licensed under version 3 of the GNU AGPL. If you would like to modify this project for commercial purposes (and not release the source code), please [contact me](https://www.ianww.com/). +Index: index.js +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/index.js b/index.js +--- a/index.js (revision 9c31a0bf89ad0e0ed534f90907658e4b03dab044) ++++ b/index.js (revision 6a9e5fb3a7eacf880277526915987847fa300a68) +@@ -15,11 +15,13 @@ + const { toChartJs, parseSize } = require('./lib/google_image_charts'); + const { renderQr, DEFAULT_QR_SIZE } = require('./lib/qr'); + ++const db = require('./lib/db'); ++ + const app = express(); + + const isDev = app.get('env') === 'development' || app.get('env') === 'test'; + +-app.set('query parser', (str) => ++app.set('query parser', str => + qs.parse(str, { + decode(s) { + // Default express implementation replaces '+' with space. We don't want +@@ -46,10 +48,10 @@ + max: limitMax, + message: + 'Please slow down your requests! This is a shared public endpoint. Email support@quickchart.io or go to https://quickchart.io/pricing/ for rate limit exceptions or to purchase a commercial license.', +- onLimitReached: (req) => { ++ onLimitReached: req => { + logger.info('User hit rate limit!', req.ip); + }, +- keyGenerator: (req) => { ++ keyGenerator: req => { + return req.headers['x-forwarded-for'] || req.ip; + }, + }); +@@ -82,7 +84,7 @@ + const u8s = enc.encode(str); + + return Array.from(u8s) +- .map((v) => String.fromCharCode(v)) ++ .map(v => String.fromCharCode(v)) + .join(''); + } + +@@ -136,7 +138,7 @@ + + function renderChartToPng(req, res, opts) { + opts.failFn = failPng; +- opts.onRenderHandler = (buf) => { ++ opts.onRenderHandler = buf => { + res + .type('image/png') + .set({ +@@ -151,7 +153,7 @@ + + function renderChartToSvg(req, res, opts) { + opts.failFn = failSvg; +- opts.onRenderHandler = (buf) => { ++ opts.onRenderHandler = buf => { + res + .type('image/svg+xml') + .set({ +@@ -166,7 +168,7 @@ + + async function renderChartToPdf(req, res, opts) { + opts.failFn = failPdf; +- opts.onRenderHandler = async (buf) => { ++ opts.onRenderHandler = async buf => { + const pdfBuf = await getPdfBufferFromPng(buf); + + res.writeHead(200, { +@@ -212,7 +214,7 @@ + untrustedInput, + ) + .then(opts.onRenderHandler) +- .catch((err) => { ++ .catch(err => { + logger.warn('Chart error', err); + opts.failFn(res, err); + }); +@@ -267,7 +269,7 @@ + const format = 'png'; + const encoding = 'UTF-8'; + renderQr(format, encoding, qrData, qrOpts) +- .then((buf) => { ++ .then(buf => { + res.writeHead(200, { + 'Content-Type': format === 'png' ? 'image/png' : 'image/svg+xml', + 'Content-Length': buf.length, +@@ -277,7 +279,7 @@ + }); + res.end(buf); + }) +- .catch((err) => { ++ .catch(err => { + failPng(res, err); + }); + +@@ -311,7 +313,7 @@ + '2.9.4' /* version */, + undefined /* format */, + converted.chart, +- ).then((buf) => { ++ ).then(buf => { + res.writeHead(200, { + 'Content-Type': 'image/png', + 'Content-Length': buf.length, +@@ -412,7 +414,7 @@ + }; + + renderQr(format, mode, qrText, qrOpts) +- .then((buf) => { ++ .then(buf => { + res.writeHead(200, { + 'Content-Type': format === 'png' ? 'image/png' : 'image/svg+xml', + 'Content-Length': buf.length, +@@ -422,7 +424,7 @@ + }); + res.end(buf); + }) +- .catch((err) => { ++ .catch(err => { + failPng(res, err); + }); + +@@ -454,6 +456,116 @@ + res.redirect(`/chart?c=${template}`); + }); + ++app.post('/chart/create', (req, res) => { ++ const { neverExpire = false } = req.body; ++ const outputFormat = (req.body.f || req.body.format || 'png').toLowerCase(); ++ const config = { ++ chart: req.body.c || req.body.chart, ++ height: req.body.h || req.body.height, ++ width: req.body.w || req.body.width, ++ backgroundColor: req.body.backgroundColor || req.body.bkg, ++ devicePixelRatio: req.body.devicePixelRatio, ++ version: req.body.v || req.body.version, ++ encoding: req.body.encoding || 'url', ++ format: outputFormat, ++ }; ++ ++ if (!config.chart) { ++ return res.status(400).json({ error: 'Chart config is required' }); ++ } ++ ++ const id = crypto.randomUUID(); ++ const expiresAt = neverExpire ++ ? null ++ : new Date(Date.now() + 6 * 30 * 24 * 60 * 60 * 1000).toISOString(); ++ const configStr = JSON.stringify(config); ++ db.run( ++ 'INSERT INTO charts (id, config, expires_at) VALUES (?, ?, ?)', ++ [id, configStr, expiresAt], ++ err => { ++ if (err) { ++ return res.status(500).json({ error: 'Failed to store chart' }); ++ } ++ res.json({ success: true, url: `${req.protocol}://${req.get('host')}/chart/render/${id}` }); ++ }, ++ ); ++}); ++ ++function applyTemplateOverrides(chartConfig, params) { ++ if (params.title) { ++ chartConfig.chart.options = chartConfig.chart.options || {}; ++ chartConfig.chart.options.title = chartConfig.chart.options.title || {}; ++ chartConfig.chart.options.title.text = params.title; ++ chartConfig.chart.options.title.display = true; ++ } ++ ++ if (params.labels) { ++ chartConfig.chart.data.labels = params.labels.split(','); ++ } ++ ++ Object.keys(params).forEach(paramKey => { ++ const dataMatch = paramKey.match(/^data(\d+)$/); ++ if (dataMatch) { ++ const index = parseInt(dataMatch[1], 10) - 1; ++ if (chartConfig.chart.data.datasets[index]) { ++ chartConfig.chart.data.datasets[index].data = params[paramKey].split(',').map(Number); ++ } ++ } ++ const backgroundColorMatch = paramKey.match(/^backgroundColor(\d+)$/); ++ if (backgroundColorMatch) { ++ const index = parseInt(backgroundColorMatch[1], 10) - 1; ++ if (chartConfig.chart.data.datasets[index]) { ++ chartConfig.chart.data.datasets[index].backgroundColor = params[paramKey] ++ .split(',') ++ .map(Number); ++ } ++ } ++ const borderColorMatch = paramKey.match(/^borderColor(\d+)$/); ++ if (borderColorMatch) { ++ const index = parseInt(borderColorMatch[1], 10) - 1; ++ if (chartConfig.chart.data.datasets[index]) { ++ chartConfig.chart.data.datasets[index].borderColor = params[paramKey] ++ .split(',') ++ .map(Number); ++ } ++ } ++ }); ++ return chartConfig; ++} ++ ++app.get('/chart/render/:key', async (req, res) => { ++ const { key } = req.params; ++ ++ db.get('SELECT config FROM charts WHERE id = ?', [key], function(err, row) { ++ if (err) { ++ res.status(500).json({ error: err.message }); ++ } ++ ++ if (!row) { ++ return res.status(404).json({ error: 'Template not found' }); ++ } ++ //return res.status(200).json({status: 'success'}); ++ let chartConfig = JSON.parse(row.config); ++ chartConfig = applyTemplateOverrides(chartConfig, req.query); ++ if (chartConfig.format === 'pdf') { ++ renderChartToPdf(req, res, chartConfig); ++ } else if (chartConfig.format === 'svg') { ++ renderChartToSvg(req, res, chartConfig); ++ } else if (!chartConfig.format || chartConfig.format === 'png') { ++ renderChartToPng(req, res, chartConfig); ++ } else { ++ logger.error(`Request for unsupported format ${outputFormat}`); ++ res.status(500).end(`Unsupported format ${outputFormat}`); ++ } ++ ++ telemetry.count('chartCount'); ++ }); ++}); ++ ++setInterval(() => { ++ db.run("DELETE FROM charts WHERE expires_at IS NOT NULL AND expires_at < datetime('now')"); ++}, 24 * 60 * 60 * 1000); ++ + const port = process.env.PORT || 3400; + const server = app.listen(port); + +Index: lib/db.js +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/lib/db.js b/lib/db.js +new file mode 100644 +--- /dev/null (revision 6a9e5fb3a7eacf880277526915987847fa300a68) ++++ b/lib/db.js (revision 6a9e5fb3a7eacf880277526915987847fa300a68) +@@ -0,0 +1,20 @@ ++const sqlite3 = require('sqlite3').verbose(); ++ ++const db = new sqlite3.Database('/var/lib/db/charts.db', err => { ++ if (err) { ++ console.error('Error connecting to database:', err.message); ++ } else { ++ console.log('Connected to SQLite database.'); ++ } ++}); ++ ++db.run(` ++ CREATE TABLE IF NOT EXISTS charts ( ++ id TEXT PRIMARY KEY, ++ config TEXT NOT NULL, ++ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, ++ expires_at TIMESTAMP ++ ) ++`); ++ ++module.exports = db; +Index: package.json +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/package.json b/package.json +--- a/package.json (revision 9c31a0bf89ad0e0ed534f90907658e4b03dab044) ++++ b/package.json (revision 6a9e5fb3a7eacf880277526915987847fa300a68) +@@ -11,7 +11,8 @@ + "scripts": { + "start": "node --max-http-header-size=65536 index.js", + "format": "prettier --write \"**/*.js\"", +- "test": "PORT=3401 NODE_ENV=test mocha --exit --recursive test/ci/", ++ "test": "PORT=3401 NODE_ENV=test mocha --exit --recursive test/ci", ++ "test-chart-create": "PORT=3401 NODE_ENV=test mocha --exit test/**/*.test.js", + "test:watch": "PORT=2998 NODE_ENV=test chokidar '**/*.js' --initial --ignore node_modules -c 'mocha --exit --recursive test/'" + }, + "overrides": { +@@ -47,6 +48,7 @@ + "qrcode": "^1.3.3", + "qs": "^6.7.0", + "sharp": "^0.32.6", ++ "sqlite3": "^5.1.7", + "text2png": "^2.1.0", + "viz.js": "^2.1.2" + }, +Index: test/ci/chart-create.test.js +IDEA additional info: +Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP +<+>UTF-8 +=================================================================== +diff --git a/test/ci/chart-create.test.js b/test/ci/chart-create.test.js +new file mode 100644 +--- /dev/null (revision 6a9e5fb3a7eacf880277526915987847fa300a68) ++++ b/test/ci/chart-create.test.js (revision 6a9e5fb3a7eacf880277526915987847fa300a68) +@@ -0,0 +1,58 @@ ++const request = require('supertest'); ++const assert = require('assert'); ++const db = require('../../lib/db'); ++const app = require('../../index'); ++describe('Chart API Tests', function() { ++ this.timeout(6000); ++ let chartId; ++ ++ it('should create a new chart', function(done) { ++ request(app) ++ .post('/chart/create') ++ .send({ ++ chart: { ++ options: { title: { display: true, text: 'Chart Title' } }, ++ type: 'bar', ++ data: { ++ labels: ['A', 'B'], ++ datasets: [{ data: [10, 20] }], ++ }, ++ }, ++ neverExpire: true, ++ }) ++ .expect(200) ++ .end((err, res) => { ++ if (err) { ++ console.error(err); ++ return done(err); ++ } ++ ++ assert.strictEqual(res.body.success, true); ++ chartId = res.body.url.split('/').pop(); // Витягуємо ID графіка ++ done(); ++ }); ++ }); ++ ++ it('should retrieve the created chart', function(done) { ++ request(app) ++ .get(`/chart/render/${chartId}`) ++ .expect(200, done); ++ }); ++ ++ it('should return 404 for non-existent chart', function(done) { ++ request(app) ++ .get('/chart/render/nonexistent-id') ++ .expect(404, done); ++ }); ++ ++ it('should apply template overrides', function(done) { ++ request(app) ++ .get(`/chart/render/${chartId}?title=TestTitle&labels=X,Y&data1=30,40`) ++ .expect(200, done); ++ }); ++ ++ after(function(done) { ++ db.run('DELETE FROM charts WHERE id = ?', [chartId]); ++ db.close(done); ++ }); ++}); diff --git a/test/ci/chart-create.test.js b/test/ci/chart-create.test.js new file mode 100644 index 0000000..83fa27d --- /dev/null +++ b/test/ci/chart-create.test.js @@ -0,0 +1,58 @@ +const request = require('supertest'); +const assert = require('assert'); +const db = require('../../lib/db'); +const app = require('../../index'); +describe('Chart API Tests', function() { + this.timeout(6000); + let chartId; + + it('should create a new chart', function(done) { + request(app) + .post('/chart/create') + .send({ + chart: { + options: { title: { display: true, text: 'Chart Title' } }, + type: 'bar', + data: { + labels: ['A', 'B'], + datasets: [{ data: [10, 20] }], + }, + }, + neverExpire: true, + }) + .expect(200) + .end((err, res) => { + if (err) { + console.error(err); + return done(err); + } + + assert.strictEqual(res.body.success, true); + chartId = res.body.url.split('/').pop(); // Витягуємо ID графіка + done(); + }); + }); + + it('should retrieve the created chart', function(done) { + request(app) + .get(`/chart/render/${chartId}`) + .expect(200, done); + }); + + it('should return 404 for non-existent chart', function(done) { + request(app) + .get('/chart/render/nonexistent-id') + .expect(404, done); + }); + + it('should apply template overrides', function(done) { + request(app) + .get(`/chart/render/${chartId}?title=TestTitle&labels=X,Y&data1=30,40`) + .expect(200, done); + }); + + after(function(done) { + db.run('DELETE FROM charts WHERE id = ?', [chartId]); + db.close(done); + }); +}); diff --git a/test/ci/charts.js b/test/ci/charts.js index bfa36c7..221b864 100644 --- a/test/ci/charts.js +++ b/test/ci/charts.js @@ -163,8 +163,11 @@ describe('charts.js', () => { charts.BASIC_CHART, ); - assert( - buf.toString().includes('= 1.0.0": version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" @@ -2914,6 +3148,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" @@ -2921,6 +3160,13 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -2933,6 +3179,13 @@ has@^1.0.1, has@^1.0.3: dependencies: function-bind "^1.1.1" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -2965,7 +3218,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-cache-semantics@^4.0.0: +http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -2981,6 +3234,15 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -3020,6 +3282,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -3060,11 +3329,21 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -3116,6 +3395,14 @@ iota-array@^1.0.0: resolved "https://registry.yarnpkg.com/iota-array/-/iota-array-1.0.0.tgz#81ef57fe5d05814cd58c2483632a99c30a0e8087" integrity sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" @@ -3161,11 +3448,6 @@ is-buffer@^1.0.2, is-buffer@~1.1.6: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" - integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== - is-callable@^1.1.4, is-callable@^1.2.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" @@ -3234,6 +3516,11 @@ is-installed-globally@^0.1.0: global-dirs "^0.1.0" is-path-inside "^1.0.0" +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== + is-negative-zero@^2.0.1, is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -3421,6 +3708,11 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -3441,10 +3733,10 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@0.2.3, json-schema@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" @@ -3649,14 +3941,6 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3700,6 +3984,33 @@ make-dir@^3.1.0: dependencies: semver "^6.0.0" +make-fetch-happen@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^6.0.0" + ssri "^8.0.0" + +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + md5@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" @@ -3792,20 +4103,13 @@ min-document@^2.19.0: dependencies: dom-walk "^0.1.0" -"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.2: +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - minimist@^1.1.3: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -3816,6 +4120,45 @@ minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-fetch@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + minipass@^3.0.0: version "3.3.4" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.4.tgz#ca99f95dd77c43c7a76bf51e6d200025eee0ffae" @@ -3823,7 +4166,19 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -minizlib@^2.1.1: +minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + +minizlib@^2.0.0, minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== @@ -3850,7 +4205,7 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.5" -mkdirp@^1.0.3: +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -3896,6 +4251,11 @@ moment@^2.10.2, moment@^2.19.3, moment@^2.22.1: resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +moment@^2.29.4: + version "2.30.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" + integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -3930,11 +4290,16 @@ mv@~2: ncp "~2.0.0" rimraf "~2.4.0" -nan@^2.14.0, nan@^2.15.0: +nan@^2.14.0: version "2.17.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== +nan@^2.17.0: + version "2.22.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.2.tgz#6b504fd029fb8f38c0990e52ad5c26772fdacfbb" + integrity sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ== + nanotimer@0.3.14: version "0.3.14" resolved "https://registry.yarnpkg.com/nanotimer/-/nanotimer-0.3.14.tgz#10d811f8d064788180096ce1f96c70846fd5a2ba" @@ -3981,6 +4346,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@^0.6.2: + version "0.6.4" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + next-tick@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" @@ -4003,6 +4373,11 @@ node-addon-api@^6.1.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" integrity "sha1-rIRwA05Y5n0MbxIEoYrmmV2cDXY= sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==" +node-addon-api@^7.0.0: + version "7.1.1" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558" + integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ== + node-bitmap@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/node-bitmap/-/node-bitmap-0.0.1.tgz#180eac7003e0c707618ef31368f62f84b2a69091" @@ -4023,6 +4398,22 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" +node-gyp@8.x: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^9.1.0" + nopt "^5.0.0" + npmlog "^6.0.0" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.2" + which "^2.0.2" + nopt@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" @@ -4057,19 +4448,22 @@ npmlog@^5.0.1: gauge "^3.0.0" set-blocking "^2.0.0" -nth-check@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.1.tgz#2efe162f5c3da06a28959fbd3db75dbeea9f0fc2" - integrity sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w== +npmlog@^6.0.0: + version "6.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" + integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== dependencies: - boolbase "^1.0.0" + are-we-there-yet "^3.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.3" + set-blocking "^2.0.0" -nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== +nth-check@^2.0.0, nth-check@^2.0.1, nth-check@~1.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== dependencies: - boolbase "~1.0.0" + boolbase "^1.0.0" oauth-sign@~0.9.0: version "0.9.0" @@ -4086,6 +4480,11 @@ object-inspect@^1.11.0, object-inspect@^1.12.2, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== +object-inspect@^1.13.3: + version "1.13.4" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213" + integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew== + object-inspect@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" @@ -4246,6 +4645,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -4365,10 +4771,10 @@ path-parse@^1.0.6, path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@0.1.7, path-to-regexp@^0.1.12: + version "0.1.12" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" + integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ== patternomaly@^1.3.2: version "1.3.2" @@ -4511,6 +4917,19 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + proxy-addr@~2.0.7: version "2.0.7" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" @@ -4519,10 +4938,10 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== psl@^1.1.28: version "1.8.0" @@ -4564,6 +4983,13 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +qs@6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906" + integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg== + dependencies: + side-channel "^1.0.6" + qs@^6.5.1, qs@^6.7.0: version "6.11.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" @@ -4802,6 +5228,11 @@ restructure@^0.5.3: dependencies: browserify-optional "^1.0.0" +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -4859,7 +5290,7 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -4976,6 +5407,35 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +side-channel-list@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" + integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + +side-channel-map@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42" + integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + +side-channel-weakmap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea" + integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== + dependencies: + call-bound "^1.0.2" + es-errors "^1.3.0" + get-intrinsic "^1.2.5" + object-inspect "^1.13.3" + side-channel-map "^1.0.1" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -4985,7 +5445,18 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.0: +side-channel@^1.0.6: + version "1.1.0" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9" + integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== + dependencies: + es-errors "^1.3.0" + object-inspect "^1.13.3" + side-channel-list "^1.0.0" + side-channel-map "^1.0.1" + side-channel-weakmap "^1.0.2" + +signal-exit@^3.0.0, signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -5034,6 +5505,11 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +smart-buffer@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + socket.io-client@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.4.0.tgz#aafb5d594a3c55a34355562fc8aea22ed9119a35" @@ -5065,6 +5541,23 @@ socketio-wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/socketio-wildcard/-/socketio-wildcard-2.0.0.tgz#2466e832276b19163563bee772388747f912475b" integrity sha1-JGboMidrGRY1Y77ncjiHR/kSR1s= +socks-proxy-agent@^6.0.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" + integrity sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ== + dependencies: + agent-base "^6.0.2" + debug "^4.3.3" + socks "^2.6.2" + +socks@^2.6.2: + version "2.8.4" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.4.tgz#07109755cdd4da03269bda4725baa061ab56d5cc" + integrity sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ== + dependencies: + ip-address "^9.0.5" + smart-buffer "^4.2.0" + source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -5087,11 +5580,28 @@ sourcemap-codec@^1.4.1: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +sqlite3@^5.1.7: + version "5.1.7" + resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-5.1.7.tgz#59ca1053c1ab38647396586edad019b1551041b7" + integrity sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog== + dependencies: + bindings "^1.5.0" + node-addon-api "^7.0.0" + prebuild-install "^7.1.1" + tar "^6.1.11" + optionalDependencies: + node-gyp "8.x" + sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -5107,6 +5617,13 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +ssri@^8.0.0, ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + static-eval@2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/static-eval/-/static-eval-2.0.2.tgz#2d1759306b1befa688938454c546b7871f806a42" @@ -5389,6 +5906,18 @@ tar-stream@^3.1.5: fast-fifo "^1.2.0" streamx "^2.15.0" +tar@^6.0.2, tar@^6.1.2: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tar@^6.1.11: version "6.1.12" resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" @@ -5619,6 +6148,20 @@ uniq@^1.0.0: resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + unique-string@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a" @@ -5758,6 +6301,13 @@ which@1.3.1, which@^1.2.9: dependencies: isexe "^2.0.0" +which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -5765,7 +6315,7 @@ wide-align@1.1.3: dependencies: string-width "^1.0.2 || 2" -wide-align@^1.1.2: +wide-align@^1.1.2, wide-align@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== @@ -5823,17 +6373,10 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^5.1.1: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.4.tgz#c7bea9f1cfb5f410de50e70e82662e562113f9a7" - integrity sha512-fFCejsuC8f9kOSu9FYaOw8CdO68O3h5v0lg4p74o8JqWpwTf9tniOD+nOB78aWoVSS6WptVUmDrp/KPsMVBWFQ== - dependencies: - async-limiter "~1.0.0" - -ws@~7.4.2: - version "7.4.6" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" - integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@^5.1.1, ws@^7.5.10, ws@~7.4.2: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== xdg-basedir@^3.0.0: version "3.0.0" @@ -5883,11 +6426,6 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"